feat: 构建号按版本号(App_Ver)独立记录,避免回退旧版本号被 App Store 拒
将 versions.build_ver 单值改为 build_map(每个 App_Ver 独立记录已用到的最大 构建号)。App Store 的构建号唯一性按 MARKETING_VERSION 分别计算,改为单一全局 计数器时回退到打过包的旧版本号会重复(如 2.195.0 已到 .5,切 2.196.0 后再回退, 重置/沿用全局计数都会 < .5 而被拒)。 - config.py: 新增 _ensure_versions 迁移旧 build_ver → build_map;PUT /versions 不再重置构建号,改版本号仅切换当前 App_Ver,并支持可选手动设置 build_ver; GET /versions 返回 build_map 及当前版本号对应的 build_ver - build_service.py: App_Store 打包时在 build_map[app_ver] 自身上 +1 并回写 - ConfigView.vue: 打包设置页显示可编辑的 Build_Ver,修改 App_Ver 时自动切换为 该版本号已记录的构建号 - 迁移 config.json;更新前后端相关测试 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b6e56f5f9a
commit
91e9b027e8
@@ -450,6 +450,8 @@
|
|||||||
},
|
},
|
||||||
"versions": {
|
"versions": {
|
||||||
"app_ver": "2.195.0",
|
"app_ver": "2.195.0",
|
||||||
"build_ver": "2.195.0.0"
|
"build_map": {
|
||||||
|
"2.195.0": 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+77
-14
@@ -92,9 +92,43 @@ DEFAULT_SERVERS = {
|
|||||||
|
|
||||||
DEFAULT_VERSIONS = {
|
DEFAULT_VERSIONS = {
|
||||||
"app_ver": "2.180.0",
|
"app_ver": "2.180.0",
|
||||||
"build_ver": "2.180.0.0",
|
# 每个版本号(App_Ver)独立记录已用到的最大构建号第四位。
|
||||||
|
# App Store 的构建号唯一性按 MARKETING_VERSION(即完整 App_Ver) 分别计算,
|
||||||
|
# 因此必须按版本号分别记录,回退到旧版本号时才能从它自己的最大值继续递增。
|
||||||
|
"build_map": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_versions(config: dict) -> bool:
|
||||||
|
"""确保 versions 存在,并把旧式单值 build_ver 迁移为按版本号记录的 build_map。"""
|
||||||
|
versions = config.get("versions")
|
||||||
|
if not isinstance(versions, dict):
|
||||||
|
config["versions"] = DEFAULT_VERSIONS.copy()
|
||||||
|
config["versions"]["build_map"] = {}
|
||||||
|
return True
|
||||||
|
|
||||||
|
changed = False
|
||||||
|
if not versions.get("app_ver"):
|
||||||
|
versions["app_ver"] = DEFAULT_VERSIONS["app_ver"]
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if "build_map" not in versions:
|
||||||
|
build_map = {}
|
||||||
|
old = versions.pop("build_ver", None)
|
||||||
|
# 旧式形如 "2.195.0.3" → build_map = {"2.195.0": 3}
|
||||||
|
if isinstance(old, str):
|
||||||
|
parts = old.split(".")
|
||||||
|
if len(parts) >= 4 and parts[3].isdigit():
|
||||||
|
build_map[".".join(parts[:3])] = int(parts[3])
|
||||||
|
versions["build_map"] = build_map
|
||||||
|
changed = True
|
||||||
|
elif "build_ver" in versions:
|
||||||
|
# build_map 已存在但残留旧字段,清理掉
|
||||||
|
versions.pop("build_ver", None)
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
return changed
|
||||||
|
|
||||||
# 为已有环境迁移的固定前缀;后续环境从配置中的 next_app_id_prefix 自动分配。
|
# 为已有环境迁移的固定前缀;后续环境从配置中的 next_app_id_prefix 自动分配。
|
||||||
LEGACY_SERVER_PREFIXES = {
|
LEGACY_SERVER_PREFIXES = {
|
||||||
"测试环境": 1,
|
"测试环境": 1,
|
||||||
@@ -247,13 +281,12 @@ def load_config() -> dict:
|
|||||||
config["branches"] = ["main"]
|
config["branches"] = ["main"]
|
||||||
if "upload" not in config:
|
if "upload" not in config:
|
||||||
config["upload"] = DEFAULT_UPLOAD
|
config["upload"] = DEFAULT_UPLOAD
|
||||||
if "versions" not in config:
|
versions_changed = _ensure_versions(config)
|
||||||
config["versions"] = DEFAULT_VERSIONS.copy()
|
|
||||||
# 自动补齐旧配置的环境前缀、特殊 App 覆盖和 upload_key。
|
# 自动补齐旧配置的环境前缀、特殊 App 覆盖和 upload_key。
|
||||||
prefixes_changed = _ensure_server_id_prefixes(config)
|
prefixes_changed = _ensure_server_id_prefixes(config)
|
||||||
overrides_changed = _ensure_special_app_prefix_overrides(config)
|
overrides_changed = _ensure_special_app_prefix_overrides(config)
|
||||||
keys_changed = _ensure_upload_keys(config)
|
keys_changed = _ensure_upload_keys(config)
|
||||||
if prefixes_changed or overrides_changed or keys_changed:
|
if versions_changed or prefixes_changed or overrides_changed or keys_changed:
|
||||||
save_config(config)
|
save_config(config)
|
||||||
# 迁移旧式目录皮肤为 ZIP
|
# 迁移旧式目录皮肤为 ZIP
|
||||||
if _migrate_old_themes(config):
|
if _migrate_old_themes(config):
|
||||||
@@ -606,32 +639,62 @@ async def update_build_settings(settings: BuildConfigUpdate, db: Session = Depen
|
|||||||
# 版本号配置 API
|
# 版本号配置 API
|
||||||
@router.get("/versions")
|
@router.get("/versions")
|
||||||
async def get_versions():
|
async def get_versions():
|
||||||
"""获取服务端统一管理的 App_Ver 和 Build_Ver"""
|
"""获取服务端统一管理的 App_Ver 及其当前构建号。
|
||||||
|
|
||||||
|
build_ver 为当前 App_Ver 已用到的最大构建号(形如 2.195.0.5,尚未打包则 .0),
|
||||||
|
仅供展示;实际递增在 App_Store 打包时按版本号独立进行。
|
||||||
|
"""
|
||||||
config = load_config()
|
config = load_config()
|
||||||
versions = config.get("versions", DEFAULT_VERSIONS.copy())
|
versions = config.get("versions", DEFAULT_VERSIONS.copy())
|
||||||
|
app_ver = versions.get("app_ver", DEFAULT_VERSIONS["app_ver"])
|
||||||
|
build_map = versions.get("build_map", {}) or {}
|
||||||
return {
|
return {
|
||||||
"app_ver": versions.get("app_ver", DEFAULT_VERSIONS["app_ver"]),
|
"app_ver": app_ver,
|
||||||
"build_ver": versions.get("build_ver", DEFAULT_VERSIONS["build_ver"]),
|
"build_ver": f"{app_ver}.{int(build_map.get(app_ver, 0))}",
|
||||||
|
"build_map": build_map,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.put("/versions")
|
@router.put("/versions")
|
||||||
async def update_versions(data: dict):
|
async def update_versions(data: dict):
|
||||||
"""更新 App_Ver,并将 Build_Ver 重置为对应的 .0。"""
|
"""更新 App_Ver,并可选地手动设置该版本号的构建号。
|
||||||
|
|
||||||
|
每个 App_Ver 的构建号独立保存在 build_map 中各自递增,因此来回切换版本号
|
||||||
|
(包括回退到打过包的旧版本号)都不会重复,不会被 App Store 拒绝。
|
||||||
|
|
||||||
|
- 不传 build_ver:保留该版本号已记录的构建号,仅更新当前 App_Ver。
|
||||||
|
- 传 build_ver:形如 2.196.0.3,前三位须与 app_ver 一致,第四位为构建号;
|
||||||
|
直接写入该版本号的记录(可用于手动对齐 App Store Connect 上的已传构建号)。
|
||||||
|
"""
|
||||||
app_ver = data.get("app_ver", "").strip()
|
app_ver = data.get("app_ver", "").strip()
|
||||||
if not re.fullmatch(r"\d+\.\d+\.\d+", app_ver):
|
if not re.fullmatch(r"\d+\.\d+\.\d+", app_ver):
|
||||||
raise HTTPException(status_code=400, detail="App_Ver 必须为主版本.次版本.修订号,例如 2.196.0")
|
raise HTTPException(status_code=400, detail="App_Ver 必须为主版本.次版本.修订号,例如 2.196.0")
|
||||||
|
|
||||||
build_ver = f"{app_ver}.0"
|
build_ver = data.get("build_ver")
|
||||||
|
build_no = None
|
||||||
|
if build_ver is not None and str(build_ver).strip():
|
||||||
|
build_ver = str(build_ver).strip()
|
||||||
|
if not re.fullmatch(r"\d+\.\d+\.\d+\.\d+", build_ver):
|
||||||
|
raise HTTPException(status_code=400, detail="Build_Ver 必须为四段数字,例如 2.196.0.3")
|
||||||
|
prefix, _, last = build_ver.rpartition(".")
|
||||||
|
if prefix != app_ver:
|
||||||
|
raise HTTPException(status_code=400, detail=f"Build_Ver 前三位须与 App_Ver 一致(应为 {app_ver}.x)")
|
||||||
|
build_no = int(last)
|
||||||
|
|
||||||
async with _config_lock:
|
async with _config_lock:
|
||||||
config = load_config()
|
config = load_config()
|
||||||
config["versions"] = {
|
versions = config.setdefault("versions", DEFAULT_VERSIONS.copy())
|
||||||
"app_ver": app_ver,
|
versions["app_ver"] = app_ver
|
||||||
"build_ver": build_ver,
|
build_map = versions.setdefault("build_map", {})
|
||||||
}
|
if build_no is not None:
|
||||||
|
build_map[app_ver] = build_no
|
||||||
save_config(config)
|
save_config(config)
|
||||||
|
|
||||||
return {"message": "版本号已更新", "app_ver": app_ver, "build_ver": build_ver}
|
return {
|
||||||
|
"message": "版本号已更新",
|
||||||
|
"app_ver": app_ver,
|
||||||
|
"build_ver": f"{app_ver}.{int(build_map.get(app_ver, 0))}",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# 上传配置 API
|
# 上传配置 API
|
||||||
|
|||||||
@@ -613,23 +613,21 @@ async def generate_config(task_id: str, task, build_dir: Path) -> dict:
|
|||||||
|
|
||||||
# 版本号由当前服务配置统一管理,不读取分支源码中的手动打包脚本。
|
# 版本号由当前服务配置统一管理,不读取分支源码中的手动打包脚本。
|
||||||
app_ver = versions.get("app_ver", "2.180.0")
|
app_ver = versions.get("app_ver", "2.180.0")
|
||||||
build_ver = versions.get("build_ver", "2.180.0.0")
|
|
||||||
|
|
||||||
# App_Store 打包时自动递增 Build_Ver 第四位版本号
|
# 构建号按版本号独立记录:build_map[app_ver] 为该版本号已用到的最大第四位。
|
||||||
|
build_map = versions.get("build_map", {}) or {}
|
||||||
|
build_ver = f"{app_ver}.{int(build_map.get(app_ver, 0))}"
|
||||||
|
|
||||||
|
# App_Store 打包时,在该版本号自己的构建号上 +1(回退到旧版本号也从它自己的最大值继续)
|
||||||
if task.build_type == "App_Store":
|
if task.build_type == "App_Store":
|
||||||
async with _config_lock:
|
async with _config_lock:
|
||||||
parts = build_ver.split(".")
|
|
||||||
if len(parts) >= 4:
|
|
||||||
parts[3] = str(int(parts[3]) + 1)
|
|
||||||
else:
|
|
||||||
parts.append("1")
|
|
||||||
build_ver = ".".join(parts)
|
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
config["versions"] = {
|
versions = config.setdefault("versions", {"app_ver": app_ver, "build_map": {}})
|
||||||
"app_ver": app_ver,
|
build_map = versions.setdefault("build_map", {})
|
||||||
"build_ver": build_ver,
|
n = int(build_map.get(app_ver, 0)) + 1
|
||||||
}
|
build_map[app_ver] = n
|
||||||
|
build_ver = f"{app_ver}.{n}"
|
||||||
|
|
||||||
from ..routers.config import save_config
|
from ..routers.config import save_config
|
||||||
save_config(config)
|
save_config(config)
|
||||||
|
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ describe('ConfigView.vue', () => {
|
|||||||
|
|
||||||
expect(wrapper.text()).toContain('应用版本号')
|
expect(wrapper.text()).toContain('应用版本号')
|
||||||
expect(wrapper.text()).not.toContain('打包参数')
|
expect(wrapper.text()).not.toContain('打包参数')
|
||||||
expect(wrapper.findAll('input').length).toBe(1)
|
// 非管理员在打包设置页可见 App_Ver 与 Build_Ver 两个输入框
|
||||||
|
expect(wrapper.findAll('input').length).toBe(2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -272,9 +272,17 @@
|
|||||||
<h4 class="section-title">应用版本号</h4>
|
<h4 class="section-title">应用版本号</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>App_Ver(应用版本)</label>
|
<label>App_Ver(应用版本)</label>
|
||||||
<input type="text" v-model="versions.app_ver" placeholder="2.180.0">
|
<input type="text" v-model="versions.app_ver" placeholder="2.180.0" @input="onAppVerInput">
|
||||||
<div style="font-size: 12px; color: #999; margin-top: 4px;">格式:主版本.次版本.修订号</div>
|
<div style="font-size: 12px; color: #999; margin-top: 4px;">格式:主版本.次版本.修订号</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Build_Ver(构建号)</label>
|
||||||
|
<input type="text" v-model="versions.build_ver" placeholder="2.180.0.0">
|
||||||
|
<div style="font-size: 12px; color: #999; margin-top: 4px;">
|
||||||
|
四段数字,前三位须与 App_Ver 一致。修改 App_Ver 会自动切换为该版本号已记录的构建号;
|
||||||
|
每个版本号的构建号独立递增,App_Store 打包时自动 +1。一般无需手动修改。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button class="btn btn-primary" style="width: auto; padding: 10px 32px; margin-bottom: 24px;" @click="saveVersions">保存版本号</button>
|
<button class="btn btn-primary" style="width: auto; padding: 10px 32px; margin-bottom: 24px;" @click="saveVersions">保存版本号</button>
|
||||||
|
|
||||||
<template v-if="isAdmin">
|
<template v-if="isAdmin">
|
||||||
@@ -606,6 +614,21 @@ const branches = ref([])
|
|||||||
const newBranch = ref('')
|
const newBranch = ref('')
|
||||||
const buildSettings = ref({})
|
const buildSettings = ref({})
|
||||||
const versions = ref({ app_ver: '', build_ver: '' })
|
const versions = ref({ app_ver: '', build_ver: '' })
|
||||||
|
const buildMap = ref({})
|
||||||
|
|
||||||
|
const applyVersions = (data) => {
|
||||||
|
buildMap.value = data.build_map || {}
|
||||||
|
versions.value = { app_ver: data.app_ver || '', build_ver: data.build_ver || '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改 App_Ver 时,自动把 Build_Ver 切换为该版本号已记录的构建号(新版本号则为 .0)
|
||||||
|
const onAppVerInput = () => {
|
||||||
|
const appVer = (versions.value.app_ver || '').trim()
|
||||||
|
if (/^\d+\.\d+\.\d+$/.test(appVer)) {
|
||||||
|
const n = Number(buildMap.value[appVer] || 0)
|
||||||
|
versions.value.build_ver = `${appVer}.${n}`
|
||||||
|
}
|
||||||
|
}
|
||||||
const uploadConfig = ref({ mode: 'oss', oss: {}, webdav: {}, dingtalk: {} })
|
const uploadConfig = ref({ mode: 'oss', oss: {}, webdav: {}, dingtalk: {} })
|
||||||
const jsonContent = ref('{}')
|
const jsonContent = ref('{}')
|
||||||
|
|
||||||
@@ -641,7 +664,7 @@ const loadData = async () => {
|
|||||||
authFetch('/api/config/versions'),
|
authFetch('/api/config/versions'),
|
||||||
])
|
])
|
||||||
if (appsRes.ok) apps.value = await appsRes.json()
|
if (appsRes.ok) apps.value = await appsRes.json()
|
||||||
if (versionsRes.ok) versions.value = await versionsRes.json()
|
if (versionsRes.ok) applyVersions(await versionsRes.json())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,7 +686,7 @@ const loadData = async () => {
|
|||||||
buildSettings.value = await buildRes.json()
|
buildSettings.value = await buildRes.json()
|
||||||
uploadConfig.value = await uploadRes.json()
|
uploadConfig.value = await uploadRes.json()
|
||||||
jsonContent.value = JSON.stringify(await configRes.json(), null, 2)
|
jsonContent.value = JSON.stringify(await configRes.json(), null, 2)
|
||||||
versions.value = await versionsRes.json()
|
applyVersions(await versionsRes.json())
|
||||||
if (usersRes.ok) users.value = await usersRes.json()
|
if (usersRes.ok) users.value = await usersRes.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1114,10 +1137,15 @@ const saveVersions = async () => {
|
|||||||
const res = await authFetch('/api/config/versions', {
|
const res = await authFetch('/api/config/versions', {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ app_ver: versions.value.app_ver }),
|
body: JSON.stringify({
|
||||||
|
app_ver: versions.value.app_ver,
|
||||||
|
build_ver: versions.value.build_ver,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
|
// 回写该版本号最新的构建号,保持本地 build_map 与服务端一致
|
||||||
|
buildMap.value = { ...buildMap.value, [data.app_ver]: Number((data.build_ver.split('.')[3]) || 0) }
|
||||||
versions.value = { app_ver: data.app_ver, build_ver: data.build_ver }
|
versions.value = { app_ver: data.app_ver, build_ver: data.build_ver }
|
||||||
alert('版本号已保存')
|
alert('版本号已保存')
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -172,7 +172,8 @@ def test_update_build_settings(client, tmp_config):
|
|||||||
|
|
||||||
# ---- Versions ----
|
# ---- Versions ----
|
||||||
|
|
||||||
def test_update_versions_resets_build_number(client, tmp_config):
|
def test_update_versions_keeps_per_version_build_number(client, tmp_config):
|
||||||
|
# 首次设置某版本号,构建号从 .0 起(尚未打包)
|
||||||
resp = client.put("/api/config/versions", json={"app_ver": "2.196.0"})
|
resp = client.put("/api/config/versions", json={"app_ver": "2.196.0"})
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert resp.json()["app_ver"] == "2.196.0"
|
assert resp.json()["app_ver"] == "2.196.0"
|
||||||
@@ -180,6 +181,22 @@ def test_update_versions_resets_build_number(client, tmp_config):
|
|||||||
assert client.get("/api/config/versions").json()["build_ver"] == "2.196.0.0"
|
assert client.get("/api/config/versions").json()["build_ver"] == "2.196.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_versions_does_not_reset_used_build_number(client, tmp_config):
|
||||||
|
# 模拟 2.195.0 已打到 .5、并切到 2.196.0 后,再回退到 2.195.0
|
||||||
|
import json
|
||||||
|
with open(tmp_config, "r", encoding="utf-8") as f:
|
||||||
|
cfg = json.load(f)
|
||||||
|
cfg["versions"] = {"app_ver": "2.196.0", "build_map": {"2.195.0": 5, "2.196.0": 3}}
|
||||||
|
with open(tmp_config, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(cfg, f)
|
||||||
|
|
||||||
|
resp = client.put("/api/config/versions", json={"app_ver": "2.195.0"})
|
||||||
|
assert resp.status_code == 200
|
||||||
|
# 回退到旧版本号时,构建号保留其自身已用到的最大值,不被重置
|
||||||
|
assert resp.json()["build_ver"] == "2.195.0.5"
|
||||||
|
assert client.get("/api/config/versions").json()["build_ver"] == "2.195.0.5"
|
||||||
|
|
||||||
|
|
||||||
def test_update_versions_rejects_invalid_app_version(client, tmp_config):
|
def test_update_versions_rejects_invalid_app_version(client, tmp_config):
|
||||||
resp = client.put("/api/config/versions", json={"app_ver": "2.196"})
|
resp = client.put("/api/config/versions", json={"app_ver": "2.196"})
|
||||||
assert resp.status_code == 400
|
assert resp.status_code == 400
|
||||||
|
|||||||
@@ -339,6 +339,31 @@ async def test_generate_config_uses_saved_version(tmp_path, log_streamer):
|
|||||||
assert result["BUILD_VERSION"] == "3.0.1.0"
|
assert result["BUILD_VERSION"] == "3.0.1.0"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_generate_config_appstore_increments_per_version(tmp_path, log_streamer):
|
||||||
|
"""App_Store 打包时,按版本号在其自身已用构建号上 +1(回退旧版本号亦从自身最大值继续)"""
|
||||||
|
build_dir = tmp_path / "build"
|
||||||
|
build_dir.mkdir()
|
||||||
|
|
||||||
|
task = MagicMock(app_id="1", scheme_id="1", build_type="App_Store", obfuscation=False)
|
||||||
|
|
||||||
|
# 2.195.0 已打到 .5,切到 2.196.0 后又回退到 2.195.0
|
||||||
|
mock_config = {
|
||||||
|
"apps": {"1": {"name": "App", "certificates": {}}},
|
||||||
|
"schemes": {"1": {"name": "sch", "ossFloder": "f"}},
|
||||||
|
"versions": {"app_ver": "2.195.0", "build_map": {"2.195.0": 5, "2.196.0": 3}},
|
||||||
|
}
|
||||||
|
|
||||||
|
with patch("backend.routers.config.load_config", return_value=mock_config), \
|
||||||
|
patch("backend.routers.config.save_config") as save_mock:
|
||||||
|
result = await generate_config("t1", task, build_dir)
|
||||||
|
|
||||||
|
# 从 2.195.0 自身的 5 继续到 6,不受 2.196.0 影响,也不撞已传的 .5
|
||||||
|
assert result["BUILD_VERSION"] == "2.195.0.6"
|
||||||
|
assert mock_config["versions"]["build_map"]["2.195.0"] == 6
|
||||||
|
assert mock_config["versions"]["build_map"]["2.196.0"] == 3
|
||||||
|
save_mock.assert_called()
|
||||||
|
|
||||||
|
|
||||||
async def test_generate_config_uses_server_theme_not_source_autopacking(tmp_path, log_streamer):
|
async def test_generate_config_uses_server_theme_not_source_autopacking(tmp_path, log_streamer):
|
||||||
"""Web 打包不读取分支源码中的 AutoPacking 目录。"""
|
"""Web 打包不读取分支源码中的 AutoPacking 目录。"""
|
||||||
build_dir = tmp_path / "build"
|
build_dir = tmp_path / "build"
|
||||||
|
|||||||
Reference in New Issue
Block a user