8cffbf6112
过程中发现并修复:本地 pre-commit 钩子在提交合并时pipeline里prettier会重排无关文件,导致本PR携带与客户端无关的上下文 本提交把20个纯格式化文件恢复为主线内容,PR只保留macOS构建管线和发布链路相关改动 后续提交不再把这些文件纳入范围
207 lines
6.0 KiB
JavaScript
207 lines
6.0 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
|
|
import {
|
|
assertRequestedVersionNotBelowChannel,
|
|
buildOssutilArgs,
|
|
issueGlobalVersion,
|
|
maxVersion,
|
|
nextVersion,
|
|
previewNextGlobalVersion,
|
|
resolveSeedBaseline,
|
|
} from './agc-global-version.mjs';
|
|
|
|
function jsonResponse(payload, status = 200) {
|
|
return {
|
|
ok: status >= 200 && status < 300,
|
|
status,
|
|
json: async () => payload,
|
|
};
|
|
}
|
|
|
|
/** 以 URL 为键的假 OSS:只读 fetch + 记录写入。 */
|
|
function createFakeOss({ objects = {} } = {}) {
|
|
const state = { ...objects };
|
|
const writes = [];
|
|
return {
|
|
state,
|
|
writes,
|
|
fetchImpl: async (url) => {
|
|
const key = String(url).replace(/^https?:\/\/[^/]+\//u, '');
|
|
if (!(key in state)) return jsonResponse(null, 404);
|
|
return jsonResponse(state[key]);
|
|
},
|
|
writeImpl: (payload, options = {}) => {
|
|
writes.push({ payload, dryRun: Boolean(options.dryRun) });
|
|
if (!options.dryRun) state['agc/global-version.json'] = payload;
|
|
return { written: !options.dryRun, payload };
|
|
},
|
|
};
|
|
}
|
|
|
|
test('播种基线取仓库版本、各渠道清单与旧迁移指针的最大值', async () => {
|
|
const oss = createFakeOss({
|
|
objects: {
|
|
'agc/dev-win/latest.json': { version: '0.1.57' },
|
|
'agc/dev-mac/latest.json': { version: '0.1.12' },
|
|
'agc/latest.json': { version: '0.1.60' },
|
|
},
|
|
});
|
|
assert.equal(
|
|
await resolveSeedBaseline({
|
|
repoVersion: '0.1.48',
|
|
env: {},
|
|
fetchImpl: oss.fetchImpl,
|
|
}),
|
|
'0.1.60',
|
|
);
|
|
assert.equal(maxVersion('0.1.9', '0.1.10', null), '0.1.10');
|
|
});
|
|
|
|
test('无总号时按播种基线发首号,并把总号写回唯一事实源', async () => {
|
|
const oss = createFakeOss({
|
|
objects: {
|
|
'agc/dev-win/latest.json': { version: '0.1.57' },
|
|
'agc/dev-mac/latest.json': { version: '0.1.12' },
|
|
},
|
|
});
|
|
const issued = await issueGlobalVersion({
|
|
channel: 'dev-win',
|
|
commit: 'a'.repeat(40),
|
|
buildId: '319',
|
|
repoVersion: '0.1.48',
|
|
env: {},
|
|
fetchImpl: oss.fetchImpl,
|
|
writeImpl: oss.writeImpl,
|
|
now: () => '2026-09-20T00:00:00.000Z',
|
|
});
|
|
assert.equal(issued, '0.1.58');
|
|
assert.deepEqual(
|
|
oss.writes.map((entry) => entry.payload.version),
|
|
['0.1.58'],
|
|
);
|
|
assert.equal(oss.state['agc/global-version.json'].version, '0.1.58');
|
|
assert.equal(oss.state['agc/global-version.json'].channel, 'dev-win');
|
|
assert.equal(oss.state['agc/global-version.json'].buildId, '319');
|
|
});
|
|
|
|
test('已有总号时只递增,不再回看渠道清单', async () => {
|
|
const oss = createFakeOss({
|
|
objects: {
|
|
'agc/global-version.json': { version: '0.2.7' },
|
|
// 渠道清单被手工改小也不能把总号拉回去。
|
|
'agc/dev-win/latest.json': { version: '0.1.10' },
|
|
},
|
|
});
|
|
const issued = await issueGlobalVersion({
|
|
channel: 'dev-mac',
|
|
env: {},
|
|
fetchImpl: oss.fetchImpl,
|
|
writeImpl: oss.writeImpl,
|
|
});
|
|
assert.equal(issued, '0.2.8');
|
|
assert.equal(oss.state['agc/global-version.json'].version, '0.2.8');
|
|
});
|
|
|
|
test('dry-run 只预览下一位,不写回、不烧号', async () => {
|
|
const oss = createFakeOss({
|
|
objects: { 'agc/global-version.json': { version: '0.3.4' } },
|
|
});
|
|
const preview = await previewNextGlobalVersion({
|
|
env: {},
|
|
fetchImpl: oss.fetchImpl,
|
|
});
|
|
assert.equal(preview, '0.3.5');
|
|
assert.equal(oss.writes.length, 0);
|
|
|
|
const issued = await issueGlobalVersion({
|
|
channel: 'dev-win',
|
|
env: { AGC_RELEASE_DRY_RUN: '1' },
|
|
fetchImpl: oss.fetchImpl,
|
|
writeImpl: oss.writeImpl,
|
|
});
|
|
assert.equal(issued, '0.3.5');
|
|
assert.deepEqual(
|
|
oss.writes.map((entry) => entry.dryRun),
|
|
[true],
|
|
);
|
|
assert.equal(oss.state['agc/global-version.json'].version, '0.3.4');
|
|
});
|
|
|
|
test('传入低于本渠道清单的号时失败关闭', () => {
|
|
assert.equal(
|
|
assertRequestedVersionNotBelowChannel({
|
|
requested: '0.1.60',
|
|
channelVersion: '0.1.60',
|
|
channel: 'dev-win',
|
|
}),
|
|
'0.1.60',
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
assertRequestedVersionNotBelowChannel({
|
|
requested: '0.1.59',
|
|
channelVersion: '0.1.60',
|
|
channel: 'dev-win',
|
|
}),
|
|
/低于 dev-win 渠道当前清单版本/u,
|
|
);
|
|
assert.equal(
|
|
assertRequestedVersionNotBelowChannel({
|
|
requested: '0.1.1',
|
|
channelVersion: null,
|
|
channel: 'dev-mac',
|
|
}),
|
|
'0.1.1',
|
|
);
|
|
});
|
|
|
|
test('写后回读不一致(并发发号)时失败关闭', async () => {
|
|
const oss = createFakeOss({
|
|
objects: { 'agc/global-version.json': { version: '0.5.1' } },
|
|
});
|
|
await assert.rejects(
|
|
issueGlobalVersion({
|
|
channel: 'dev-win',
|
|
env: {},
|
|
// 模拟另一个发号进程在写入后覆盖了总号。
|
|
writeImpl: (payload, options) => {
|
|
const result = oss.writeImpl(payload, options);
|
|
// 另一个发号进程紧随其后覆盖总号。
|
|
oss.state['agc/global-version.json'] = { version: '0.5.9' };
|
|
return result;
|
|
},
|
|
fetchImpl: oss.fetchImpl,
|
|
}),
|
|
/写后回读不一致/u,
|
|
);
|
|
});
|
|
|
|
test('nextVersion 只在 patch 位递增', () => {
|
|
assert.equal(nextVersion('0.1.9'), '0.1.10');
|
|
assert.equal(nextVersion('1.0.0'), '1.0.1');
|
|
assert.throws(() => nextVersion('0.1'), /不是有效的三段版本号/u);
|
|
});
|
|
|
|
test('ossutil 参数默认使用 v1 签名,并可按需带 region 与 v4', () => {
|
|
const base = {
|
|
args: ['cp', '--force', '/tmp/a.json', 'oss://agc-dev/agc/global-version.json'],
|
|
endpoint: 'oss-rg-china-mainland.aliyuncs.com',
|
|
accessKeyId: 'id',
|
|
accessKeySecret: 'secret',
|
|
env: {},
|
|
};
|
|
const v1 = buildOssutilArgs(base);
|
|
assert.equal(v1[v1.indexOf('--sign-version') + 1], 'v1');
|
|
assert.ok(!v1.includes('--region'));
|
|
assert.equal(v1[v1.indexOf('--access-key-id') + 1], 'id');
|
|
assert.equal(v1[v1.indexOf('--access-key-secret') + 1], 'secret');
|
|
|
|
const v4 = buildOssutilArgs({
|
|
...base,
|
|
env: { AGC_OSS_SIGN_VERSION: 'v4', AGC_OSS_REGION: 'cn-beijing' },
|
|
});
|
|
assert.equal(v4[v4.indexOf('--region') + 1], 'cn-beijing');
|
|
assert.equal(v4[v4.indexOf('--sign-version') + 1], 'v4');
|
|
});
|