@kungfu-tech/buildchain 3.0.6-alpha.0 → 3.0.6-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.
Files changed (83) hide show
  1. package/README.md +4 -4
  2. package/actions/promote-buildchain-ref/README.md +13 -1
  3. package/bin/buildchain.mjs +13 -1
  4. package/contracts/auditable-demo-scenario-v1.schema.json +52 -0
  5. package/contracts/release-candidate-recovery-v1.schema.json +104 -0
  6. package/dist/site/buildchain-contract.json +134 -34
  7. package/dist/site/buildchain-site.json +178 -47
  8. package/dist/site/capability-registry.json +5 -5
  9. package/dist/site/cli-registry.json +40 -4
  10. package/dist/site/controller-registry.json +60 -4
  11. package/dist/site/kfd-claims.json +190 -20
  12. package/dist/site/kfd-upstream-aggregate.json +9 -9
  13. package/dist/site/manual-registry.json +10 -10
  14. package/dist/site/node-api-registry.json +1295 -80
  15. package/dist/site/page-registry.json +164 -33
  16. package/dist/site/public-surface-audit.json +428 -21
  17. package/dist/site/publication-authority-registry.json +66 -1
  18. package/dist/site/publication-registry.json +4 -4
  19. package/dist/site/release-provenance.json +2 -0
  20. package/dist/site/site-manifest.json +14 -14
  21. package/dist/site/workflow-registry.json +203 -15
  22. package/docs/MAP.md +2 -0
  23. package/docs/auditable-demo.md +58 -11
  24. package/docs/aws-us-elastic-runner-burst-plane.md +114 -80
  25. package/docs/cli-reference.md +154 -0
  26. package/docs/dev-alpha-candidate-patrol.md +13 -5
  27. package/docs/dev-delivery-warrant.md +158 -0
  28. package/docs/node-api-reference.md +105 -44
  29. package/docs/publication-authority.md +11 -0
  30. package/docs/publish-transaction.md +10 -1
  31. package/docs/release-candidate.md +140 -12
  32. package/docs/release-governance.md +66 -1
  33. package/docs/reusable-build-surface.md +11 -1
  34. package/docs/shifu-gate-profiles.md +12 -1
  35. package/docs/versioning.md +2 -0
  36. package/package.json +5 -2
  37. package/packages/core/buildchain-contract.js +30 -2
  38. package/packages/core/buildchain-publication-authority.js +3 -1
  39. package/packages/core/channel-candidate.js +2 -21
  40. package/packages/core/channel-promotion-baseline.js +199 -0
  41. package/packages/core/dev-delivery-candidate-identity.js +94 -0
  42. package/packages/core/dev-delivery-common.js +73 -0
  43. package/packages/core/dev-delivery-proof.js +252 -0
  44. package/packages/core/dev-delivery-warrant-cancellation.js +94 -0
  45. package/packages/core/dev-delivery-warrant-settlement.js +73 -0
  46. package/packages/core/dev-delivery-warrant.js +591 -0
  47. package/packages/core/index.js +7 -0
  48. package/packages/core/publication-sealed-bundle.js +10 -1
  49. package/packages/core/release-candidate-recovery.js +539 -0
  50. package/scripts/audit-publication-control-plane.mjs +12 -4
  51. package/scripts/auditable-demo-bundle-verification.mjs +148 -0
  52. package/scripts/auditable-demo-platform.mjs +86 -50
  53. package/scripts/auditable-demo-presentation.mjs +83 -0
  54. package/scripts/auditable-demo-renditions.mjs +264 -0
  55. package/scripts/auditable-demo.mjs +24 -30
  56. package/scripts/aws-windows-jit-campaign-core.mjs +7 -8
  57. package/scripts/aws-windows-jit-controller.mjs +1 -0
  58. package/scripts/aws-windows-jit-core.mjs +1 -1
  59. package/scripts/build-contract-core.mjs +58 -3
  60. package/scripts/buildchain-cli-help.mjs +8 -0
  61. package/scripts/buildchain-patrol.mjs +9 -0
  62. package/scripts/check-inventory.mjs +25 -3
  63. package/scripts/dev-alpha-candidate-patrol.mjs +45 -48
  64. package/scripts/dev-delivery-proof.mjs +193 -0
  65. package/scripts/dev-delivery-warrant.mjs +426 -0
  66. package/scripts/dev-pr-auto-merge.mjs +497 -55
  67. package/scripts/dev-pr-delivery-warrant.mjs +209 -0
  68. package/scripts/dispatch-artifact-signing-authority.mjs +2 -4
  69. package/scripts/gate-profile-core.mjs +24 -0
  70. package/scripts/generate-channel-promotion-workflow.mjs +30 -14
  71. package/scripts/generate-site-bundle.mjs +2 -2
  72. package/scripts/git-fetch-process-tree.mjs +142 -0
  73. package/scripts/lifecycle-substage-evidence.mjs +274 -0
  74. package/scripts/locked-source-checkout.mjs +6 -3
  75. package/scripts/publication-candidate-sealer.mjs +104 -0
  76. package/scripts/release-candidate-resolver.mjs +91 -5
  77. package/scripts/resolve-artifact-transfer-mode.mjs +9 -0
  78. package/scripts/resolve-build-contract.mjs +7 -0
  79. package/scripts/resume-from-candidate-run.mjs +598 -0
  80. package/scripts/route-offline-runners.mjs +1 -0
  81. package/scripts/run-lifecycle-core.mjs +9 -9
  82. package/scripts/shifu-gate-profile.mjs +10 -16
  83. package/scripts/site-capability-metadata.mjs +15 -0
