@kungfu-tech/buildchain 3.0.5-alpha.5 → 3.0.5-alpha.7

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 (43) hide show
  1. package/README.md +31 -0
  2. package/actions/macos-credential-island/README.md +8 -0
  3. package/bin/buildchain.mjs +1 -1
  4. package/contracts/auditable-demo-media-profiles-v1.json +61 -0
  5. package/contracts/auditable-demo-scenario-v1.schema.json +153 -0
  6. package/contracts/evidence/auditable-demo-responsive-web-delivery-v1.json +3 -3
  7. package/contracts/evidence/auditable-demo-web-delivery-v1.json +3 -3
  8. package/dist/site/buildchain-contract.json +57 -6
  9. package/dist/site/buildchain-site.json +42 -26
  10. package/dist/site/capability-registry.json +1 -1
  11. package/dist/site/cli-registry.json +6 -2
  12. package/dist/site/kfd-claims.json +49 -9
  13. package/dist/site/kfd-upstream-aggregate.json +1 -1
  14. package/dist/site/manual-registry.json +3 -3
  15. package/dist/site/node-api-registry.json +205 -23
  16. package/dist/site/page-registry.json +32 -12
  17. package/dist/site/public-surface-audit.json +94 -6
  18. package/dist/site/publication-authority-registry.json +42 -1
  19. package/dist/site/publication-registry.json +4 -4
  20. package/dist/site/site-manifest.json +7 -7
  21. package/dist/site/workflow-registry.json +63 -0
  22. package/docs/auditable-demo.md +74 -7
  23. package/docs/aws-us-elastic-runner-burst-plane.md +16 -6
  24. package/docs/cli-reference.md +24 -2
  25. package/docs/node-api-reference.md +69 -51
  26. package/package.json +1 -1
  27. package/packages/core/buildchain-contract.js +36 -1
  28. package/packages/core/buildchain-publication-authority.js +2 -0
  29. package/packages/core/release-propagation-pickup.js +386 -0
  30. package/packages/core/release-propagation-release.js +61 -18
  31. package/packages/core/release-propagation-work.js +7 -3
  32. package/packages/core/release-propagation.js +12 -2
  33. package/scripts/auditable-demo-platform.mjs +506 -0
  34. package/scripts/auditable-demo-renditions.mjs +6 -1
  35. package/scripts/auditable-demo.mjs +14 -4
  36. package/scripts/aws-windows-jit-campaign-core.mjs +19 -3
  37. package/scripts/aws-windows-jit-campaign.mjs +19 -2
  38. package/scripts/build-standalone-binary.mjs +4 -0
  39. package/scripts/buildchain-cli-help.mjs +2 -1
  40. package/scripts/check-inventory.mjs +5 -0
  41. package/scripts/finalize-native-artifact-signing-result.mjs +37 -15
  42. package/scripts/generate-site-bundle.mjs +2 -0
  43. package/scripts/release-propagation.mjs +77 -2
@@ -46,10 +46,14 @@ function armPlan() {
46
46
  armedAt: arg("armed-at", new Date().toISOString()),
47
47
  expiresAt: arg("expires-at"),
48
48
  phaseSpendBaselineUsd: arg("phase-spend-baseline-usd"),
49
+ maxAcceptedInstances: arg("max-accepted-instances"),
49
50
  });
50
51
  }
51
52
 
52
- function confirm(plan, { phaseSpendBaseline = true } = {}) {
53
+ function confirm(
54
+ plan,
55
+ { phaseSpendBaseline = true, maxAcceptedInstances = true } = {},
56
+ ) {
53
57
  if (arg("confirm-campaign-id") !== plan.campaign.id) {
54
58
  throw new Error("--confirm-campaign-id must equal the campaign id");
55
59
  }
@@ -71,6 +75,16 @@ function confirm(plan, { phaseSpendBaseline = true } = {}) {
71
75
  "--confirm-phase-spend-baseline-usd must equal the phase spend baseline",
72
76
  );
73
77
  }
78
+ if (
79
+ maxAcceptedInstances &&
80
+ (!arg("confirm-max-accepted-instances").trim() ||
81
+ Number(arg("confirm-max-accepted-instances")) !==
82
+ plan.limits.maxAcceptedInstances)
83
+ ) {
84
+ throw new Error(
85
+ "--confirm-max-accepted-instances must equal the campaign slot ceiling",
86
+ );
87
+ }
74
88
  }
