@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.
- package/actions/promote-buildchain-ref/README.md +11 -3
- package/bin/buildchain.mjs +78 -0
- package/dist/site/agent-index.json +1 -0
- package/dist/site/artifact-schemas.json +1 -0
- package/dist/site/buildchain-contract.json +163 -38
- package/dist/site/buildchain-site.json +73 -17
- package/dist/site/capability-registry.json +6 -5
- package/dist/site/cli-registry.json +36 -0
- package/dist/site/controller-registry.json +121 -10
- package/dist/site/kfd-claims.json +305 -17
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +16 -2
- package/dist/site/node-api-registry.json +43 -4
- package/dist/site/page-registry.json +58 -11
- package/dist/site/public-surface-audit.json +212 -19
- package/dist/site/publication-authority-registry.json +778 -0
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +4 -0
- package/dist/site/site-manifest.json +15 -6
- package/dist/site/workflow-registry.json +173 -12
- package/docs/MAP.md +2 -1
- package/docs/publication-artifacts.md +25 -18
- package/docs/publication-authority.md +213 -0
- package/docs/release-governance.md +3 -1
- package/docs/shifu-gate-profiles.md +6 -0
- package/package.json +5 -1
- package/packages/core/buildchain-kfd-claims.js +5 -0
- package/packages/core/buildchain-publication-authority.js +80 -0
- package/packages/core/controller-evidence.js +8 -8
- package/packages/core/index.js +35 -0
- package/packages/core/publication-artifact-candidate.js +128 -0
- package/packages/core/publication-authority.js +764 -0
- package/packages/core/publication-control-plane-audit.js +135 -0
- package/scripts/assemble-publication-artifact-admission.mjs +190 -0
- package/scripts/assemble-self-publication-admission.mjs +183 -0
- package/scripts/audit-publication-control-plane.mjs +409 -0
- package/scripts/check-inventory.mjs +25 -2
- package/scripts/generate-site-bundle.mjs +16 -0
- package/scripts/publication-artifact-candidate.mjs +130 -0
- 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
|
-
: "
|
|
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({
|