@kungfu-tech/buildchain 2.12.6 → 2.12.7-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 (40) hide show
  1. package/actions/promote-buildchain-ref/README.md +11 -3
  2. package/bin/buildchain.mjs +78 -0
  3. package/dist/site/agent-index.json +1 -0
  4. package/dist/site/artifact-schemas.json +1 -0
  5. package/dist/site/buildchain-contract.json +163 -38
  6. package/dist/site/buildchain-site.json +73 -17
  7. package/dist/site/capability-registry.json +6 -5
  8. package/dist/site/cli-registry.json +36 -0
  9. package/dist/site/controller-registry.json +121 -10
  10. package/dist/site/kfd-claims.json +305 -17
  11. package/dist/site/kfd-upstream-aggregate.json +1 -1
  12. package/dist/site/manual-registry.json +16 -2
  13. package/dist/site/node-api-registry.json +43 -4
  14. package/dist/site/page-registry.json +58 -11
  15. package/dist/site/public-surface-audit.json +212 -19
  16. package/dist/site/publication-authority-registry.json +778 -0
  17. package/dist/site/publication-registry.json +4 -4
  18. package/dist/site/release-provenance.json +4 -0
  19. package/dist/site/site-manifest.json +15 -6
  20. package/dist/site/workflow-registry.json +173 -12
  21. package/docs/MAP.md +2 -1
  22. package/docs/publication-artifacts.md +25 -18
  23. package/docs/publication-authority.md +213 -0
  24. package/docs/release-governance.md +3 -1
  25. package/docs/shifu-gate-profiles.md +6 -0
  26. package/package.json +5 -1
  27. package/packages/core/buildchain-kfd-claims.js +5 -0
  28. package/packages/core/buildchain-publication-authority.js +80 -0
  29. package/packages/core/controller-evidence.js +8 -8
  30. package/packages/core/index.js +35 -0
  31. package/packages/core/publication-artifact-candidate.js +128 -0
  32. package/packages/core/publication-authority.js +764 -0
  33. package/packages/core/publication-control-plane-audit.js +135 -0
  34. package/scripts/assemble-publication-artifact-admission.mjs +190 -0
  35. package/scripts/assemble-self-publication-admission.mjs +183 -0
  36. package/scripts/audit-publication-control-plane.mjs +409 -0
  37. package/scripts/check-inventory.mjs +25 -2
  38. package/scripts/generate-site-bundle.mjs +16 -0
  39. package/scripts/publication-artifact-candidate.mjs +130 -0
  40. package/scripts/workflow-friction-report.mjs +13 -2
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+
7
+ import {
8
+ createPublicationArtifactCandidate,
9
+ resolvePublicationCandidateFile,
10
+ } from "../packages/core/publication-artifact-candidate.js";
11
+
12
+ export { resolvePublicationCandidateFile };
13
+
14
+ function flag(name, fallback = "") {
15
+ const index = process.argv.indexOf(`--${name}`);
16
+ return index === -1 ? fallback : String(process.argv[index + 1] || "");
17
+ }
18
+
19
+ function requiredFlag(name) {
20
+ const value = flag(name).trim();
21
+ if (!value) throw new Error(`--${name} is required`);
22
+ return value;
23
+ }
24
+
25
+ function filesNamed(root, name) {
26
+ const matches = [];
27
+ const pending = [path.resolve(root)];
28
+ while (pending.length > 0) {
29
+ const current = pending.pop();
30
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
31
+ const full = path.join(current, entry.name);
32
+ if (entry.isDirectory()) pending.push(full);
33
+ else if (entry.isFile() && entry.name === name) matches.push(full);
34
+ }
35
+ }
36
+ return matches.sort();
37
+ }
38
+
39
+ function oneJson(root, name) {
40
+ const matches = filesNamed(root, name);
41
+ if (matches.length !== 1)
42
+ throw new Error(
43
+ `expected exactly one ${name} under ${root}, found ${matches.length}`,
44
+ );
45
+ return JSON.parse(fs.readFileSync(matches[0], "utf8"));
46
+ }
47
+
48
+ function collectFiles(root) {
49
+ const absoluteRoot = path.resolve(root);
50
+ const files = [];
51
+ const pending = [absoluteRoot];
52
+ while (pending.length > 0) {
53
+ const current = pending.pop();
54
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
55
+ const full = path.join(current, entry.name);
56
+ if (entry.isDirectory()) pending.push(full);
57
+ else if (entry.isFile()) {
58
+ files.push({
59
+ path: path.relative(absoluteRoot, full).split(path.sep).join("/"),
60
+ size: fs.statSync(full).size,
61
+ sha256: crypto
62
+ .createHash("sha256")
63
+ .update(fs.readFileSync(full))
64
+ .digest("hex"),
65
+ });
66
+ }
67
+ }
68
+ }
69
+ return files.sort((left, right) => left.path.localeCompare(right.path));
70
+ }
71
+
72
+ export function buildPublicationArtifactCandidate({
73
+ artifactRoot,
74
+ controllerRoot,
75
+ repository,
76
+ sourceSha,
77
+ sourceTreeSha,
78
+ runtimeSha,
79
+ } = {}) {
80
+ const resolvedArtifactRoot = path.resolve(artifactRoot);
81
+ const resolvedControllerRoot = path.resolve(controllerRoot);
82
+ const evidence = {
83
+ repository,
84
+ sourceSha,
85
+ sourceTreeSha,
86
+ runtimeSha,
87
+ manifest: oneJson(resolvedArtifactRoot, "publication-artifact.json"),
88
+ passport: oneJson(
89
+ resolvedArtifactRoot,
90
+ "publication-artifact-passport.json",
91
+ ),
92
+ controllerReceipt: oneJson(resolvedControllerRoot, "receipt.json"),
93
+ files: collectFiles(resolvedArtifactRoot),
94
+ };
95
+ const candidate = createPublicationArtifactCandidate(evidence);
96
+ return { schemaVersion: 1, candidate, evidence };
97
+ }
98
+
99
+ function main() {
100
+ const result = buildPublicationArtifactCandidate({
101
+ artifactRoot: requiredFlag("artifact-root"),
102
+ controllerRoot: requiredFlag("controller-root"),
103
+ repository: requiredFlag("repository"),
104
+ sourceSha: requiredFlag("source-sha"),
105
+ sourceTreeSha: requiredFlag("source-tree-sha"),
106
+ runtimeSha: requiredFlag("runtime-sha"),
107
+ });
108
+ const output = flag("output");
109
+ if (output) {
110
+ fs.mkdirSync(path.dirname(path.resolve(output)), { recursive: true });
111
+ fs.writeFileSync(
112
+ path.resolve(output),
113
+ `${JSON.stringify(result, null, 2)}\n`,
114
+ );
115
+ }
116
+ if (process.argv.includes("--json") || !output)
117
+ process.stdout.write(`${JSON.stringify(result)}\n`);
118
+ }
119
+
120
+ if (
121
+ process.argv[1] &&
122
+ import.meta.url === pathToFileURL(process.argv[1]).href
123
+ ) {
124
+ try {
125
+ main();
126
+ } catch (error) {
127
+ console.error(`publication artifact candidate: ${error.message}`);
128
+ process.exitCode = 1;
129
+ }
130
+ }
@@ -202,6 +202,8 @@ export async function classifyWorkflowFriction({
202
202
  buildWorkflowName = DEFAULT_BUILD_WORKFLOW_NAME,
203
203
  releaseCandidateOutcome = env("BUILDCHAIN_RC_RESOLVE_OUTCOME"),
204
204
  releaseCandidateDiagnosis = env("BUILDCHAIN_RC_DIAGNOSIS"),
205
+ promotionOutcome = env("BUILDCHAIN_PROMOTION_OUTCOME"),
206
+ promotionDiagnosis = env("BUILDCHAIN_PROMOTION_DIAGNOSIS"),
205
207
  runUrl = env("BUILDCHAIN_WORKFLOW_RUN_URL"),
206
208
  outputDir = ".buildchain/workflow-friction",
207
209
  fetchImpl = globalThis.fetch,
@@ -256,14 +258,23 @@ export async function classifyWorkflowFriction({
256
258
  if (releaseCandidateOutcome === "failure") {
257
259
  diagnosisParts.push("Promotion reached the post-Verify workflow before required PR-stage RC evidence could be resolved.");
258
260
  }
259
- if (releaseCandidateDiagnosis) {
261
+ if (releaseCandidateOutcome === "failure" && releaseCandidateDiagnosis) {
260
262
  diagnosisParts.push(releaseCandidateDiagnosis);
261
263
  }
264
+ if (promotionOutcome === "failure") {
265
+ diagnosisParts.push(
266
+ promotionDiagnosis
267
+ ? `Promotion failed: ${promotionDiagnosis}`
268
+ : "Promotion failed after PR-stage release-candidate evidence resolved successfully.",
269
+ );
270
+ }
262
271
  diagnosisParts.push(...workflowRunDiagnostics);
263
272
  const diagnosis = diagnosisParts.join(" ") || "Buildchain ref promotion failed after Verify succeeded; inspect the classified evidence and keep the fix in Buildchain.";
264
273
  const nextAction = frictionClass === "late-fail-fast"
265
274
  ? "Move the missing/stale RC evidence check earlier or make the promotion workflow consume the exact PR-stage RC passport before any publish side effect."
266
- : "Deduplicate the PR/build path or tighten Buildchain workflow gates so the next channel promotion reaches publish exactly once.";
275
+ : ["duplicate-channel-pr", "duplicate-heavy-build"].includes(frictionClass)
276
+ ? "Deduplicate the PR/build path or tighten Buildchain workflow gates so the next channel promotion reaches publish exactly once."
277
+ : "Fix the concrete promotion failure above, then rerun through the protected channel workflow; do not treat successful RC resolution as the failure diagnosis.";
267
278
  fs.mkdirSync(outputDir, { recursive: true });
268
279
  const bodyFile = path.join(outputDir, "issue-body.md");
269
280
  const body = buildWorkflowFrictionBody({