AGC 版本号以仓库为准,新增 bump-version 提升版本命令 #298
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@genarrative/ai-game-creator-shell",
|
||||
"private": true,
|
||||
"version": "0.1.12",
|
||||
"version": "0.1.19",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node scripts/start-tauri-dev.mjs",
|
||||
@@ -9,6 +9,7 @@
|
||||
"dev-stack": "node scripts/start-dev-stack.mjs",
|
||||
"build": "node scripts/build-release.mjs",
|
||||
"release:upload": "node scripts/release-upload.mjs",
|
||||
"bump-version": "node scripts/bump-version.mjs",
|
||||
"skill-pack:check": "node scripts/check-skill-pack.mjs",
|
||||
"skill-pack:sync": "node scripts/check-skill-pack.mjs --write",
|
||||
"skill-pack:test": "node --test scripts/check-skill-pack.test.mjs",
|
||||
|
||||
@@ -5,6 +5,7 @@ import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const appRoot = fileURLToPath(new URL('..', import.meta.url));
|
||||
const repoRoot = path.resolve(appRoot, '../..');
|
||||
const defaultReleaseTarget = 'x86_64-pc-windows-msvc';
|
||||
const releaseTarget =
|
||||
process.env.AGC_BUILD_TARGET?.trim() || defaultReleaseTarget;
|
||||
@@ -49,16 +50,23 @@ function parseVersion(value, label) {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function nextPatchVersion(localVersion, remoteVersion) {
|
||||
const local = parseVersion(localVersion, '本地版本');
|
||||
const remote =
|
||||
remoteVersion == null ? null : parseVersion(remoteVersion, 'OSS版本');
|
||||
const base = remote && compareVersions(remote, local) > 0 ? remote : local;
|
||||
const [major, minor, patch] = base.split('.').map(Number);
|
||||
if (patch === Number.MAX_SAFE_INTEGER) {
|
||||
throw new Error(`版本号 patch 已达到上限:${base}`);
|
||||
export function bumpVersion(current, target = 'patch') {
|
||||
const [major, minor, patch] =
|
||||
parseVersion(current, '当前版本').split('.').map(Number);
|
||||
if (target === 'major') return `${major + 1}.0.0`;
|
||||
if (target === 'minor') return `${major}.${minor + 1}.0`;
|
||||
if (target === 'patch' || target == null) {
|
||||
if (patch === Number.MAX_SAFE_INTEGER) {
|
||||
throw new Error(`版本号 patch 已达到上限:${current}`);
|
||||
}
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
}
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
if (typeof target === 'string' && /^\d+\.\d+\.\d+$/u.test(target)) {
|
||||
return parseVersion(target, '指定版本');
|
||||
}
|
||||
throw new Error(
|
||||
`无法识别的版本目标:${String(target)}(支持 patch / minor / major / 明确的三段版本号)`,
|
||||
);
|
||||
}
|
||||
|
||||
async function readRemoteVersion() {
|
||||
@@ -88,14 +96,52 @@ function replaceVersionLine(source, version, pattern, label) {
|
||||
return source.replace(pattern, `$1${version}$3`);
|
||||
}
|
||||
|
||||
export async function prepareReleaseVersion() {
|
||||
const localVersion = parseVersion(readPackageJson().version, '本地版本');
|
||||
const remoteVersion = await readRemoteVersion();
|
||||
const requestedVersion = process.env.AGC_RELEASE_VERSION?.trim();
|
||||
const nextVersion = requestedVersion
|
||||
? parseVersion(requestedVersion, '指定版本')
|
||||
: nextPatchVersion(localVersion, remoteVersion);
|
||||
const versionFileSources = [
|
||||
{
|
||||
file: packageJsonPath,
|
||||
pattern: /("version"\s*:\s*")([^"]+)(")/u,
|
||||
label: 'package.json',
|
||||
},
|
||||
{
|
||||
file: rootPackageLockPath,
|
||||
pattern: /("apps\/ai-game-creator-shell"\s*:\s*\{\s*\n\s*"name"\s*:\s*"@genarrative\/ai-game-creator-shell"\s*,\s*\n\s*"version"\s*:\s*")([^"]+)(")/u,
|
||||
label: 'package-lock.json',
|
||||
},
|
||||
{
|
||||
file: tauriConfigPath,
|
||||
pattern: /("version"\s*:\s*")([^"]+)(")/u,
|
||||
label: 'tauri.conf.json',
|
||||
},
|
||||
{
|
||||
file: cargoManifestPath,
|
||||
pattern: /(^\[package\][\s\S]*?^version\s*=\s*")([^"]+)(")/mu,
|
||||
label: 'Cargo.toml',
|
||||
},
|
||||
{
|
||||
file: cargoLockPath,
|
||||
pattern: /(^name\s*=\s*"genarrative-ai-game-creator-shell"\s*\nversion\s*=\s*")([^"]+)(")/mu,
|
||||
label: 'Cargo.lock',
|
||||
},
|
||||
];
|
||||
|
||||
export function readLocalVersion() {
|
||||
return parseVersion(readPackageJson().version, '本地版本');
|
||||
}
|
||||
|
||||
export function validateVersionConsistency(expectedVersion) {
|
||||
for (const source of versionFileSources) {
|
||||
const match = fs.readFileSync(source.file, 'utf8').match(source.pattern);
|
||||
const actual = match ? match[2].trim() : '<未找到>';
|
||||
if (actual !== expectedVersion) {
|
||||
throw new Error(
|
||||
`AGC 版本号不一致:${source.label} 应为 ${expectedVersion},实际 ${actual}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function writeVersionFiles(version) {
|
||||
const nextVersion = parseVersion(version, '目标版本');
|
||||
const packageSource = fs.readFileSync(packageJsonPath, 'utf8');
|
||||
fs.writeFileSync(
|
||||
packageJsonPath,
|
||||
@@ -151,14 +197,47 @@ export async function prepareReleaseVersion() {
|
||||
),
|
||||
);
|
||||
|
||||
console.log(
|
||||
requestedVersion
|
||||
? `[ai-game-creator-shell] 使用指定版本 ${nextVersion}(本地 ${localVersion} / OSS ${remoteVersion ?? '不存在'})`
|
||||
: `[ai-game-creator-shell] 版本 ${localVersion} / OSS ${remoteVersion ?? '不存在'} -> ${nextVersion}`,
|
||||
);
|
||||
return nextVersion;
|
||||
}
|
||||
|
||||
export function assertVersionCommitted() {
|
||||
const repoPaths = versionFileSources.map((source) =>
|
||||
path.relative(repoRoot, source.file).replaceAll('\\', '/'),
|
||||
);
|
||||
const changed =
|
||||
spawnSync(
|
||||
'git',
|
||||
['diff', '--quiet', 'HEAD', '--', ...repoPaths],
|
||||
{ cwd: repoRoot },
|
||||
).status === 1;
|
||||
if (changed) {
|
||||
throw new Error(
|
||||
'版本文件相对 HEAD 存在未提交改动,请先提交后再发布:运行 npm --prefix apps/ai-game-creator-shell run bump-version -- --commit',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function prepareReleaseVersion() {
|
||||
const localVersion = readLocalVersion();
|
||||
validateVersionConsistency(localVersion);
|
||||
console.log(
|
||||
`[ai-game-creator-shell] 使用仓库版本 ${localVersion}(不再自动递增或自由指定版本)`,
|
||||
);
|
||||
return localVersion;
|
||||
}
|
||||
|
||||
export async function assertVersionNotBelowOss(version) {
|
||||
const remoteVersion = await readRemoteVersion();
|
||||
if (remoteVersion && compareVersions(version, remoteVersion) < 0) {
|
||||
throw new Error(
|
||||
`仓库版本 ${version} 低于线上 OSS 版本 ${remoteVersion}。请先运行 npm --prefix apps/ai-game-creator-shell run bump-version 提升版本后再发布。`,
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
`[ai-game-creator-shell] 线上 OSS 版本 ${remoteVersion ?? '不存在'},发布版本 ${version}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function runTauriBuild(args = []) {
|
||||
const noBundle = args.includes('--no-bundle');
|
||||
const hasTarget = args.includes('--target');
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { test } from 'node:test';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import {
|
||||
bumpVersion,
|
||||
compareVersions,
|
||||
createUpdateManifest,
|
||||
nextPatchVersion,
|
||||
selectReleaseArtifact,
|
||||
} from './build-release.mjs';
|
||||
|
||||
test('selects an explicit release artifact when configured', () => {
|
||||
const artifactPath = new URL('../package.json', import.meta.url).pathname;
|
||||
const artifactPath = fileURLToPath(
|
||||
new URL('../package.json', import.meta.url),
|
||||
);
|
||||
const previous = process.env.AGC_UPDATE_ARTIFACT;
|
||||
process.env.AGC_UPDATE_ARTIFACT = artifactPath;
|
||||
try {
|
||||
@@ -30,7 +33,7 @@ test('does not select unsupported files', () => {
|
||||
|
||||
test('manifest contains version, download URL and integrity fields', () => {
|
||||
const manifest = createUpdateManifest(
|
||||
new URL('../package.json', import.meta.url).pathname,
|
||||
fileURLToPath(new URL('../package.json', import.meta.url)),
|
||||
);
|
||||
assert.match(manifest.version, /^\d+\.\d+\.\d+$/u);
|
||||
assert.match(
|
||||
@@ -46,7 +49,7 @@ test('manifest preserves multiline release notes', () => {
|
||||
process.env.AGC_UPDATE_RELEASE_NOTES = '第一行\n第二行\r\n第三行';
|
||||
try {
|
||||
const manifest = createUpdateManifest(
|
||||
new URL('../package.json', import.meta.url).pathname,
|
||||
fileURLToPath(new URL('../package.json', import.meta.url)),
|
||||
);
|
||||
assert.equal(manifest.releaseNotes, '第一行\n第二行\r\n第三行');
|
||||
} finally {
|
||||
@@ -55,11 +58,14 @@ test('manifest preserves multiline release notes', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('next release version follows the higher local or OSS version', () => {
|
||||
test('bump version follows the requested target', () => {
|
||||
assert.equal(compareVersions('0.1.15', '0.1.12'), 1);
|
||||
assert.equal(nextPatchVersion('0.1.12', '0.1.15'), '0.1.16');
|
||||
assert.equal(nextPatchVersion('0.1.18', '0.1.15'), '0.1.19');
|
||||
assert.equal(nextPatchVersion('0.1.12', null), '0.1.13');
|
||||
assert.equal(compareVersions('0.1.12', '0.1.12'), 0);
|
||||
assert.equal(bumpVersion('0.1.19', 'patch'), '0.1.20');
|
||||
assert.equal(bumpVersion('0.1.19'), '0.1.20');
|
||||
assert.equal(bumpVersion('0.1.19', 'minor'), '0.2.0');
|
||||
assert.equal(bumpVersion('0.1.19', 'major'), '1.0.0');
|
||||
assert.equal(bumpVersion('0.1.12', '0.1.25'), '0.1.25');
|
||||
});
|
||||
|
||||
test('release upload forces overwrite for versioned artifact and latest pointer', () => {
|
||||
@@ -72,3 +78,12 @@ test('release upload forces overwrite for versioned artifact and latest pointer'
|
||||
2,
|
||||
);
|
||||
});
|
||||
|
||||
test('release upload verifies the version is committed and not below OSS', () => {
|
||||
const source = readFileSync(
|
||||
new URL('./release-upload.mjs', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
assert.match(source, /assertVersionCommitted\(\)/u);
|
||||
assert.match(source, /assertVersionNotBelowOss\(/u);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const appRoot = fileURLToPath(new URL('..', import.meta.url));
|
||||
const repoRoot = path.resolve(appRoot, '../..');
|
||||
|
||||
const {
|
||||
bumpVersion,
|
||||
readLocalVersion,
|
||||
writeVersionFiles,
|
||||
} = await import('./build-release.mjs');
|
||||
|
||||
const versionFiles = [
|
||||
'apps/ai-game-creator-shell/package.json',
|
||||
'package-lock.json',
|
||||
'apps/ai-game-creator-shell/src-tauri/tauri.conf.json',
|
||||
'apps/ai-game-creator-shell/src-tauri/Cargo.toml',
|
||||
'apps/ai-game-creator-shell/src-tauri/Cargo.lock',
|
||||
];
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = argv.slice(2);
|
||||
const explicitIndex = args.indexOf('--version');
|
||||
const explicit = explicitIndex >= 0 ? args[explicitIndex + 1] : null;
|
||||
const target =
|
||||
explicit ??
|
||||
args.find((arg) => arg === 'patch' || arg === 'minor' || arg === 'major') ??
|
||||
'patch';
|
||||
return { target, commit: args.includes('--commit') };
|
||||
}
|
||||
|
||||
function runGit(args) {
|
||||
const result = spawnSync('git', args, { cwd: repoRoot, stdio: 'inherit' });
|
||||
if (result.error) throw result.error;
|
||||
return result.status ?? 1;
|
||||
}
|
||||
|
||||
function hasUncommittedVersionChanges() {
|
||||
return (
|
||||
spawnSync('git', ['diff', '--quiet', '--', ...versionFiles], {
|
||||
cwd: repoRoot,
|
||||
}).status === 1
|
||||
);
|
||||
}
|
||||
|
||||
function otherStagedFiles() {
|
||||
const result = spawnSync(
|
||||
'git',
|
||||
['diff', '--cached', '--name-only'],
|
||||
{ cwd: repoRoot, encoding: 'utf8' },
|
||||
);
|
||||
if (result.status !== 0) {
|
||||
throw new Error('无法读取已暂存文件列表');
|
||||
}
|
||||
const versionSet = new Set(versionFiles);
|
||||
return result.stdout
|
||||
.split('\n')
|
||||
.map((file) => file.trim())
|
||||
.filter(Boolean)
|
||||
.filter((file) => !versionSet.has(file));
|
||||
}
|
||||
|
||||
const { target, commit } = parseArgs(process.argv);
|
||||
|
||||
// 提交前先确认没有其他已暂存改动,避免把无关改动一并提交;若存在则在改动任何文件前中止。
|
||||
if (commit) {
|
||||
const others = otherStagedFiles();
|
||||
if (others.length > 0) {
|
||||
console.error(
|
||||
`[bump-version] 检测到其他已暂存改动,为避免误提交,请先单独处理后再运行 --commit:\n ${others.join('\n ')}`,
|
||||
);
|
||||
console.error(
|
||||
'提示:用 git restore --staged <file> 取消暂存,或先提交这些改动。',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const current = readLocalVersion();
|
||||
|
||||
// 若版本文件已带着一次未提交的提升(例如先默认跑过一次),再 --commit 时不再重复递增,直接提交现有改动。
|
||||
const next =
|
||||
commit && hasUncommittedVersionChanges()
|
||||
? current
|
||||
: bumpVersion(current, target);
|
||||
|
||||
writeVersionFiles(next);
|
||||
|
||||
if (!commit) {
|
||||
console.log(
|
||||
`[bump-version] 版本 ${current} -> ${next}(已写入版本文件,未创建提交;如需提交加 --commit)`,
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const addStatus = runGit(['add', ...versionFiles]);
|
||||
if (addStatus !== 0) process.exit(addStatus);
|
||||
|
||||
const hasDiff =
|
||||
spawnSync('git', ['diff', '--cached', '--quiet'], {
|
||||
cwd: repoRoot,
|
||||
}).status === 1;
|
||||
|
||||
if (!hasDiff) {
|
||||
console.log(`[bump-version] 版本未变化(已是 ${current}),未创建提交`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const commitStatus = runGit([
|
||||
'commit',
|
||||
'-m',
|
||||
`提升 AGC 版本至 ${next}`,
|
||||
'-m',
|
||||
`- AGC 客户端版本提升至 ${next}`,
|
||||
'-m',
|
||||
'- 同步更新 package.json、根 package-lock.json、tauri.conf.json、Cargo.toml、Cargo.lock 中的 AGC 包条目',
|
||||
]);
|
||||
if (commitStatus !== 0) process.exit(commitStatus);
|
||||
|
||||
console.log(`[bump-version] 已提交版本 ${next}(本地提交,未推送)`);
|
||||
@@ -9,8 +9,13 @@ if (!/^[a-z0-9][a-z0-9.-]{1,62}$/u.test(bucket) || /[\r\n\0]/u.test(endpoint)) {
|
||||
}
|
||||
process.env.AGC_UPDATE_OSS_BASE_URL ||= `https://${bucket}.${endpoint}/agc`;
|
||||
|
||||
const { generateUpdateManifest, prepareReleaseVersion, runTauriBuild } =
|
||||
await import('./build-release.mjs');
|
||||
const {
|
||||
assertVersionCommitted,
|
||||
assertVersionNotBelowOss,
|
||||
generateUpdateManifest,
|
||||
prepareReleaseVersion,
|
||||
runTauriBuild,
|
||||
} = await import('./build-release.mjs');
|
||||
|
||||
function runOssutil(args) {
|
||||
const binary = process.env.OSSUTIL_BIN?.trim() || 'ossutil';
|
||||
@@ -36,7 +41,9 @@ function runOssutil(args) {
|
||||
if (result.status !== 0) process.exit(result.status ?? 1);
|
||||
}
|
||||
|
||||
await prepareReleaseVersion();
|
||||
const releaseVersion = await prepareReleaseVersion();
|
||||
assertVersionCommitted();
|
||||
await assertVersionNotBelowOss(releaseVersion);
|
||||
runTauriBuild([]);
|
||||
const { artifact, manifestPath, manifest } = generateUpdateManifest();
|
||||
const artifactKey = `agc/${manifest.version}/${path.basename(artifact)}`;
|
||||
|
||||
+1
-1
@@ -1703,7 +1703,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "genarrative-ai-game-creator-shell"
|
||||
version = "0.1.12"
|
||||
version = "0.1.19"
|
||||
dependencies = [
|
||||
"agent-runtime-core",
|
||||
"axum",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "genarrative-ai-game-creator-shell"
|
||||
version = "0.1.12"
|
||||
version = "0.1.19"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Genarrative AI Game Creator",
|
||||
"version": "0.1.12",
|
||||
"version": "0.1.19",
|
||||
"identifier": "world.genarrative.ai-game-creator",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm --prefix ../.. run agc:serve",
|
||||
|
||||
@@ -29,21 +29,62 @@ AGC 每次启动时由根窗口检查一次公开 OSS 更新清单。清单默
|
||||
|
||||
## 发布约定
|
||||
|
||||
当前发布目标固定为 Windows x64 NSIS。执行 `npm run ai-game-creator-shell:build` 会先读取
|
||||
`VITE_AGC_UPDATE_MANIFEST_URL`(默认 `https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc/latest.json`)的
|
||||
`latest.json`,取本地与 OSS 的较高版本并递增一个 patch,然后同步更新 package、Tauri 和 Cargo
|
||||
版本后再向 Tauri 传入 `--target x86_64-pc-windows-msvc` 构建。OSS 清单首次不存在时按本地版本递增;
|
||||
OSS 请求失败、清单格式错误或版本无效会终止发布,避免覆盖线上版本。构建完成后自动扫描 `.exe`
|
||||
安装包,并在 `apps/ai-game-creator-shell/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/latest.json`
|
||||
生成包含版本、下载地址、大小和 SHA-256 的清单。可通过 `AGC_BUILD_TARGET` 显式覆盖目标(发布仍应使用
|
||||
Windows x64),通过 `AGC_UPDATE_ARTIFACT` 指定要发布的安装包,通过 `AGC_UPDATE_OSS_BASE_URL` 指定
|
||||
OSS 前缀,通过 `AGC_RELEASE_VERSION` 指定三段版本号(仅在明确需要复现指定版本时使用),通过
|
||||
`AGC_UPDATE_RELEASE_NOTES` 写入发布说明,支持多行文本且保留内部换行;`--no-bundle` smoke 构建不会读取 OSS、修改版本或生成清单。
|
||||
> ⚠️ 发布新版本时,版本号提升只发生在构建/上传工作区内,**不会自动回写仓库**。
|
||||
> 🔑 版本号以仓库为准。发布流程不再自动递增版本,也不再支持 `AGC_RELEASE_VERSION` 自由指定版本。
|
||||
> 发布前必须先运行版本提升命令,把目标版本写回仓库并提交,发布只读取这份已提交的版本。
|
||||
|
||||
**发布前先提升版本(开发者执行)**
|
||||
|
||||
执行以下任一命令,会把 AGC 版本写入 `package.json`、根 `package-lock.json`、
|
||||
`tauri.conf.json`、`Cargo.toml`、`Cargo.lock` 五处。**默认只写入版本文件、不会创建提交**;
|
||||
需要创建提交时在命令末尾加 `--commit`(只做本地提交,push 由开发者自行确认):
|
||||
|
||||
```bash
|
||||
# 提升一个 patch(默认),只写入版本文件,例如 0.1.19 -> 0.1.20
|
||||
npm run ai-game-creator-shell:bump-version
|
||||
npm run ai-game-creator-shell:bump-version -- patch
|
||||
|
||||
# 提升版本并本地提交
|
||||
npm run ai-game-creator-shell:bump-version -- --commit
|
||||
|
||||
# 提升 minor / major
|
||||
npm run ai-game-creator-shell:bump-version -- minor
|
||||
npm run ai-game-creator-shell:bump-version -- major
|
||||
|
||||
# 指定明确的三段版本(一般只用于复现/回填)
|
||||
npm run ai-game-creator-shell:bump-version -- --version 0.1.25
|
||||
|
||||
# 指定版本并提交
|
||||
npm run ai-game-creator-shell:bump-version -- --version 0.1.25 --commit
|
||||
```
|
||||
|
||||
若先用默认方式写入版本(未提交),再补 `--commit`,命令会识别到工作区已提升、直接提交现有改动,
|
||||
**不会重复递增**。提交前若检测到**除版本文件外的其他已暂存改动**,会中止并提示先处理,避免误提交无关内容。
|
||||
提交标题固定为“提升 AGC 版本至 x.y.z”。
|
||||
|
||||
**构建/发布只读仓库版本(`npm run ai-game-creator-shell:build` 与 `release:upload`)**
|
||||
|
||||
构建与发布都不再改写版本文件,而是读取仓库版本。前者只校验五个版本来源一致(不一致会中止,
|
||||
防止漂移);后者(`release:upload`)在上传前还会做两道校验:
|
||||
|
||||
1. 校验版本相对 `HEAD` 已提交(`assertVersionCommitted`):若版本文件存在未提交改动会中止,
|
||||
确保本地发布与 Jenkins 检出的是同一个版本。
|
||||
2. 通过 `assertVersionNotBelowOss` 读取 OSS `latest.json`:若仓库版本**低于**线上版本会中止,
|
||||
并提示先运行 `bump-version`,避免回退线上版本;OSS 读取失败或清单格式错误同样会中止。
|
||||
仓库版本等于或高于线上版本才放行。
|
||||
|
||||
当前发布目标固定为 Windows x64 NSIS,会向 Tauri 传入 `--target x86_64-pc-windows-msvc` 构建。
|
||||
构建完成后自动扫描 `.exe` 安装包,并在
|
||||
`apps/ai-game-creator-shell/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/latest.json`
|
||||
生成包含版本、下载地址、大小和 SHA-256 的清单,版本取仓库 `package.json` 的 `version`。
|
||||
可通过 `AGC_BUILD_TARGET` 显式覆盖目标(发布仍应使用 Windows x64),通过 `AGC_UPDATE_ARTIFACT`
|
||||
指定要发布的安装包,通过 `AGC_UPDATE_OSS_BASE_URL` 指定 OSS 前缀,通过 `AGC_UPDATE_RELEASE_NOTES`
|
||||
写入发布说明(支持多行文本且保留内部换行);`--no-bundle` smoke 构建不会读取 OSS、修改版本或生成清单。
|
||||
|
||||
每次发布安装包上传完成后,再使用 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`。默认上传到
|
||||
先按上述规则取仓库版本并校验、构建 Windows x64 NSIS,再上传安装包和 `latest.json`。默认上传到
|
||||
`agc-dev` / `oss-rg-china-mainland.aliyuncs.com`,也可用 `AGC_OSS_BUCKET`、`AGC_OSS_ENDPOINT` 和 `OSSUTIL_BIN`
|
||||
覆盖;本机执行时凭据由 ossutil 本机配置读取,不能写入仓库或命令行参数。
|
||||
|
||||
@@ -63,6 +104,6 @@ Jenkins Checkout 的 `git clean -fdx` 会清理该构建目录,因此每次全
|
||||
Jenkins Job 在“Build and upload”阶段通过受保护凭据 ID `AliyunAccessKeyId` 和
|
||||
`AliyunaccessKeySecret` 注入 AccessKey,仅在当前进程运行时传给 ossutil,不写入仓库、workspace 或构建日志;
|
||||
本机运行仍使用 ossutil 配置。凭据必须具备 `PutObject` 权限;OSS 对客户端保持公共读即可,公共读本身不授予
|
||||
Jenkins 上传权限。由于版本号取决于 OSS 当前清单,Job 已关闭并发构建;若 Jenkins
|
||||
上存在多个 AGC 发布 Job,还应使用同一个 Lockable Resource 串行化发布。Job 参数
|
||||
`AGC_RELEASE_VERSION` 留空时自动递增,填写后会使用指定版本并更新对应的 `latest.json`,因此回滚或测试旧版本前应确认不会覆盖线上更新入口。
|
||||
Jenkins 上传权限。由于版本号以仓库为准、发布只读仓库版本且禁止回退线上版本,Job 已关闭并发构建;若 Jenkins
|
||||
上存在多个 AGC 发布 Job,还应使用同一个 Lockable Resource 串行化发布。发布前请先在仓库上运行
|
||||
`npm run ai-game-creator-shell:bump-version` 提升并提交版本,再触发发布流水线。
|
||||
|
||||
@@ -21,7 +21,6 @@ pipeline {
|
||||
parameters {
|
||||
string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '源码分支')
|
||||
string(name: 'COMMIT_HASH', defaultValue: '', description: '可选,指定属于 SOURCE_BRANCH 的 Git commit')
|
||||
string(name: 'AGC_RELEASE_VERSION', defaultValue: '', description: '可选,指定三段版本号;留空则按 OSS 与本地版本自动递增 patch')
|
||||
text(name: 'AGC_UPDATE_RELEASE_NOTES', defaultValue: '', description: '可选,支持多行文本,写入 latest.json 的发布说明')
|
||||
string(name: 'OSSUTIL_BIN', defaultValue: 'ossutil', description: 'ossutil 或 ossutil.exe 的绝对路径/命令名')
|
||||
}
|
||||
@@ -127,7 +126,6 @@ pipeline {
|
||||
withEnv([
|
||||
"PATH=${env.AGC_WINDOWS_PATH}",
|
||||
"OSSUTIL_BIN=${params.OSSUTIL_BIN}",
|
||||
"AGC_RELEASE_VERSION=${params.AGC_RELEASE_VERSION}",
|
||||
"AGC_UPDATE_RELEASE_NOTES=${params.AGC_UPDATE_RELEASE_NOTES}",
|
||||
]) {
|
||||
powershell '''
|
||||
|
||||
Generated
+1
-1
@@ -93,7 +93,7 @@
|
||||
},
|
||||
"apps/ai-game-creator-shell": {
|
||||
"name": "@genarrative/ai-game-creator-shell",
|
||||
"version": "0.1.12",
|
||||
"version": "0.1.19",
|
||||
"dependencies": {
|
||||
"@cubone/react-file-manager": "^1.35.0",
|
||||
"@genarrative/image-canvas-core": "0.1.0",
|
||||
|
||||
@@ -172,6 +172,7 @@
|
||||
"ai-game-creator-shell:dev-server": "npm --prefix apps/ai-game-creator-shell run dev-server",
|
||||
"ai-game-creator-shell:build": "npm --prefix apps/ai-game-creator-shell run build --",
|
||||
"ai-game-creator-shell:release:upload": "npm --prefix apps/ai-game-creator-shell run release:upload",
|
||||
"ai-game-creator-shell:bump-version": "npm --prefix apps/ai-game-creator-shell run bump-version --",
|
||||
"ai-game-creator-shell:llm-status": "npm --prefix apps/ai-game-creator-shell run llm-status --",
|
||||
"ai-game-creator-shell:agent-task": "npm --prefix apps/ai-game-creator-shell run agent-task --",
|
||||
"agc:chat": "npm --prefix apps/ai-game-creator-shell run chat --",
|
||||
|
||||
Reference in New Issue
Block a user