import { spawnSync } from 'node:child_process'; import path from 'node:path'; import { formatOssutilCommand, readReleaseDryRun } from './release-oss.mjs'; const bucket = process.env.AGC_OSS_BUCKET?.trim() || 'agc-dev'; const endpoint = process.env.AGC_OSS_ENDPOINT?.trim() || 'oss-rg-china-mainland.aliyuncs.com'; if (!/^[a-z0-9][a-z0-9.-]{1,62}$/u.test(bucket) || /[\r\n\0]/u.test(endpoint)) { throw new Error('OSS bucket 或 endpoint 配置无效'); } process.env.AGC_UPDATE_OSS_BASE_URL ||= `https://${bucket}.${endpoint}/agc`; const dryRun = readReleaseDryRun(); const { buildRelease } = await import('./build-release.mjs'); function runOssutil(args) { const binary = process.env.OSSUTIL_BIN?.trim() || 'ossutil'; const accessKeyId = process.env.AGC_OSS_ACCESS_KEY_ID?.trim(); const accessKeySecret = process.env.AGC_OSS_ACCESS_KEY_SECRET; if (Boolean(accessKeyId) !== Boolean(accessKeySecret)) { throw new Error('OSS AccessKey ID 和 Secret 必须同时提供'); } if (dryRun) { // 演练:只打印将要执行的上传,凭据以占位符呈现,不写入 OSS。 console.log( `[dry-run] ${formatOssutilCommand({ binary, args, endpoint, credentials: Boolean(accessKeyId), })}`, ); return; } const credentialArgs = accessKeyId ? ['--access-key-id', accessKeyId, '--access-key-secret', accessKeySecret] : []; const result = spawnSync( binary, [...args, '--endpoint', endpoint, ...credentialArgs], { stdio: 'inherit', shell: false, }, ); if (result.error) { throw new Error(`无法执行 ${binary},请先安装并配置 ossutil`); } if (result.status !== 0) process.exit(result.status ?? 1); } const { artifact, channel, legacyManifestPath, manifest, manifestPath } = await buildRelease(process.argv.slice(2)); const artifactKey = `agc/${channel}/${manifest.version}/${path.basename(artifact)}`; // Jenkins/ossutil 默认会在目标对象已存在时交互询问并按默认值跳过; // 发布清单是固定的 latest 指针,必须显式覆盖,否则流水线会误报成功但远端仍保留旧版本。 runOssutil(['cp', '--force', artifact, `oss://${bucket}/${artifactKey}`]); runOssutil([ 'cp', '--force', `${artifact}.sig`, `oss://${bucket}/${artifactKey}.sig`, ]); runOssutil([ 'cp', '--force', manifestPath, `oss://${bucket}/agc/${channel}/latest.json`, ]); console.log(`[ai-game-creator-shell] 已上传 oss://${bucket}/${artifactKey}`); console.log( `[ai-game-creator-shell] 已上传 oss://${bucket}/agc/${channel}/latest.json`, ); if (legacyManifestPath) { // 迁移桥:让仍走旧 sha256 清单的已发布客户端升级到新协议,一个版本周期后删除。 runOssutil([ 'cp', '--force', legacyManifestPath, `oss://${bucket}/agc/latest.json`, ]); console.log( `[ai-game-creator-shell] 已上传迁移指针 oss://${bucket}/agc/latest.json`, ); } if (dryRun) { console.log('[ai-game-creator-shell] dry-run:未写入任何 OSS 对象'); }