mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:38:14 +08:00
285 lines
7.3 KiB
JavaScript
285 lines
7.3 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
const {
|
|
auditReleaseCandidateContract,
|
|
createReleaseCandidateContract,
|
|
parseArguments,
|
|
runCli,
|
|
} = require('../../scripts/ql3-release-candidate-contract.cjs');
|
|
const {
|
|
readReleaseIdentity,
|
|
} = require('../../scripts/lib/ql3-release-identity.cjs');
|
|
|
|
const root = path.resolve(__dirname, '../..');
|
|
const version = readReleaseIdentity(root).version;
|
|
const identity = Object.freeze({
|
|
version,
|
|
sourceRevision: 'a'.repeat(40),
|
|
sourceRef: `refs/tags/v${version}`,
|
|
});
|
|
|
|
test('freezes an independent low-resource local release family', () => {
|
|
const contract = createReleaseCandidateContract({
|
|
root,
|
|
...identity,
|
|
releaseScope: 'local',
|
|
});
|
|
assert.deepEqual(
|
|
contract.images.map((entry) => entry.image),
|
|
['local', 'local-operator'],
|
|
);
|
|
assert.equal(contract.releasePlan.clusterEvidenceRequired, false);
|
|
assert.deepEqual(contract.deploymentFamilies.local.profiles, [
|
|
'edge',
|
|
'standalone',
|
|
]);
|
|
assert.equal(contract.workspace.packageCount, 18);
|
|
assert.equal(
|
|
contract.compatibility.releaseIdentitySchema,
|
|
'qinglong/release-identity@v2',
|
|
);
|
|
assert.deepEqual(contract.compatibility.architectureSupport, {
|
|
tier1: ['amd64', 'arm64'],
|
|
candidates: ['ppc64le', 's390x'],
|
|
experimentalBlocked: ['arm/v7'],
|
|
legacyOnly: ['arm/v6', '386'],
|
|
legacyLine: '2.x',
|
|
});
|
|
assert.deepEqual(contract.compatibility.platforms, [
|
|
'linux/amd64',
|
|
'linux/arm64',
|
|
]);
|
|
assert.match(
|
|
contract.compatibility.releaseIdentityDigest,
|
|
/^sha256:[a-f0-9]{64}$/u,
|
|
);
|
|
assert.match(contract.contractDigest, /^sha256:[a-f0-9]{64}$/u);
|
|
assert.equal(
|
|
contract.requiredGates.includes('architecture-support-tier'),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
contract.requiredGates.includes('offline-deployment-lock-materialization'),
|
|
true,
|
|
);
|
|
assert.deepEqual(
|
|
auditReleaseCandidateContract(contract, {
|
|
root,
|
|
...identity,
|
|
releaseScope: 'local',
|
|
}),
|
|
{
|
|
compatible: true,
|
|
contractDigest: contract.contractDigest,
|
|
releaseScope: 'local',
|
|
workspacePackageCount: 18,
|
|
images: ['local', 'local-operator'],
|
|
clusterEvidenceRequired: false,
|
|
},
|
|
);
|
|
});
|
|
|
|
test('closes the cluster release family with the Worker image', () => {
|
|
const contract = createReleaseCandidateContract({
|
|
root,
|
|
...identity,
|
|
releaseScope: 'cluster',
|
|
});
|
|
assert.deepEqual(
|
|
contract.images.map((entry) => entry.image),
|
|
['control', 'control-ai', 'admin', 'worker'],
|
|
);
|
|
assert.equal(contract.releasePlan.clusterEvidenceRequired, true);
|
|
assert.equal(contract.releasePlan.osMatrix.length, 8);
|
|
assert.deepEqual(
|
|
[
|
|
...new Set(
|
|
contract.releasePlan.osMatrix.map((entry) => entry.image_arch),
|
|
),
|
|
],
|
|
['amd64', 'arm64'],
|
|
);
|
|
assert.deepEqual(
|
|
contract.releasePlan.osMatrix.slice(0, 2).map((entry) => ({
|
|
runner: entry.runner,
|
|
node_arch: entry.node_arch,
|
|
image_arch: entry.image_arch,
|
|
})),
|
|
[
|
|
{
|
|
runner: 'ubuntu-24.04',
|
|
node_arch: 'x64',
|
|
image_arch: 'amd64',
|
|
},
|
|
{
|
|
runner: 'ubuntu-24.04-arm',
|
|
node_arch: 'arm64',
|
|
image_arch: 'arm64',
|
|
},
|
|
],
|
|
);
|
|
assert.equal(
|
|
contract.requiredGates.includes('edge-and-standalone-rollout'),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
contract.requiredGates.includes('cloudnativepg-disaster-recovery-evidence'),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test('combines local and cluster families without weakening either gate', () => {
|
|
const contract = createReleaseCandidateContract({
|
|
root,
|
|
...identity,
|
|
releaseScope: 'all',
|
|
});
|
|
assert.deepEqual(
|
|
contract.images.map((entry) => entry.image),
|
|
['control', 'control-ai', 'admin', 'worker', 'local', 'local-operator'],
|
|
);
|
|
assert.equal(
|
|
contract.requiredGates.includes('edge-and-standalone-rollout'),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
contract.requiredGates.includes('local-operator-entrypoint'),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
contract.requiredGates.includes('cross-image-release-set'),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
contract.requiredGates.includes('durable-oci-release-catalog'),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
contract.requiredGates.includes('offline-deployment-lock-materialization'),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
contract.requiredGates.includes('worker-management-production-evidence'),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test('rejects tag, version and source identity drift', () => {
|
|
assert.throws(
|
|
() =>
|
|
createReleaseCandidateContract({
|
|
root,
|
|
...identity,
|
|
sourceRef: 'refs/heads/next',
|
|
releaseScope: 'local',
|
|
}),
|
|
/exact version tag/,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
createReleaseCandidateContract({
|
|
root,
|
|
...identity,
|
|
version: '2.21.0',
|
|
releaseScope: 'local',
|
|
}),
|
|
/QingLong 3 SemVer/,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
createReleaseCandidateContract({
|
|
root,
|
|
...identity,
|
|
sourceRevision: 'movable',
|
|
releaseScope: 'local',
|
|
}),
|
|
/Git SHA-1/,
|
|
);
|
|
const nextVersion = `${version}-drift`;
|
|
assert.throws(
|
|
() =>
|
|
createReleaseCandidateContract({
|
|
root,
|
|
...identity,
|
|
version: nextVersion,
|
|
sourceRef: `refs/tags/v${nextVersion}`,
|
|
releaseScope: 'local',
|
|
}),
|
|
/repository release identity/,
|
|
);
|
|
});
|
|
|
|
test('rejects a source-derived report mutated after creation', () => {
|
|
const contract = createReleaseCandidateContract({
|
|
root,
|
|
...identity,
|
|
releaseScope: 'local',
|
|
});
|
|
contract.releasePlan.publishMatrix[0].repository = 'unreviewed';
|
|
assert.throws(
|
|
() =>
|
|
auditReleaseCandidateContract(contract, {
|
|
root,
|
|
...identity,
|
|
releaseScope: 'local',
|
|
}),
|
|
/differs from the source-derived contract/,
|
|
);
|
|
});
|
|
|
|
test('writes once and independently audits the exact report through the CLI', (t) => {
|
|
const directory = fs.realpathSync(
|
|
fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-candidate-')),
|
|
);
|
|
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
|
const report = path.join(directory, 'contract.json');
|
|
const common = [
|
|
`--version=${version}`,
|
|
`--source-revision=${identity.sourceRevision}`,
|
|
`--source-ref=refs/tags/v${version}`,
|
|
'--release-scope=local',
|
|
];
|
|
const output = { write() {} };
|
|
runCli(['--mode=create', ...common, `--output=${report}`], root, output);
|
|
assert.equal(fs.statSync(report).mode & 0o777, 0o600);
|
|
assert.equal(
|
|
runCli(['--mode=audit', ...common, `--report=${report}`], root, output)
|
|
.compatible,
|
|
true,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
runCli(['--mode=create', ...common, `--output=${report}`], root, output),
|
|
/output must be unused/,
|
|
);
|
|
});
|
|
|
|
test('parses only exact closed create and audit modes', () => {
|
|
const common = [
|
|
`--version=${version}`,
|
|
`--source-revision=${identity.sourceRevision}`,
|
|
`--source-ref=refs/tags/v${version}`,
|
|
'--release-scope=local',
|
|
];
|
|
assert.equal(
|
|
parseArguments(['--mode=create', ...common, '--output=/tmp/report.json'])
|
|
.mode,
|
|
'create',
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
parseArguments([
|
|
'--mode=create',
|
|
...common,
|
|
'--output=/tmp/report.json',
|
|
'--extra=true',
|
|
]),
|
|
/arguments are invalid/,
|
|
);
|
|
});
|