@kungfu-tech/buildchain 3.0.2-alpha.0 → 3.0.2-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/actions/github-artifact-attestation/README.md +10 -0
- package/actions/promote-buildchain-ref/README.md +7 -0
- package/bin/buildchain.mjs +5 -0
- package/bin/internal/trust-release-cli.mjs +74 -3
- package/contracts/auditable-demo-media-profiles-v1.json +168 -0
- package/contracts/evidence/auditable-demo-web-delivery-v1.json +103 -0
- package/contracts/fixtures/auditable-demo-web-delivery-v1/complete-transcript.txt +2 -0
- package/contracts/fixtures/auditable-demo-web-delivery-v1/public-projection.json +16 -0
- package/contracts/fixtures/auditable-demo-web-delivery-v1/scene.json +12 -0
- package/dist/site/artifact-schemas.json +5 -1
- package/dist/site/buildchain-contract.json +133 -34
- package/dist/site/buildchain-site.json +159 -40
- package/dist/site/capability-registry.json +14 -13
- package/dist/site/cli-registry.json +12 -0
- package/dist/site/controller-registry.json +72 -4
- package/dist/site/kfd-claims.json +338 -20
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +24 -9
- package/dist/site/node-api-registry.json +89 -11
- package/dist/site/page-registry.json +135 -25
- package/dist/site/public-surface-audit.json +203 -21
- package/dist/site/publication-authority-registry.json +72 -2
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-model.json +2 -1
- package/dist/site/release-passport-check-manifest.json +1 -0
- package/dist/site/release-provenance.json +6 -0
- package/dist/site/schemas/release-passport-v1.schema.json +6 -0
- package/dist/site/site-manifest.json +21 -13
- package/dist/site/workflow-registry.json +167 -13
- package/docs/MAP.md +5 -1
- package/docs/auditable-demo.md +55 -3
- package/docs/binary-distribution.md +7 -0
- package/docs/cli.md +9 -0
- package/docs/dev-alpha-candidate-patrol.md +111 -0
- package/docs/github-artifact-attestation.md +219 -0
- package/docs/release-governance.md +32 -0
- package/docs/release-passport.md +13 -0
- package/docs/reusable-build-surface.md +83 -61
- package/docs/runtime-train-validation.md +21 -0
- package/docs/versioning.md +1 -0
- package/package.json +8 -1
- package/packages/core/artifact-signing-result.js +228 -0
- package/packages/core/artifact-signing.js +412 -0
- package/packages/core/buildchain-config.js +58 -0
- package/packages/core/buildchain-contract.js +14 -0
- package/packages/core/buildchain-kfd-claims.js +5 -0
- package/packages/core/buildchain-publication-authority.js +3 -0
- package/packages/core/cache-evidence.js +288 -0
- package/packages/core/channel-candidate.js +186 -0
- package/packages/core/detached-artifact-signature.js +121 -0
- package/packages/core/diagnostics.js +276 -10
- package/packages/core/github-artifact-attestation.js +642 -0
- package/packages/core/github-governance-authority.js +77 -16
- package/packages/core/index.js +62 -0
- package/packages/core/publication-authority.js +1 -1
- package/packages/core/release-passport-contract.js +2 -0
- package/packages/core/release-passport.js +60 -3
- package/scripts/auditable-demo.mjs +520 -28
- package/scripts/build-contract-core.mjs +31 -0
- package/scripts/buildchain-channel-router.mjs +8 -2
- package/scripts/check-inventory.mjs +9 -0
- package/scripts/create-github-artifact-attestation-policy.mjs +62 -0
- package/scripts/dev-alpha-candidate-patrol.mjs +864 -0
- package/scripts/dispatch-artifact-signing-authority.mjs +152 -0
- package/scripts/finalize-native-artifact-signing-result.mjs +96 -0
- package/scripts/generate-channel-promotion-workflow.mjs +6 -0
- package/scripts/generate-site-bundle.mjs +20 -0
- package/scripts/import-artifact-signing-results.mjs +76 -0
- package/scripts/inspect-artifact-signing-requests.mjs +101 -0
- package/scripts/locked-source-checkout.mjs +48 -0
- package/scripts/materialize-artifact-signing-request.mjs +66 -0
- package/scripts/merge-artifact-signing-results.mjs +76 -0
- package/scripts/publish-github-artifact-attestation-evidence.mjs +201 -0
- package/scripts/reconcile-github-governance.mjs +158 -25
- package/scripts/release-candidate-resolver.mjs +12 -0
- package/scripts/release-line-policy.mjs +27 -0
- package/scripts/runtime-ref-core.mjs +10 -6
- package/scripts/seal-artifact-signing-requests.mjs +368 -0
- package/scripts/shifu-gate-profile.mjs +26 -20
- package/scripts/sign-detached-artifact-requests.mjs +237 -0
- package/scripts/stage-github-artifact-attestation-inputs.mjs +65 -0
- package/scripts/verify-artifact-signing-results.mjs +99 -0
|
@@ -90,6 +90,12 @@ function readJson(filePath, label) {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
function writeJson(filePath, value) {
|
|
94
|
+
const target = path.resolve(filePath);
|
|
95
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
96
|
+
fs.writeFileSync(target, `${JSON.stringify(value, null, 2)}\n`);
|
|
97
|
+
}
|
|
98
|
+
|
|
93
99
|
function githubApi(route, { method = "GET", body } = {}) {
|
|
94
100
|
const args = [
|
|
95
101
|
"api",
|
|
@@ -142,6 +148,42 @@ function readRuleset(repository, rulesetId) {
|
|
|
142
148
|
return normalizeGithubRulesetSnapshot(response.data);
|
|
143
149
|
}
|
|
144
150
|
|
|
151
|
+
function readMatchingRulesetEntries(repository, branch) {
|
|
152
|
+
const response = githubApi(
|
|
153
|
+
`repos/${repository}/rulesets?includes_parents=false&per_page=100`,
|
|
154
|
+
);
|
|
155
|
+
const include = `refs/heads/${branch}`;
|
|
156
|
+
return (response.data || [])
|
|
157
|
+
.map((entry) => ({
|
|
158
|
+
id: Number(entry.id),
|
|
159
|
+
ruleset: readRuleset(repository, entry.id),
|
|
160
|
+
}))
|
|
161
|
+
.filter(({ ruleset }) => {
|
|
162
|
+
const refName = ruleset.conditions?.ref_name || {};
|
|
163
|
+
return (
|
|
164
|
+
ruleset.target === "branch" &&
|
|
165
|
+
(refName.include || []).length === 1 &&
|
|
166
|
+
refName.include[0] === include &&
|
|
167
|
+
(refName.exclude || []).length === 0
|
|
168
|
+
);
|
|
169
|
+
})
|
|
170
|
+
.sort((left, right) => left.ruleset.name.localeCompare(right.ruleset.name));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function readMatchingRulesets(repository, branch) {
|
|
174
|
+
return readMatchingRulesetEntries(repository, branch).map(
|
|
175
|
+
({ ruleset }) => ruleset,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function rulesetInventory(repository, branch) {
|
|
180
|
+
return {
|
|
181
|
+
repository,
|
|
182
|
+
targetRef: branch,
|
|
183
|
+
matchingRulesets: readMatchingRulesets(repository, branch),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
145
187
|
function snapshotCore(repository, branch, protection) {
|
|
146
188
|
return {
|
|
147
189
|
schemaVersion: 1,
|
|
@@ -200,8 +242,8 @@ function rulesetPlan(args) {
|
|
|
200
242
|
...planCore,
|
|
201
243
|
planRoot: githubGovernanceDigest(planCore),
|
|
202
244
|
};
|
|
203
|
-
|
|
204
|
-
|
|
245
|
+
writeJson(snapshotOutput, snapshot);
|
|
246
|
+
writeJson(planOutput, finalPlan);
|
|
205
247
|
return finalPlan;
|
|
206
248
|
}
|
|
207
249
|
|
|
@@ -209,13 +251,45 @@ function rulesetPolicyPlan(args) {
|
|
|
209
251
|
const repository = required(flag(args, "repository"), "--repository");
|
|
210
252
|
const branch = required(flag(args, "branch"), "--branch")
|
|
211
253
|
.replace(/^refs\/heads\//, "");
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
throw new Error("--ruleset-id must be a positive integer");
|
|
215
|
-
}
|
|
254
|
+
const requestedRulesetId = flag(args, "ruleset-id");
|
|
255
|
+
const rulesetName = required(flag(args, "ruleset-name"), "--ruleset-name");
|
|
216
256
|
const snapshotOutput = required(flag(args, "snapshot-output"), "--snapshot-output");
|
|
217
257
|
const planOutput = required(flag(args, "plan-output"), "--plan-output");
|
|
218
|
-
const
|
|
258
|
+
const matchingEntries = readMatchingRulesetEntries(repository, branch);
|
|
259
|
+
const inventory = {
|
|
260
|
+
repository,
|
|
261
|
+
targetRef: branch,
|
|
262
|
+
matchingRulesets: matchingEntries.map(({ ruleset }) => ruleset),
|
|
263
|
+
};
|
|
264
|
+
let rulesetId = null;
|
|
265
|
+
let before = null;
|
|
266
|
+
if (requestedRulesetId) {
|
|
267
|
+
rulesetId = Number(requestedRulesetId);
|
|
268
|
+
if (!Number.isInteger(rulesetId) || rulesetId <= 0) {
|
|
269
|
+
throw new Error("--ruleset-id must be a positive integer");
|
|
270
|
+
}
|
|
271
|
+
before = readRuleset(repository, rulesetId);
|
|
272
|
+
if (
|
|
273
|
+
!inventory.matchingRulesets.some(
|
|
274
|
+
(ruleset) =>
|
|
275
|
+
githubGovernanceDigest(ruleset) === githubGovernanceDigest(before),
|
|
276
|
+
)
|
|
277
|
+
) {
|
|
278
|
+
throw new Error("--ruleset-id does not target the exact requested branch");
|
|
279
|
+
}
|
|
280
|
+
} else if (matchingEntries.length === 1) {
|
|
281
|
+
before = matchingEntries[0].ruleset;
|
|
282
|
+
rulesetId = matchingEntries[0].id;
|
|
283
|
+
if (!Number.isInteger(rulesetId) || rulesetId <= 0) {
|
|
284
|
+
throw new Error("matching ruleset id could not be resolved");
|
|
285
|
+
}
|
|
286
|
+
} else if (inventory.matchingRulesets.length > 1) {
|
|
287
|
+
throw new Error("multiple exact-target rulesets are ambiguous");
|
|
288
|
+
} else if (!hasFlag(args, "create-if-missing")) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
"exact-target ruleset is absent; pass --create-if-missing to plan creation",
|
|
291
|
+
);
|
|
292
|
+
}
|
|
219
293
|
const targetPolicy = resolveGithubGovernanceTargetPolicy({
|
|
220
294
|
repository,
|
|
221
295
|
targetRef: branch,
|
|
@@ -225,6 +299,7 @@ function rulesetPolicyPlan(args) {
|
|
|
225
299
|
contract: "kungfu-buildchain-github-governance-ruleset-rollback-snapshot",
|
|
226
300
|
repository,
|
|
227
301
|
targetRef: branch,
|
|
302
|
+
rulesetExists: Boolean(before),
|
|
228
303
|
rulesetId,
|
|
229
304
|
ruleset: before,
|
|
230
305
|
};
|
|
@@ -236,13 +311,16 @@ function rulesetPolicyPlan(args) {
|
|
|
236
311
|
repository,
|
|
237
312
|
targetRef: branch,
|
|
238
313
|
rulesetId,
|
|
239
|
-
|
|
314
|
+
rulesetName,
|
|
315
|
+
inventory,
|
|
240
316
|
rollbackSnapshot: before,
|
|
241
317
|
desiredProtection: {
|
|
242
318
|
strictRequiredChecks: targetPolicy.strictRequiredChecks,
|
|
243
319
|
requiredCheckBindings: targetPolicy.requiredCheckBindings,
|
|
244
320
|
requiredApprovals: targetPolicy.requiredApprovals,
|
|
245
321
|
rulesetBypassActors: [],
|
|
322
|
+
blockDeletions: hasFlag(args, "block-deletions"),
|
|
323
|
+
blockNonFastForward: hasFlag(args, "block-non-fast-forward"),
|
|
246
324
|
},
|
|
247
325
|
});
|
|
248
326
|
const boundCore = {
|
|
@@ -255,8 +333,8 @@ function rulesetPolicyPlan(args) {
|
|
|
255
333
|
...planCore,
|
|
256
334
|
planRoot: githubGovernanceDigest(planCore),
|
|
257
335
|
};
|
|
258
|
-
|
|
259
|
-
|
|
336
|
+
writeJson(snapshotOutput, snapshot);
|
|
337
|
+
writeJson(planOutput, finalPlan);
|
|
260
338
|
return finalPlan;
|
|
261
339
|
}
|
|
262
340
|
|
|
@@ -276,20 +354,29 @@ function rulesetApply(args) {
|
|
|
276
354
|
snapshotRoot !== githubGovernanceDigest(snapshotCore)) {
|
|
277
355
|
throw new Error("ruleset rollback snapshot root mismatch");
|
|
278
356
|
}
|
|
279
|
-
const
|
|
280
|
-
|
|
357
|
+
const currentInventory = rulesetInventory(
|
|
358
|
+
rollout.repository,
|
|
359
|
+
rollout.targetRef,
|
|
360
|
+
);
|
|
361
|
+
if (githubGovernanceDigest(currentInventory) !== rollout.inventoryRoot) {
|
|
281
362
|
throw new Error("live GitHub ruleset drifted after planning; apply stopped");
|
|
282
363
|
}
|
|
283
|
-
githubApi(rollout.operations[0].endpoint, {
|
|
364
|
+
const result = githubApi(rollout.operations[0].endpoint, {
|
|
284
365
|
method: rollout.operations[0].method,
|
|
285
366
|
body: rollout.operations[0].body,
|
|
286
367
|
});
|
|
287
|
-
const
|
|
368
|
+
const rulesetId =
|
|
369
|
+
rollout.rulesetId ||
|
|
370
|
+
Number(result.data?.id || 0);
|
|
371
|
+
if (!Number.isInteger(rulesetId) || rulesetId <= 0) {
|
|
372
|
+
throw new Error("GitHub did not return the created ruleset id");
|
|
373
|
+
}
|
|
374
|
+
const after = readRuleset(rollout.repository, rulesetId);
|
|
288
375
|
const afterRoot = githubGovernanceDigest(after);
|
|
289
376
|
if (afterRoot !== rollout.expectedObservation.rulesetRoot) {
|
|
290
377
|
throw new Error("post-change GitHub ruleset read-back does not match the rollout plan");
|
|
291
378
|
}
|
|
292
|
-
|
|
379
|
+
const receiptCore = {
|
|
293
380
|
schemaVersion: 1,
|
|
294
381
|
contract: "kungfu-buildchain-github-governance-ruleset-rollout-receipt",
|
|
295
382
|
status: "applied",
|
|
@@ -297,7 +384,21 @@ function rulesetApply(args) {
|
|
|
297
384
|
snapshotRoot: rollout.snapshotRoot,
|
|
298
385
|
afterRoot,
|
|
299
386
|
repository: rollout.repository,
|
|
300
|
-
|
|
387
|
+
targetRef: rollout.targetRef,
|
|
388
|
+
rulesetId,
|
|
389
|
+
rollback:
|
|
390
|
+
rollout.action === "create"
|
|
391
|
+
? {
|
|
392
|
+
method: "DELETE",
|
|
393
|
+
endpoint: rulesetEndpoint(rollout.repository, rulesetId),
|
|
394
|
+
body: null,
|
|
395
|
+
preconditionRoot: afterRoot,
|
|
396
|
+
}
|
|
397
|
+
: rollout.rollback[0],
|
|
398
|
+
};
|
|
399
|
+
return {
|
|
400
|
+
...receiptCore,
|
|
401
|
+
receiptRoot: githubGovernanceDigest(receiptCore),
|
|
301
402
|
};
|
|
302
403
|
}
|
|
303
404
|
|
|
@@ -311,14 +412,46 @@ function rulesetRollback(args) {
|
|
|
311
412
|
if (rollout.snapshotRoot !== confirmed) {
|
|
312
413
|
throw new Error("--confirm-rollback-root does not match the frozen ruleset snapshot");
|
|
313
414
|
}
|
|
314
|
-
|
|
415
|
+
let rulesetId = rollout.rulesetId;
|
|
416
|
+
let operation = rollout.rollback[0];
|
|
417
|
+
if (operation.requiresApplyReceipt) {
|
|
418
|
+
const receipt = readJson(
|
|
419
|
+
required(flag(args, "apply-receipt"), "--apply-receipt"),
|
|
420
|
+
"ruleset apply receipt",
|
|
421
|
+
);
|
|
422
|
+
const { receiptRoot, ...receiptCore } = receipt;
|
|
423
|
+
if (
|
|
424
|
+
receiptRoot !== githubGovernanceDigest(receiptCore) ||
|
|
425
|
+
receipt.planRoot !== rollout.planRoot ||
|
|
426
|
+
receipt.repository !== rollout.repository ||
|
|
427
|
+
receipt.targetRef !== rollout.targetRef
|
|
428
|
+
) {
|
|
429
|
+
throw new Error("ruleset apply receipt does not match the rollout plan");
|
|
430
|
+
}
|
|
431
|
+
rulesetId = Number(receipt.rulesetId);
|
|
432
|
+
operation = receipt.rollback;
|
|
433
|
+
const current = readRuleset(rollout.repository, rulesetId);
|
|
434
|
+
if (githubGovernanceDigest(current) !== operation.preconditionRoot) {
|
|
435
|
+
throw new Error("created ruleset drifted after apply; rollback stopped");
|
|
436
|
+
}
|
|
437
|
+
}
|
|
315
438
|
githubApi(operation.endpoint, {
|
|
316
439
|
method: operation.method,
|
|
317
440
|
body: operation.body,
|
|
318
441
|
});
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
442
|
+
if (operation.method === "DELETE") {
|
|
443
|
+
const remaining = readMatchingRulesets(
|
|
444
|
+
rollout.repository,
|
|
445
|
+
rollout.targetRef,
|
|
446
|
+
);
|
|
447
|
+
if (remaining.length !== 0) {
|
|
448
|
+
throw new Error("created ruleset remains after rollback");
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
const restored = readRuleset(rollout.repository, rulesetId);
|
|
452
|
+
if (githubGovernanceDigest(restored) !== operation.preconditionRoot) {
|
|
453
|
+
throw new Error("ruleset rollback read-back does not match the frozen snapshot");
|
|
454
|
+
}
|
|
322
455
|
}
|
|
323
456
|
return {
|
|
324
457
|
schemaVersion: 1,
|
|
@@ -327,7 +460,7 @@ function rulesetRollback(args) {
|
|
|
327
460
|
planRoot: rollout.planRoot,
|
|
328
461
|
snapshotRoot: rollout.snapshotRoot,
|
|
329
462
|
repository: rollout.repository,
|
|
330
|
-
rulesetId
|
|
463
|
+
rulesetId,
|
|
331
464
|
};
|
|
332
465
|
}
|
|
333
466
|
|
|
@@ -368,8 +501,8 @@ function protectionPolicyPlan(args) {
|
|
|
368
501
|
...boundCore,
|
|
369
502
|
planRoot: githubGovernanceDigest(boundCore),
|
|
370
503
|
};
|
|
371
|
-
|
|
372
|
-
|
|
504
|
+
writeJson(snapshotOutput, snapshotWithRoot);
|
|
505
|
+
writeJson(planOutput, finalPlan);
|
|
373
506
|
return finalPlan;
|
|
374
507
|
}
|
|
375
508
|
|
|
@@ -419,8 +552,8 @@ function plan(args) {
|
|
|
419
552
|
...boundCore,
|
|
420
553
|
planRoot: githubGovernanceDigest(boundCore),
|
|
421
554
|
};
|
|
422
|
-
|
|
423
|
-
|
|
555
|
+
writeJson(snapshotOutput, snapshotWithRoot);
|
|
556
|
+
writeJson(planOutput, finalPlan);
|
|
424
557
|
return finalPlan;
|
|
425
558
|
}
|
|
426
559
|
|
|
@@ -608,6 +608,10 @@ export async function resolveReleaseCandidateArtifacts({
|
|
|
608
608
|
}
|
|
609
609
|
const passport = JSON.parse(fs.readFileSync(passportPath, "utf8"));
|
|
610
610
|
const platformManifestPaths = findDownloadedFiles(payloadDir, "manifest.json");
|
|
611
|
+
const githubArtifactAttestationPolicyPaths = findDownloadedFiles(
|
|
612
|
+
payloadDir,
|
|
613
|
+
"github-artifact-attestation-policy.json",
|
|
614
|
+
);
|
|
611
615
|
const npmTarballPaths = publishArtifactKind === "npm"
|
|
612
616
|
? findDownloadedFilesByExtension(payloadDir, [".tgz"])
|
|
613
617
|
: [];
|
|
@@ -639,6 +643,7 @@ export async function resolveReleaseCandidateArtifacts({
|
|
|
639
643
|
buildSummary: outputPath(buildSummaryPath),
|
|
640
644
|
payloads: outputPath(payloadDir),
|
|
641
645
|
platformManifests: platformManifestPaths.map(outputPath),
|
|
646
|
+
githubArtifactAttestationPolicies: githubArtifactAttestationPolicyPaths.map(outputPath),
|
|
642
647
|
npmTarballs: npmTarballPaths.map(outputPath),
|
|
643
648
|
releaseAssets: releaseAssetPaths.map(outputPath),
|
|
644
649
|
publishRequiredArtifacts: outputPath(requiredArtifactsPath),
|
|
@@ -647,6 +652,7 @@ export async function resolveReleaseCandidateArtifacts({
|
|
|
647
652
|
candidateHash: passport.candidateHash || "",
|
|
648
653
|
payloadCount: payloadArtifacts.length,
|
|
649
654
|
platformManifestCount: platformManifestPaths.length,
|
|
655
|
+
githubArtifactAttestationPolicyCount: githubArtifactAttestationPolicyPaths.length,
|
|
650
656
|
npmTarballCount: npmTarballPaths.length,
|
|
651
657
|
publishRequiredArtifacts: generatedRequiredArtifacts,
|
|
652
658
|
};
|
|
@@ -684,6 +690,12 @@ export async function resolveReleaseCandidateArtifactsCli() {
|
|
|
684
690
|
"release-candidate-payload-dir": result.paths?.payloads || "",
|
|
685
691
|
"release-candidate-platform-manifest-paths": (result.paths?.platformManifests || []).join(","),
|
|
686
692
|
"release-candidate-platform-manifest-count": String(result.platformManifestCount || 0),
|
|
693
|
+
"release-candidate-github-artifact-attestation-policy-paths": (
|
|
694
|
+
result.paths?.githubArtifactAttestationPolicies || []
|
|
695
|
+
).join(","),
|
|
696
|
+
"release-candidate-github-artifact-attestation-policy-count": String(
|
|
697
|
+
result.githubArtifactAttestationPolicyCount || 0,
|
|
698
|
+
),
|
|
687
699
|
"release-candidate-npm-tarball-paths": (result.paths?.npmTarballs || []).join(","),
|
|
688
700
|
"release-candidate-npm-tarball-count": String(result.npmTarballCount || 0),
|
|
689
701
|
"release-candidate-github-release-artifact-paths": (
|
|
@@ -70,6 +70,18 @@ export function parsePublishGateChannelRef(ref) {
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export function parseAuthorityRef(ref) {
|
|
74
|
+
const normalizedRef = normalizeRef(ref);
|
|
75
|
+
const match = normalizedRef.match(/^authority\/v(\d+)\/v(\d+\.\d+)\/[A-Za-z0-9._/-]+$/);
|
|
76
|
+
if (!match) return undefined;
|
|
77
|
+
return {
|
|
78
|
+
major: Number(match[1]),
|
|
79
|
+
loose: Number(match[2]),
|
|
80
|
+
normalizedRef,
|
|
81
|
+
lineSuffix: `/v${match[1]}/v${match[2]}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
73
85
|
export function getChannel(ref) {
|
|
74
86
|
const versionStateTarget = parseVersionStateRef(ref);
|
|
75
87
|
if (versionStateTarget) return versionStateTarget.channel;
|
|
@@ -116,6 +128,7 @@ export function getBumpKeyword({ cwd = process.cwd(), headRef, baseRef, loose =
|
|
|
116
128
|
const versionStateTarget = parseVersionStateRef(headRef);
|
|
117
129
|
const releaseLineRecoveryTarget = parseReleaseLineRecoveryRef(headRef);
|
|
118
130
|
const publishGateTarget = parsePublishGateChannelRef(headRef);
|
|
131
|
+
const authorityTarget = parseAuthorityRef(baseRef);
|
|
119
132
|
const headChannel = getChannel(headRef);
|
|
120
133
|
const baseChannel = getChannel(baseRef);
|
|
121
134
|
const key = `${headChannel}->${baseChannel}`;
|
|
@@ -130,6 +143,20 @@ export function getBumpKeyword({ cwd = process.cwd(), headRef, baseRef, loose =
|
|
|
130
143
|
const preminor = headChannel === "release" && lts;
|
|
131
144
|
const majorGate = headChannel === "release" && baseChannel === MAJOR_GATE_CHANNEL;
|
|
132
145
|
|
|
146
|
+
if (authorityTarget) {
|
|
147
|
+
const headMatch = normalizeRef(headRef).match(/^dev\/v(\d+)\/v(\d+\.\d+)$/);
|
|
148
|
+
if (
|
|
149
|
+
!headMatch ||
|
|
150
|
+
Number(headMatch[1]) !== authorityTarget.major ||
|
|
151
|
+
Number(headMatch[2]) !== authorityTarget.loose ||
|
|
152
|
+
version.major !== authorityTarget.major ||
|
|
153
|
+
looseVersionNumber !== authorityTarget.loose
|
|
154
|
+
) {
|
|
155
|
+
throw new Error(`Versions not match for head/base refs: ${headRef} -> ${baseRef}`);
|
|
156
|
+
}
|
|
157
|
+
return "none";
|
|
158
|
+
}
|
|
159
|
+
|
|
133
160
|
if (releaseLineRecoveryTarget) {
|
|
134
161
|
if (baseChannel !== "release" || releaseLineRecoveryTarget.normalizedRef !== normalizeRef(baseRef)) {
|
|
135
162
|
throw new Error(`Versions not match for head/base refs: ${headRef} -> ${baseRef}`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const EXACT_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
2
2
|
const TRAIN_REF_RE = /^train\/v\d+\/v\d+\.\d+\/[A-Za-z0-9._/-]+$/;
|
|
3
|
+
const AUTHORITY_REF_RE = /^authority\/v\d+\/v\d+\.\d+\/[A-Za-z0-9._/-]+$/;
|
|
3
4
|
const OFFICIAL_CHANNEL_REF_RE = /^v\d+(?:\.\d+)?(?:-alpha)?$/;
|
|
4
5
|
|
|
5
6
|
export function parseWorkflowShellRef(workflowRef = "", fallback = "v2", buildchainRepository = "kungfu-systems/buildchain") {
|
|
@@ -20,6 +21,9 @@ export function classifyBuildchainRuntimeRef(ref = "") {
|
|
|
20
21
|
if (TRAIN_REF_RE.test(value)) {
|
|
21
22
|
return "train";
|
|
22
23
|
}
|
|
24
|
+
if (AUTHORITY_REF_RE.test(value)) {
|
|
25
|
+
return "authority";
|
|
26
|
+
}
|
|
23
27
|
if (/^v\d+(?:\.\d+)?$/.test(value) || /^v\d+\.\d+\.\d+$/.test(value)) {
|
|
24
28
|
return "stable";
|
|
25
29
|
}
|
|
@@ -58,16 +62,16 @@ export function normalizeRequestedRuntimeRef(requestedRef = "") {
|
|
|
58
62
|
officialChannel: false,
|
|
59
63
|
};
|
|
60
64
|
}
|
|
61
|
-
const
|
|
62
|
-
if (!TRAIN_REF_RE.test(
|
|
65
|
+
const protectedRef = requested.replace(/^refs\/heads\//, "");
|
|
66
|
+
if (!TRAIN_REF_RE.test(protectedRef) && !AUTHORITY_REF_RE.test(protectedRef)) {
|
|
63
67
|
throw new Error(
|
|
64
|
-
"buildchain-ref override must be train/vN/vN.M/<capability>, refs/heads/train/vN/vN.M/<capability>, or an exact 40-character SHA",
|
|
68
|
+
"buildchain-ref override must be train/vN/vN.M/<capability>, refs/heads/train/vN/vN.M/<capability>, authority/vN/vN.M/<capability>, refs/heads/authority/vN/vN.M/<capability>, or an exact 40-character SHA",
|
|
65
69
|
);
|
|
66
70
|
}
|
|
67
71
|
return {
|
|
68
|
-
ref:
|
|
69
|
-
fullRef: `refs/heads/${
|
|
70
|
-
class: "train",
|
|
72
|
+
ref: protectedRef,
|
|
73
|
+
fullRef: `refs/heads/${protectedRef}`,
|
|
74
|
+
class: AUTHORITY_REF_RE.test(protectedRef) ? "authority" : "train",
|
|
71
75
|
exactSha: false,
|
|
72
76
|
officialChannel: false,
|
|
73
77
|
};
|