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:
shenlei
2026-09-02 10:51:35 +09:00
co-authored by Claude Opus 5
parent 9b4667cab5
commit 6867a3cb01
8 changed files with 216 additions and 163 deletions
+6 -5
View File
@@ -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)