75
89
 
76
90
  function killSwitchTopic() {
@@ -132,7 +146,10 @@ export function main() {
132
146
  expiresAt: new Date(now.getTime() + 1000).toISOString(),
133
147
  phaseSpendBaselineUsd: 0,
134
148
  });
135
- confirm(plan, { phaseSpendBaseline: false });
149
+ confirm(plan, {
150
+ phaseSpendBaseline: false,
151
+ maxAcceptedInstances: false,
152
+ });
136
153
  const topic = killSwitchTopic();
137
154
  aws(
138
155
  plan,
@@ -352,7 +352,11 @@ export function buildStandaloneBinary({
352
352
  name,
353
353
  version,
354
354
  platform: triple,
355
+ platformId: process.platform === "linux" && process.arch === "x64" ? "linux-x64" : triple,
355
356
  binary: relativePath(cwd, binaryPath),
357
+ sha256: crypto.createHash("sha256").update(fs.readFileSync(binaryPath)).digest("hex"),
358
+ sourceSha: sourceSha(cwd),
359
+ runtimeDependencies: [],
356
360
  archive: relativePath(cwd, archivePath),
357
361
  node: process.version,
358
362
  observability: {
@@ -208,7 +208,7 @@ export const BUILDCHAIN_USAGE = `Usage:
208
208
  [--execute] [--json]
209
209
  buildchain paper status [--cwd <dir>] [--json]
210
210
  buildchain paper resume [--cwd <dir>] [--buildchain-ref <ref>] [--execute] [--json]
211
- buildchain release-propagation <plan|write-lock> ...
211
+ buildchain release-propagation <plan|write-lock|work|pickup> ...
212
212
  buildchain badges readme [--cwd <dir>] [--readme <path>] [--check] [--write] [--json]
213
213
  buildchain badges bundle [--cwd <dir>] [--readme <path>] [--claims <csv>] [--check] [--write] [--json]
214
214
  buildchain homebrew update-formula --package <name> --release-passport <file-or-url> [--write] [--json]
@@ -246,6 +246,7 @@ Examples:
246
246
  buildchain paper preflight --json
247
247
  buildchain paper status --json
248
248
  buildchain release-propagation plan --graph graph.json --upstream-release release.json --json
249
+ buildchain release-propagation pickup plan --config manual-upstreams.json --source-id buildchain --channel release --current-version 3.0.3 --json
249
250
  buildchain kfd status --json
250
251
  buildchain kfd schema list --json
251
252
  buildchain kfd 1 witness --json
@@ -73,6 +73,7 @@ const requiredPaths = [
73
73
  "docs/github-artifact-attestation.md",
74
74
  "docs/shifu-gate-profiles.md",
75
75
  "docs/auditable-demo.md",
76
+ "contracts/auditable-demo-scenario-v1.schema.json",
76
77
  "contracts/auditable-demo-media-profiles-v1.json",
77
78
  "contracts/evidence/auditable-demo-web-delivery-v1.json",
78
79
  "contracts/evidence/auditable-demo-responsive-web-delivery-v1.json",
@@ -103,6 +104,8 @@ const requiredPaths = [
103
104
  "scripts/seal-macos-credential-input.mjs",
104
105
  "scripts/shifu-gate-profile.mjs",
105
106
  "scripts/auditable-demo.mjs",
107
+ "scripts/auditable-demo-platform.mjs",
108
+ "scripts/auditable-demo-capture.py",
106
109
  "scripts/resolve-artifact-coordinates.mjs",
107
110
  "scripts/artifact-relay-s3.mjs",
108
111
  "scripts/anchored-version-material.mjs",
@@ -154,6 +157,8 @@ const requiredPaths = [
154
157
  ".github/workflows/.build.yml",
155
158
  ".github/workflows/.gate-profile.yml",
156
159
  ".github/workflows/.auditable-demo.yml",
160
+ ".github/workflows/.declarative-auditable-demo.yml",
161
+ ".github/workflows/auditable-demo.yml",
157
162
  ".github/workflows/build.yml",
158
163
  ".github/workflows/build-surface-fixture.yml",
159
164
  ".github/workflows/candidate-lab.yml",
@@ -12,6 +12,7 @@ import {
12
12
  artifactSigningEvidenceDigest,
13
13
  createArtifactSigningResult,
14
14
  } from "../packages/core/artifact-signing-result.js";
15
+ import { acceptedMacosCredentialEvidence } from "../actions/macos-credential-island/dmg-assembly.js";
15
16
  import { writeGitHubOutputs } from "./build-contract-core.mjs";
16
17
 
17
18
  function required(value, label) {
@@ -32,15 +33,39 @@ function resolveBelow(root, relative, label) {
32
33
  return target;
33
34
  }
34
35
 
36
+ function expectedCredentialExecution(
37
+ appBundleResult,
38
+ expectedRunId,
39
+ expectedRunAttempt,
40
+ ) {
41
+ if (!appBundleResult) return null;
42
+ return {
43
+ runId: required(expectedRunId, "credential execution run id"),
44
+ runAttempt: required(
45
+ expectedRunAttempt,
46
+ "credential execution run attempt",
47
+ ),
48
+ };
49
+ }
50
+
51
+ function acceptedProviderEvidence(evidence, request, expectedExecution) {
52
+ return expectedExecution
53
+ ? acceptedMacosCredentialEvidence(evidence, request, expectedExecution)
54
+ : evidence.status === "passed" &&
55
+ evidence.provider === request.signature.provider;
56
+ }
57
+
35
58
  export function finalizeNativeArtifactSigningResult({
36
59
  requestRoot = process.env.BUILDCHAIN_SIGNING_REQUEST_ROOT,
37
60
  requestPath = process.env.BUILDCHAIN_SIGNING_REQUEST_PATH,
38
61
  signedPayload = process.env.BUILDCHAIN_SIGNED_PAYLOAD,
39
62
  evidencePath = process.env.BUILDCHAIN_SIGNING_EVIDENCE,
40
- credentialArtifactRoot =
41
- process.env.BUILDCHAIN_SIGNING_CREDENTIAL_ARTIFACT_ROOT,
63
+ credentialArtifactRoot = process.env
64
+ .BUILDCHAIN_SIGNING_CREDENTIAL_ARTIFACT_ROOT,
42
65
  outputRoot = process.env.BUILDCHAIN_SIGNING_RESULT_ROOT,
43
66
  checks = process.env.BUILDCHAIN_SIGNING_VERIFICATION_CHECKS,
67
+ expectedRunId = process.env.GITHUB_RUN_ID,
68
+ expectedRunAttempt = process.env.GITHUB_RUN_ATTEMPT,
44
69
  } = {}) {
45
70
  const requests = path.resolve(required(requestRoot, "signing request root"));
46
71
  const request = JSON.parse(
@@ -77,19 +102,16 @@ export function finalizeNativeArtifactSigningResult({
77
102
  fs.copyFileSync(evidenceSource, evidenceOutput, fs.constants.COPYFILE_EXCL);
78
103
  const evidenceDocument = JSON.parse(fs.readFileSync(evidenceOutput, "utf8"));
79
104
  const appBundleResult = request.artifact.kind === "app-bundle";
80
- const providerEvidencePassed = appBundleResult
81
- ? evidenceDocument.schema ===
82
- "buildchain.macos-credential-island-evidence/v1" &&
83
- evidenceDocument.status === "accepted" &&
84
- evidenceDocument.source?.repository === request.source.repository &&
85
- evidenceDocument.source?.sha === request.source.sha &&
86
- evidenceDocument.source?.treeSha === request.source.treeSha &&
87
- evidenceDocument.buildchain?.runtimeSha === request.runtime.sha &&
88
- evidenceDocument.app?.architecture === request.artifact.arch &&
89
- evidenceDocument.notarization?.application?.status === "Accepted" &&
90
- evidenceDocument.notarization?.diskImage?.status === "Accepted"
91
- : evidenceDocument.status === "passed" &&
92
- evidenceDocument.provider === request.signature.provider;
105
+ const expectedExecution = expectedCredentialExecution(
106
+ appBundleResult,
107
+ expectedRunId,
108
+ expectedRunAttempt,
109
+ );
110
+ const providerEvidencePassed = acceptedProviderEvidence(
111
+ evidenceDocument,
112
+ request,
113
+ expectedExecution,
114
+ );
93
115
  if (!providerEvidencePassed) {
94
116
  throw new Error(
95
117
  "provider evidence does not prove the requested native signature",
@@ -767,6 +767,7 @@ function buildSiteBundle() {
767
767
  ["build", "channel-build-router"],
768
768
  [".build", "reusable-build"],
769
769
  [".auditable-demo", "auditable-demo"],
770
+ [".declarative-auditable-demo", "declarative-auditable-demo"],
770
771
  ["web-surface", "site-app-deployment"],
771
772
  ["buildchain-ref-promotion", "release-governance"],
772
773
  ["release-line-bootstrap", "release-governance"],
@@ -793,6 +794,7 @@ function buildSiteBundle() {
793
794
  ]);
794
795
  const statusById = new Map([
795
796
  [".auditable-demo", "preview"],
797
+ [".declarative-auditable-demo", "preview"],
796
798
  ["release-propagation", "preview"],
797
799
  ["candidate-lab", "repository-internal"],
798
800
  ["build-surface-fixture", "repository-internal"],
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
+ import { execFileSync } from "node:child_process";
4
5
  import { pathToFileURL } from "node:url";
5
6
  import {
6
7
  claimReleasePropagationWork,
@@ -15,6 +16,10 @@ import {
15
16
  resumeReleasePropagationWork,
16
17
  verifyReleasePropagationWork,
17
18
  writeReleasePropagationLock,
19
+ createManualUpstreamPickupCapture,
20
+ createManualUpstreamPickupPlan,
21
+ normalizeManualUpstreamPickupConfig,
22
+ resolveNpmRegistryRelease,
18
23
  } from "../packages/core/release-propagation.js";
19
24
 
20
25
  function usage() {
@@ -61,6 +66,15 @@ function usage() {
61
66
  --completion-decision <json-or-path>
62
67
  --expected-work-root <sha256:...>
63
68
  [--output <file>] [--json]
69
+ buildchain release-propagation pickup plan --config <json-or-path>
70
+ --source-id <id> --channel <alpha|release>
71
+ --current-version <version>
72
+ [--output <file>] [--json]
73
+ buildchain release-propagation pickup create --config <json-or-path>
74
+ --source-id <id> --channel <alpha|release>
75
+ --current-version <version>
76
+ --expected-downstream-base-sha <git-sha>
77
+ [--output <file>] [--json]
64
78
  `;
65
79
  }
66
80
 
@@ -171,7 +185,61 @@ function runWorkCli(args) {
171
185
  throw new Error(`unsupported release-propagation work command: ${mode}`);
172
186
  }
173
187
 
174
- export function runReleasePropagationCli(argv = process.argv.slice(2)) {
188
+ async function resolvePickupPlan(args) {
189
+ const config = normalizeManualUpstreamPickupConfig(readJsonFlag(args, "config"));
190
+ const sourceId = readFlag(args, "source-id");
191
+ const channel = readFlag(args, "channel");
192
+ const source = config.sources.find((entry) => entry.id === sourceId);
193
+ if (!source) throw new Error(`manual pickup source is not configured: ${sourceId}`);
194
+ const distTag = source.distTags[channel];
195
+ if (!distTag) throw new Error("manual pickup channel must be alpha or release");
196
+ const packageMetadata = args.includes("--package-metadata")
197
+ ? readJsonFlag(args, "package-metadata")
198
+ : JSON.parse(execFileSync("npm", ["view", `${source.package}@${distTag}`, "--json", "--registry=https://registry.npmjs.org/"], {
199
+ encoding: "utf8",
200
+ maxBuffer: 16 * 1024 * 1024,
201
+ }));
202
+ const attestationUrl = packageMetadata.dist?.attestations?.url;
203
+ if (!attestationUrl) throw new Error("published npm package does not expose an attestation URL");
204
+ let attestations;
205
+ if (args.includes("--attestations")) {
206
+ attestations = readJsonFlag(args, "attestations");
207
+ } else {
208
+ const response = await fetch(attestationUrl, { headers: { accept: "application/json" } });
209
+ if (!response.ok) throw new Error(`npm attestation lookup failed: HTTP ${response.status}`);
210
+ attestations = await response.json();
211
+ }
212
+ const upstreamRelease = resolveNpmRegistryRelease({ source, channel, packageMetadata, attestations });
213
+ return createManualUpstreamPickupPlan({
214
+ config,
215
+ sourceId,
216
+ channel,
217
+ currentVersion: readFlag(args, "current-version"),
218
+ upstreamRelease,
219
+ });
220
+ }
221
+
222
+ async function runPickupCli(args) {
223
+ const [mode = "", ...pickupArgs] = args;
224
+ if (!mode || mode === "--help" || mode === "-h") {
225
+ process.stdout.write(usage());
226
+ return;
227
+ }
228
+ if (mode !== "plan" && mode !== "create") throw new Error(`unsupported release-propagation pickup command: ${mode}`);
229
+ const plan = await resolvePickupPlan(pickupArgs);
230
+ const result = mode === "create"
231
+ ? createManualUpstreamPickupCapture({
232
+ plan,
233
+ expectedDownstreamBaseSha: readFlag(pickupArgs, "expected-downstream-base-sha"),
234
+ })
235
+ : plan;
236
+ writeOutput(readFlag(pickupArgs, "output", ""), result);
237
+ if (hasFlag(pickupArgs, "json")) printJson(result);
238
+ else if (mode === "plan") process.stdout.write(`manual upstream pickup: ${plan.source.id} ${plan.currentVersion} -> ${plan.resolvedVersion} (${plan.status})\n`);
239
+ else process.stdout.write(`manual upstream pickup next action: ${result.nextAction}\n`);
240
+ }
241
+
242
+ export async function runReleasePropagationCli(argv = process.argv.slice(2)) {
175
243
  const [mode = "", ...args] = argv;
176
244
  if (!mode || mode === "--help" || mode === "-h") {
177
245
  process.stdout.write(usage());
@@ -206,6 +274,10 @@ export function runReleasePropagationCli(argv = process.argv.slice(2)) {
206
274
  runWorkCli(args);
207
275
  return;
208
276
  }
277
+ if (mode === "pickup") {
278
+ await runPickupCli(args);
279
+ return;
280
+ }
209
281
  if (mode === "write-lock") {
210
282
  const plan = readReleasePropagationJson(readFlag(args, "plan"), {
211
283
  label: "--plan",
@@ -259,5 +331,8 @@ export function runReleasePropagationCli(argv = process.argv.slice(2)) {
259
331
  }
260
332
 
261
333
  if (!process.env.BUILDCHAIN_EMBEDDED_ENTRYPOINT && process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
262
- runReleasePropagationCli();
334
+ runReleasePropagationCli().catch((error) => {
335
+ console.error(`buildchain release-propagation: ${error.message}`);
336
+ process.exitCode = 1;
337
+ });
263
338
  }