diff --git a/CLAUDE.md b/CLAUDE.md index 4c8a110..c174624 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,7 +26,7 @@ FastAPI + Vue 3 iOS app auto-packaging service. Supports Ad_Hoc and App_Store bu - `backend/config.py` — loads .env, defines all config constants - `frontend/src/views/` — BuildView, HistoryView, ConfigView - `config.json` — runtime config (apps, schemes, servers, upload, branches, versions) managed via web UI -- Versions are per-track: `release` (master/develop, build number auto-increments on App_Store) and `feature` (self-test, never increments); each branch's track lives in `config["branch_track"]` +- Versions are per-track: each release branch (master, develop, …) has its own track named after the branch (build number auto-increments on App_Store), and all self-test branches share the `feature` track (never increments); each branch's track name lives in `config["branch_track"]`, track records in `config["versions"]["tracks"]` carry `kind` (release/feature) ## Conventions diff --git a/README.md b/README.md index 20c78f0..365c33a 100644 --- a/README.md +++ b/README.md @@ -187,21 +187,24 @@ GIT_PASSWORD=<仅 read_repository 权限的 Personal Access Token> | App 管理 | 应用名称、Scheme、服务器环境、upload_key 等 | | Scheme 管理 | 打包 Scheme 配置 | | 服务器环境 | 测试/正式/自定义环境的 API 地址、Universal Link 等 | -| 分支管理 | 可打包的代码分支列表,以及每个分支所属的版本轨道 | +| 分支管理 | 可打包的代码分支列表,以及每个分支所属的版本轨道(上架分支各自独立) | | 版本号 | 服务端统一管理 App_Ver 和 Build_Ver,不修改分支源码;按版本轨道分开管理(见下) | | 上传配置 | 服务端内置 OSS / WebDAV 分发,生成 IPA、manifest、下载页和二维码 | ### 版本号与版本轨道 -版本号按 **轨道(track)** 分开管理,两条轨道的 App_Ver 各自独立填写、互不影响: +版本号按 **轨道(track)** 分开管理,各轨道的 App_Ver 各自独立填写、互不影响: | 轨道 | 适用分支 | 构建号行为 | |------|----------|-----------| -| `release`(上架) | master、develop(共用同一套版本号与构建号序列) | App_Store 打包时按当前 App_Ver 自动 +1 | -| `feature`(自测) | feature/* 等其他分支 | 永不自增,始终使用配置页填写的值(默认 `.0`) | +| 上架轨(轨道名即分支名,如 `master`、`develop`) | 每个上架分支各一条,版本号彼此独立 | App_Store 打包时按当前 App_Ver 自动 +1 | +| `feature`(自测) | feature/* 等其他分支,共用一条 | 永不自增,始终使用配置页填写的值(默认 `.0`) | -- 分支归属默认按名字推断(master/main/develop/dev/release-*/hotfix-* → release,其余 → feature),可在「分支管理」页逐个改。 +- 分支归属默认按名字推断(master/main/develop/dev/release-*/hotfix-* → 自己的上架轨,其余 → feature),可在「分支管理」页逐个改;改成「上架」即为该分支新建一条以分支名命名的轨道。 +- 「打包设置」页按上架分支分块填写版本号,最后一块是共用的自测版本号。 - 每条轨道内的构建号按 App_Ver 独立记录(`build_map`),回退到打过包的旧版本号时会从它自己的最大值继续,不会与 App Store Connect 上已传构建号冲突。 +- App Store 的构建号唯一性按 App_Ver 全局判定,因此两个上架分支若临时用了同一个 App_Ver,递增时会跨轨道取最大值 +1,避免重号被拒。 +- 旧配置(master/develop 共用的 `release` 轨)首次启动时自动拆分为各分支自己的轨道,已用构建号原样复制过去。 ### 服务端自动化资源 diff --git a/backend/routers/config.py b/backend/routers/config.py index e8d90a2..e0e2a49 100644 --- a/backend/routers/config.py +++ b/backend/routers/config.py @@ -93,74 +93,128 @@ DEFAULT_SERVERS = { DEFAULT_APP_VER = "2.180.0" -# 版本轨道:上架分支(master/develop)共用 release,自测分支(feature/*)用 feature。 -# 两条轨道的 App_Ver 各自独立填写,互不影响。 -TRACK_RELEASE = "release" -TRACK_FEATURE = "feature" -TRACKS = (TRACK_RELEASE, TRACK_FEATURE) +# 版本轨道:每个上架分支(master / develop / release-* 等)各有一条独立轨道, +# 轨道名即分支名,App_Store 打包时构建号自增;所有自测分支共用一条 feature 轨, +# 构建号永不自增。各轨道的 App_Ver 互不影响。 +KIND_RELEASE = "release" +KIND_FEATURE = "feature" +TRACK_FEATURE = "feature" # 自测轨的固定轨道名 +LEGACY_RELEASE_TRACK = "release" # 旧版 master/develop 共用的上架轨,迁移时按分支拆开 # 按名字推断轨道的默认规则;config["branch_track"] 中的显式配置优先。 RELEASE_BRANCH_NAMES = {"master", "main", "develop", "dev"} RELEASE_BRANCH_PREFIXES = ("release/", "release-", "hotfix/", "hotfix-") -def _default_track_for_branch(branch: str) -> str: - """按分支名推断所属版本轨道。""" +def _is_release_branch(branch: str) -> bool: + """按分支名推断是否为上架分支。""" name = (branch or "").strip().lower() - if name in RELEASE_BRANCH_NAMES or name.startswith(RELEASE_BRANCH_PREFIXES): - return TRACK_RELEASE - return TRACK_FEATURE + return name in RELEASE_BRANCH_NAMES or name.startswith(RELEASE_BRANCH_PREFIXES) -def _new_track(app_ver: str = DEFAULT_APP_VER) -> dict: +def _release_track_name(branch: str) -> str: + """上架轨道名即分支名;与固定的自测轨重名时加前缀避让。""" + return f"release:{branch}" if branch == TRACK_FEATURE else branch + + +def _default_track_for_branch(branch: str) -> str: + """按分支名推断所属版本轨道名。""" + return _release_track_name(branch) if _is_release_branch(branch) else TRACK_FEATURE + + +def _new_track(app_ver: str = DEFAULT_APP_VER, kind: str = KIND_RELEASE) -> dict: # 每个版本号(App_Ver)独立记录已用到的最大构建号第四位。 # App Store 的构建号唯一性按 MARKETING_VERSION(即完整 App_Ver) 分别计算, # 因此必须按版本号分别记录,回退到旧版本号时才能从它自己的最大值继续递增。 - return {"app_ver": app_ver, "build_map": {}} + return {"kind": kind, "app_ver": app_ver, "build_map": {}} + + +def _seed_release_track(tracks: dict, legacy: dict = None) -> dict: + """新建上架轨的初值:优先沿用旧共用轨(含已用构建号),否则沿用任一上架轨的 App_Ver。""" + if legacy: + return {"kind": KIND_RELEASE, "app_ver": legacy["app_ver"], "build_map": dict(legacy["build_map"])} + for track in tracks.values(): + if track.get("kind") == KIND_RELEASE: + return _new_track(track.get("app_ver") or DEFAULT_APP_VER) + return _new_track() + + +def _ensure_track(config: dict, track_name: str) -> dict: + """确保轨道存在;新建的上架轨沿用已有上架轨的 App_Ver,构建号从 .0 起。""" + tracks = config.setdefault("versions", {}).setdefault("tracks", {}) + track = tracks.get(track_name) + if not isinstance(track, dict): + track = tracks[track_name] = ( + _new_track(kind=KIND_FEATURE) if track_name == TRACK_FEATURE + else _seed_release_track(tracks) + ) + return track + + +def _legacy_single_track(versions: dict) -> dict: + """把最旧的单轨结构读成 {app_ver, build_map}。""" + app_ver = versions.get("app_ver") or DEFAULT_APP_VER + build_map = versions.get("build_map") + if not isinstance(build_map, dict): + build_map = {} + # 更旧的单值形式 "2.195.0.3" → {"2.195.0": 3} + old = versions.get("build_ver") + if isinstance(old, str): + parts = old.split(".") + if len(parts) >= 4 and parts[3].isdigit(): + build_map[".".join(parts[:3])] = int(parts[3]) + versions.pop("app_ver", None) + versions.pop("build_map", None) + versions.pop("build_ver", None) + return {"app_ver": app_ver, "build_map": build_map} def _ensure_versions(config: dict) -> bool: - """确保 versions 为双轨结构,并迁移旧式单轨/单值 build_ver 配置。 + """确保 versions 为「每个上架分支一条轨 + 一条 feature 轨」结构,并迁移旧配置。 - 旧式 ``{"app_ver": ..., "build_map": ...}`` 整体迁入 release 轨; - feature 轨用同一个 App_Ver 初始化,构建号从 .0 起且永不自增。 + 旧式单轨 ``{"app_ver": ..., "build_map": ...}`` 和旧的 master/develop 共用 + ``release`` 轨,都会按上架分支拆成各自独立的轨道,并复制已用构建号, + 使拆分后每个分支都从原来的构建号继续递增。 """ versions = config.get("versions") changed = False - if not isinstance(versions, dict): - config["versions"] = {"tracks": {TRACK_RELEASE: _new_track(), TRACK_FEATURE: _new_track()}} - return True - - tracks = versions.get("tracks") - if not isinstance(tracks, dict): - # 旧式单轨结构 → release 轨 - legacy_app_ver = versions.get("app_ver") or DEFAULT_APP_VER - legacy_build_map = versions.get("build_map") - if not isinstance(legacy_build_map, dict): - legacy_build_map = {} - # 更旧的单值形式 "2.195.0.3" → {"2.195.0": 3} - old = versions.get("build_ver") - if isinstance(old, str): - parts = old.split(".") - if len(parts) >= 4 and parts[3].isdigit(): - legacy_build_map[".".join(parts[:3])] = int(parts[3]) - versions.pop("app_ver", None) - versions.pop("build_map", None) - versions.pop("build_ver", None) - tracks = { - TRACK_RELEASE: {"app_ver": legacy_app_ver, "build_map": legacy_build_map}, - TRACK_FEATURE: _new_track(legacy_app_ver), - } - versions["tracks"] = tracks + versions = {} + config["versions"] = versions changed = True - for name in TRACKS: - track = tracks.get(name) + legacy = None # 需要拆分到各上架分支的旧共用轨 + tracks = versions.get("tracks") + if not isinstance(tracks, dict): + legacy = _legacy_single_track(versions) + tracks = {} + versions["tracks"] = tracks + changed = True + else: + # 迁移后的轨道都带 kind;没有 kind 的 release 轨即旧的共用上架轨 + old = tracks.get(LEGACY_RELEASE_TRACK) + if isinstance(old, dict) and "kind" not in old: + build_map = old.get("build_map") + legacy = { + "app_ver": old.get("app_ver") or DEFAULT_APP_VER, + "build_map": build_map if isinstance(build_map, dict) else {}, + } + tracks.pop(LEGACY_RELEASE_TRACK) + changed = True + + feature = tracks.get(TRACK_FEATURE) + if not isinstance(feature, dict): + tracks[TRACK_FEATURE] = _new_track((legacy or {}).get("app_ver") or DEFAULT_APP_VER, KIND_FEATURE) + changed = True + + for name, track in tracks.items(): if not isinstance(track, dict): - tracks[name] = _new_track() + track = tracks[name] = _new_track() + changed = True + kind = KIND_FEATURE if name == TRACK_FEATURE else KIND_RELEASE + if track.get("kind") != kind: + track["kind"] = kind changed = True - continue if not track.get("app_ver"): track["app_ver"] = DEFAULT_APP_VER changed = True @@ -168,33 +222,101 @@ def _ensure_versions(config: dict) -> bool: track["build_map"] = {} changed = True - # 分支归属:只存显式配置,缺省按名字规则推断 + # 分支归属:值为轨道名,缺省按分支名规则推断 branch_track = config.get("branch_track") if not isinstance(branch_track, dict): branch_track = {} config["branch_track"] = branch_track changed = True for branch in config.get("branches", []) or []: - if branch_track.get(branch) not in TRACKS: - branch_track[branch] = _default_track_for_branch(branch) + name = branch_track.get(branch) + if name == LEGACY_RELEASE_TRACK and LEGACY_RELEASE_TRACK not in tracks: + # 旧的共用上架轨 → 该分支自己的轨 + name = _release_track_name(branch) + if not isinstance(name, str) or not name: + name = _default_track_for_branch(branch) + if branch_track.get(branch) != name: + branch_track[branch] = name + changed = True + if name not in tracks: + tracks[name] = _seed_release_track(tracks, legacy) changed = True return changed +def _primary_track_name(config: dict) -> str: + """主上架轨:第一个上架分支所属的轨道;没有上架分支时退回 feature 轨。 + + 仅用于顶层 app_ver/build_ver 的兼容字段和未指定轨道时的缺省值。 + """ + for branch in config.get("branches", []) or []: + name = get_track_name(config, branch) + if name != TRACK_FEATURE: + return name + return TRACK_FEATURE + + def get_track_name(config: dict, branch: str) -> str: """取分支所属的版本轨道名。""" - track = (config.get("branch_track") or {}).get(branch) - return track if track in TRACKS else _default_track_for_branch(branch) + name = (config.get("branch_track") or {}).get(branch) + tracks = (config.get("versions") or {}).get("tracks") or {} + if isinstance(name, str) and name in tracks: + return name + if name == LEGACY_RELEASE_TRACK: # 尚未迁移的旧配置 + return _release_track_name(branch) + return _default_track_for_branch(branch) def get_track(config: dict, track_name: str) -> dict: - """取某条轨道的版本记录(不存在时返回默认值,不写回配置)。""" + """取某条轨道的版本记录副本(不存在时返回默认值;只读,不写回配置)。 + + 未迁移的旧记录没有 kind,按轨道名补上:只有固定的 feature 轨是自测轨。 + """ tracks = (config.get("versions") or {}).get("tracks") or {} + kind = KIND_FEATURE if track_name == TRACK_FEATURE else KIND_RELEASE track = tracks.get(track_name) if not isinstance(track, dict): - return _new_track() - return track + return _new_track(kind=kind) + return {**track, "kind": track.get("kind") or kind} + + +def is_release_track(track: dict) -> bool: + """该轨道是否为上架轨(App_Store 打包时构建号自增)。""" + return track.get("kind", KIND_RELEASE) == KIND_RELEASE + + +def next_build_no(tracks: dict, app_ver: str) -> int: + """该 App_Ver 下一个可用的构建号。 + + 构建号唯一性由 App Store 按 MARKETING_VERSION 全局判定,与分支无关, + 因此取所有上架轨在这个版本号上已用到的最大值再 +1,避免两个上架分支 + 临时使用同一 App_Ver 时产生重复构建号被拒。 + """ + used = [ + int((track.get("build_map") or {}).get(app_ver, 0)) + for track in tracks.values() + if isinstance(track, dict) and is_release_track(track) + ] + return max(used, default=0) + 1 + + +def resolve_track(config: dict, branch: str, track: str) -> str: + """把用户传入的轨道值解析为轨道名。 + + 兼容按类型指定:``release`` 表示该分支自己的上架轨,``feature`` 表示共用自测轨; + 也可直接传已存在的轨道名(多个分支共用同一条版本线)。 + """ + value = (track or "").strip() + if not value: + return _default_track_for_branch(branch) + if value == KIND_FEATURE: + return TRACK_FEATURE + if value == KIND_RELEASE: + return _release_track_name(branch) + if value in ((config.get("versions") or {}).get("tracks") or {}): + return value + raise HTTPException(status_code=400, detail=f"未知的版本轨道: {track}") def track_build_ver(track: dict) -> str: @@ -203,6 +325,7 @@ def track_build_ver(track: dict) -> str: build_map = track.get("build_map") or {} return f"{app_ver}.{int(build_map.get(app_ver, 0))}" + # 为已有环境迁移的固定前缀;后续环境从配置中的 next_app_id_prefix 自动分配。 LEGACY_SERVER_PREFIXES = { "测试环境": 1, @@ -652,43 +775,47 @@ async def get_branches(): @router.post("/branches") async def add_branch(data: dict): - """新增分支,可指定所属版本轨道(缺省按分支名推断)""" + """新增分支,可指定所属版本轨道(缺省按分支名推断)。 + + track 传 release 表示这个分支用自己独立的上架版本号,feature 表示共用自测版本号; + 也可直接传已有轨道名,让该分支与另一个分支共用同一条版本线。 + """ name = data.get("name", "").strip() if not name: raise HTTPException(status_code=400, detail="分支名称不能为空") - track = (data.get("track") or "").strip() or _default_track_for_branch(name) - if track not in TRACKS: - raise HTTPException(status_code=400, detail=f"未知的版本轨道: {track}") - async with _config_lock: config = load_config() branches = config.get("branches", ["main"]) if name in branches: raise HTTPException(status_code=400, detail="分支已存在") + track = resolve_track(config, name, data.get("track")) branches.append(name) config["branches"] = branches config.setdefault("branch_track", {})[name] = track + _ensure_track(config, track) save_config(config) return {"message": "分支已添加", "track": track} @router.put("/branches/track") async def update_branch_track(data: dict): - """修改分支所属的版本轨道(release=上架,feature=自测)。 + """修改分支所属的版本轨道(release=该分支独立的上架版本号,feature=共用自测版本号)。 - 分支名可能含 /(如 feature/login),因此走请求体而非路径参数。 + 改成 release 时会为这个分支建一条以分支名命名的轨道(初值沿用已有上架轨的 App_Ver), + 此后它的版本号与其它分支互不影响。分支名可能含 /(如 feature/login),因此走请求体而非路径参数。 """ branch = (data.get("branch") or "").strip() - track = (data.get("track") or "").strip() - if track not in TRACKS: - raise HTTPException(status_code=400, detail=f"未知的版本轨道: {track}") + if not (data.get("track") or "").strip(): + raise HTTPException(status_code=400, detail="未指定版本轨道") async with _config_lock: config = load_config() if branch not in config.get("branches", []): raise HTTPException(status_code=404, detail="分支不存在") + track = resolve_track(config, branch, data.get("track")) config.setdefault("branch_track", {})[branch] = track + _ensure_track(config, track) save_config(config) return {"message": "分支版本轨道已更新", "branch": branch, "track": track} @@ -755,24 +882,29 @@ async def update_build_settings(settings: BuildConfigUpdate, db: Session = Depen # 版本号配置 API @router.get("/versions") async def get_versions(): - """获取两条版本轨道各自的 App_Ver 及其当前构建号。 + """获取各条版本轨道的 App_Ver 及其当前构建号。 + 每个上架分支(master / develop 等)一条独立轨道,轨道名即分支名;自测分支共用 feature 轨。 build_ver 为该轨道当前 App_Ver 已用到的最大构建号(形如 2.195.0.5,尚未打包则 .0), - 仅供展示;实际递增只发生在 release 轨的 App_Store 打包时,feature 轨永不自增。 - 顶层 app_ver / build_ver 为 release 轨的镜像,保留给旧客户端。 + 仅供展示;实际递增只发生在上架轨的 App_Store 打包时,feature 轨永不自增。 + 顶层 app_ver / build_ver 为主上架轨的镜像,保留给旧客户端。 """ config = load_config() - tracks = {} - for name in TRACKS: - track = get_track(config, name) - tracks[name] = { + tracks = { + name: { + "kind": KIND_FEATURE if name == TRACK_FEATURE else KIND_RELEASE, "app_ver": track.get("app_ver") or DEFAULT_APP_VER, "build_ver": track_build_ver(track), "build_map": track.get("build_map") or {}, } + for name, track in ((config.get("versions") or {}).get("tracks") or {}).items() + if isinstance(track, dict) + } + primary = _primary_track_name(config) return { - **tracks[TRACK_RELEASE], + **{k: v for k, v in tracks.get(primary, {}).items() if k != "kind"}, "tracks": tracks, + "primary_track": primary, "branch_track": { branch: get_track_name(config, branch) for branch in config.get("branches", []) or [] @@ -787,14 +919,12 @@ async def update_versions(data: dict): 每个 App_Ver 的构建号在所属轨道内独立保存于 build_map 各自递增,因此来回切换 版本号(包括回退到打过包的旧版本号)都不会重复,不会被 App Store 拒绝。 - - track:release(master/develop,上架用)或 feature(自测用),缺省 release。 + - track:轨道名,上架分支为分支名(如 master、develop),自测为 feature;缺省为主上架轨。 - 不传 build_ver:保留该版本号已记录的构建号,仅更新该轨道的当前 App_Ver。 - 传 build_ver:形如 2.196.0.3,前三位须与 app_ver 一致,第四位为构建号; 直接写入该版本号的记录(可用于手动对齐 App Store Connect 上的已传构建号)。 """ - track_name = (data.get("track") or TRACK_RELEASE).strip() - if track_name not in TRACKS: - raise HTTPException(status_code=400, detail=f"未知的版本轨道: {track_name}") + track_name = (data.get("track") or "").strip() app_ver = data.get("app_ver", "").strip() if not re.fullmatch(r"\d+\.\d+\.\d+", app_ver): @@ -814,7 +944,10 @@ async def update_versions(data: dict): async with _config_lock: config = load_config() tracks = config.setdefault("versions", {}).setdefault("tracks", {}) - track = tracks.setdefault(track_name, _new_track()) + track_name = track_name or _primary_track_name(config) + if track_name not in tracks: + raise HTTPException(status_code=400, detail=f"未知的版本轨道: {track_name}") + track = tracks[track_name] track["app_ver"] = app_ver build_map = track.setdefault("build_map", {}) if build_no is not None: diff --git a/backend/services/build_service.py b/backend/services/build_service.py index b7888bc..af576ad 100644 --- a/backend/services/build_service.py +++ b/backend/services/build_service.py @@ -610,7 +610,7 @@ async def generate_config(task_id: str, task, build_dir: Path) -> dict: from ..routers.config import ( load_config, _config_lock, get_track, get_track_name, track_build_ver, - TRACK_RELEASE, _new_track, + is_release_track, next_build_no, _ensure_track, ) config = load_config() @@ -621,29 +621,29 @@ async def generate_config(task_id: str, task, build_dir: Path) -> dict: scheme = schemes.get(task.scheme_id, {}) # 版本号由当前服务配置统一管理,不读取分支源码中的手动打包脚本。 - # 按分支所属轨道取版本号:master/develop 走 release 轨(上架用),feature 走 feature 轨(自测用), - # 两条轨道的 App_Ver 各自独立。 + # 按分支所属轨道取版本号:每个上架分支(master、develop 等)一条独立轨道, + # 自测分支共用 feature 轨,各轨道的 App_Ver 互不影响。 track_name = get_track_name(config, task.branch) track = get_track(config, track_name) app_ver = track.get("app_ver") or "2.180.0" # 构建号按版本号独立记录:build_map[app_ver] 为该版本号已用到的最大第四位。 build_ver = track_build_ver(track) + is_release = is_release_track(track) await log_streamer.emit( task_id, - f"版本轨道: {track_name}({'上架,App_Store 打包自增' if track_name == TRACK_RELEASE else '自测,构建号不自增'})", + f"版本轨道: {track_name}({'上架,App_Store 打包自增' if is_release else '自测,构建号不自增'})", ) - # release 轨的 App_Store 打包,在该版本号自己的构建号上 +1 - # (回退到旧版本号也从它自己的最大值继续);feature 轨始终使用手动设定的构建号。 - if track_name == TRACK_RELEASE and task.build_type == "App_Store": + # 上架轨的 App_Store 打包,在该版本号的构建号上 +1(回退到旧版本号也从它自己的最大值继续); + # feature 轨始终使用手动设定的构建号。 + if is_release and task.build_type == "App_Store": async with _config_lock: config = load_config() - tracks = config.setdefault("versions", {}).setdefault("tracks", {}) - track = tracks.setdefault(track_name, _new_track(app_ver)) - build_map = track.setdefault("build_map", {}) - n = int(build_map.get(app_ver, 0)) + 1 - build_map[app_ver] = n + track = _ensure_track(config, track_name) + track.setdefault("app_ver", app_ver) + n = next_build_no(config["versions"]["tracks"], app_ver) + track.setdefault("build_map", {})[app_ver] = n build_ver = f"{app_ver}.{n}" from ..routers.config import save_config diff --git a/config.example.json b/config.example.json index b8e2b38..cc3e3ea 100644 --- a/config.example.json +++ b/config.example.json @@ -74,9 +74,10 @@ }, "versions": { "tracks": { - "release": { "app_ver": "2.180.0", "build_map": {} }, - "feature": { "app_ver": "2.180.0", "build_map": {} } + "master": { "kind": "release", "app_ver": "2.180.0", "build_map": {} }, + "develop": { "kind": "release", "app_ver": "2.180.0", "build_map": {} }, + "feature": { "kind": "feature", "app_ver": "2.180.0", "build_map": {} } } }, - "branch_track": { "master": "release", "develop": "release" } + "branch_track": { "master": "master", "develop": "develop" } } diff --git a/frontend/src/__tests__/ConfigView.test.js b/frontend/src/__tests__/ConfigView.test.js index bb7cd4c..a0decf9 100644 --- a/frontend/src/__tests__/ConfigView.test.js +++ b/frontend/src/__tests__/ConfigView.test.js @@ -27,11 +27,13 @@ function mockFetch(url) { '/api/config/versions': { app_ver: '2.195.0', build_ver: '2.195.0.0', + primary_track: 'main', tracks: { - release: { app_ver: '2.195.0', build_ver: '2.195.0.0', build_map: {} }, - feature: { app_ver: '2.100.0', build_ver: '2.100.0.0', build_map: {} }, + main: { kind: 'release', app_ver: '2.195.0', build_ver: '2.195.0.0', build_map: {} }, + dev: { kind: 'release', app_ver: '2.196.0', build_ver: '2.196.0.0', build_map: {} }, + feature: { kind: 'feature', app_ver: '2.100.0', build_ver: '2.100.0.0', build_map: {} }, }, - branch_track: { main: 'release', dev: 'release' }, + branch_track: { main: 'main', dev: 'dev' }, }, '/api/config': { apps: {}, schemes: {}, branches: ['main'] }, } @@ -191,7 +193,7 @@ describe('ConfigView.vue', () => { expect(fetch.mock.calls.map(c => c[0])).toEqual(['/api/config/apps']) }) - it('两条版本轨道各自独立展示与保存', async () => { + it('每个上架分支与自测轨各自独立展示与保存', async () => { const calls = [] fetch.mockImplementation((url, opts) => { if (url === '/api/config/versions' && opts?.method === 'PUT') { @@ -213,10 +215,11 @@ describe('ConfigView.vue', () => { await wrapper.vm.$nextTick() const inputs = wrapper.findAll('input[type="text"]') - expect(inputs[0].element.value).toBe('2.195.0') // release App_Ver - expect(inputs[2].element.value).toBe('2.100.0') // feature App_Ver + expect(inputs[0].element.value).toBe('2.195.0') // main(上架)App_Ver + expect(inputs[2].element.value).toBe('2.196.0') // dev(上架)App_Ver + expect(inputs[4].element.value).toBe('2.100.0') // feature App_Ver - await inputs[2].setValue('2.101.0') + await inputs[4].setValue('2.101.0') const saveBtn = wrapper.findAll('.btn-primary').find(b => b.text() === '保存自测版本号') await saveBtn.trigger('click') await flushPromises() diff --git a/frontend/src/views/ConfigView.vue b/frontend/src/views/ConfigView.vue index 7b1b426..94d4141 100644 --- a/frontend/src/views/ConfigView.vue +++ b/frontend/src/views/ConfigView.vue @@ -171,9 +171,9 @@