@kungfu-tech/buildchain 4.1.3 → 4.1.4-alpha.0
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/AGENTS.md +11 -13
- package/CONTRIBUTING.md +32 -9
- package/README.md +56 -140
- package/actions/release/promotion/ref/README.md +1 -1
- package/architecture/action-taxonomy.json +1 -1
- package/architecture/agent-change-map.md +32 -45
- package/architecture/build-orchestration.json +1 -1
- package/architecture/capability-state-machine-manifest.json +5 -5
- package/architecture/ci-lane-change-budget.json +92 -92
- package/architecture/code-layout.json +2 -12
- package/architecture/core-mechanisms.json +38 -39
- package/architecture/cross-platform-adopter-qualification.json +1 -1
- package/architecture/dev-delivery-orchestration.json +2 -2
- package/architecture/floating-consumer-policy.json +3 -3
- package/architecture/internal-capabilities.json +27 -28
- package/architecture/maintainability-debt.json +60 -94
- package/architecture/maintainability-policy.json +7 -13
- package/architecture/minimal-consumer-migration.json +43 -5014
- package/architecture/platform-stage-checkpoints.json +1 -1
- package/architecture/release-tail-contract.json +2 -2
- package/architecture/release-topology.json +31 -25
- package/architecture/stage-capsule-qualification.json +1 -1
- package/architecture/universal-workflow-bootstrap.json +60 -60
- package/architecture/universal-workflow-capability-policy.json +3 -3
- package/architecture/universal-workflow-fault-campaign.json +7 -7
- package/architecture/workflow-taxonomy.json +59 -60
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/dist/site/buildchain-contract.json +138 -66
- package/dist/site/buildchain-site.json +257 -331
- package/dist/site/capability-registry.json +4 -4
- package/dist/site/cli-registry.json +6 -142
- package/dist/site/controller-registry.json +19 -19
- package/dist/site/kfd-claims.json +481 -529
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +22 -22
- package/dist/site/node-api-registry.json +82 -339
- package/dist/site/page-registry.json +229 -304
- package/dist/site/public-surface-audit.json +544 -754
- package/dist/site/publication-authority-registry.json +316 -316
- package/dist/site/publication-registry.json +1 -1
- package/dist/site/site-manifest.json +23 -23
- package/dist/site/workflow-registry.json +1071 -1027
- package/docs/MAP.md +28 -20
- package/docs/auditable-demo.md +17 -49
- package/docs/bootstrap-recovery.md +46 -37
- package/docs/cli-reference.md +2 -46
- package/docs/cli.md +97 -186
- package/docs/code-organization.md +7 -5
- package/docs/dev-alpha-candidate-patrol.md +19 -42
- package/docs/dev-delivery-orchestration.md +15 -11
- package/docs/dev-delivery-qualification-landing-adr.md +10 -9
- package/docs/dev-delivery-warrant.md +17 -16
- package/docs/dev-qualification-patrol.md +25 -20
- package/docs/getting-started.md +80 -139
- package/docs/github-artifact-attestation.md +31 -81
- package/docs/install.md +66 -30
- package/docs/lifecycle-protocol.md +19 -57
- package/docs/migration-inventory.md +17 -45
- package/docs/next-development-transition.md +13 -13
- package/docs/node-api-reference.md +122 -138
- package/docs/oci-publication.md +13 -10
- package/docs/publication-artifacts.md +128 -486
- package/docs/publication-authority.md +15 -45
- package/docs/publish-transaction.md +13 -11
- package/docs/release-candidate.md +126 -355
- package/docs/release-discussions.md +36 -67
- package/docs/release-flow.md +80 -339
- package/docs/release-governance.md +114 -962
- package/docs/release-promotion-request.md +45 -43
- package/docs/release-propagation.md +13 -27
- package/docs/release-tail-provider-plane.md +38 -65
- package/docs/reusable-build-surface.md +78 -196
- package/docs/runtime-train-validation.md +8 -6
- package/docs/stable-candidate-patrol.md +13 -66
- package/docs/v4-adopter-delivery.md +15 -15
- package/docs/v4-declarative-release-tail-migration.md +19 -17
- package/docs/v4-production-release.md +11 -6
- package/docs/v4-publication-rehearsal.md +12 -27
- package/docs/v4-stage-capsule.md +14 -20
- package/docs/v4-tail-reseal.md +34 -20
- package/docs/web-surface-deployments.md +26 -264
- package/docs/workflow-catalog.md +31 -31
- package/docs/workflow-path-migration.md +10 -4
- package/package.json +5 -4
- package/packages/core/adoption/buildchain-kfd-claims.js +7 -7
- package/packages/core/adoption/cli/output.mjs +1 -9
- package/packages/core/adoption/cli/product-gate.mjs +1 -15
- package/packages/core/adoption/cli/project.mjs +9 -10
- package/packages/core/adoption/cli/standards.mjs +1 -14
- package/packages/core/adoption/cli/support.mjs +0 -2
- package/packages/core/adoption/cli/surface-register.mjs +0 -2
- package/packages/core/adoption/commands/init-repo.mjs +77 -485
- package/packages/core/adoption/consumer-init.js +279 -0
- package/packages/core/adoption/qualification/admission.js +1 -1
- package/packages/core/build/cli/cache.mjs +0 -2
- package/packages/core/build/cli/facts.mjs +0 -3
- package/packages/core/build/cli/homebrew.mjs +0 -4
- package/packages/core/build/cli/lifecycle.mjs +0 -3
- package/packages/core/build/github-artifact-attestation.js +2 -2
- package/packages/core/build/platform-stage-checkpoints.js +1 -1
- package/packages/core/build/signing/dispatch.js +1 -1
- package/packages/core/consumer/buildchain-config.js +9 -0
- package/packages/core/consumer/contract/inspection.js +351 -9
- package/packages/core/consumer/contract/javascript-syntax.js +439 -0
- package/packages/core/consumer/contract/javascript-values.js +61 -0
- package/packages/core/consumer/contract/javascript.js +555 -0
- package/packages/core/consumer/contract/local-validation.js +84 -0
- package/packages/core/contracts/buildchain-contract.js +39 -8
- package/packages/core/contracts/cli/introspection.mjs +1 -9
- package/packages/core/contracts/public-surface-cli.js +0 -1
- package/packages/core/dev-delivery/dev-delivery-contract-surface.js +2 -2
- package/packages/core/governance/buildchain-publication-authority.js +24 -24
- package/packages/core/governance/cli/doctor.mjs +58 -42
- package/packages/core/governance/commands/architecture.mjs +6 -1
- package/packages/core/governance/commands/audit-publication-control-plane.mjs +1 -1
- package/packages/core/governance/commands/workflow-friction-report.mjs +1 -1
- package/packages/core/governance/github-governance-authority.js +2 -2
- package/packages/core/governance/publication/collection.js +1 -1
- package/packages/core/index.js +1 -3
- package/packages/core/observability/cli/candidate.mjs +0 -2
- package/packages/core/observability/cli/context.mjs +0 -5
- package/packages/core/observability/cli/diagnostics.mjs +0 -7
- package/packages/core/observability/cli/events.mjs +2 -10
- package/packages/core/paper/commands/paper-work-fleet-cli.mjs +1 -1
- package/packages/core/paper/commands/paper.mjs +33 -422
- package/packages/core/paper/consumer-status.js +72 -0
- package/packages/core/paper/operations/identity.js +0 -3
- package/packages/core/paper/operations/migration.js +460 -0
- package/packages/core/paper/operations/preflight-checks.js +8 -68
- package/packages/core/paper/operations/scaffold.js +16 -326
- package/packages/core/paper/operations/status.js +27 -53
- package/packages/core/paper/paper-agent-entry.js +36 -147
- package/packages/core/paper/paper-fleet.js +24 -39
- package/packages/core/paper/paper-repository.js +16 -0
- package/packages/core/paper/paper-runtime-channels.js +1 -1
- package/packages/core/paper/paper-scaffold-content.js +153 -162
- package/packages/core/paper/paper-work.js +3 -10
- package/packages/core/paper/paper.js +43 -5
- package/packages/core/providers/github/artifact-attestation.js +1 -1
- package/packages/core/providers/github/pipeline-context-artifacts.js +172 -0
- package/packages/core/publication/authority/control-plane.js +2 -2
- package/packages/core/publication/authority/seal.js +1 -1
- package/packages/core/publication/cli/dispatch.mjs +1 -12
- package/packages/core/publication/pipeline/actions.js +16 -9
- package/packages/core/publication/pipeline/apply.js +17 -9
- package/packages/core/publication/pipeline/context.js +29 -0
- package/packages/core/publication/pipeline/prepare.js +15 -6
- package/packages/core/publication/pipeline/recovery-materials.js +63 -1
- package/packages/core/publication/pipeline/recovery-prepare.js +21 -9
- package/packages/core/publication/pipeline/stable-entry.js +12 -7
- package/packages/core/publication/pipeline/stable-products.js +21 -19
- package/packages/core/publication/pipeline/version-actions.js +15 -6
- package/packages/core/publication/publication-rehearsal-projection.js +0 -9
- package/packages/core/release/cli/dispatch.mjs +1 -4
- package/packages/core/release/cli/inspection-handlers.mjs +1 -33
- package/packages/core/release/cli/release-handlers.mjs +2 -19
- package/packages/core/release/cli/verification-handlers.mjs +4 -25
- package/packages/core/release/next-development-projection.js +13 -13
- package/packages/core/release/passport/locations.js +13 -8
- package/packages/core/release/release-passport.js +70 -5
- package/packages/core/web/cli/badges.mjs +0 -4
- package/packages/core/web/publication-admission.js +1 -1
- package/packages/core/web/publication-capability.js +1 -1
- package/packages/core/web/readme-badges.js +1 -1
- package/packages/core/workflow/cli/main.mjs +1 -4
- package/packages/core/workflow/cli/process.mjs +1 -1
- package/packages/core/workflow/commands/buildchain-cli-help.mjs +2 -8
- package/packages/core/workflow/engine/conformance.js +1 -1
- package/packages/core/workflow/pipeline/build.js +21 -1
- package/packages/core/workflow/pipeline/delivery-observation.js +6 -2
- package/packages/core/workflow/workflow-taxonomy.mjs +61 -17
- package/scripts/check-code-layout.mjs +2 -0
- package/scripts/check-core-mechanism-inventory.mjs +29 -13
- package/scripts/check-floating-consumer-policy-contract.mjs +3 -3
- package/scripts/check-inventory.mjs +27 -27
- package/scripts/check-maintainability.mjs +28 -5
- package/scripts/check-public-dogfood-contract.mjs +3 -3
- package/scripts/check-release-topology.mjs +1 -1
- package/scripts/check-self-consumer-contract.mjs +7 -7
- package/scripts/check-universal-workflow-bootstrap.mjs +1 -1
- package/scripts/generate-channel-promotion-workflow.mjs +1 -1
- package/scripts/generate-minimal-consumer-contract.mjs +21 -1
- package/scripts/generate-site-bundle.mjs +2 -1
- package/scripts/generate-tail-reseal-fixture.mjs +3 -3
- package/scripts/inventory/promotion.mjs +1 -1
- package/scripts/next-development-self-dogfood-harness.mjs +1 -1
- package/scripts/next-development-self-dogfood.mjs +4 -4
- package/scripts/site-capability-metadata.mjs +2 -6
- package/scripts/source-metric-lineage.mjs +62 -7
- package/scripts/verify-golden-path.mjs +31 -35
- package/templates/native-dev-delivery.yml +1 -1
- package/templates/universal-buildchain-bootstrap.yml +1 -1
- package/packages/core/paper/operations/bootstrap.js +0 -75
- package/packages/core/paper/operations/plans.js +0 -213
- package/packages/core/paper/operations/provisioning.js +0 -113
- package/packages/core/paper/operations/scaffold-config.js +0 -77
- package/packages/core/paper/operations/scaffold-workflows.js +0 -116
- package/packages/core/paper/operations/workspace.js +0 -60
- package/packages/core/paper/paper-npm-bootstrap.js +0 -492
|
@@ -3,12 +3,15 @@ import path from "node:path";
|
|
|
3
3
|
import { execFileSync } from "node:child_process";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { bindConsumerSource } from "../../consumer/contract/identity.js";
|
|
6
|
+
import { inspectConsumerContract } from "../../consumer/contract/inspection.js";
|
|
7
|
+
import { validateConsumerWiring } from "../../consumer/contract/local-validation.js";
|
|
6
8
|
import { recordDigest } from "../../release/discussion/envelope.js";
|
|
7
9
|
import { consumerCommandSession } from "../../runtime/consumer-shell.js";
|
|
8
10
|
import { createNativeChildEnvironment } from "../../dev-delivery/native/execution.js";
|
|
9
11
|
|
|
10
12
|
function inspectTrackedBytes(cwd, tree) {
|
|
11
13
|
const root = fs.realpathSync(cwd);
|
|
14
|
+
const files = Object.create(null);
|
|
12
15
|
const entries = execFileSync(
|
|
13
16
|
"git",
|
|
14
17
|
["-C", root, "ls-tree", "-r", "-z", tree],
|
|
@@ -48,7 +51,13 @@ function inspectTrackedBytes(cwd, tree) {
|
|
|
48
51
|
throw new Error(
|
|
49
52
|
`Build tracked source bytes differ from the admitted tree: ${file}`,
|
|
50
53
|
);
|
|
54
|
+
Object.defineProperty(files, file, {
|
|
55
|
+
enumerable: true,
|
|
56
|
+
get: () =>
|
|
57
|
+
link ? fs.readlinkSync(absolute) : fs.readFileSync(absolute, "utf8"),
|
|
58
|
+
});
|
|
51
59
|
}
|
|
60
|
+
return files;
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
export function inspectPipelineSource(cwd, expected) {
|
|
@@ -82,7 +91,18 @@ export function inspectPipelineSource(cwd, expected) {
|
|
|
82
91
|
"Build source has tracked modifications at its verification boundary",
|
|
83
92
|
);
|
|
84
93
|
// Index flags and local Git filters cannot qualify different product bytes.
|
|
85
|
-
inspectTrackedBytes(cwd, expected.tree);
|
|
94
|
+
const files = inspectTrackedBytes(cwd, expected.tree);
|
|
95
|
+
const { channel } = validateConsumerWiring(cwd, expected.configPath);
|
|
96
|
+
const inspection = inspectConsumerContract(files, {
|
|
97
|
+
channel,
|
|
98
|
+
configPath: expected.configPath,
|
|
99
|
+
});
|
|
100
|
+
if (inspection.controlIssues.length)
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Consumer control wiring is invalid: ${inspection.controlIssues.join("; ")}`,
|
|
103
|
+
);
|
|
104
|
+
// Unresolved product language/tool edges are not a static safety proof.
|
|
105
|
+
// Execution remains confined to the runtime's read-only product jobs below.
|
|
86
106
|
return plan;
|
|
87
107
|
}
|
|
88
108
|
|
|
@@ -65,9 +65,13 @@ export async function observePipelineWorker(session, current, queue, host) {
|
|
|
65
65
|
run,
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
|
+
// Retained executions keep the workflow identity that actually produced them.
|
|
69
|
+
// The old path is read-only evidence, never a new dispatch or consumer alias.
|
|
68
70
|
const entries = (run.referenced_workflows || []).filter((entry) =>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
[".ops-dev-auto-merge.yml", "public-ops-dev-auto-merge.yml"].some((file) =>
|
|
72
|
+
entry.path?.startsWith(
|
|
73
|
+
`kungfu-systems/buildchain/.github/workflows/${file}@`,
|
|
74
|
+
),
|
|
71
75
|
),
|
|
72
76
|
);
|
|
73
77
|
if (entries.length !== 1) return null;
|
|
@@ -64,13 +64,12 @@ function validateEntries(policy, errors) {
|
|
|
64
64
|
if (
|
|
65
65
|
entry.path &&
|
|
66
66
|
!(
|
|
67
|
-
[".build", "
|
|
68
|
-
|
|
69
|
-
) && entry.path === `.github/workflows/${entry.id}.yml`
|
|
67
|
+
[".build", "buildchain", "buildchain-recover"].includes(entry.id) &&
|
|
68
|
+
entry.path === `.github/workflows/${entry.id}.yml`
|
|
70
69
|
)
|
|
71
70
|
)
|
|
72
71
|
errors.push(
|
|
73
|
-
`${entry.id}: explicit path is reserved for build
|
|
72
|
+
`${entry.id}: explicit path is reserved for the internal build engine and generated consumer pair`,
|
|
74
73
|
);
|
|
75
74
|
const file = workflowPath(entry);
|
|
76
75
|
if (!WORKFLOW.test(file) || paths.has(file))
|
|
@@ -82,6 +81,33 @@ function validateEntries(policy, errors) {
|
|
|
82
81
|
);
|
|
83
82
|
validateEntryOwnership(entry, errors);
|
|
84
83
|
}
|
|
84
|
+
const publicEntries = policy.entries.filter(
|
|
85
|
+
(entry) => entry.role === "public",
|
|
86
|
+
);
|
|
87
|
+
if (
|
|
88
|
+
policy.entries
|
|
89
|
+
.filter((entry) => entry.role === "self")
|
|
90
|
+
.map((entry) => entry.id)
|
|
91
|
+
.sort()
|
|
92
|
+
.join(",") !== "buildchain,buildchain-recover"
|
|
93
|
+
)
|
|
94
|
+
errors.push(
|
|
95
|
+
"repository event roots must be exactly the generated consumer pair",
|
|
96
|
+
);
|
|
97
|
+
if (
|
|
98
|
+
publicEntries.length !== 2 ||
|
|
99
|
+
["pipeline", "recover"].some(
|
|
100
|
+
(purpose) =>
|
|
101
|
+
!publicEntries.some(
|
|
102
|
+
(entry) =>
|
|
103
|
+
entry.id === purpose &&
|
|
104
|
+
entry.category === "ops" &&
|
|
105
|
+
entry.purpose === purpose &&
|
|
106
|
+
entry.invocation === "reusable",
|
|
107
|
+
),
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
errors.push("consumer public entries must be exactly pipeline and recover");
|
|
85
111
|
return policy.entries;
|
|
86
112
|
}
|
|
87
113
|
|
|
@@ -100,11 +126,9 @@ export function discoverWorkflowFiles(root) {
|
|
|
100
126
|
return visit(directory).sort();
|
|
101
127
|
}
|
|
102
128
|
|
|
103
|
-
function validateWorkflow(
|
|
129
|
+
function validateWorkflow(entry, text, declared, errors) {
|
|
130
|
+
if (typeof text !== "string") return;
|
|
104
131
|
const file = workflowPath(entry),
|
|
105
|
-
absolute = path.join(root, file);
|
|
106
|
-
if (!fs.existsSync(absolute) || !fs.lstatSync(absolute).isFile()) return;
|
|
107
|
-
const text = fs.readFileSync(absolute, "utf8"),
|
|
108
132
|
document = parseWorkflowDocument(text);
|
|
109
133
|
if (
|
|
110
134
|
(entry.invocation === "reusable") !==
|
|
@@ -258,23 +282,41 @@ function validateGateIntegration(root, errors) {
|
|
|
258
282
|
|
|
259
283
|
export function checkWorkflowTaxonomy(
|
|
260
284
|
root,
|
|
261
|
-
{ integration = true, documentation = true } = {},
|
|
285
|
+
{ integration = true, documentation = true, files } = {},
|
|
262
286
|
) {
|
|
263
287
|
const errors = [],
|
|
264
|
-
policy =
|
|
288
|
+
policy = files
|
|
289
|
+
? JSON.parse(files[TAXONOMY_PATH] || "null")
|
|
290
|
+
: readWorkflowTaxonomy(root),
|
|
265
291
|
entries = validateEntries(policy, errors);
|
|
266
292
|
if (errors.length) return { ok: false, errors };
|
|
293
|
+
files ??= Object.fromEntries(
|
|
294
|
+
discoverWorkflowFiles(root).map((file) => {
|
|
295
|
+
const absolute = path.join(root, file);
|
|
296
|
+
return [
|
|
297
|
+
file,
|
|
298
|
+
fs.lstatSync(absolute).isFile()
|
|
299
|
+
? fs.readFileSync(absolute, "utf8")
|
|
300
|
+
: null,
|
|
301
|
+
];
|
|
302
|
+
}),
|
|
303
|
+
);
|
|
267
304
|
const declared = new Set(entries.map(workflowPath)),
|
|
268
|
-
observed = new Set(
|
|
305
|
+
observed = new Set(
|
|
306
|
+
Object.keys(files).filter((file) =>
|
|
307
|
+
/^\.github\/workflows\/.*\.ya?ml$/iu.test(file),
|
|
308
|
+
),
|
|
309
|
+
);
|
|
269
310
|
for (const file of observed) {
|
|
270
311
|
if (!declared.has(file)) errors.push(`unregistered workflow: ${file}`);
|
|
271
|
-
if (
|
|
312
|
+
if (typeof files[file] !== "string")
|
|
272
313
|
errors.push(`workflow must be a regular file: ${file}`);
|
|
273
314
|
}
|
|
274
315
|
for (const file of declared)
|
|
275
316
|
if (!observed.has(file))
|
|
276
317
|
errors.push(`registered workflow missing: ${file}`);
|
|
277
|
-
for (const entry of entries)
|
|
318
|
+
for (const entry of entries)
|
|
319
|
+
validateWorkflow(entry, files[workflowPath(entry)], declared, errors);
|
|
278
320
|
if (integration) validateGateIntegration(root, errors);
|
|
279
321
|
if (documentation) {
|
|
280
322
|
const file = path.join(root, TAXONOMY_DOC);
|
|
@@ -324,9 +366,9 @@ export function renderWorkflowCatalog(policy) {
|
|
|
324
366
|
"",
|
|
325
367
|
"Generated from `architecture/workflow-taxonomy.json`. Every workflow has one canonical implementation; alternate paths and historical forwarding aliases are rejected.",
|
|
326
368
|
"",
|
|
327
|
-
"
|
|
369
|
+
"The two public workflows own normal pipeline execution and exact-attempt recovery. Component workflows are internal runtime implementation, including once-only setup and the dispatch signing service; consumers do not wire these components. Self workflows are the generated consumer pair. Actions own execution steps; JS adapters and Rust/WASM own implementation.",
|
|
328
370
|
"",
|
|
329
|
-
"Names use `public
|
|
371
|
+
"Names use `public-ops-pipeline.yml`, `public-ops-recover.yml`, internal `.<category>-<purpose>.yml`, and the generated self callers `buildchain.yml` and `buildchain-recover.yml`. Categories are `build`, `release`, and `ops`; the internal build engine retains `.build.yml`.",
|
|
330
372
|
"",
|
|
331
373
|
"Register ownership before adding a workflow. `pnpm run check:workflows` validates source, calls, required CI integration and independent review ownership. `pnpm run generate:workflows` regenerates this catalog.",
|
|
332
374
|
"",
|
|
@@ -366,6 +408,8 @@ function validateEntryOwnership(entry, errors) {
|
|
|
366
408
|
errors.push(`${entry.id}: invalid invocation`);
|
|
367
409
|
if ((entry.role === "self") !== (entry.invocation === "repository"))
|
|
368
410
|
errors.push(`${entry.id}: role and invocation disagree`);
|
|
369
|
-
if (entry.
|
|
370
|
-
errors.push(
|
|
411
|
+
if (entry.invocation === "dispatch-service" && entry.role !== "component")
|
|
412
|
+
errors.push(
|
|
413
|
+
`${entry.id}: dispatch services must remain internal components`,
|
|
414
|
+
);
|
|
371
415
|
}
|
|
@@ -204,6 +204,8 @@ export function inspectPublicActionNodes(actions, publicNodes) {
|
|
|
204
204
|
const issues = [];
|
|
205
205
|
if (!Array.isArray(publicNodes))
|
|
206
206
|
return ["public Action registry must be an array"];
|
|
207
|
+
if (publicNodes.length)
|
|
208
|
+
issues.push("Actions are internal runtime nodes; consumers use pipeline and recovery workflows");
|
|
207
209
|
const known = new Set(
|
|
208
210
|
actions.map((action) => action.directory.replace(/^actions\//u, "")),
|
|
209
211
|
);
|
|
@@ -34,15 +34,24 @@ function registrySurfaceIds(root) {
|
|
|
34
34
|
(entry) => `cli:${entry.id}`,
|
|
35
35
|
);
|
|
36
36
|
const workflows = loadJson(root, "dist/site/workflow-registry.json");
|
|
37
|
-
const workflowIds = workflows.workflows.flatMap((entry) =>
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
const workflowIds = workflows.workflows.flatMap((entry) =>
|
|
38
|
+
[entry.id, ...(entry.taxonomy ? [entry.taxonomy.id] : [])].map((id) => [
|
|
39
|
+
`workflow:${id}`,
|
|
40
|
+
entry.apiRole === "public" ? "public" : "internal",
|
|
41
|
+
]),
|
|
42
|
+
);
|
|
43
|
+
const actionIds = workflows.actions.map((entry) => [
|
|
44
|
+
`action:${entry.id}`,
|
|
45
|
+
entry.apiRole === "public" ? "public" : "internal",
|
|
40
46
|
]);
|
|
41
|
-
const actionIds = workflows.actions.map((entry) => `action:${entry.id}`);
|
|
42
47
|
const exports = Object.keys(loadJson(root, "package.json").exports).map(
|
|
43
48
|
(entry) => `export:${entry}`,
|
|
44
49
|
);
|
|
45
|
-
return new
|
|
50
|
+
return new Map(
|
|
51
|
+
[...cli, ...exports]
|
|
52
|
+
.map((id) => [id, "public"])
|
|
53
|
+
.concat(workflowIds, actionIds),
|
|
54
|
+
);
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
function listFiles(root, relativeRoot) {
|
|
@@ -99,12 +108,12 @@ function validateOwnership(mechanism, ownedSources, issues) {
|
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
function validateSurfaces(mechanism, surfaces, issues) {
|
|
102
|
-
for (const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
for (const role of ["public", "internal"])
|
|
112
|
+
for (const surface of mechanism[`${role}Surfaces`] || [])
|
|
113
|
+
if (surfaces.get(surface) !== role)
|
|
114
|
+
issues.push(
|
|
115
|
+
`${mechanism.id}: ${role} surface is absent or has a different role in generated/package registries: ${surface}`,
|
|
116
|
+
);
|
|
108
117
|
}
|
|
109
118
|
|
|
110
119
|
function validateGitRefStore(root, mechanism, issues) {
|
|
@@ -140,12 +149,15 @@ function validateReverseScanReferences(root, reverseScan, issues) {
|
|
|
140
149
|
function validateSurfaceKinds(inventory, issues) {
|
|
141
150
|
const surfaceKinds = new Set(
|
|
142
151
|
(inventory.mechanisms || []).flatMap((mechanism) =>
|
|
143
|
-
|
|
152
|
+
[
|
|
153
|
+
...(mechanism.publicSurfaces || []),
|
|
154
|
+
...(mechanism.internalSurfaces || []),
|
|
155
|
+
].map((surface) => surface.split(":")[0]),
|
|
144
156
|
),
|
|
145
157
|
);
|
|
146
158
|
for (const kind of ["action", "cli", "export", "workflow"]) {
|
|
147
159
|
if (!surfaceKinds.has(kind))
|
|
148
|
-
issues.push(`reverse scan has no ${kind}
|
|
160
|
+
issues.push(`reverse scan has no ${kind} surface coverage`);
|
|
149
161
|
}
|
|
150
162
|
return surfaceKinds;
|
|
151
163
|
}
|
|
@@ -329,6 +341,10 @@ function checkCoreMechanismInventory({
|
|
|
329
341
|
(sum, entry) => sum + entry.publicSurfaces.length,
|
|
330
342
|
0,
|
|
331
343
|
),
|
|
344
|
+
internalSurfaces: inventory.mechanisms.reduce(
|
|
345
|
+
(sum, entry) => sum + (entry.internalSurfaces || []).length,
|
|
346
|
+
0,
|
|
347
|
+
),
|
|
332
348
|
dependencyCycles: inventory.maintainability.dependencyCycles,
|
|
333
349
|
...reverseScan,
|
|
334
350
|
};
|
|
@@ -116,10 +116,10 @@ export function checkFloatingConsumerPolicyContract() {
|
|
|
116
116
|
assertOrdered("actions/publication/candidate/plan/action.yml", [
|
|
117
117
|
"Admit source checkout", "Enforce v4 floating consumer policy", "Resolve controller identities",
|
|
118
118
|
]);
|
|
119
|
-
const publication = YAML.parse(read(".github/workflows
|
|
119
|
+
const publication = YAML.parse(read(".github/workflows/.build-publication.yml"));
|
|
120
120
|
if (!Object.values(publication.jobs).some((job) => job.steps?.some((step) => step.uses?.endsWith("/actions/publication/candidate/plan"))))
|
|
121
121
|
fail("publication does not invoke its consumer policy node");
|
|
122
|
-
const stageCanary = read(".github/workflows
|
|
122
|
+
const stageCanary = read(".github/workflows/.build-stage-capsule-canary.yml");
|
|
123
123
|
if (
|
|
124
124
|
!stageCanary.includes("consumer-admission:") ||
|
|
125
125
|
!jobDependsOn(stageCanary, "qualify", "consumer-admission")
|
|
@@ -128,7 +128,7 @@ export function checkFloatingConsumerPolicyContract() {
|
|
|
128
128
|
"Stage Capsule qualification is not transitively gated by consumer admission",
|
|
129
129
|
);
|
|
130
130
|
}
|
|
131
|
-
const promotion = read(".github/workflows
|
|
131
|
+
const promotion = read(".github/workflows/.release-candidate-promote.yml");
|
|
132
132
|
if (
|
|
133
133
|
!promotion.includes("consumer-admission:") ||
|
|
134
134
|
!jobDependsOn(promotion, "invoke", "consumer-admission")
|
|
@@ -146,23 +146,23 @@ const requiredPaths = [
|
|
|
146
146
|
".buildchain/alpha-contract-lock.json",
|
|
147
147
|
".buildchain/release-impact.json",
|
|
148
148
|
".github/actionlint.yaml",
|
|
149
|
-
".github/workflows
|
|
150
|
-
".github/workflows
|
|
151
|
-
".github/workflows
|
|
152
|
-
".github/workflows
|
|
153
|
-
".github/workflows
|
|
154
|
-
".github/workflows
|
|
155
|
-
".github/workflows
|
|
156
|
-
".github/workflows
|
|
149
|
+
".github/workflows/.ops-release-governance.yml",
|
|
150
|
+
".github/workflows/.ops-dev-auto-merge.yml",
|
|
151
|
+
".github/workflows/.ops-patrol.yml",
|
|
152
|
+
".github/workflows/.ops-patrol-daily.yml",
|
|
153
|
+
".github/workflows/.ops-patrol-weekly.yml",
|
|
154
|
+
".github/workflows/.ops-patrol-monthly.yml",
|
|
155
|
+
".github/workflows/.ops-observed-evidence.yml",
|
|
156
|
+
".github/workflows/.release-candidate-promote.yml",
|
|
157
157
|
".github/workflows/.release-promote.yml",
|
|
158
|
-
".github/workflows
|
|
159
|
-
".github/workflows
|
|
158
|
+
".github/workflows/.release-propagation.yml",
|
|
159
|
+
".github/workflows/.release-paper.yml",
|
|
160
160
|
".github/workflows/.release-binary-assets.yml",
|
|
161
161
|
".github/workflows/.build.yml",
|
|
162
162
|
".github/workflows/.build-gate-profile.yml",
|
|
163
163
|
".github/workflows/.build-demo-adapter.yml",
|
|
164
|
-
".github/workflows
|
|
165
|
-
".github/workflows
|
|
164
|
+
".github/workflows/.build-demo.yml",
|
|
165
|
+
".github/workflows/.build-candidate.yml",
|
|
166
166
|
"fixtures/libnode-shaped/buildchain.toml",
|
|
167
167
|
"fixtures/libnode-shaped/.github/workflows/build.yml",
|
|
168
168
|
"fixtures/libnode-shaped/package.json",
|
|
@@ -281,7 +281,7 @@ for (const requiredSnippet of [
|
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
const channelBuildWorkflow = fs.readFileSync(
|
|
284
|
-
path.join(root, ".github/workflows
|
|
284
|
+
path.join(root, ".github/workflows/.build-candidate.yml"),
|
|
285
285
|
"utf8",
|
|
286
286
|
);
|
|
287
287
|
const advancedPromotionWorkflow = fs.readFileSync(
|
|
@@ -289,7 +289,7 @@ const advancedPromotionWorkflow = fs.readFileSync(
|
|
|
289
289
|
"utf8",
|
|
290
290
|
);
|
|
291
291
|
const channelPromotionWorkflow = fs.readFileSync(
|
|
292
|
-
path.join(root, ".github/workflows
|
|
292
|
+
path.join(root, ".github/workflows/.release-candidate-promote.yml"),
|
|
293
293
|
"utf8",
|
|
294
294
|
);
|
|
295
295
|
assertPromotionInventory(root);
|
|
@@ -306,7 +306,7 @@ const actionlintConfig = fs.readFileSync(
|
|
|
306
306
|
"utf8",
|
|
307
307
|
);
|
|
308
308
|
for (const requiredSnippet of [
|
|
309
|
-
".github/workflows
|
|
309
|
+
".github/workflows/.build-candidate.yml:",
|
|
310
310
|
".github/workflows/.build.yml:",
|
|
311
311
|
".github/workflows/.build-gate-profile.yml:",
|
|
312
312
|
'property "workflow_ref" is not defined in object type',
|
|
@@ -859,25 +859,25 @@ for (const requiredSnippet of [
|
|
|
859
859
|
}
|
|
860
860
|
const reusableBuildSurfaceDoc = fs.readFileSync(path.join(root, "docs/reusable-build-surface.md"), "utf8");
|
|
861
861
|
for (const requiredSnippet of [
|
|
862
|
-
"
|
|
863
|
-
"
|
|
864
|
-
"
|
|
865
|
-
"
|
|
866
|
-
"
|
|
867
|
-
"
|
|
868
|
-
"
|
|
869
|
-
"
|
|
870
|
-
"
|
|
871
|
-
"
|
|
862
|
+
".buildchain/buildchain.toml",
|
|
863
|
+
"schema = 2",
|
|
864
|
+
"[[products]]",
|
|
865
|
+
"public-ops-pipeline.yml@v4",
|
|
866
|
+
"public-ops-recover.yml@v4",
|
|
867
|
+
"Without a lock",
|
|
868
|
+
"exact Git commit, tree and TOML blob",
|
|
869
|
+
"without provider credentials",
|
|
870
|
+
"original attempt",
|
|
871
|
+
"Consumers do not call",
|
|
872
872
|
]) {
|
|
873
873
|
if (!reusableBuildSurfaceDoc.includes(requiredSnippet)) {
|
|
874
|
-
throw new Error(`
|
|
874
|
+
throw new Error(`consumer build surface doc missing schema-2 contract snippet: ${requiredSnippet}`);
|
|
875
875
|
}
|
|
876
876
|
}
|
|
877
877
|
for (const [docName, docSource] of Object.entries({ "docs/cli.md": cliDoc, "docs/install.md": installDoc })) {
|
|
878
878
|
for (const requiredSnippet of [
|
|
879
879
|
"minimumReleaseAgeExclude",
|
|
880
|
-
"@kungfu-tech/buildchain@4.
|
|
880
|
+
"@kungfu-tech/buildchain@4.1.3",
|
|
881
881
|
"package/version-specific",
|
|
882
882
|
]) {
|
|
883
883
|
if (!docSource.includes(requiredSnippet)) {
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
projectRenamedMetrics,
|
|
6
|
+
projectWorkflowMetrics,
|
|
7
|
+
} from "./source-metric-lineage.mjs";
|
|
8
|
+
import {
|
|
9
|
+
workflowPath,
|
|
10
|
+
TAXONOMY_PATH,
|
|
11
|
+
} from "../packages/core/workflow/workflow-taxonomy.mjs";
|
|
5
12
|
import path from "node:path";
|
|
6
13
|
import { execFileSync } from "node:child_process";
|
|
7
14
|
import { pathToFileURL } from "node:url";
|
|
@@ -417,10 +424,25 @@ function checkMaintainability({ root = process.cwd() } = {}) {
|
|
|
417
424
|
if (file.endsWith(".rs")) baselineFiles[file] = metrics;
|
|
418
425
|
}
|
|
419
426
|
const baselineTests = testMetricsAtRevision(root, extendedCoverageRevision);
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
427
|
+
const taxonomyCoordinates = (policy) =>
|
|
428
|
+
policy.entries.map((entry) => ({
|
|
429
|
+
id: entry.id,
|
|
430
|
+
path: workflowPath(entry),
|
|
431
|
+
}));
|
|
432
|
+
const workflowLineage = projectWorkflowMetrics({
|
|
433
|
+
before: taxonomyCoordinates(
|
|
434
|
+
JSON.parse(
|
|
435
|
+
gitOutput(root, [
|
|
436
|
+
"show",
|
|
437
|
+
`${extendedCoverageRevision}:${TAXONOMY_PATH}`,
|
|
438
|
+
]),
|
|
439
|
+
),
|
|
440
|
+
),
|
|
441
|
+
after: taxonomyCoordinates(readJson(root, TAXONOMY_PATH)),
|
|
442
|
+
metrics: workflowMetricsAtRevision(root, extendedCoverageRevision),
|
|
443
|
+
currentPaths: new Set(Object.keys(current.workflows)),
|
|
444
|
+
});
|
|
445
|
+
const baselineWorkflows = workflowLineage.metrics;
|
|
424
446
|
const issues = evaluateExceptionGovernance({ policy });
|
|
425
447
|
issues.push(...evaluateExceptionBudget({ policy }));
|
|
426
448
|
const hotspots = collectHotspots(root, current, 20, debt.hotspots || []);
|
|
@@ -469,6 +491,7 @@ function checkMaintainability({ root = process.cwd() } = {}) {
|
|
|
469
491
|
trackedFiles: current.repository.trackedFiles,
|
|
470
492
|
sourceFiles: current.repository.handMaintainedSourceFiles,
|
|
471
493
|
measurementRenames: lineage.renames,
|
|
494
|
+
workflowMeasurementRenames: workflowLineage.renames,
|
|
472
495
|
publicSurface: current.publicSurface,
|
|
473
496
|
hotspots: current.hotspots,
|
|
474
497
|
governedDebtSurfaces: Object.values(debt.surfaces).reduce(
|
|
@@ -11,7 +11,7 @@ const DEFAULT_ROOT = path.resolve(
|
|
|
11
11
|
"..",
|
|
12
12
|
);
|
|
13
13
|
const CALLER_PATH = ".github/workflows/self-build-public-consumer-dogfood.yml";
|
|
14
|
-
const REUSABLE_PATH = ".github/workflows
|
|
14
|
+
const REUSABLE_PATH = ".github/workflows/.build-stage-capsule-canary.yml";
|
|
15
15
|
export const PUBLIC_DOGFOOD_ENTRY_REF = "v4";
|
|
16
16
|
const PRIVATE_CONSUMER = ["buildchain", "self", "dogfood"].join("-");
|
|
17
17
|
const PRIVATE_SHADOW = ["kungfu", "shadow"].join("-");
|
|
@@ -201,7 +201,7 @@ function assertWorkflowInventory(root) {
|
|
|
201
201
|
fail(`${relative} invokes the private qualification script directly`);
|
|
202
202
|
if (
|
|
203
203
|
relative !== CALLER_PATH &&
|
|
204
|
-
text.includes(".github/workflows
|
|
204
|
+
text.includes(".github/workflows/.build-stage-capsule-canary.yml@")
|
|
205
205
|
)
|
|
206
206
|
fail(`${relative} creates a second v4 dogfood caller`);
|
|
207
207
|
if (relative !== REUSABLE_PATH && relative !== CALLER_PATH)
|
|
@@ -275,7 +275,7 @@ function assertArchitecture(root) {
|
|
|
275
275
|
!dogfood ||
|
|
276
276
|
dogfood.callerWorkflow !== CALLER_PATH ||
|
|
277
277
|
dogfood.reusableWorkflow !==
|
|
278
|
-
"kungfu-systems/buildchain/.github/workflows
|
|
278
|
+
"kungfu-systems/buildchain/.github/workflows/.build-stage-capsule-canary.yml" ||
|
|
279
279
|
dogfood.runtimeBinding !== "entry-selected-runtime" ||
|
|
280
280
|
dogfood.consumerSourceBinding !== "github.sha" ||
|
|
281
281
|
JSON.stringify(dogfood.executableStages) !==
|
|
@@ -532,7 +532,7 @@ export function checkReleaseTopology() {
|
|
|
532
532
|
assertPipelinePublicationTopology(ledger, discoverReleaseTopology, root);
|
|
533
533
|
const preview = ledger.postPublicationScope;
|
|
534
534
|
assert.deepEqual(preview.workflowPaths, [
|
|
535
|
-
".github/workflows
|
|
535
|
+
".github/workflows/.release-oci-compose-preview.yml",
|
|
536
536
|
]);
|
|
537
537
|
const previewTopology = discoverReleaseTopology(
|
|
538
538
|
preview.workflowPaths,
|
|
@@ -2,12 +2,11 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { parse as yaml } from "yaml";
|
|
5
|
-
import
|
|
5
|
+
import * as consumerPlan from "../packages/core/consumer/contract/plan.js";
|
|
6
6
|
import { consumerWorkflows } from "../packages/core/consumer/contract/entries.js";
|
|
7
7
|
import { workflowPath } from "../packages/core/workflow/workflow-taxonomy.mjs";
|
|
8
8
|
|
|
9
|
-
export const SELF_ENTRY_CHANNEL = "v4
|
|
10
|
-
export const SELF_CONFIG_PATH = ".buildchain/minimal-consumer.toml";
|
|
9
|
+
export const SELF_ENTRY_CHANNEL = "v4";
|
|
11
10
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
11
|
const PLATFORMS = ["linux-x64", "macos-arm64", "windows-x64"];
|
|
13
12
|
|
|
@@ -19,10 +18,11 @@ export function checkSelfConsumerContract(root = ROOT) {
|
|
|
19
18
|
const fail = (message) => {
|
|
20
19
|
throw new Error(`self-consumer-contract: ${message}`);
|
|
21
20
|
};
|
|
22
|
-
const plan = compileConsumerPlan(read(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const plan = consumerPlan.compileConsumerPlan(read(consumerPlan.CONFIG_PATH));
|
|
22
|
+
const retired = path.join(root, ".buildchain/minimal-consumer.toml");
|
|
23
|
+
if (fs.lstatSync(retired, { throwIfNoEntry: false }))
|
|
24
|
+
fail("retired transition config must not remain in the self consumer tree");
|
|
25
|
+
const callers = consumerWorkflows();
|
|
26
26
|
for (const [file, bytes] of Object.entries(callers))
|
|
27
27
|
if (read(file) !== bytes) fail(`${file}: generated caller drift`);
|
|
28
28
|
const taxonomy = JSON.parse(read("architecture/workflow-taxonomy.json"));
|
|
@@ -41,7 +41,7 @@ assert.equal(executor.with["mutation-token"], "${{ inputs.secrets-buildchain-pro
|
|
|
41
41
|
assert.equal(executor.with.token, "${{ github.token }}");
|
|
42
42
|
assert.doesNotMatch(read(contract.bootstrap.publicWorkflow), /admission-policy-json:/u);
|
|
43
43
|
const template = parse(contract.bootstrap.consumerTemplate);
|
|
44
|
-
assert.equal(template.jobs.bootstrap.uses, "kungfu-systems/buildchain/.github/workflows
|
|
44
|
+
assert.equal(template.jobs.bootstrap.uses, "kungfu-systems/buildchain/.github/workflows/.ops-bootstrap.yml@v4");
|
|
45
45
|
assert.equal(template.jobs.bootstrap.with["runtime-ref"], "${{ inputs.runtime-ref }}");
|
|
46
46
|
assert.equal(contract.bootstrap.selfDogfoodActive, false);
|
|
47
47
|
assert.equal(fs.existsSync(path.join(root, contract.bootstrap.selfDogfoodWorkflow)), false,
|
|
@@ -41,4 +41,4 @@ export function generateChannelPromotionWorkflow(source) {
|
|
|
41
41
|
}, { lineWidth: 0, aliasDuplicateObjects: false });
|
|
42
42
|
}
|
|
43
43
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url))
|
|
44
|
-
writeWorkflowSource(root, ".github/workflows
|
|
44
|
+
writeWorkflowSource(root, ".github/workflows/.release-candidate-promote.yml", generateChannelPromotionWorkflow(fs.readFileSync(path.join(root, ".github/workflows/.release-promote.yml"), "utf8")));
|
|
@@ -5,6 +5,7 @@ import { parse as yaml } from "yaml";
|
|
|
5
5
|
import { format } from "prettier";
|
|
6
6
|
import { standardConsumerExample } from "../packages/core/consumer/contract/examples.js";
|
|
7
7
|
import { inspectConsumerContract } from "../packages/core/consumer/contract/inspection.js";
|
|
8
|
+
import { validateConsumerWiring } from "../packages/core/consumer/contract/local-validation.js";
|
|
8
9
|
import { workflowPath } from "../packages/core/workflow/workflow-taxonomy.mjs";
|
|
9
10
|
|
|
10
11
|
const root = process.cwd(),
|
|
@@ -13,6 +14,25 @@ const tracked = execFileSync("git", ["ls-files", "-z"], { encoding: "utf8" })
|
|
|
13
14
|
.split("\0")
|
|
14
15
|
.filter(Boolean);
|
|
15
16
|
|
|
17
|
+
if (check) {
|
|
18
|
+
const files = Object.create(null);
|
|
19
|
+
for (const file of tracked)
|
|
20
|
+
Object.defineProperty(files, file, {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: () => fs.readFileSync(path.join(root, file), "utf8"),
|
|
23
|
+
});
|
|
24
|
+
const { channel } = validateConsumerWiring(root);
|
|
25
|
+
const inspection = inspectConsumerContract(files, { channel });
|
|
26
|
+
if (inspection.controlIssues.length)
|
|
27
|
+
throw new Error(inspection.controlIssues.join("\n"));
|
|
28
|
+
const unresolved = inspection.commandClosures.filter(
|
|
29
|
+
(closure) => !closure.commandEdgesResolved,
|
|
30
|
+
).length;
|
|
31
|
+
console.log(
|
|
32
|
+
`Self control wiring verified; ${unresolved} product graphs remain unresolved and require read-only runtime execution.`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
16
36
|
function output(file, value) {
|
|
17
37
|
const target = path.join(root, file);
|
|
18
38
|
if (check) {
|
|
@@ -143,7 +163,7 @@ output(
|
|
|
143
163
|
await format(
|
|
144
164
|
JSON.stringify({
|
|
145
165
|
schema: "buildchain.consumer-migration/v1",
|
|
146
|
-
stage: "self-
|
|
166
|
+
stage: "self-default-entry; final-release-qualification-pending",
|
|
147
167
|
workflows: workflowInventory(),
|
|
148
168
|
consumerSources: consumerSourceInventory(),
|
|
149
169
|
}),
|
|
@@ -887,8 +887,9 @@ function buildSiteBundle() {
|
|
|
887
887
|
["self-hosted-runner-smoke", "compatibility-fixture"],
|
|
888
888
|
]);
|
|
889
889
|
const engineeringHousekeeper = semanticId.startsWith("engineering-housekeeper");
|
|
890
|
+
if (classification?.role === "component") statusById.set(semanticId, "repository-internal");
|
|
890
891
|
return {
|
|
891
|
-
...entry,
|
|
892
|
+
...entry, apiRole: classification?.role === "public" ? "public" : "implementation",
|
|
892
893
|
...(classification ? { taxonomy: { id: classification.id, role: classification.role, category: classification.category, purpose: classification.purpose, status: classification.status, canonicalPath: workflowPath(classification), ...(classification.role === "self" && classification.migration ? { contractPath: classification.migration.previousPath } : {}), compatibilityAlias: entry.path !== workflowPath(classification) } } : {}),
|
|
893
894
|
surface: surfaceById.get(semanticId) || (entry.path.includes("/.") ? "reusable-workflow" : "repository-workflow"),
|
|
894
895
|
capabilityGroup: workflowCapabilityGroup({
|
|
@@ -19,8 +19,8 @@ export function tailResealFixturePolicyReceipt(fixture, policy) {
|
|
|
19
19
|
try {
|
|
20
20
|
fs.mkdirSync(path.join(root, ".github/workflows"), { recursive: true });
|
|
21
21
|
fs.writeFileSync(
|
|
22
|
-
path.join(root, ".github/workflows
|
|
23
|
-
"jobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows
|
|
22
|
+
path.join(root, ".github/workflows/.build-candidate.yml"),
|
|
23
|
+
"jobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build-stage-capsule-canary.yml@v4-alpha\n",
|
|
24
24
|
);
|
|
25
25
|
fs.mkdirSync(path.join(root, ".buildchain"), { recursive: true });
|
|
26
26
|
for (const [file, ref, resolvedSha] of [
|
|
@@ -51,7 +51,7 @@ export function tailResealFixturePolicyReceipt(fixture, policy) {
|
|
|
51
51
|
root,
|
|
52
52
|
repository: fixture.repository,
|
|
53
53
|
sourceSha: fixture.source.sha,
|
|
54
|
-
invokedWorkflow: "
|
|
54
|
+
invokedWorkflow: ".build-stage-capsule-canary.yml",
|
|
55
55
|
expectedInvocationChannel: "alpha",
|
|
56
56
|
resolvedRuntimeSha: fixture.runtime.sha,
|
|
57
57
|
policy,
|
|
@@ -8,7 +8,7 @@ import { generateChannelPromotionWorkflow } from "../generate-channel-promotion-
|
|
|
8
8
|
|
|
9
9
|
export function assertPromotionInventory(root) {
|
|
10
10
|
const read = (file) => fs.readFileSync(path.join(root, file), "utf8");
|
|
11
|
-
const publicPath = ".github/workflows
|
|
11
|
+
const publicPath = ".github/workflows/.release-candidate-promote.yml";
|
|
12
12
|
const api = YAML.parse(read(publicPath));
|
|
13
13
|
assert.equal(
|
|
14
14
|
read(publicPath),
|