mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): enforce architecture support tiers
This commit is contained in:
@@ -5,7 +5,14 @@ const path = require('node:path');
|
||||
const semver = require('semver');
|
||||
|
||||
const RELEASE_IDENTITY_PATH = 'ql3-release.json';
|
||||
const RELEASE_IDENTITY_SCHEMA = 'qinglong/release-identity@v1';
|
||||
const RELEASE_IDENTITY_SCHEMA = 'qinglong/release-identity@v2';
|
||||
const ARCHITECTURE_SUPPORT = Object.freeze({
|
||||
tier1: Object.freeze(['amd64', 'arm64']),
|
||||
candidates: Object.freeze(['ppc64le', 's390x']),
|
||||
experimentalBlocked: Object.freeze(['arm/v7']),
|
||||
legacyOnly: Object.freeze(['arm/v6', '386']),
|
||||
legacyLine: '2.x',
|
||||
});
|
||||
const MAX_RELEASE_IDENTITY_BYTES = 4096;
|
||||
const VERSION_PATTERN =
|
||||
/^3\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$/u;
|
||||
@@ -38,10 +45,11 @@ function normalizeReleaseIdentity(value) {
|
||||
'product',
|
||||
'version',
|
||||
'node',
|
||||
'architectureSupport',
|
||||
'workspacePackageCount',
|
||||
'legacyRootPackageExcluded',
|
||||
]) ||
|
||||
value.schemaVersion !== 1 ||
|
||||
value.schemaVersion !== 2 ||
|
||||
value.schema !== RELEASE_IDENTITY_SCHEMA ||
|
||||
value.product !== 'qinglong3' ||
|
||||
typeof value.version !== 'string' ||
|
||||
@@ -50,6 +58,15 @@ function normalizeReleaseIdentity(value) {
|
||||
!exactKeys(value.node, ['version', 'engine']) ||
|
||||
value.node.version !== '24.18.0' ||
|
||||
value.node.engine !== '>=24.18.0 <25' ||
|
||||
!exactKeys(value.architectureSupport, [
|
||||
'tier1',
|
||||
'candidates',
|
||||
'experimentalBlocked',
|
||||
'legacyOnly',
|
||||
'legacyLine',
|
||||
]) ||
|
||||
JSON.stringify(value.architectureSupport) !==
|
||||
JSON.stringify(ARCHITECTURE_SUPPORT) ||
|
||||
value.workspacePackageCount !== 18 ||
|
||||
value.legacyRootPackageExcluded !== true
|
||||
) {
|
||||
@@ -58,6 +75,15 @@ function normalizeReleaseIdentity(value) {
|
||||
return Object.freeze({
|
||||
...value,
|
||||
node: Object.freeze({ ...value.node }),
|
||||
architectureSupport: Object.freeze({
|
||||
...value.architectureSupport,
|
||||
tier1: Object.freeze([...value.architectureSupport.tier1]),
|
||||
candidates: Object.freeze([...value.architectureSupport.candidates]),
|
||||
experimentalBlocked: Object.freeze([
|
||||
...value.architectureSupport.experimentalBlocked,
|
||||
]),
|
||||
legacyOnly: Object.freeze([...value.architectureSupport.legacyOnly]),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,6 +115,7 @@ function readReleaseIdentity(root) {
|
||||
}
|
||||
|
||||
module.exports = Object.freeze({
|
||||
ARCHITECTURE_SUPPORT,
|
||||
MAX_RELEASE_IDENTITY_BYTES,
|
||||
RELEASE_IDENTITY_PATH,
|
||||
RELEASE_IDENTITY_SCHEMA,
|
||||
|
||||
@@ -17,6 +17,18 @@ const PREDICATE_TYPE =
|
||||
'https://qinglong.dev/attestations/release-candidate-contract/v1';
|
||||
const MAX_REPORT_BYTES = 1024 * 1024;
|
||||
const RELEASE_SCOPES = Object.freeze(['all', 'cluster', 'local']);
|
||||
const NATIVE_ARCHITECTURES = Object.freeze({
|
||||
amd64: Object.freeze({
|
||||
runner: 'ubuntu-24.04',
|
||||
node_arch: 'x64',
|
||||
image_arch: 'amd64',
|
||||
}),
|
||||
arm64: Object.freeze({
|
||||
runner: 'ubuntu-24.04-arm',
|
||||
node_arch: 'arm64',
|
||||
image_arch: 'arm64',
|
||||
}),
|
||||
});
|
||||
const LOCAL_IMAGES = Object.freeze([
|
||||
Object.freeze({
|
||||
image: 'local',
|
||||
@@ -92,6 +104,18 @@ function selectedImages(scope) {
|
||||
return [...CLUSTER_IMAGES, ...LOCAL_IMAGES];
|
||||
}
|
||||
|
||||
function nativeArchitectureMatrix(architectureSupport) {
|
||||
return architectureSupport.tier1.map((architecture) => {
|
||||
const native = NATIVE_ARCHITECTURES[architecture];
|
||||
if (!native) {
|
||||
fail(
|
||||
`Tier 1 architecture lacks a native release runner: ${architecture}`,
|
||||
);
|
||||
}
|
||||
return native;
|
||||
});
|
||||
}
|
||||
|
||||
function validateIdentity(options) {
|
||||
if (
|
||||
typeof options.version !== 'string' ||
|
||||
@@ -147,6 +171,9 @@ function createReleaseCandidateContract(options) {
|
||||
})
|
||||
.sort((left, right) => left.name.localeCompare(right.name, 'en'));
|
||||
const images = selectedImages(options.releaseScope);
|
||||
const nativeArchitectures = nativeArchitectureMatrix(
|
||||
releaseIdentity.architectureSupport,
|
||||
);
|
||||
const imageManifests = images.map((image) => {
|
||||
const manifest = readJson(
|
||||
path.join(root, image.runtime_root, 'package.json'),
|
||||
@@ -182,24 +209,14 @@ function createReleaseCandidateContract(options) {
|
||||
});
|
||||
});
|
||||
const publishMatrix = images.map(({ dockerfile, target, ...image }) => image);
|
||||
const osMatrix = images.flatMap((image) => [
|
||||
{
|
||||
const osMatrix = images.flatMap((image) =>
|
||||
nativeArchitectures.map((architecture) => ({
|
||||
image: image.image,
|
||||
runner: 'ubuntu-24.04',
|
||||
node_arch: 'x64',
|
||||
image_arch: 'amd64',
|
||||
...architecture,
|
||||
dockerfile: image.dockerfile,
|
||||
target: image.target,
|
||||
},
|
||||
{
|
||||
image: image.image,
|
||||
runner: 'ubuntu-24.04-arm',
|
||||
node_arch: 'arm64',
|
||||
image_arch: 'arm64',
|
||||
dockerfile: image.dockerfile,
|
||||
target: image.target,
|
||||
},
|
||||
]);
|
||||
})),
|
||||
);
|
||||
const unsigned = {
|
||||
schemaVersion: 1,
|
||||
schema: SCHEMA,
|
||||
@@ -220,7 +237,18 @@ function createReleaseCandidateContract(options) {
|
||||
),
|
||||
nodeVersion: releaseIdentity.node.version,
|
||||
nodeEngine: releaseIdentity.node.engine,
|
||||
platforms: ['linux/amd64', 'linux/arm64'],
|
||||
architectureSupport: {
|
||||
tier1: [...releaseIdentity.architectureSupport.tier1],
|
||||
candidates: [...releaseIdentity.architectureSupport.candidates],
|
||||
experimentalBlocked: [
|
||||
...releaseIdentity.architectureSupport.experimentalBlocked,
|
||||
],
|
||||
legacyOnly: [...releaseIdentity.architectureSupport.legacyOnly],
|
||||
legacyLine: releaseIdentity.architectureSupport.legacyLine,
|
||||
},
|
||||
platforms: releaseIdentity.architectureSupport.tier1.map(
|
||||
(architecture) => `linux/${architecture}`,
|
||||
),
|
||||
},
|
||||
workspace: {
|
||||
packageCount: workspacePackages.length,
|
||||
@@ -248,6 +276,7 @@ function createReleaseCandidateContract(options) {
|
||||
requiredGates: [
|
||||
'package-boundary',
|
||||
'source-tag-version-identity',
|
||||
'architecture-support-tier',
|
||||
'native-os-vulnerability',
|
||||
'multiarch-oci-layout',
|
||||
'production-dependency-audit',
|
||||
|
||||
@@ -284,11 +284,20 @@ function auditReleaseVersionContract(rootInput = DEFAULT_ROOT) {
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
schemaVersion: 1,
|
||||
schemaVersion: identity.schemaVersion,
|
||||
schema: RELEASE_IDENTITY_SCHEMA,
|
||||
version: identity.version,
|
||||
nodeVersion: identity.node.version,
|
||||
nodeEngine: identity.node.engine,
|
||||
architectureSupport: Object.freeze({
|
||||
tier1: Object.freeze([...identity.architectureSupport.tier1]),
|
||||
candidates: Object.freeze([...identity.architectureSupport.candidates]),
|
||||
experimentalBlocked: Object.freeze([
|
||||
...identity.architectureSupport.experimentalBlocked,
|
||||
]),
|
||||
legacyOnly: Object.freeze([...identity.architectureSupport.legacyOnly]),
|
||||
legacyLine: identity.architectureSupport.legacyLine,
|
||||
}),
|
||||
legacyRootPackageVersion: legacyRoot.version,
|
||||
legacyRootExcluded: true,
|
||||
workspacePackageCount: workspacePaths.length,
|
||||
|
||||
Reference in New Issue
Block a user