@@ -74,6 +74,7 @@ const requiredPaths = [
74
74
  "docs/shifu-gate-profiles.md",
75
75
  "docs/auditable-demo.md",
76
76
  "contracts/auditable-demo-scenario-v1.schema.json",
77
+ "contracts/release-candidate-recovery-v1.schema.json",
77
78
  "contracts/auditable-demo-media-profiles-v1.json",
78
79
  "contracts/evidence/auditable-demo-web-delivery-v1.json",
79
80
  "contracts/evidence/auditable-demo-responsive-web-delivery-v1.json",
@@ -117,6 +118,7 @@ const requiredPaths = [
117
118
  "scripts/publication-commit-evidence.mjs",
118
119
  "scripts/publication-reproducibility.mjs",
119
120
  "scripts/release-candidate-resolver.mjs",
121
+ "scripts/resume-from-candidate-run.mjs",
120
122
  "scripts/buildchain-patrol.mjs",
121
123
  "scripts/observed-evidence.mjs",
122
124
  "scripts/workflow-friction-report.mjs",
@@ -134,9 +136,11 @@ const requiredPaths = [
134
136
  ".github/actionlint.yaml",
135
137
  ".github/workflows/self-hosted-runner-smoke.yml",
136
138
  ".github/workflows/buildchain-ref-promotion.yml",
139
+ ".github/workflows/buildchain-candidate-recovery-dogfood-failure.yml",
137
140
  ".github/workflows/release-line-bootstrap.yml",
138
141
  ".github/workflows/release-governance-reconcile.yml",
139
142
  ".github/workflows/dev-pr-auto-merge.yml",
143
+ ".github/workflows/buildchain-dev-delivery.yml",
140
144
  ".github/workflows/buildchain-patrol.yml",
141
145
  ".github/workflows/patrol-daily.yml",
142
146
  ".github/workflows/patrol-weekly.yml",
@@ -272,16 +276,25 @@ if (channelPromotionWorkflow !== generateChannelPromotionWorkflow(advancedPromot
272
276
  }
273
277
  for (const requiredSnippet of [
274
278
  "buildchain-channel:",
275
- `/.github/workflows/.release-candidate-promote.yml@v${selfDogfoodMajor}-alpha`,
279
+ `/${promotionShellRouting.alpha.workflowPath}@${promotionShellRouting.alpha.callRef}`,
276
280
  `/${promotionShellRouting.stable.workflowPath}@${promotionShellRouting.stable.callRef}`,
277
281
  `STABLE_SHELL_REF: v${selfDogfoodMajor}`,
278
282
  "promotion-contract-lock-digest:",
279
283
  "authorize-promotion-runtime-override.cjs",
284
+ "BUILDCHAIN_ROUTER_REPOSITORY: ${{ inputs.buildchain-repository }}",
285
+ "BUILDCHAIN_RESUME_RUNTIME_SHA: ${{ inputs.resume-buildchain-runtime-sha }}",
286
+ "git ls-remote",
287
+ "Recovery router ref does not match resume-buildchain-runtime-sha",
280
288
  ]) {
281
289
  if (!channelPromotionWorkflow.includes(requiredSnippet)) {
282
290
  throw new Error(`channel promotion workflow missing routing contract: ${requiredSnippet}`);
283
291
  }
284
292
  }
293
+ for (const forbiddenSnippet of ["job.workflow_repository", "job.workflow_sha"]) {
294
+ if (channelPromotionWorkflow.includes(forbiddenSnippet)) {
295
+ throw new Error(`channel promotion workflow uses unsupported GitHub context: ${forbiddenSnippet}`);
296
+ }
297
+ }
285
298
  const promotionOverrideAuthorization = fs.readFileSync(
286
299
  path.join(root, "scripts/authorize-promotion-runtime-override.cjs"),
287
300
  "utf8",
@@ -983,10 +996,12 @@ for (const requiredSnippet of [
983
996
  "github.event.workflow_run.event == 'push'",
984
997
  "!startsWith(github.event.workflow_run.display_title, 'chore(release): prepare v')",
985
998
  "!startsWith(github.event.workflow_run.display_title, 'chore(release): release v')",
986
- "target-sha: ${{ github.event.workflow_run.head_sha || inputs.sha || github.sha }}",
999
+ "resume-candidate-run-id:",
1000
+ "resume-expected-source-tree:",
1001
+ "resume-buildchain-runtime-sha:",
987
1002
  "github-release: true",
988
1003
  "release-passport-buildchain-self-kfd: true",
989
- "publish-required-artifacts-json: \"[]\"",
1004
+ 'artifact-patterns: "buildchain-package-*"',
990
1005
  "release-passport-impact-json: .buildchain/release-impact.json",
991
1006
  ]) {
992
1007
  if (!buildchainRefPromotionWorkflow.includes(requiredSnippet)) {
@@ -1040,6 +1055,13 @@ for (const requiredSnippet of [
1040
1055
  "BUILDCHAIN_STABLE_RELEASE_POLICY: .buildchain/stable-release-policy.json",
1041
1056
  "Consumer has no binary-distribution.yml; standalone binary dispatch is not applicable.",
1042
1057
  "gh workflow view binary-distribution.yml",
1058
+ "resume-candidate-run-id:",
1059
+ "node .buildchain/runtime/scripts/resume-from-candidate-run.mjs",
1060
+ "publish-sealed-bundle-root: ${{ steps.rc.outputs.publish-sealed-bundle-root }}",
1061
+ "BUILDCHAIN_EXPECTED_TRANSACTION_ID: ${{ inputs.resume-transaction-id }}",
1062
+ "if: ${{ inputs.resume-candidate-run-id == '' }}",
1063
+ "Bridge Buildchain self-runtime dependencies",
1064
+ "ln -s .buildchain/runtime/node_modules node_modules",
1043
1065
  ]) {
1044
1066
  if (!releaseCandidatePromoteWorkflow.includes(requiredSnippet)) {
1045
1067
  throw new Error(`release candidate promote workflow missing KFD gate pass-through: ${requiredSnippet}`);
@@ -5,6 +5,10 @@ import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import crypto from "node:crypto";
7
7
  import { decideChannelCandidate } from "../packages/core/channel-candidate.js";
8
+ import {
9
+ resolveManagedPromotionBaseline,
10
+ resolvePatrolSourceInputs,
11
+ } from "../packages/core/channel-promotion-baseline.js";
8
12
 
9
13
  export const DEV_ALPHA_CANDIDATE_STATE_SCHEMA =
10
14
  "kungfu-buildchain-dev-alpha-candidate-state/v1";
@@ -301,8 +305,7 @@ export function normalizeDevAlphaPatrolOptions(options = {}) {
301
305
  false,
302
306
  ),
303
307
  mergeMethod: autoMergeMethod(
304
- options.mergeMethod ??
305
- process.env.BUILDCHAIN_CHANNEL_PATROL_MERGE_METHOD,
308
+ options.mergeMethod ?? process.env.BUILDCHAIN_CHANNEL_PATROL_MERGE_METHOD,
306
309
  ),
307
310
  dryRun: bool(
308
311
  options.dryRun ?? process.env.BUILDCHAIN_CHANNEL_PATROL_DRY_RUN,
@@ -321,7 +324,10 @@ export function normalizeDevAlphaPatrolOptions(options = {}) {
321
324
  function latestWorkflowEvidence(runs, workflowPathValue, sourceSha) {
322
325
  const matching = runs
323
326
  .filter(
324
- (run) => run.path === workflowPathValue && run.head_sha === sourceSha,
327
+ (run) =>
328
+ run.conclusion !== "cancelled" &&
329
+ run.path === workflowPathValue &&
330
+ run.head_sha === sourceSha,
325
331
  )
326
332
  .sort((left, right) => Number(right.id) - Number(left.id));
327
333
  if (matching.length === 0)
@@ -356,7 +362,9 @@ function workflowEvidenceIsFreshAndSuccessful(run, { now, maxAgeSeconds }) {
356
362
 
357
363
  function latestRunsBySha(runs, workflowPathValue) {
358
364
  const latest = new Map();
359
- for (const run of runs.filter((row) => row.path === workflowPathValue)) {
365
+ for (const run of runs.filter(
366
+ (row) => row.path === workflowPathValue && row.conclusion !== "cancelled",
367
+ )) {
360
368
  const current = latest.get(run.head_sha);
361
369
  if (!current || Number(run.id) > Number(current.id))
362
370
  latest.set(run.head_sha, run);
@@ -620,52 +628,26 @@ export async function runDevAlphaCandidatePatrol(
620
628
  repository: options.repository,
621
629
  token: process.env.GITHUB_TOKEN,
622
630
  });
623
- const [observedSourceHeadSha, targetSha] = await Promise.all([
624
- client.resolveBranch(options.sourceBranch),
625
- client.resolveBranch(options.targetBranch),
626
- ]);
627
- const headComparison = await client.compare(targetSha, observedSourceHeadSha);
628
631
  const requiredWorkflowPaths = [
629
632
  options.devWorkflowPath,
630
633
  options.alphaWorkflowPath,
631
634
  ];
632
- let sourceSha = observedSourceHeadSha;
633
- let comparison = headComparison;
634
- let workflowEvidence = [];
635
- let skippedNewerCommitCount = 0;
636
- let qualificationError;
637
- if (
638
- headComparison.status === "ahead" &&
639
- Number(headComparison.ahead_by) > 0
640
- ) {
641
- const [sourceHistory, ...workflowRunSets] = await Promise.all([
642
- client.listBranchHistory(options.sourceBranch, targetSha),
643
- ...requiredWorkflowPaths.map((workflow) =>
644
- client.listCompletedWorkflowRuns(workflow, options.sourceBranch),
645
- ),
646
- ]);
647
- try {
648
- const selected = selectLatestQualifiedSource({
649
- sourceHistory,
650
- workflowRunsByPath: new Map(
651
- requiredWorkflowPaths.map((workflow, index) => [
652
- workflow,
653
- workflowRunSets[index],
654
- ]),
655
- ),
656
- requiredWorkflowPaths,
657
- now: options.now,
658
- maxAgeSeconds: options.maxAgeSeconds,
659
- });
660
- sourceSha = selected.sourceSha;
661
- skippedNewerCommitCount = selected.skippedNewerCommitCount;
662
- workflowEvidence = selected.workflowEvidence;
663
- if (sourceSha !== observedSourceHeadSha)
664
- comparison = await client.compare(targetSha, sourceSha);
665
- } catch (error) {
666
- qualificationError = error;
667
- }
668
- }
635
+ const {
636
+ observedSourceHeadSha,
637
+ targetSha,
638
+ headComparison,
639
+ targetBaseline,
640
+ sourceSha,
641
+ comparison,
642
+ workflowEvidence,
643
+ skippedNewerCommitCount,
644
+ qualificationError,
645
+ } = await resolvePatrolSourceInputs({
646
+ client,
647
+ options,
648
+ requiredWorkflowPaths,
649
+ selectQualifiedSource: selectLatestQualifiedSource,
650
+ });
669
651
  const decision = qualificationError
670
652
  ? blockedCandidateDecision({
671
653
  options,
@@ -682,9 +664,12 @@ export async function runDevAlphaCandidatePatrol(
682
664
  targetSha,
683
665
  comparison: { status: comparison.status, aheadBy: comparison.ahead_by },
684
666
  selection: {
685
- mode: "latest-qualified-source-ancestor",
667
+ mode: targetBaseline
668
+ ? "latest-qualified-source-after-managed-promotion"
669
+ : "latest-qualified-source-ancestor",
686
670
  observedSourceHeadSha,
687
671
  skippedNewerCommitCount,
672
+ ...(targetBaseline ? { targetBaseline } : {}),
688
673
  },
689
674
  workflowEvidence,
690
675
  requiredWorkflowPaths,
@@ -927,7 +912,12 @@ export async function runDevAlphaCandidatePatrol(
927
912
  }
928
913
  }
929
914
  }
930
- const autoMerge = await armCandidateAutoMerge(options, controllerState, pullRequest, client);
915
+ const autoMerge = await armCandidateAutoMerge(
916
+ options,
917
+ controllerState,
918
+ pullRequest,
919
+ client,
920
+ );
931
921
  return {
932
922
  schema: "kungfu-buildchain-dev-alpha-candidate-patrol/v1",
933
923
  dryRun: options.dryRun,
@@ -1015,6 +1005,13 @@ export function createGitHubChannelCandidateClient({
1015
1005
  async compare(baseSha, headSha) {
1016
1006
  return api(`/repos/${owner}/${repo}/compare/${baseSha}...${headSha}`);
1017
1007
  },
1008
+ resolveManagedPromotionBaseline: (targetSha, targetBranch) =>
1009
+ resolveManagedPromotionBaseline({
1010
+ api: (requestPath) => api(`/repos/${owner}/${repo}${requestPath}`),
1011
+ targetSha,
1012
+ targetBranch,
1013
+ parseCandidate: managedCandidateFromPullRequest,
1014
+ }),
1018
1015
  async listCompletedWorkflowRuns(workflowPathValue, sourceBranch) {
1019
1016
  const runs = [];
1020
1017
  for (let page = 1; page <= 10; page += 1) {
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { classifyDevDeliveryDelta, createIntegrationDeliveryProof, createProjectCutReplayPlan, createProjectCutReplayProof, createSourceQualificationProof, verifyIntegrationDeliveryProof, verifyProjectCutReplayProof, verifySourceQualificationProof } from "../packages/core/dev-delivery-warrant.js";
5
+
6
+ function flag(args, name, fallback = "") {
7
+ const index = args.indexOf(`--${name}`);
8
+ return index === -1 ? fallback : args[index + 1] || "";
9
+ }
10
+
11
+ function hasFlag(args, name) {
12
+ return args.includes(`--${name}`);
13
+ }
14
+
15
+ function jsonFile(file, label) {
16
+ if (!file) throw new Error(`${label} is required`);
17
+ return JSON.parse(fs.readFileSync(file, "utf8"));
18
+ }
19
+
20
+ function jsonList(value, label, { required = false } = {}) {
21
+ let parsed;
22
+ try {
23
+ parsed = JSON.parse(value || "[]");
24
+ } catch (cause) {
25
+ throw new Error(`${label} must be a JSON array: ${cause.message}`);
26
+ }
27
+ if (!Array.isArray(parsed) || (required && parsed.length === 0)) {
28
+ throw new Error(`${label} must be ${required ? "a non-empty" : "a"} JSON array`);
29
+ }
30
+ return parsed;
31
+ }
32
+
33
+ function boolean(value, fallback = false) {
34
+ if (value === undefined || value === null || value === "") return fallback;
35
+ if (typeof value === "boolean") return value;
36
+ return ["1", "true", "yes", "on"].includes(String(value).trim().toLowerCase());
37
+ }
38
+
39
+ export function devDeliveryProofCliOptions(args = [], environment = process.env) {
40
+ const [command = "", ...rest] = args;
41
+ return {
42
+ command,
43
+ repository: flag(rest, "repository", environment.GITHUB_REPOSITORY),
44
+ protectedBase: flag(rest, "branch", environment.BUILDCHAIN_DEV_DELIVERY_BRANCH || environment.GITHUB_BASE_REF),
45
+ sourceIdentityRoot: flag(rest, "source-identity-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_IDENTITY_ROOT),
46
+ sourceHead: flag(rest, "source-head", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_HEAD),
47
+ sourcePatchRoot: flag(rest, "source-patch-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_PATCH_ROOT),
48
+ planRoot: flag(rest, "plan-root", environment.BUILDCHAIN_DEV_DELIVERY_PLAN_ROOT),
49
+ closureRoot: flag(rest, "closure-root", environment.BUILDCHAIN_DEV_DELIVERY_CLOSURE_ROOT),
50
+ dependencyRoot: flag(rest, "dependency-root", environment.BUILDCHAIN_DEV_DELIVERY_DEPENDENCY_ROOT),
51
+ toolchainRoot: flag(rest, "toolchain-root", environment.BUILDCHAIN_DEV_DELIVERY_TOOLCHAIN_ROOT),
52
+ affectedPaths: flag(rest, "affected-paths-json", environment.BUILDCHAIN_DEV_DELIVERY_AFFECTED_PATHS || "[]"),
53
+ shardEvidenceRoots: flag(rest, "shard-evidence-roots-json", environment.BUILDCHAIN_DEV_DELIVERY_SHARD_EVIDENCE_ROOTS || "[]"),
54
+ qualifiedAt: flag(rest, "qualified-at", environment.BUILDCHAIN_DEV_DELIVERY_QUALIFIED_AT),
55
+ sourceProofPath: flag(rest, "source-proof", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_PROOF),
56
+ sourceProofRoot: flag(rest, "source-proof-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_PROOF_ROOT),
57
+ warrantResultPath: flag(rest, "warrant-result", environment.BUILDCHAIN_DEV_DELIVERY_WARRANT_RESULT),
58
+ currentBase: flag(rest, "current-base", environment.BUILDCHAIN_DEV_DELIVERY_CURRENT_BASE),
59
+ previousBase: flag(rest, "previous-base", environment.BUILDCHAIN_DEV_DELIVERY_PREVIOUS_BASE),
60
+ replayTree: flag(rest, "replay-tree", environment.BUILDCHAIN_DEV_DELIVERY_REPLAY_TREE),
61
+ qualificationReceiptPath: flag(rest, "qualification-receipt", environment.BUILDCHAIN_DEV_DELIVERY_QUALIFICATION_RECEIPT),
62
+ mergeGroupHead: flag(rest, "merge-group-head", environment.BUILDCHAIN_DEV_DELIVERY_MERGE_GROUP_HEAD),
63
+ mergeGroupTree: flag(rest, "merge-group-tree", environment.BUILDCHAIN_DEV_DELIVERY_MERGE_GROUP_TREE),
64
+ requiredContextRoots: flag(rest, "required-context-roots-json", environment.BUILDCHAIN_DEV_DELIVERY_REQUIRED_CONTEXT_ROOTS || "[]"),
65
+ verifiedAt: flag(rest, "verified-at", environment.BUILDCHAIN_DEV_DELIVERY_VERIFIED_AT),
66
+ pullRequestNumber: flag(rest, "pull-request", environment.BUILDCHAIN_DEV_DELIVERY_PR_NUMBER),
67
+ changedPaths: flag(rest, "changed-paths-json", environment.BUILDCHAIN_DEV_DELIVERY_CHANGED_PATHS || "[]"),
68
+ graphKnown: boolean(flag(rest, "graph-known", environment.BUILDCHAIN_DEV_DELIVERY_GRAPH_KNOWN), false),
69
+ outputPath: flag(rest, "output", environment.BUILDCHAIN_DEV_DELIVERY_PROOF_OUTPUT || ".buildchain/dev-delivery/proof.json"),
70
+ json: hasFlag(rest, "json"),
71
+ };
72
+ }
73
+
74
+ function sourceProof(options) {
75
+ return createSourceQualificationProof({
76
+ repository: options.repository,
77
+ protectedBase: options.protectedBase,
78
+ sourceIdentityRoot: options.sourceIdentityRoot,
79
+ sourceHead: options.sourceHead,
80
+ sourcePatchRoot: options.sourcePatchRoot,
81
+ planRoot: options.planRoot,
82
+ closureRoot: options.closureRoot,
83
+ dependencyRoot: options.dependencyRoot,
84
+ toolchainRoot: options.toolchainRoot,
85
+ affectedPaths: jsonList(options.affectedPaths, "affected paths"),
86
+ shardEvidenceRoots: jsonList(options.shardEvidenceRoots, "shard evidence roots", { required: true }),
87
+ qualifiedAt: options.qualifiedAt,
88
+ });
89
+ }
90
+
91
+ function activeWarrant(options) {
92
+ const result = jsonFile(options.warrantResultPath, "Warrant result");
93
+ const warrant = result.warrant || result.observation?.activeWarrant;
94
+ if (result.schema !== "kungfu.buildchain.dev-delivery-command-result/v1" || !warrant) {
95
+ throw new Error("Warrant result does not contain an active Buildchain Delivery Warrant");
96
+ }
97
+ return warrant;
98
+ }
99
+
100
+ export function runDevDeliveryProofCommand(options) {
101
+ if (options.command === "source") return sourceProof(options);
102
+ if (options.command === "verify-source") {
103
+ return verifySourceQualificationProof(jsonFile(options.sourceProofPath, "source proof"));
104
+ }
105
+ if (options.command === "classify") {
106
+ return classifyDevDeliveryDelta({
107
+ proof: jsonFile(options.sourceProofPath, "source proof"),
108
+ current: {
109
+ sourceIdentityRoot: options.sourceIdentityRoot,
110
+ sourcePatchRoot: options.sourcePatchRoot,
111
+ planRoot: options.planRoot,
112
+ closureRoot: options.closureRoot,
113
+ dependencyRoot: options.dependencyRoot,
114
+ toolchainRoot: options.toolchainRoot,
115
+ graphKnown: options.graphKnown,
116
+ changedPaths: jsonList(options.changedPaths, "changed paths"),
117
+ },
118
+ });
119
+ }
120
+ if (options.command === "replay") {
121
+ return createProjectCutReplayPlan({
122
+ repository: options.repository,
123
+ protectedBase: options.protectedBase,
124
+ pullRequestNumber: options.pullRequestNumber,
125
+ sourceHead: options.sourceHead,
126
+ previousBase: options.previousBase,
127
+ currentBase: options.currentBase,
128
+ sourcePatchRoot: options.sourcePatchRoot,
129
+ replayTree: options.replayTree,
130
+ });
131
+ }
132
+ if (options.command === "replay-proof") {
133
+ return createProjectCutReplayProof({
134
+ repository: options.repository,
135
+ protectedBase: options.protectedBase,
136
+ pullRequestNumber: options.pullRequestNumber,
137
+ sourceHead: options.sourceHead,
138
+ sourcePatchRoot: options.sourcePatchRoot,
139
+ currentBase: options.currentBase,
140
+ replayTree: options.replayTree,
141
+ qualificationReceipt: jsonFile(options.qualificationReceiptPath, "Project Cut qualification receipt"),
142
+ requiredContextRoots: jsonList(options.requiredContextRoots, "required context roots", { required: true }),
143
+ verifiedAt: options.verifiedAt,
144
+ });
145
+ }
146
+ if (options.command === "verify-replay") {
147
+ return verifyProjectCutReplayProof(jsonFile(options.sourceProofPath, "Project Cut replay proof"));
148
+ }
149
+ if (options.command === "integration") {
150
+ const sourceProofRoot = options.sourceProofRoot || jsonFile(options.sourceProofPath, "source proof").proofRoot;
151
+ return createIntegrationDeliveryProof({
152
+ repository: options.repository,
153
+ protectedBase: options.protectedBase,
154
+ sourceProofRoot,
155
+ currentBase: options.currentBase,
156
+ replayTree: options.replayTree,
157
+ mergeGroupHead: options.mergeGroupHead,
158
+ mergeGroupTree: options.mergeGroupTree,
159
+ warrant: activeWarrant(options),
160
+ requiredContextRoots: jsonList(options.requiredContextRoots, "required context roots", { required: true }),
161
+ verifiedAt: options.verifiedAt,
162
+ });
163
+ }
164
+ if (options.command === "verify-integration") {
165
+ return verifyIntegrationDeliveryProof(jsonFile(options.sourceProofPath, "integration proof"));
166
+ }
167
+ throw new Error("unsupported dev delivery proof command");
168
+ }
169
+
170
+ function usage() {
171
+ return "Usage:\n buildchain dev proof <source|verify-source|classify|replay|replay-proof|verify-replay|integration|verify-integration> [options] [--output FILE] [--json]\n";
172
+ }
173
+
174
+ async function main() {
175
+ const args = process.argv.slice(2);
176
+ if (args.length === 0 || hasFlag(args, "help")) {
177
+ process.stdout.write(usage());
178
+ return;
179
+ }
180
+ const options = devDeliveryProofCliOptions(args);
181
+ const result = runDevDeliveryProofCommand(options);
182
+ fs.mkdirSync(path.dirname(options.outputPath), { recursive: true });
183
+ fs.writeFileSync(options.outputPath, `${JSON.stringify(result, null, 2)}\n`);
184
+ if (options.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
185
+ else process.stdout.write(`Buildchain dev delivery proof ${options.command}: ${result.proofRoot || result.reason}\n`);
186
+ }
187
+
188
+ if (import.meta.url === `file://${process.argv[1]}`) {
189
+ main().catch((error) => {
190
+ console.error(`buildchain dev proof: ${error.message}`);
191
+ process.exit(1);
192
+ });
193
+ }