feat: 服务端集成自动打包流程

This commit is contained in:
shen
2026-07-17 22:34:04 +08:00
parent a2437a039d
commit d1f070b251
221 changed files with 7284 additions and 447 deletions
+36 -44
View File
@@ -12,7 +12,15 @@ from sqlalchemy.orm import Session
from ..database import get_db
from ..models import BuildConfig
from ..schemas import BuildConfigUpdate
from ..config import CONFIG_JSON_PATH, DEFAULT_MAX_CONCURRENT_BUILDS, DEFAULT_BUILD_DIR_RETENTION_HOURS, SKINS_DIR, AUTOPACKING_DIR
from ..config import (
BUILD_BASE_DIR,
BOOTSTRAP_CONFIG_PATH,
AUTOMATION_DIR,
CONFIG_JSON_PATH,
DEFAULT_MAX_CONCURRENT_BUILDS,
DEFAULT_BUILD_DIR_RETENTION_HOURS,
SKINS_DIR,
)
from ..deps import get_current_user
router = APIRouter(prefix="/api/config", tags=["config"], dependencies=[Depends(get_current_user)])
@@ -67,6 +75,11 @@ DEFAULT_SERVERS = {
}
}
DEFAULT_VERSIONS = {
"app_ver": "2.180.0",
"build_ver": "2.180.0.0",
}
def _ensure_upload_keys(config: dict) -> bool:
"""为没有 upload_key 的 app 自动生成,返回是否有变更"""
@@ -82,6 +95,7 @@ def _ensure_upload_keys(config: dict) -> bool:
def _migrate_old_themes(config: dict) -> bool:
"""将旧式目录皮肤迁移为 ZIP 存入 data/skins/,返回是否有变更"""
changed = False
themes_dir = AUTOMATION_DIR / "themes"
for app_id, app in config.get("apps", {}).items():
upload_key = app.get("upload_key", "")
if not upload_key:
@@ -93,9 +107,9 @@ def _migrate_old_themes(config: dict) -> bool:
# 已经是 ZIP 皮肤(upload_key 目录下有对应 ZIP),跳过
if (SKINS_DIR / upload_key / f"{theme}.zip").exists():
continue
# 在 AutoPacking 目录下查找皮肤目录
# 在服务自带的默认皮肤目录下查找旧主题
skin_name = Path(theme).name # "AutoPacking/ymh" → "ymh", "ymh" → "ymh"
theme_dir = AUTOPACKING_DIR / skin_name
theme_dir = themes_dir / skin_name
if not theme_dir.is_dir():
continue
app_skins_dir = SKINS_DIR / upload_key
@@ -122,15 +136,10 @@ def _migrate_old_themes(config: dict) -> bool:
def load_config() -> dict:
"""读取 config.json"""
if not CONFIG_JSON_PATH.exists():
# 迁移:从旧的 AutoPacking/config.json 迁移到持久化目录
old_config = AUTOPACKING_DIR / "config.json"
if old_config.exists():
# 首次部署:优先从项目根目录下的 config.json 导入
if BOOTSTRAP_CONFIG_PATH.exists():
CONFIG_JSON_PATH.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(old_config, CONFIG_JSON_PATH)
# 迁移旧的 skins 目录
old_skins = AUTOPACKING_DIR / "skins"
if old_skins.exists():
shutil.copytree(old_skins, SKINS_DIR, dirs_exist_ok=True)
shutil.copy2(BOOTSTRAP_CONFIG_PATH, CONFIG_JSON_PATH)
else:
return {"apps": {}, "schemes": {}, "servers": DEFAULT_SERVERS, "branches": ["main"], "upload": DEFAULT_UPLOAD}
with open(CONFIG_JSON_PATH, "r", encoding="utf-8") as f:
@@ -142,6 +151,8 @@ def load_config() -> dict:
config["branches"] = ["main"]
if "upload" not in config:
config["upload"] = DEFAULT_UPLOAD
if "versions" not in config:
config["versions"] = DEFAULT_VERSIONS.copy()
# 自动为缺少 upload_key 的 app 生成唯一标识
if _ensure_upload_keys(config):
save_config(config)
@@ -428,7 +439,7 @@ async def get_build_settings(db: Session = Depends(get_db)):
return {
"max_concurrent_builds": int(get_build_config_value(db, "max_concurrent_builds", DEFAULT_MAX_CONCURRENT_BUILDS)),
"build_dir_retention_hours": int(get_build_config_value(db, "build_dir_retention_hours", DEFAULT_BUILD_DIR_RETENTION_HOURS)),
"build_base_dir": get_build_config_value(db, "build_base_dir", "/Users/shen/Documents"),
"build_base_dir": get_build_config_value(db, "build_base_dir", str(BUILD_BASE_DIR)),
}
@@ -469,50 +480,31 @@ async def update_build_settings(settings: BuildConfigUpdate, db: Session = Depen
# 版本号配置 API
@router.get("/versions")
async def get_versions():
"""获取 App_Ver 和 Build_Ver"""
from ..config import AUTOPACKING_DIR
start_build_path = AUTOPACKING_DIR / "start_build_app.py"
app_ver = "2.180.0"
build_ver = "2.180.0.0"
if start_build_path.exists():
content = start_build_path.read_text(encoding="utf-8")
for line in content.split("\n"):
if line.startswith("App_Ver") and '"' in line:
app_ver = line.split('"')[1]
elif line.startswith("Build_Ver") and '"' in line:
build_ver = line.split('"')[1]
return {"app_ver": app_ver, "build_ver": build_ver}
"""获取服务端统一管理的 App_Ver 和 Build_Ver"""
config = load_config()
versions = config.get("versions", DEFAULT_VERSIONS.copy())
return {
"app_ver": versions.get("app_ver", DEFAULT_VERSIONS["app_ver"]),
"build_ver": versions.get("build_ver", DEFAULT_VERSIONS["build_ver"]),
}
@router.put("/versions")
async def update_versions(data: dict):
"""更新 App_Ver 和 Build_Ver"""
from ..config import AUTOPACKING_DIR
app_ver = data.get("app_ver", "").strip()
build_ver = data.get("build_ver", "").strip()
if not app_ver or not build_ver:
raise HTTPException(status_code=400, detail="版本号不能为空")
start_build_path = AUTOPACKING_DIR / "start_build_app.py"
if not start_build_path.exists():
raise HTTPException(status_code=404, detail="start_build_app.py 不存在")
config = load_config()
config["versions"] = {
"app_ver": app_ver,
"build_ver": build_ver,
}
async with _config_lock:
content = start_build_path.read_text(encoding="utf-8")
new_lines = []
for line in content.split("\n"):
if line.startswith("App_Ver") and '"' in line:
new_lines.append(f'App_Ver = "{app_ver}"')
elif line.startswith("Build_Ver") and '"' in line:
new_lines.append(f'Build_Ver = "{build_ver}"')
else:
new_lines.append(line)
start_build_path.write_text("\n".join(new_lines), encoding="utf-8")
save_config(config)
return {"message": "版本号已更新", "app_ver": app_ver, "build_ver": build_ver}