diff --git a/apps/ai-game-creator-shell/scripts/build-release.test.mjs b/apps/ai-game-creator-shell/scripts/build-release.test.mjs index ffc8bb8b2..798bd206f 100644 --- a/apps/ai-game-creator-shell/scripts/build-release.test.mjs +++ b/apps/ai-game-creator-shell/scripts/build-release.test.mjs @@ -1,4 +1,5 @@ import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; import { test } from 'node:test'; import { @@ -46,3 +47,14 @@ test('next release version follows the higher local or OSS version', () => { assert.equal(nextPatchVersion('0.1.18', '0.1.15'), '0.1.19'); assert.equal(nextPatchVersion('0.1.12', null), '0.1.13'); }); + +test('release upload forces overwrite for versioned artifact and latest pointer', () => { + const source = readFileSync( + new URL('./release-upload.mjs', import.meta.url), + 'utf8', + ); + assert.equal( + (source.match(/runOssutil\(\['cp', '--force'/gu) ?? []).length, + 2, + ); +}); diff --git a/apps/ai-game-creator-shell/scripts/release-upload.mjs b/apps/ai-game-creator-shell/scripts/release-upload.mjs index e2085345a..ee5af4452 100644 --- a/apps/ai-game-creator-shell/scripts/release-upload.mjs +++ b/apps/ai-game-creator-shell/scripts/release-upload.mjs @@ -40,7 +40,9 @@ await prepareReleaseVersion(); runTauriBuild([]); const { artifact, manifestPath, manifest } = generateUpdateManifest(); const artifactKey = `agc/${manifest.version}/${path.basename(artifact)}`; -runOssutil(['cp', artifact, `oss://${bucket}/${artifactKey}`]); -runOssutil(['cp', manifestPath, `oss://${bucket}/agc/latest.json`]); +// Jenkins/ossutil 默认会在目标对象已存在时交互询问并按默认值跳过; +// 发布清单是固定的 latest 指针,必须显式覆盖,否则流水线会误报成功但远端仍保留旧版本。 +runOssutil(['cp', '--force', artifact, `oss://${bucket}/${artifactKey}`]); +runOssutil(['cp', '--force', manifestPath, `oss://${bucket}/agc/latest.json`]); console.log(`[ai-game-creator-shell] 已上传 oss://${bucket}/${artifactKey}`); console.log(`[ai-game-creator-shell] 已上传 oss://${bucket}/agc/latest.json`); diff --git a/docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md b/docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md index a5e11496c..455c37670 100644 --- a/docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md +++ b/docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md @@ -40,7 +40,7 @@ Windows x64),通过 `AGC_UPDATE_ARTIFACT` 指定要发布的安装包,通 OSS 前缀,通过 `AGC_RELEASE_VERSION` 指定三段版本号(仅在明确需要复现指定版本时使用),通过 `AGC_UPDATE_RELEASE_NOTES` 写入发布说明;`--no-bundle` smoke 构建不会读取 OSS、修改版本或生成清单。 -每次发布安装包上传完成后,再上传同一目录生成的 `latest.json`,确保 `downloadUrl` 指向已存在的 OSS 对象;清单和安装包均使用公开可读对象,不在清单中保存凭据、签名或本地路径。构建脚本本身不负责上传 OSS,发布流水线通过 `release:upload` 完成上传。 +每次发布安装包上传完成后,再使用 ossutil 的 `--force` 覆盖上传同一目录生成的 `latest.json`,确保固定的 latest 指针和 `downloadUrl` 指向已存在的 OSS 对象;未显式强制覆盖时,ossutil 在目标已存在时会交互询问并按默认值跳过,不能作为 Jenkins 非交互发布方式。清单和安装包均使用公开可读对象,不在清单中保存凭据、签名或本地路径。构建脚本本身不负责上传 OSS,发布流水线通过 `release:upload` 完成上传。 如需一键构建并上传,可执行 `npm run ai-game-creator-shell:release:upload`。该命令要求本机已安装并配置 `ossutil`, 先按上述规则比较 OSS 版本、递增 patch、构建 Windows x64 NSIS,再上传安装包和 `latest.json`。默认上传到