@kungfu-tech/buildchain 4.1.3-alpha.9 → 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 +33 -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/providers/github/pipeline-release-evidence.js +17 -98
- package/packages/core/providers/github/pipeline-stable-baseline.js +86 -0
- package/packages/core/providers/github/pipeline-stable-source.js +22 -4
- package/packages/core/providers/github/release-evidence-assets.js +97 -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/journal.js +13 -1
- package/packages/core/publication/pipeline/prepare.js +15 -6
- package/packages/core/publication/pipeline/recovery-import.js +1 -1
- 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 -3
- 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
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse, stringify } from "smol-toml";
|
|
4
|
+
import YAML from "yaml";
|
|
5
|
+
import {
|
|
6
|
+
consumerAgentInstructions,
|
|
7
|
+
consumerConfiguration,
|
|
8
|
+
} from "../../adoption/consumer-init.js";
|
|
9
|
+
import { consumerWorkflows } from "../../consumer/contract/entries.js";
|
|
10
|
+
import { compileConsumerPlan } from "../../consumer/contract/plan.js";
|
|
11
|
+
import { validateConsumerWiring } from "../../consumer/contract/local-validation.js";
|
|
12
|
+
import {
|
|
13
|
+
loadBuildchainConfig,
|
|
14
|
+
validateBuildchainConfig,
|
|
15
|
+
} from "../../consumer/buildchain-config.js";
|
|
16
|
+
import {
|
|
17
|
+
gitResult,
|
|
18
|
+
gitValue,
|
|
19
|
+
PAPER_PATHS,
|
|
20
|
+
sha256Text,
|
|
21
|
+
stableJson,
|
|
22
|
+
} from "../paper-repository.js";
|
|
23
|
+
import { buildchainPackageIdentity } from "./runtime.js";
|
|
24
|
+
import {
|
|
25
|
+
BUILDCHAIN_PACKAGE_NAME,
|
|
26
|
+
PAPER_MIGRATION_CONTRACT,
|
|
27
|
+
} from "./identity.js";
|
|
28
|
+
import { jsonText } from "./files.js";
|
|
29
|
+
|
|
30
|
+
const LEGACY_WORKFLOWS = new Map([
|
|
31
|
+
["build.yml", ["public-build-publication.yml", ".build-publication.yml"]],
|
|
32
|
+
[
|
|
33
|
+
".build-candidate.yml",
|
|
34
|
+
["public-build-publication.yml", ".build-publication.yml"],
|
|
35
|
+
],
|
|
36
|
+
["verify.yml", ["public-build-check.yml", ".build-check.yml"]],
|
|
37
|
+
[
|
|
38
|
+
"public-release-paper.yml",
|
|
39
|
+
["public-release-paper.yml", ".release-paper.yml"],
|
|
40
|
+
],
|
|
41
|
+
[".release-paper.yml", ["public-release-paper.yml", ".release-paper.yml"]],
|
|
42
|
+
]);
|
|
43
|
+
const LEGACY_SCRIPTS = {
|
|
44
|
+
"buildchain:paper": "buildchain paper",
|
|
45
|
+
"paper:preflight": "buildchain paper preflight --json",
|
|
46
|
+
"paper:agent:verify": "buildchain paper agent verify --json",
|
|
47
|
+
"paper:work:start": "buildchain paper work start",
|
|
48
|
+
"paper:work:submit": "buildchain paper work submit",
|
|
49
|
+
"paper:status": "buildchain paper status --json",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export function paperFileTarget(cwd, relative) {
|
|
53
|
+
const parts = relative.split("/");
|
|
54
|
+
for (let index = 1; index <= parts.length; index++) {
|
|
55
|
+
const target = path.join(cwd, ...parts.slice(0, index));
|
|
56
|
+
const stat = fs.lstatSync(target, { throwIfNoEntry: false });
|
|
57
|
+
if (
|
|
58
|
+
stat &&
|
|
59
|
+
(stat.isSymbolicLink() ||
|
|
60
|
+
(index < parts.length ? !stat.isDirectory() : !stat.isFile()))
|
|
61
|
+
)
|
|
62
|
+
throw new Error(
|
|
63
|
+
`${relative}: Paper scaffold requires regular files and directories`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
return path.join(cwd, relative);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function regularSource(cwd, relative) {
|
|
70
|
+
const absolute = paperFileTarget(cwd, relative);
|
|
71
|
+
return fs.existsSync(absolute)
|
|
72
|
+
? fs.readFileSync(absolute, "utf8")
|
|
73
|
+
: undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function retireWorkflows(cwd, files) {
|
|
77
|
+
regularSource(cwd, ".github/workflows/buildchain.yml");
|
|
78
|
+
const directory = path.join(cwd, ".github/workflows");
|
|
79
|
+
if (!fs.existsSync(directory)) return;
|
|
80
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
81
|
+
if (!/\.ya?ml$/iu.test(entry.name) && entry.isFile()) continue;
|
|
82
|
+
const relative = `.github/workflows/${entry.name}`;
|
|
83
|
+
const source = regularSource(cwd, relative);
|
|
84
|
+
if (files.has(relative)) {
|
|
85
|
+
if (source !== files.get(relative))
|
|
86
|
+
throw new Error(`Existing shared caller differs: ${relative}`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const callees = LEGACY_WORKFLOWS.get(entry.name);
|
|
90
|
+
const workflow = YAML.parse(source);
|
|
91
|
+
const jobs = Object.values(workflow?.jobs || {});
|
|
92
|
+
if (
|
|
93
|
+
!callees ||
|
|
94
|
+
!jobs.length ||
|
|
95
|
+
jobs.some((job) => {
|
|
96
|
+
const match =
|
|
97
|
+
typeof job?.uses === "string" &&
|
|
98
|
+
/^kungfu-systems\/buildchain\/\.github\/workflows\/([^@]+)@(v[34](?:-alpha)?|[0-9a-f]{40})$/u.exec(
|
|
99
|
+
job.uses,
|
|
100
|
+
);
|
|
101
|
+
return (
|
|
102
|
+
!match ||
|
|
103
|
+
!callees.includes(match[1]) ||
|
|
104
|
+
Object.hasOwn(job, "steps") ||
|
|
105
|
+
Object.hasOwn(job, "runs-on")
|
|
106
|
+
);
|
|
107
|
+
})
|
|
108
|
+
)
|
|
109
|
+
throw new Error(
|
|
110
|
+
`Unowned or customized Paper workflow requires explicit product migration: ${relative}`,
|
|
111
|
+
);
|
|
112
|
+
files.set(relative, null);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function retireControl(cwd, files, relative, contract, digestKey) {
|
|
117
|
+
const source = regularSource(cwd, relative);
|
|
118
|
+
if (source === undefined) return;
|
|
119
|
+
const value = JSON.parse(source);
|
|
120
|
+
const { [digestKey]: digest, ...payload } = value;
|
|
121
|
+
if (value.contract !== contract || digest !== sha256Text(stableJson(payload)))
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Cannot retire an unverified Paper control file: ${relative}`,
|
|
124
|
+
);
|
|
125
|
+
files.set(relative, null);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function productCommands(config, stage) {
|
|
129
|
+
const lifecycle = config.lifecycle || {};
|
|
130
|
+
const value = lifecycle[stage];
|
|
131
|
+
if (
|
|
132
|
+
!value?.commands?.length ||
|
|
133
|
+
value.shell ||
|
|
134
|
+
Object.keys(value.env || {}).length ||
|
|
135
|
+
Object.keys(lifecycle.env || {}).length ||
|
|
136
|
+
value.timeoutMinutes ||
|
|
137
|
+
value.retries !== 1
|
|
138
|
+
)
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Move ${stage} shell, environment, retry and script settings into product commands before Paper migration`,
|
|
141
|
+
);
|
|
142
|
+
return value.commands;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function migratedPackage(current, config, runtimeVersion) {
|
|
146
|
+
if (current.version && current.version !== config.publication.version)
|
|
147
|
+
throw new Error(
|
|
148
|
+
"Paper package and publication versions disagree; migration cannot choose a version authority",
|
|
149
|
+
);
|
|
150
|
+
const scripts = { ...(current.scripts || {}) };
|
|
151
|
+
for (const [name, command] of Object.entries(LEGACY_SCRIPTS)) {
|
|
152
|
+
if (scripts[name] !== undefined && scripts[name] !== command)
|
|
153
|
+
throw new Error(
|
|
154
|
+
`Customized managed Paper script requires explicit migration: ${name}`,
|
|
155
|
+
);
|
|
156
|
+
delete scripts[name];
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
...current,
|
|
160
|
+
private: true,
|
|
161
|
+
version: config.publication.version,
|
|
162
|
+
description: current.description || config.publication.title,
|
|
163
|
+
scripts,
|
|
164
|
+
devDependencies: {
|
|
165
|
+
...(current.devDependencies || {}),
|
|
166
|
+
"@kungfu-tech/buildchain": runtimeVersion,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function migrationFiles({ cwd, buildchainRoot, buildchainVersion }) {
|
|
172
|
+
regularSource(cwd, PAPER_PATHS.config);
|
|
173
|
+
const loaded = loadBuildchainConfig(cwd);
|
|
174
|
+
const instructions = consumerAgentInstructions(
|
|
175
|
+
regularSource(cwd, "AGENTS.md") || "",
|
|
176
|
+
);
|
|
177
|
+
if (loaded?.config.schema === 2) {
|
|
178
|
+
validateBuildchainConfig(cwd, {
|
|
179
|
+
requireLifecycleStages: ["build", "verify"],
|
|
180
|
+
});
|
|
181
|
+
if (!loaded.config.products.some((product) => product.type === "paper"))
|
|
182
|
+
throw new Error("Paper migration requires a Paper product");
|
|
183
|
+
validateConsumerWiring(cwd, loaded.path);
|
|
184
|
+
const files = new Map([["AGENTS.md", instructions]]);
|
|
185
|
+
const packageText = regularSource(cwd, "package.json");
|
|
186
|
+
const sourcePackage =
|
|
187
|
+
packageText === undefined ? null : JSON.parse(packageText);
|
|
188
|
+
if (sourcePackage?.devDependencies?.["@kungfu-tech/buildchain"]) {
|
|
189
|
+
const version = buildchainPackageIdentity(
|
|
190
|
+
buildchainRoot,
|
|
191
|
+
buildchainVersion,
|
|
192
|
+
).version;
|
|
193
|
+
if (!/^4\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version))
|
|
194
|
+
throw new Error("Paper migration requires an exact v4 CLI dependency");
|
|
195
|
+
sourcePackage.devDependencies["@kungfu-tech/buildchain"] = version;
|
|
196
|
+
files.set("package.json", jsonText(sourcePackage));
|
|
197
|
+
const workspace = regularSource(cwd, PAPER_PATHS.pnpmWorkspace);
|
|
198
|
+
if (workspace !== undefined)
|
|
199
|
+
files.set(
|
|
200
|
+
PAPER_PATHS.pnpmWorkspace,
|
|
201
|
+
paperPnpmWorkspace(workspace, version),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return files;
|
|
205
|
+
}
|
|
206
|
+
const config = loaded?.config;
|
|
207
|
+
if (
|
|
208
|
+
config?.project?.type !== "publication-artifact" ||
|
|
209
|
+
config.publication?.kind !== "paper"
|
|
210
|
+
)
|
|
211
|
+
throw new Error(
|
|
212
|
+
"Paper migration requires a schema-1 Paper publication or a schema-2 Paper product",
|
|
213
|
+
);
|
|
214
|
+
const raw = parse(regularSource(cwd, PAPER_PATHS.config));
|
|
215
|
+
const allowed = new Set([
|
|
216
|
+
"schema",
|
|
217
|
+
"project",
|
|
218
|
+
"publication",
|
|
219
|
+
"publish",
|
|
220
|
+
"lifecycle",
|
|
221
|
+
"next_development",
|
|
222
|
+
]);
|
|
223
|
+
if (Object.keys(raw).some((key) => !allowed.has(key)))
|
|
224
|
+
throw new Error(
|
|
225
|
+
"Paper migration cannot discard additional configuration sections; move them into product sources first",
|
|
226
|
+
);
|
|
227
|
+
const current = JSON.parse(regularSource(cwd, "package.json") || "null");
|
|
228
|
+
if (!current || Array.isArray(current) || typeof current !== "object")
|
|
229
|
+
throw new Error("Paper migration requires a valid source package.json");
|
|
230
|
+
const runtimeVersion = buildchainPackageIdentity(
|
|
231
|
+
buildchainRoot,
|
|
232
|
+
buildchainVersion,
|
|
233
|
+
).version;
|
|
234
|
+
if (!/^4\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(runtimeVersion))
|
|
235
|
+
throw new Error("Paper migration requires an exact v4 CLI dependency");
|
|
236
|
+
const plan = parse(
|
|
237
|
+
consumerConfiguration({
|
|
238
|
+
type: "paper",
|
|
239
|
+
version: config.publication.version,
|
|
240
|
+
}),
|
|
241
|
+
);
|
|
242
|
+
const product = plan.products[0];
|
|
243
|
+
product.build = productCommands(config, "build");
|
|
244
|
+
product.verify = productCommands(config, "verify");
|
|
245
|
+
if (config.lifecycle.install)
|
|
246
|
+
product.install = productCommands(config, "install");
|
|
247
|
+
if (
|
|
248
|
+
Object.keys(raw.lifecycle || {}).some(
|
|
249
|
+
(key) => !["build", "verify", "install"].includes(key),
|
|
250
|
+
)
|
|
251
|
+
)
|
|
252
|
+
throw new Error(
|
|
253
|
+
"Paper migration cannot retain consumer release lifecycle hooks or global shell settings",
|
|
254
|
+
);
|
|
255
|
+
product.artifacts = config.publication.artifactPaths.map(
|
|
256
|
+
(artifact, index) => ({
|
|
257
|
+
id: index ? `pdf-${index + 1}` : "main",
|
|
258
|
+
path: artifact,
|
|
259
|
+
kind: "pdf",
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
262
|
+
if (product.artifacts.some((artifact) => !artifact.path.endsWith(".pdf")))
|
|
263
|
+
throw new Error("Paper migration requires explicit PDF artifacts");
|
|
264
|
+
product.targets[0].artifacts = product.artifacts.map(
|
|
265
|
+
(artifact) => artifact.id,
|
|
266
|
+
);
|
|
267
|
+
const toml = stringify(plan);
|
|
268
|
+
compileConsumerPlan(toml);
|
|
269
|
+
const { version: _version, ...metadata } = raw.publication;
|
|
270
|
+
const metadataPath = "paper/publication-metadata.json";
|
|
271
|
+
const metadataBytes = jsonText({
|
|
272
|
+
schema: "buildchain.paper-product-metadata/v1",
|
|
273
|
+
...metadata,
|
|
274
|
+
});
|
|
275
|
+
const existingMetadata = regularSource(cwd, metadataPath);
|
|
276
|
+
if (existingMetadata !== undefined && existingMetadata !== metadataBytes)
|
|
277
|
+
throw new Error(
|
|
278
|
+
`Paper migration would overwrite product metadata: ${metadataPath}`,
|
|
279
|
+
);
|
|
280
|
+
const files = new Map([
|
|
281
|
+
[PAPER_PATHS.config, toml],
|
|
282
|
+
[
|
|
283
|
+
"package.json",
|
|
284
|
+
jsonText(migratedPackage(current, config, runtimeVersion)),
|
|
285
|
+
],
|
|
286
|
+
["AGENTS.md", instructions],
|
|
287
|
+
[metadataPath, metadataBytes],
|
|
288
|
+
...Object.entries(consumerWorkflows()),
|
|
289
|
+
]);
|
|
290
|
+
retireWorkflows(cwd, files);
|
|
291
|
+
retireControl(
|
|
292
|
+
cwd,
|
|
293
|
+
files,
|
|
294
|
+
PAPER_PATHS.provisioningAuthority,
|
|
295
|
+
"kungfu-buildchain-paper-provisioning-authority",
|
|
296
|
+
"authorityDigest",
|
|
297
|
+
);
|
|
298
|
+
retireControl(
|
|
299
|
+
cwd,
|
|
300
|
+
files,
|
|
301
|
+
PAPER_PATHS.agentEntry,
|
|
302
|
+
"kungfu-buildchain-paper-agent-entry",
|
|
303
|
+
"entryDigest",
|
|
304
|
+
);
|
|
305
|
+
const pin = regularSource(cwd, PAPER_PATHS.versionPin);
|
|
306
|
+
if (pin !== undefined) {
|
|
307
|
+
if (!/^[34]\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?\s*$/u.test(pin))
|
|
308
|
+
throw new Error("Unrecognized Paper runtime pin cannot be retired");
|
|
309
|
+
files.set(PAPER_PATHS.versionPin, null);
|
|
310
|
+
}
|
|
311
|
+
const workspace = regularSource(cwd, PAPER_PATHS.pnpmWorkspace);
|
|
312
|
+
if (workspace !== undefined)
|
|
313
|
+
files.set(
|
|
314
|
+
PAPER_PATHS.pnpmWorkspace,
|
|
315
|
+
paperPnpmWorkspace(workspace, runtimeVersion),
|
|
316
|
+
);
|
|
317
|
+
// Existing channel locks and publication receipts are preserved byte-for-byte.
|
|
318
|
+
return files;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function paperPnpmWorkspace(current, buildchainVersion) {
|
|
322
|
+
const entry = `${BUILDCHAIN_PACKAGE_NAME}@${buildchainVersion}`;
|
|
323
|
+
const source = String(current || "");
|
|
324
|
+
const lines = source ? source.replace(/\r\n/g, "\n").split("\n") : [];
|
|
325
|
+
if (lines.at(-1) === "") lines.pop();
|
|
326
|
+
const keyLines = lines
|
|
327
|
+
.map((line, index) =>
|
|
328
|
+
/^minimumReleaseAgeExclude:\s*(?:#.*)?$/.test(line) ? index : -1,
|
|
329
|
+
)
|
|
330
|
+
.filter((index) => index >= 0);
|
|
331
|
+
const unsupportedKey = lines.some(
|
|
332
|
+
(line) =>
|
|
333
|
+
/^minimumReleaseAgeExclude\s*:/.test(line) &&
|
|
334
|
+
!/^minimumReleaseAgeExclude:\s*(?:#.*)?$/.test(line),
|
|
335
|
+
);
|
|
336
|
+
if (unsupportedKey || keyLines.length > 1) {
|
|
337
|
+
throw new Error(
|
|
338
|
+
"paper migration requires minimumReleaseAgeExclude to be one top-level block sequence",
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
if (keyLines.length === 0) {
|
|
342
|
+
const prefix = lines.length > 0 ? [...lines, ""] : [];
|
|
343
|
+
return `${[...prefix, "minimumReleaseAgeExclude:", ` - '${entry}'`].join(
|
|
344
|
+
"\n",
|
|
345
|
+
)}\n`;
|
|
346
|
+
}
|
|
347
|
+
const keyIndex = keyLines[0];
|
|
348
|
+
let blockEnd = lines.length;
|
|
349
|
+
for (let index = keyIndex + 1; index < lines.length; index += 1) {
|
|
350
|
+
if (/^[^\s#]/.test(lines[index])) {
|
|
351
|
+
blockEnd = index;
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const retained = [];
|
|
356
|
+
for (const line of lines.slice(keyIndex + 1, blockEnd)) {
|
|
357
|
+
if (!line.trim() || /^\s*#/.test(line)) {
|
|
358
|
+
retained.push(line);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const item = line.match(/^\s*-\s+(.+?)\s*(?:#.*)?$/);
|
|
362
|
+
if (!item) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
"paper migration requires minimumReleaseAgeExclude to contain scalar package entries",
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
const value = item[1]
|
|
368
|
+
.trim()
|
|
369
|
+
.replace(/^'(.*)'$/, "$1")
|
|
370
|
+
.replace(/^"(.*)"$/, "$1");
|
|
371
|
+
if (!value.startsWith(`${BUILDCHAIN_PACKAGE_NAME}@`)) retained.push(line);
|
|
372
|
+
}
|
|
373
|
+
return `${[
|
|
374
|
+
...lines.slice(0, keyIndex + 1),
|
|
375
|
+
...retained,
|
|
376
|
+
` - '${entry}'`,
|
|
377
|
+
...lines.slice(blockEnd),
|
|
378
|
+
].join("\n")}\n`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function applyPaperMigration(plan) {
|
|
382
|
+
if (!plan || plan.contract !== PAPER_MIGRATION_CONTRACT) {
|
|
383
|
+
throw new Error("paper migration plan contract mismatch");
|
|
384
|
+
}
|
|
385
|
+
if (!plan.ok) {
|
|
386
|
+
return {
|
|
387
|
+
...plan,
|
|
388
|
+
dryRun: false,
|
|
389
|
+
written: [],
|
|
390
|
+
updated: [],
|
|
391
|
+
removed: [],
|
|
392
|
+
ok: false,
|
|
393
|
+
errorCode: "paper-migration-blocked",
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
if (fs.realpathSync(plan.cwd) !== plan.cwd)
|
|
397
|
+
throw new Error("Paper migration root changed after planning");
|
|
398
|
+
if (
|
|
399
|
+
gitValue(plan.cwd, ["rev-parse", "HEAD"]) !== plan.source.head ||
|
|
400
|
+
gitResult(plan.cwd, ["status", "--porcelain"]).stdout !== ""
|
|
401
|
+
)
|
|
402
|
+
throw new Error(
|
|
403
|
+
"paper migration source changed after planning; no stale plan was applied",
|
|
404
|
+
);
|
|
405
|
+
for (const entry of plan._plannedFiles) paperFileTarget(plan.cwd, entry.path);
|
|
406
|
+
const written = [];
|
|
407
|
+
const updated = [];
|
|
408
|
+
const removed = [];
|
|
409
|
+
for (const entry of plan._plannedFiles) {
|
|
410
|
+
if (entry.action === "unchanged") continue;
|
|
411
|
+
const target = path.resolve(plan.cwd, entry.path);
|
|
412
|
+
const exists = fs.existsSync(target);
|
|
413
|
+
const current =
|
|
414
|
+
exists && fs.statSync(target).isFile()
|
|
415
|
+
? fs.readFileSync(target, "utf8")
|
|
416
|
+
: undefined;
|
|
417
|
+
const currentSha256 = current === undefined ? "" : sha256Text(current);
|
|
418
|
+
if (
|
|
419
|
+
(entry.action === "create" && exists) ||
|
|
420
|
+
(["update", "remove"].includes(entry.action) &&
|
|
421
|
+
currentSha256 !== entry.currentSha256)
|
|
422
|
+
) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
`paper migration race detected at ${entry.path}; no stale plan was applied`,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
for (const entry of plan._plannedFiles) {
|
|
429
|
+
if (entry.action === "unchanged") continue;
|
|
430
|
+
const target = path.resolve(plan.cwd, entry.path);
|
|
431
|
+
if (entry.action === "remove") {
|
|
432
|
+
fs.unlinkSync(target);
|
|
433
|
+
removed.push(entry.path);
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
437
|
+
fs.writeFileSync(target, entry.content, {
|
|
438
|
+
flag: entry.action === "create" ? "wx" : "w",
|
|
439
|
+
});
|
|
440
|
+
(entry.action === "create" ? written : updated).push(entry.path);
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
...plan,
|
|
444
|
+
ok: true,
|
|
445
|
+
dryRun: false,
|
|
446
|
+
written,
|
|
447
|
+
updated,
|
|
448
|
+
removed,
|
|
449
|
+
idempotent:
|
|
450
|
+
written.length === 0 && updated.length === 0 && removed.length === 0,
|
|
451
|
+
nextActions: [
|
|
452
|
+
{
|
|
453
|
+
id: "paper-preflight",
|
|
454
|
+
command: `buildchain paper preflight --cwd ${JSON.stringify(plan.cwd)} --offline --json`,
|
|
455
|
+
description:
|
|
456
|
+
"Verify the migrated repository before any external mutation.",
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
};
|
|
460
|
+
}
|
|
@@ -197,73 +197,13 @@ export function paperPreflightPublicationChecks({ npm, status }) {
|
|
|
197
197
|
},
|
|
198
198
|
];
|
|
199
199
|
}
|
|
200
|
-
export function paperPreflightNextActions({
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
repositoryActions,
|
|
206
|
-
generatedWriteAuthority,
|
|
207
|
-
status,
|
|
208
|
-
}) {
|
|
209
|
-
const actions = [];
|
|
210
|
-
if (validationError) {
|
|
211
|
-
actions.push({
|
|
212
|
-
id: "repair-config",
|
|
213
|
-
command: "buildchain validate --require-lifecycle-stages verify",
|
|
214
|
-
description: validationError,
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
if (!source.clean) {
|
|
218
|
-
actions.push({
|
|
219
|
-
id: "commit-source",
|
|
220
|
-
command: "git status --short",
|
|
221
|
-
description:
|
|
222
|
-
"Review and commit the exact source before deterministic publication.",
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
if (!lockEvaluation.compatible) {
|
|
226
|
-
actions.push({
|
|
227
|
-
id: "refresh-contract-lock",
|
|
228
|
-
command: "buildchain paper scaffold --json",
|
|
229
|
-
description:
|
|
230
|
-
"Review the current runtime contract and resolve the contract-lock difference without overwriting repository files.",
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
if (npm.package.exists === false || npm.trust.configured === false) {
|
|
234
|
-
actions.push({
|
|
235
|
-
id: "bootstrap-npm",
|
|
236
|
-
command: "buildchain paper bootstrap npm --json",
|
|
237
|
-
description:
|
|
238
|
-
"Run the public-package bootstrap and Trusted Publishing dry-run.",
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
if (
|
|
242
|
-
repositoryActions.status === "observed" &&
|
|
243
|
-
(repositoryActions.defaultWorkflowPermissions !== "read" ||
|
|
244
|
-
repositoryActions.canApprovePullRequestReviews !== false)
|
|
245
|
-
) {
|
|
246
|
-
actions.push({
|
|
247
|
-
id: "constrain-repository-actions",
|
|
248
|
-
command: "",
|
|
249
|
-
description:
|
|
250
|
-
"Set default workflow permissions to read and disable Actions pull-request approval through the repository provisioner.",
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
if (generatedWriteAuthority.configured === false) {
|
|
254
|
-
actions.push({
|
|
255
|
-
id: "configure-generated-write-authority",
|
|
256
|
-
command: "",
|
|
200
|
+
export function paperPreflightNextActions() {
|
|
201
|
+
return [
|
|
202
|
+
{
|
|
203
|
+
id: "migrate-consumer-contract",
|
|
204
|
+
command: "buildchain paper migrate --json",
|
|
257
205
|
description:
|
|
258
|
-
"
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (status.deterministicBuild.status !== "qualifying") {
|
|
262
|
-
actions.push({
|
|
263
|
-
id: "build-paper",
|
|
264
|
-
command: "buildchain paper build --execute --json",
|
|
265
|
-
description: "Run the existing qualifying two-clean-build gate.",
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
return actions;
|
|
206
|
+
"Review migration of this legacy Paper repository to schema 2 and the shared normal/recovery caller pair. Historical receipts remain unchanged.",
|
|
207
|
+
},
|
|
208
|
+
];
|
|
269
209
|
}
|