feat: 版本轨道按分支彻底分开,自测分支不再共用 feature 轨
每个分支各有一条独立轨道(轨道名即分支名,master/main 沿用历史名 release), 轨道类型 kind 只决定构建号行为:release 上架自增、feature 自测不自增。 - branch_track 映射分支 → 轨道名,新增分支自动建同名轨道 - 迁移:旧的共用 release / feature 轨按分支拆开并复制已用构建号, 拆完没有分支引用的旧共用轨直接删除;无自测分支时保留 feature 轨作模板 - 分支管理页的下拉只改轨道类型(是否自增),不再影响版本号归属 - 打包设置页按分支分块填写版本号 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJaZ913U5GVdTkYHiRpvDU
This commit is contained in:
+34
-23
@@ -229,19 +229,20 @@ def test_update_versions_rejects_invalid_app_version(client, tmp_config):
|
||||
|
||||
|
||||
def test_versions_tracks_are_independent(client, tmp_config):
|
||||
# 每个上架分支一条轨道(main/master 为 release,其余即分支名),App_Ver 各自独立
|
||||
# 每个分支一条轨道(main/master 为 release,其余即分支名),App_Ver 各自独立
|
||||
assert client.post("/api/config/branches", json={"name": "feature/login"}).status_code == 200
|
||||
assert client.put("/api/config/versions", json={"track": "release", "app_ver": "2.196.0"}).status_code == 200
|
||||
assert client.put("/api/config/versions", json={"track": "dev", "app_ver": "2.198.0"}).status_code == 200
|
||||
assert client.put("/api/config/versions", json={"track": "feature", "app_ver": "2.100.0"}).status_code == 200
|
||||
assert client.put("/api/config/versions", json={"track": "feature/login", "app_ver": "2.100.0"}).status_code == 200
|
||||
|
||||
data = client.get("/api/config/versions").json()
|
||||
assert data["branch_track"] == {"main": "release", "dev": "dev"}
|
||||
assert data["branch_track"] == {"main": "release", "dev": "dev", "feature/login": "feature/login"}
|
||||
assert data["tracks"]["release"]["app_ver"] == "2.196.0"
|
||||
assert data["tracks"]["dev"]["app_ver"] == "2.198.0"
|
||||
assert data["tracks"]["feature"]["app_ver"] == "2.100.0"
|
||||
assert data["tracks"]["feature"]["build_ver"] == "2.100.0.0"
|
||||
assert data["tracks"]["feature/login"]["app_ver"] == "2.100.0"
|
||||
assert data["tracks"]["feature/login"]["build_ver"] == "2.100.0.0"
|
||||
assert data["tracks"]["release"]["kind"] == "release"
|
||||
assert data["tracks"]["feature"]["kind"] == "feature"
|
||||
assert data["tracks"]["feature/login"]["kind"] == "feature"
|
||||
# 顶层字段镜像主上架轨(第一个上架分支),兼容旧客户端
|
||||
assert data["primary_track"] == "release"
|
||||
assert data["app_ver"] == "2.196.0"
|
||||
@@ -252,8 +253,8 @@ def test_update_versions_rejects_unknown_track(client, tmp_config):
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_legacy_versions_split_per_release_branch(client, tmp_config):
|
||||
"""最旧的单轨配置:拆成每个上架分支一条轨,各自沿用原来的已用构建号"""
|
||||
def test_legacy_versions_split_per_branch(client, tmp_config):
|
||||
"""最旧的单轨配置:拆成每个分支一条轨,各自沿用原来的已用构建号"""
|
||||
import json
|
||||
with open(tmp_config, "r", encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
@@ -271,9 +272,6 @@ def test_legacy_versions_split_per_release_branch(client, tmp_config):
|
||||
"build_ver": "2.195.0.7",
|
||||
"build_map": {"2.195.0": 7},
|
||||
}
|
||||
# feature 轨以同一 App_Ver 初始化,构建号从 .0 起
|
||||
assert data["tracks"]["feature"]["app_ver"] == "2.195.0"
|
||||
assert data["tracks"]["feature"]["build_ver"] == "2.195.0.0"
|
||||
# 拆分后各轨独立:改 release 不影响 dev
|
||||
assert client.put("/api/config/versions", json={"track": "release", "app_ver": "2.196.0"}).status_code == 200
|
||||
data = client.get("/api/config/versions").json()
|
||||
@@ -281,26 +279,40 @@ def test_legacy_versions_split_per_release_branch(client, tmp_config):
|
||||
assert data["tracks"]["dev"]["app_ver"] == "2.195.0"
|
||||
|
||||
|
||||
def test_legacy_shared_release_track_splits_per_branch(client, tmp_config):
|
||||
"""旧的 master/develop 共用 release 轨:master 留在 release 轨,其余分支拆出自己的轨"""
|
||||
def test_legacy_shared_tracks_split_per_branch(client, tmp_config):
|
||||
"""旧的共用轨:master 留在 release 轨,其余分支(含自测分支)各拆出自己的轨"""
|
||||
import json
|
||||
with open(tmp_config, "r", encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
cfg["branches"] = ["main", "dev", "feature/login", "feature/pay"]
|
||||
cfg["versions"] = {"tracks": {
|
||||
"release": {"app_ver": "2.195.0", "build_map": {"2.195.0": 4}},
|
||||
"feature": {"app_ver": "2.100.0", "build_map": {}},
|
||||
}}
|
||||
cfg["branch_track"] = {"main": "release", "dev": "release"}
|
||||
cfg["branch_track"] = {
|
||||
"main": "release", "dev": "release",
|
||||
"feature/login": "feature", "feature/pay": "feature",
|
||||
}
|
||||
with open(tmp_config, "w", encoding="utf-8") as f:
|
||||
json.dump(cfg, f)
|
||||
|
||||
data = client.get("/api/config/versions").json()
|
||||
assert data["branch_track"] == {"main": "release", "dev": "dev"}
|
||||
assert data["branch_track"] == {
|
||||
"main": "release", "dev": "dev",
|
||||
"feature/login": "feature/login", "feature/pay": "feature/pay",
|
||||
}
|
||||
for track in ("release", "dev"):
|
||||
assert data["tracks"][track]["kind"] == "release"
|
||||
assert data["tracks"][track]["app_ver"] == "2.195.0"
|
||||
assert data["tracks"][track]["build_ver"] == "2.195.0.4"
|
||||
assert data["tracks"]["feature"]["app_ver"] == "2.100.0"
|
||||
for track in ("feature/login", "feature/pay"):
|
||||
assert data["tracks"][track]["kind"] == "feature"
|
||||
assert data["tracks"][track]["app_ver"] == "2.100.0"
|
||||
|
||||
# 自测分支的版本号各自独立
|
||||
assert client.put("/api/config/versions", json={"track": "feature/login", "app_ver": "2.101.0"}).status_code == 200
|
||||
data = client.get("/api/config/versions").json()
|
||||
assert data["tracks"]["feature/login"]["app_ver"] == "2.101.0"
|
||||
assert data["tracks"]["feature/pay"]["app_ver"] == "2.100.0"
|
||||
|
||||
|
||||
def test_branch_track_defaults_and_override(client, tmp_config):
|
||||
@@ -308,22 +320,21 @@ def test_branch_track_defaults_and_override(client, tmp_config):
|
||||
assert client.post("/api/config/branches", json={"name": "feature/login"}).status_code == 200
|
||||
|
||||
data = client.get("/api/config/versions").json()
|
||||
# 上架分支各有一条独立轨道(main/master 为 release,其余即分支名),自测分支共用 feature 轨
|
||||
# 每个分支一条自己的轨道,类型按分支名推断
|
||||
assert data["branch_track"]["develop"] == "develop"
|
||||
assert data["branch_track"]["feature/login"] == "feature"
|
||||
assert data["branch_track"]["feature/login"] == "feature/login"
|
||||
assert data["tracks"]["develop"]["kind"] == "release"
|
||||
assert data["tracks"]["feature/login"]["kind"] == "feature"
|
||||
|
||||
# 手动改归属:改成上架后拥有自己的轨道
|
||||
# 手动改类型:轨道名不变,只改构建号是否自增
|
||||
resp = client.put("/api/config/branches/track", json={"branch": "feature/login", "track": "release"})
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["track"] == "feature/login"
|
||||
data = client.get("/api/config/versions").json()
|
||||
assert data["branch_track"]["feature/login"] == "feature/login"
|
||||
assert data["tracks"]["feature/login"]["kind"] == "release"
|
||||
|
||||
# 改回自测则重新共用 feature 轨
|
||||
assert client.put("/api/config/branches/track", json={"branch": "feature/login", "track": "feature"}).status_code == 200
|
||||
assert client.get("/api/config/versions").json()["branch_track"]["feature/login"] == "feature"
|
||||
assert client.get("/api/config/versions").json()["tracks"]["feature/login"]["kind"] == "feature"
|
||||
|
||||
assert client.put("/api/config/branches/track", json={"branch": "nope", "track": "release"}).status_code == 404
|
||||
assert client.put("/api/config/branches/track", json={"branch": "develop", "track": "beta"}).status_code == 400
|
||||
|
||||
@@ -426,7 +426,7 @@ async def test_generate_config_build_no_skips_other_release_track(tmp_path, log_
|
||||
|
||||
|
||||
async def test_generate_config_feature_branch_never_increments(tmp_path, log_streamer):
|
||||
"""feature 分支用自己的版本号,且构建号永不自增(即使是 App_Store 打包)"""
|
||||
"""自测分支用自己那条轨道的版本号,且构建号永不自增(即使是 App_Store 打包)"""
|
||||
build_dir = tmp_path / "build"
|
||||
build_dir.mkdir()
|
||||
|
||||
@@ -435,7 +435,7 @@ async def test_generate_config_feature_branch_never_increments(tmp_path, log_str
|
||||
|
||||
mock_config = _versions_config({
|
||||
"release": _release("2.195.0", {"2.195.0": 5}),
|
||||
"feature": _feature("2.100.0"),
|
||||
"feature/login": _feature("2.100.0"),
|
||||
})
|
||||
|
||||
with patch("backend.routers.config.load_config", return_value=mock_config), \
|
||||
@@ -444,7 +444,7 @@ async def test_generate_config_feature_branch_never_increments(tmp_path, log_str
|
||||
|
||||
assert result["VERSION"] == "2.100.0"
|
||||
assert result["BUILD_VERSION"] == "2.100.0.0"
|
||||
assert mock_config["versions"]["tracks"]["feature"]["build_map"] == {}
|
||||
assert mock_config["versions"]["tracks"]["feature/login"]["build_map"] == {}
|
||||
save_mock.assert_not_called()
|
||||
|
||||
|
||||
@@ -458,9 +458,10 @@ async def test_generate_config_branch_track_override(tmp_path, log_streamer):
|
||||
|
||||
mock_config = _versions_config({
|
||||
"release": _release("2.196.0"),
|
||||
"feature": _feature("2.100.0"),
|
||||
"feature/login": _feature("2.100.0"),
|
||||
})
|
||||
mock_config["branch_track"] = {"release/2.196": "feature"}
|
||||
# release/2.196 按名字本应是上架轨,显式指到 feature/login 那条轨道
|
||||
mock_config["branch_track"] = {"release/2.196": "feature/login"}
|
||||
|
||||
with patch("backend.routers.config.load_config", return_value=mock_config):
|
||||
result = await generate_config("t1", task, build_dir)
|
||||
|
||||
Reference in New Issue
Block a user