@xemahq/edition-tooling 0.0.0-stage → 0.1.1

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 (219) hide show
  1. package/LICENSE +175 -0
  2. package/dist/cli.d.ts +7 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +47 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/edition.d.ts +4 -0
  7. package/dist/commands/edition.d.ts.map +1 -0
  8. package/dist/commands/edition.js +58 -0
  9. package/dist/commands/edition.js.map +1 -0
  10. package/dist/edition/verify-image-evidence.bundle.mjs +72104 -0
  11. package/dist/edition/verify-image-evidence.bundle.provenance.json +4119 -0
  12. package/dist/lib/exit-codes.d.ts +6 -0
  13. package/dist/lib/exit-codes.d.ts.map +1 -0
  14. package/dist/lib/exit-codes.js +21 -0
  15. package/dist/lib/exit-codes.js.map +1 -0
  16. package/dist/runtime/edition/run-entry.d.ts +10 -0
  17. package/dist/runtime/edition/run-entry.d.ts.map +1 -0
  18. package/dist/runtime/edition/run-entry.js +49 -0
  19. package/dist/runtime/edition/run-entry.js.map +1 -0
  20. package/dist/runtime/edition/verb-map.d.ts +70 -0
  21. package/dist/runtime/edition/verb-map.d.ts.map +1 -0
  22. package/dist/runtime/edition/verb-map.js +166 -0
  23. package/dist/runtime/edition/verb-map.js.map +1 -0
  24. package/edition/README.md +291 -0
  25. package/edition/adapter-bundled-versions.mjs +128 -0
  26. package/edition/adapter-host-versions.mjs +76 -0
  27. package/edition/assemble-producer-input.mjs +1423 -0
  28. package/edition/assemble-third-party-input.mjs +348 -0
  29. package/edition/authored-source-check.mjs +141 -0
  30. package/edition/authority-binding.mjs +23 -0
  31. package/edition/authorization-base.mjs +357 -0
  32. package/edition/biome-content.mjs +327 -0
  33. package/edition/build-biome-index.mjs +180 -0
  34. package/edition/build-ledger-completeness.mjs +67 -0
  35. package/edition/build-ledger.mjs +107 -0
  36. package/edition/build-platform-service-index.mjs +292 -0
  37. package/edition/build-runtime-image-source-index.mjs +1197 -0
  38. package/edition/built-verifier.mjs +55 -0
  39. package/edition/candidate-edition.mjs +735 -0
  40. package/edition/canonical-evidence-json.mjs +82 -0
  41. package/edition/canonical-repository-url.mjs +61 -0
  42. package/edition/catalogue-edition.mjs +49 -0
  43. package/edition/catalogue-evidence.mjs +228 -0
  44. package/edition/catalogue-fetch.mjs +124 -0
  45. package/edition/catalogue-lock-field.mjs +41 -0
  46. package/edition/catalogue-pin.mjs +71 -0
  47. package/edition/catalogue.mjs +237 -0
  48. package/edition/chart-set-source-commit.mjs +97 -0
  49. package/edition/check-committed-image-evidence.mjs +412 -0
  50. package/edition/checkout-indexed-source-repositories.mjs +283 -0
  51. package/edition/collected-refusals.mjs +59 -0
  52. package/edition/comment-blanker.mjs +75 -0
  53. package/edition/connector-source-authority.mjs +60 -0
  54. package/edition/contract-authority.mjs +122 -0
  55. package/edition/cosign-certificate-constraints.mjs +50 -0
  56. package/edition/cross-repo-build-inputs.mjs +158 -0
  57. package/edition/customer-definition.mjs +72 -0
  58. package/edition/customer-edition-verbs.mjs +610 -0
  59. package/edition/customer-flags.mjs +15 -0
  60. package/edition/definition-checkout.mjs +96 -0
  61. package/edition/develop-build-record.mjs +122 -0
  62. package/edition/develop-catalogue-free-resolve.mjs +282 -0
  63. package/edition/develop-catalogue-free-vendor.mjs +118 -0
  64. package/edition/develop-channel-evidence.mjs +23 -0
  65. package/edition/develop-channel-record.mjs +49 -0
  66. package/edition/develop-channel-satellites.mjs +65 -0
  67. package/edition/develop-chart-roster.mjs +96 -0
  68. package/edition/develop-chart-set.mjs +147 -0
  69. package/edition/develop-current-resolution.mjs +29 -0
  70. package/edition/develop-edition-invariants.mjs +142 -0
  71. package/edition/develop-edition-resolve.mjs +250 -0
  72. package/edition/develop-edition.mjs +332 -0
  73. package/edition/develop-engine-image.mjs +97 -0
  74. package/edition/develop-freeze.mjs +304 -0
  75. package/edition/develop-git-bundles.mjs +296 -0
  76. package/edition/develop-graph.mjs +34 -0
  77. package/edition/develop-image-coordinate.mjs +85 -0
  78. package/edition/develop-image-probe.mjs +122 -0
  79. package/edition/develop-image-record.mjs +260 -0
  80. package/edition/develop-resolve-report.mjs +159 -0
  81. package/edition/develop-select-records.mjs +90 -0
  82. package/edition/develop-selection-line.mjs +29 -0
  83. package/edition/develop-selection-pins.mjs +36 -0
  84. package/edition/develop-snapshot-inputs.mjs +112 -0
  85. package/edition/develop-store-root.mjs +20 -0
  86. package/edition/dispatched-runtime-components.mjs +172 -0
  87. package/edition/dist-check.mjs +1174 -0
  88. package/edition/distribution-evidence-set.mjs +1064 -0
  89. package/edition/distribution-paths.mjs +191 -0
  90. package/edition/edition-artifact.mjs +529 -0
  91. package/edition/edition-biome-manifests.mjs +85 -0
  92. package/edition/edition-ceiling.mjs +182 -0
  93. package/edition/edition-channel.mjs +27 -0
  94. package/edition/edition-chart-set.mjs +104 -0
  95. package/edition/edition-config.mjs +374 -0
  96. package/edition/edition-constants.mjs +110 -0
  97. package/edition/edition-contract-parse.mjs +179 -0
  98. package/edition/edition-contract.json +100 -0
  99. package/edition/edition-image-seal.mjs +115 -0
  100. package/edition/edition-kind.mjs +10 -0
  101. package/edition/edition-list.mjs +35 -0
  102. package/edition/edition-locks.mjs +78 -0
  103. package/edition/edition-pin-verbs.mjs +183 -0
  104. package/edition/edition-pin.mjs +17 -0
  105. package/edition/edition-pinned-inputs.mjs +116 -0
  106. package/edition/edition-protocol.mjs +285 -0
  107. package/edition/edition-publisher.mjs +21 -0
  108. package/edition/edition-seal.mjs +257 -0
  109. package/edition/edition-source-pins.mjs +211 -0
  110. package/edition/edition-unpin.mjs +16 -0
  111. package/edition/edition-workspace.mjs +205 -0
  112. package/edition/evidence-checkout.mjs +439 -0
  113. package/edition/evidence-grammars.mjs +18 -0
  114. package/edition/evidence-input-mode.mjs +136 -0
  115. package/edition/evidence-trust-pin.mjs +392 -0
  116. package/edition/evidence-trust-policy.mjs +374 -0
  117. package/edition/external-source-walk.mjs +486 -0
  118. package/edition/first-party-source-ids.mjs +62 -0
  119. package/edition/fleet-coordinate-finality.mjs +363 -0
  120. package/edition/fulcio-certificate-extensions.mjs +278 -0
  121. package/edition/git-json.mjs +54 -0
  122. package/edition/git-source-auth.mjs +131 -0
  123. package/edition/image-delivery-entitlement-descriptor.mjs +106 -0
  124. package/edition/image-evidence-replay-state.mjs +242 -0
  125. package/edition/image-evidence.mjs +1328 -0
  126. package/edition/image-lock-service-rows.mjs +26 -0
  127. package/edition/image-subject-identity.mjs +19 -0
  128. package/edition/images-from-record.mjs +216 -0
  129. package/edition/indexes-at-pins.mjs +13 -0
  130. package/edition/infra-images-lock.mjs +105 -0
  131. package/edition/kernel-capability-domain.mjs +30 -0
  132. package/edition/kernel-contract-authority.mjs +526 -0
  133. package/edition/kernel-contract-source-path-set.mjs +96 -0
  134. package/edition/kernel-package.mjs +237 -0
  135. package/edition/kernel-pin-source.mjs +63 -0
  136. package/edition/kernel-protocol-choice.mjs +239 -0
  137. package/edition/kernel-signer-kind.mjs +23 -0
  138. package/edition/lib.mjs +3322 -0
  139. package/edition/locked-bundle-signer.mjs +297 -0
  140. package/edition/map-with-concurrency.mjs +19 -0
  141. package/edition/no-follow-file.mjs +94 -0
  142. package/edition/oci-evidence-verbs.mjs +421 -0
  143. package/edition/oci-image-source-evidence.mjs +377 -0
  144. package/edition/operation-grant-admission.mjs +285 -0
  145. package/edition/operation-requirements.mjs +282 -0
  146. package/edition/oras-process.mjs +35 -0
  147. package/edition/oras.mjs +664 -0
  148. package/edition/pin-source.mjs +242 -0
  149. package/edition/pinned-cosign.mjs +86 -0
  150. package/edition/plan-image-evidence-refresh.mjs +428 -0
  151. package/edition/platform-lock-readability.mjs +903 -0
  152. package/edition/platform-service-class.mjs +85 -0
  153. package/edition/platform-service-ownership.mjs +91 -0
  154. package/edition/platform-service-source-authority.mjs +105 -0
  155. package/edition/positive-closure.mjs +162 -0
  156. package/edition/previous-image-lock.mjs +25 -0
  157. package/edition/produce-distribution-completeness-evidence.mjs +317 -0
  158. package/edition/produce-edition-catalogue-evidence.mjs +252 -0
  159. package/edition/produce-image-delivery-entitlement-evidence.mjs +302 -0
  160. package/edition/produce-image-source-evidence.mjs +788 -0
  161. package/edition/producer-evidence-bundle.mjs +201 -0
  162. package/edition/producer-evidence-index-file.mjs +4 -0
  163. package/edition/record-refused.mjs +11 -0
  164. package/edition/registry-client-factory.mjs +21 -0
  165. package/edition/registry-credentials.mjs +225 -0
  166. package/edition/release-catalogue.mjs +48 -0
  167. package/edition/release-channel-guard.mjs +108 -0
  168. package/edition/release-edition-artifact.mjs +152 -0
  169. package/edition/release-edition.mjs +54 -0
  170. package/edition/remedy-command.mjs +89 -0
  171. package/edition/repin-evidence-trust.mjs +109 -0
  172. package/edition/required-resource-provider.mjs +58 -0
  173. package/edition/resolve-chart-set.mjs +376 -0
  174. package/edition/resolve-customer-edition.mjs +374 -0
  175. package/edition/resolve-distribution.mjs +2642 -0
  176. package/edition/resolve-edition.mjs +192 -0
  177. package/edition/resolve-images.mjs +2344 -0
  178. package/edition/resolve-infra-images.mjs +88 -0
  179. package/edition/seal-image-digests.mjs +954 -0
  180. package/edition/sealed-distribution-lock.mjs +22 -0
  181. package/edition/sealed-evidence-accept.mjs +82 -0
  182. package/edition/shallow-checkout.mjs +18 -0
  183. package/edition/sigstore-bundle-media-type.mjs +4 -0
  184. package/edition/source-authority.mjs +713 -0
  185. package/edition/source-closure.mjs +115 -0
  186. package/edition/store-publisher-trust.mjs +40 -0
  187. package/edition/strict-json.mjs +121 -0
  188. package/edition/string-order.mjs +5 -0
  189. package/edition/third-party-admission.mjs +118 -0
  190. package/edition/third-party-bundle-registry.mjs +349 -0
  191. package/edition/third-party-certificate-extensions.mjs +58 -0
  192. package/edition/third-party-image-pins.mjs +41 -0
  193. package/edition/third-party-pins-trusted.mjs +24 -0
  194. package/edition/third-party-producer-evidence.mjs +76 -0
  195. package/edition/third-party-source-check.mjs +28 -0
  196. package/edition/third-party-source-context.mjs +41 -0
  197. package/edition/third-party-source-evidence.mjs +326 -0
  198. package/edition/third-party-source-flags.mjs +148 -0
  199. package/edition/third-party-source-pins.mjs +270 -0
  200. package/edition/third-party-source-uniqueness.mjs +54 -0
  201. package/edition/third-party-source-walk.mjs +118 -0
  202. package/edition/third-party-source-writer.mjs +228 -0
  203. package/edition/third-party-subjects.mjs +128 -0
  204. package/edition/trust-policy-arming-verb.mjs +60 -0
  205. package/edition/trust-policy-arming.mjs +144 -0
  206. package/edition/trust-policy-image-ownership.mjs +85 -0
  207. package/edition/trusted-executable.mjs +166 -0
  208. package/edition/verifier-binding.mjs +45 -0
  209. package/edition/verifier-digest-pin.mjs +17 -0
  210. package/edition/verifier-files.mjs +8 -0
  211. package/edition/verifier-kernel-schemas.mjs +22 -0
  212. package/edition/verifier-provenance.mjs +64 -0
  213. package/edition/verify-catalogue-evidence.mjs +247 -0
  214. package/edition/verify-image-evidence.mjs +493 -0
  215. package/edition/verify-image-platforms.mjs +523 -0
  216. package/edition/why.mjs +56 -0
  217. package/edition/workspace-sections.mjs +287 -0
  218. package/package.json +83 -4
  219. package/README.md +0 -3
@@ -0,0 +1,250 @@
1
+ // develop-edition-resolve — resolve and seal one DEVELOP edition from a snapshot.
2
+ //
3
+ // The whole pipeline, in the order the release runs it (every check reports its refusal; see develop-resolve-report.mjs), over the snapshot's
4
+ // injected inputs: distribution lock -> plane invariants (reservation table,
5
+ // confinement; develop-edition-invariants.mjs) -> authorization base (composed, never
6
+ // written) -> images lock -> develop seal, plus the chart-set lock: the frozen
7
+ // deploy commit's develop chart set (develop-chart-set.mjs). Nothing here writes to the editions
8
+ // trees; the caller writes the artifact (edition-artifact.mjs).
9
+ //
10
+ // What the resolve takes on trust and what it proves is its LANE's, never a flag of the resolve:
11
+ //
12
+ // the channel lane (`developChannelLane`, `develop resolve --images-from --channel`, every deployment we run):
13
+ // the engine image is the caller's `--engine-image` digest; every first-party image digest is the build record's
14
+ // (images-from-record.mjs), satellites checked only for consistency with their producers' channel records; a
15
+ // git-sourced OCI bundle is taken by the digest its tag serves (develop-git-bundles.mjs
16
+ // `createDevelopGitBundleDigestReader`); the third-party pins are read as the data root holds them. No cosign,
17
+ // no content hash, no bundle-signer pre-check and no trust-pin read.
18
+ // the verified vendor lane of the catalogue-free customer resolve (develop-catalogue-free-vendor.mjs, the official
19
+ // lane): the engine found and cosign-proven, the images probed by computed content hash, the git-sourced bundles
20
+ // cosign-verified, the bundle signers asserted, the third-party pins held to the evidence trust pin.
21
+ import { adapterHostRow, assertAdapterBundlesMatchHost, fetchRegistryManifest } from './adapter-bundled-versions.mjs';
22
+ import { biomeSourceTreeIds, readBiomeSourceFacts, sealLock } from './biome-content.mjs';
23
+ import { buildCatalogue } from './catalogue.mjs';
24
+ import { resolveDevelopChartSet } from './develop-chart-set.mjs';
25
+ import { assertDevelopEditionInvariants } from './develop-edition-invariants.mjs';
26
+ import { createDevelopGitBundleDigestReader } from './develop-git-bundles.mjs';
27
+ import { createDevelopImageProbe } from './develop-image-probe.mjs';
28
+ import { BLOCKED, DevelopResolveCheck, RESOLVE_CHECK_DEPENDENCIES, createResolveReport } from './develop-resolve-report.mjs';
29
+ import { assertFrozenCheckouts, buildDevelopSnapshotInputs } from './develop-snapshot-inputs.mjs';
30
+ import { EditionChannel } from './edition-constants.mjs';
31
+ import { sealImages } from './edition-image-seal.mjs';
32
+ import { assertKernelEditionProtocol } from './edition-protocol.mjs';
33
+ import { dataRoot, kernelPackageDir } from './edition-workspace.mjs';
34
+ import { firstPartySources } from './first-party-source-ids.mjs';
35
+ import { resolveImagesFromRecord, sealImagesFromRecord } from './images-from-record.mjs';
36
+ import { kernelRowOfAuthority } from './kernel-package.mjs';
37
+ import { loadKernelDistributionSchemas } from './lib.mjs';
38
+ import { GitSourcedBundle, assertLockBundleSigners, isBundleSignerRefusal } from './locked-bundle-signer.mjs';
39
+ import { admitDistributionLock, ungrantedOptionalLinesOfRefusal } from './operation-grant-admission.mjs';
40
+ import { deployPinKey } from './pin-source.mjs';
41
+ import { admitResolvedLock, composeAuthorizationBase, prepareDistributionWithIndex } from './resolve-distribution.mjs';
42
+ import { externalSourceTrees, resolveImages, sourceTreeLocator } from './resolve-images.mjs';
43
+ import { createPinnedSourceAuthority } from './source-authority.mjs';
44
+ import { loadThirdPartySourcePins } from './third-party-source-pins.mjs';
45
+
46
+ /**
47
+ * The steps the resolve runs, named so a test can stand in for the ones that
48
+ * need a real snapshot and observe that the later ones are never reached.
49
+ */
50
+ export const DEVELOP_EDITION_STEPS = Object.freeze({
51
+ assertFrozenCheckouts,
52
+ buildSnapshotInputs: buildDevelopSnapshotInputs,
53
+ assertKernelEditionProtocol,
54
+ prepareDistributionWithIndex,
55
+ assertDevelopEditionInvariants,
56
+ assertAdapterBundlesMatchHost,
57
+ composeAuthorizationBase,
58
+ resolveImages,
59
+ sealImages,
60
+ sealImagesFromRecord,
61
+ resolveDevelopChartSet,
62
+ });
63
+
64
+ const stderrProgress = (line) => process.stderr.write(`${line}\n`);
65
+
66
+ /**
67
+ * The first half of every develop resolve: the frozen snapshot's inputs and the
68
+ * distribution lock composed over them, NOT yet admitted. `resolveDevelopEdition`
69
+ * admits it as one of its checks; `resolveDevelopSnapshot` admits it at once. Both
70
+ * start here, so the content hash each computes for a subject is the same by construction.
71
+ */
72
+ async function prepareDevelopSnapshot({ id, pins, steps, phase, developGitBundles }) {
73
+ const schemas = await loadKernelDistributionSchemas();
74
+ steps.assertFrozenCheckouts(pins);
75
+ const inputs = await steps.buildSnapshotInputs({ id, pins, schemas });
76
+ phase('snapshot inputs built');
77
+ const prepared = await steps.prepareDistributionWithIndex(id, { channel: EditionChannel.Develop, inputs: developGitBundles === undefined ? inputs : { ...inputs, developGitBundles } });
78
+ // The schemas the lock was composed with are the ones that admit it.
79
+ return { inputs, ...prepared };
80
+ }
81
+
82
+ /**
83
+ * The image-record snapshot: prepared as the resolve prepares it, then admitted (a refusal
84
+ * throws). It seals no distribution lock, so it verifies no git-sourced OCI bundle (no registry, no cosign): its lock
85
+ * rows of a git source carry none, which image-record does not read.
86
+ */
87
+ export async function resolveDevelopSnapshot({ id, pins, steps = DEVELOP_EDITION_STEPS, phase = () => {} }) {
88
+ const snapshot = await prepareDevelopSnapshot({ id, pins, steps, phase });
89
+ admitResolvedLock(snapshot.schemas, snapshot.lock);
90
+ return snapshot;
91
+ }
92
+
93
+ /**
94
+ * The channel lane (see the header): what every deployment we run resolves with.
95
+ *
96
+ * @param {object} request
97
+ * @param {string} request.engineImage `<repository>@sha256:<64 hex>`, the caller's (already validated by the verb)
98
+ * @param {object} request.record the parsed image build record (`--images-from`)
99
+ * @param {Function} request.verifySatellites the channel check of its satellites (develop-channel-satellites.mjs)
100
+ */
101
+ export function developChannelLane({ engineImage, record, verifySatellites }) {
102
+ return Object.freeze({
103
+ engineImage: async () => engineImage,
104
+ developGitBundles: createDevelopGitBundleDigestReader({ registryRoot: dataRoot() }),
105
+ checkBundleSigners: false,
106
+ thirdPartyPins: () => loadThirdPartySourcePins(dataRoot()),
107
+ imagesFrom: { record, verifySatellites },
108
+ });
109
+ }
110
+
111
+ const LANE_KEYS = Object.freeze(['engineImage', 'developGitBundles', 'checkBundleSigners', 'thirdPartyPins']);
112
+
113
+ /** A lane names every choice; one it leaves out is a defect of the caller, never a default. */
114
+ function assertLane(lane) {
115
+ const missing = LANE_KEYS.filter((key) => lane?.[key] === undefined);
116
+ if (missing.length > 0) {throw new Error(`resolveDevelopEdition: the lane names no ${missing.join(', ')}`);}
117
+ if (lane.imagesFrom !== undefined && (lane.imagesFrom.record === undefined || lane.imagesFrom.verifySatellites === undefined)) {
118
+ throw new Error('resolveDevelopEdition takes an image build record and its satellite check together');
119
+ }
120
+ }
121
+
122
+ /**
123
+ * @param {object} request
124
+ * @param {object} request.lane `developChannelLane(...)` or the catalogue-free vendor step's verified lane: the engine
125
+ * image, the git-bundle reader or verifier, whether bundle signers are pre-checked, the third-party pins, and either
126
+ * `imagesFrom` (digests from the build record, no probe and no hash) or none (the probe by computed content hash)
127
+ * @param {object|null} [request.previousLock] the previous edition's parsed images lock: its composed artifacts carry their generation forward (null: generation 1)
128
+ */
129
+ export async function resolveDevelopEdition({ id, pins, lane, env = process.env, sealOptions = {}, fetchManifest = fetchRegistryManifest, progress = stderrProgress, steps = DEVELOP_EDITION_STEPS, declaredSources = firstPartySources('naming the develop repository of each first-party image'), previousLock = null }) {
130
+ assertLane(lane);
131
+ const { imagesFrom } = lane;
132
+ const startedAt = Date.now();
133
+ // One line per phase, with the time since the resolve began, so the step is never silent for long.
134
+ const phase = (name) => progress(`[develop-edition] ${name} (+${Date.now() - startedAt}ms)`);
135
+ // Every check runs unless a check it needs was refused; all refusals are reported together, and nothing is sealed or written then.
136
+ const report = createResolveReport();
137
+ const checked = (check, fn, options) => report.run(check, RESOLVE_CHECK_DEPENDENCIES[check], fn, options);
138
+ const chartSetLock = await checked(DevelopResolveCheck.ChartSet, async () => {
139
+ const chartSet = await steps.resolveDevelopChartSet({ xemaDeploySha: pins[deployPinKey()] });
140
+ phase('chart set resolved');
141
+ return chartSet;
142
+ });
143
+ const engineImage = await checked(DevelopResolveCheck.EngineImage, async () => {
144
+ const image = await lane.engineImage();
145
+ phase('engine image resolved');
146
+ return image;
147
+ });
148
+ await checked(DevelopResolveCheck.EditionProtocol, () => steps.assertKernelEditionProtocol(kernelPackageDir()));
149
+ // A git-sourced biome's OCI bundle, by the lane's reader (by digest) or verifier (develop-git-bundles.mjs).
150
+ const snapshot = await checked(DevelopResolveCheck.DistributionLock, () => prepareDevelopSnapshot({ id, pins, steps, phase, developGitBundles: lane.developGitBundles }), {
151
+ classify: (error) => (isBundleSignerRefusal(error) ? DevelopResolveCheck.BundleSigner : DevelopResolveCheck.DistributionLock),
152
+ });
153
+ const lockProduced = snapshot !== BLOCKED;
154
+ const { schemas, inputs, lock, index } = lockProduced ? snapshot : {};
155
+ if (lockProduced && lane.checkBundleSigners) {
156
+ await checked(DevelopResolveCheck.BundleSigner, () => assertLockBundleSigners({ lock, schemas, gitSourcedBundle: GitSourcedBundle.DevelopVerified }));
157
+ }
158
+ // The plane invariants, against the graph this candidate froze (develop-edition-invariants.mjs).
159
+ await checked(DevelopResolveCheck.PlaneInvariants, () => {
160
+ const graded = steps.assertDevelopEditionInvariants({ id, lock, biomeIndex: inputs.biomeIndex, schemas });
161
+ phase(`reservation table asserted (${graded.reservedDomains} domains); confinement asserted (${graded.confinedBiomes} declaration(s))`);
162
+ });
163
+ await checked(DevelopResolveCheck.OperationAdmission, () => {
164
+ try {
165
+ admitDistributionLock(schemas, lock, { report: (line) => report.notice(line) });
166
+ } catch (error) {
167
+ ungrantedOptionalLinesOfRefusal(error).forEach((line) => report.notice(line));
168
+ throw error;
169
+ }
170
+ phase('operation requirements admitted');
171
+ });
172
+ await checked(DevelopResolveCheck.AdapterBundles, () => steps.assertAdapterBundlesMatchHost({ lock, hostRow: adapterHostRow(index), fetchManifest }));
173
+ const authorizationLock = await checked(DevelopResolveCheck.AuthorizationBase, async () => {
174
+ const composed = await steps.composeAuthorizationBase(id, lock, index);
175
+ phase('distribution lock resolved');
176
+ return composed;
177
+ });
178
+ // Every subject's develop image is probed the moment its hash is known, in
179
+ // parallel with the hashing of the rest; an absent image refuses here, naming
180
+ // all of them, before any later phase runs.
181
+ let recorded;
182
+ const imageLock = await checked(DevelopResolveCheck.DevelopImages, async () => {
183
+ if (imagesFrom !== undefined) {
184
+ const fromRecord = await resolveImagesFromRecord({ id, inputs, lock, record: imagesFrom.record, resolveImages: steps.resolveImages, verifySatellites: imagesFrom.verifySatellites, previousLock });
185
+ ({ recorded } = fromRecord);
186
+ phase(`image subjects taken from the build record (${fromRecord.imageLock.subjects.length} subjects); satellites checked against their channel records`);
187
+ return fromRecord.imageLock;
188
+ }
189
+ const probe = createDevelopImageProbe({
190
+ distributionId: id,
191
+ declaredSources,
192
+ env,
193
+ ...(sealOptions.distributionsRoot === undefined ? {} : { distributionsRoot: sealOptions.distributionsRoot }),
194
+ ...(sealOptions.fetchImpl === undefined ? {} : { fetchImpl: sealOptions.fetchImpl }),
195
+ ...(sealOptions.run === undefined ? {} : { run: sealOptions.run }),
196
+ progress,
197
+ });
198
+ const resolved = await steps.resolveImages(id, { inputs, lock, onSubjectHashed: probe.observe, previousLock });
199
+ phase(`image content hashes computed (${resolved.imageLock.subjects.length} subjects)`);
200
+ await probe.settle();
201
+ phase('every develop image is present');
202
+ return resolved.imageLock;
203
+ });
204
+ try {
205
+ report.settle();
206
+ } catch (error) {
207
+ progress(error.message);
208
+ throw error;
209
+ }
210
+ if (report.noticeText() !== '') {progress(report.noticeText());}
211
+ const sealArguments = { imageLock, declaredSources, env, progress, thirdPartyPins: lane.thirdPartyPins(), ...sealOptions };
212
+ const sealedImageLock = recorded === undefined
213
+ ? await steps.sealImages({ channel: EditionChannel.Develop, ...sealArguments })
214
+ : await steps.sealImagesFromRecord({ ...sealArguments, recorded });
215
+ phase('images sealed');
216
+ // The unsealed lock becomes the one v5 grammar only here, once the images it
217
+ // owns are sealed: each biomeContentHash is recomputed from the frozen sources.
218
+ const authority = createPinnedSourceAuthority({ pins: inputs.runtimeImageSourceIndex.repositoryPins });
219
+ const biomeSources = {
220
+ schemas,
221
+ imageLock: sealedImageLock,
222
+ locate: sourceTreeLocator(externalSourceTrees(lock)),
223
+ vendoredCommit: (repo) => authority.vendoredSourceCommitForRepository(repo),
224
+ };
225
+ // ONE walk of the biome checkouts: the content hashes and the tree ids both read it.
226
+ const sourceFactsById = await readBiomeSourceFacts({ ...biomeSources, lock });
227
+ phase(`biome sources read (${sourceFactsById.size} biomes)`);
228
+ const sealedLock = await sealLock({ ...biomeSources, unsealed: lock, sourceFactsById, gitSourcedBundle: GitSourcedBundle.DevelopVerified });
229
+ phase('distribution lock sealed');
230
+ const sourceTreeIds = await biomeSourceTreeIds({ ...biomeSources, lock, sourceFactsById });
231
+ const catalogue = buildCatalogue({
232
+ sourceTreeIds,
233
+ offeringId: id,
234
+ channel: EditionChannel.Develop,
235
+ distributionLock: lock,
236
+ imageLock: sealedImageLock,
237
+ chartSetLock,
238
+ biomeIndex: inputs.biomeIndex,
239
+ platformServiceIndex: inputs.platformServiceIndex,
240
+ contractAuthority: inputs.runtimeImageSourceIndex.contractAuthority,
241
+ kernel: await kernelRowOfAuthority({
242
+ channel: EditionChannel.Develop,
243
+ kernelPackageDir: kernelPackageDir(),
244
+ contractAuthority: inputs.runtimeImageSourceIndex.contractAuthority,
245
+ }),
246
+ leaf: inputs.composed.leaf,
247
+ });
248
+ phase('catalogue built');
249
+ return { distributionLock: sealedLock, imageLock: sealedImageLock, authorizationLock, chartSetLock, catalogue, engineImage };
250
+ }
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env node
2
+ // develop-edition — the develop edition's command line.
3
+ //
4
+ // The vendor verbs are the CHANNEL lane every deployment we run uses: images by digest from their producers' build
5
+ // records, read through a registry channel tag, and nothing else. No cosign, no signature or attestation check, no
6
+ // content-hash recomputation, no lock-algorithm check, no seal verification. The customer forms (`--definition`,
7
+ // `--definition-at`) are the official lane and keep their verification.
8
+ //
9
+ // select-records --branch develop --channel <tag> --out <file>
10
+ // for every develop-graph member that does not keep its current resolution, read the one manifest
11
+ // <root>/build-records/<name>:<tag> (a tag the producer's CI moves to its newest green record; tags are never
12
+ // listed, so <root> may be a pull-through proxy), parse its single build-record layer with the CLI's own
13
+ // @xemahq/contracts BuildRecordSchema (select-records takes no --kernel: it runs before any member pin exists) and hold it to the member (its producer repository, branch develop). The selection is
14
+ // { channel, pins, snapshot, images, currentResolution }, written once (wx); images[] entries carry `member`, the
15
+ // owning repo handle, and the record's digest and contentHash. Each member prints
16
+ // `<short name>: channel <tag> -> record <12-hex commit> (run <run>)`, a member that keeps its current resolution
17
+ // (a declared currentResolution, develop-current-resolution.mjs) `xema-docs: current resolution (leaves the edition,
18
+ // XEMA-2373)` with no registry read. A missing tag or a malformed record refuses the whole run naming the member and
19
+ // writes nothing (develop-select-records.mjs).
20
+ // freeze-graph --data-sha <sha>
21
+ // --deploy-sha <sha> [--editions-root-url <https url>]
22
+ // [--overrides <json> | --member-pins <selection file>] --out <file>
23
+ // resolve refs/heads/develop once per develop-graph repository (the data root's
24
+ // firstPartySources, at each declared URL) and the editions root; the three checked-out
25
+ // repositories are pinned at what the workflow checked out; write the pins. --member-pins takes a select-records
26
+ // selection (only .pins, 40-hex, is read) and pins each graph member at its selected record commit instead of its
27
+ // develop head: the selection must pin exactly the graph minus its currentResolution members (which must equal
28
+ // the graph members' declared currentResolution), a missing or extra member is refused by name, and a currentResolution
29
+ // member is pinned at its develop head. --member-pins together with --overrides is refused. The kernel comes from the
30
+ // pinned xema-base member checkout (--kernel names its contracts at the folded path, kernel-pin-source.mjs): the
31
+ // kernel pin is that member's pin, and --kernel-sha is refused. The pin must declare an edition protocol this CLI
32
+ // supports (the check choose-kernel applies to a pin).
33
+ // freeze-sources <id> --pins <file> [--overrides <json>] --out <file>
34
+ // add one pin per source the edition's chain declares (run after the editions
35
+ // root is checked out and passed as --editions-root); write the final pins.
36
+ // image-record <id> --selection <file> --pins <file> --out <file> --channel <tag>
37
+ // write the edition's image build record (the file `resolve --images-from` reads) from a `select-records --channel
38
+ // <tag>` selection (develop-image-record.mjs). The subjects are the checked-out snapshot's first-party image
39
+ // subjects, enumerated with the selection standing in for every hash: each takes the selection's image of its
40
+ // develop repository (digest AND contentHash), whose selected commit must be the snapshot's, and goes in
41
+ // `satellites`, checked against its producer's <tag> record as `resolve` checks it. The one declared exception: a
42
+ // subject of a member that keeps its current resolution (XEMA-2373) has no build record, so its content hash is
43
+ // computed and its develop image probed by it, and it goes in `images`. Platforms are read from the registry by
44
+ // digest. Any subject without an image, any commit mismatch refuses the whole run and writes nothing, once (wx).
45
+ // Third-party subjects are ignored. Takes the roots `resolve` takes.
46
+ // target <id> --pins <file> --registry <root> --engine-image <repository>@sha256:<digest> [--images-from <image build record>]
47
+ // print <root>/<develop segment>/xema-edition-<id>:<tag>, the tag being sha256 of the canonical
48
+ // { cli, engine, pins, sourceImages } (snapshotEditionTag): every input of the bytes the artifact records,
49
+ // so the same pins with another engine, source-image map or edition-tooling version are another artifact. With
50
+ // --images-from the record's digest (imageBuildRecordDigest) is one more input, named imageBuildRecord; resolve
51
+ // prints the same target. The root is always the caller's registry root (e.g. registry.example.com/acme); the
52
+ // develop namespace segment and the repository prefix come from edition-contract.json, and no root is implied.
53
+ // check-existing <id> --pins <file> --artifact <dir> --engine-image <repository>@sha256:<digest>
54
+ // refuse to reuse a pulled artifact whose descriptor disagrees with this snapshot (its evidence trust the channel
55
+ // lane's `not-armed`, develop-channel-evidence.mjs).
56
+ // resolve <id> --pins <file> --out <dir> --registry <root> --engine-image <repository>@sha256:<digest>
57
+ // --images-from <image build record> --channel <tag> --selection <select-records file>
58
+ // (--previous-lock <file> --previous-lock-sha256 <hex> | --first-seal) [--local]
59
+ // resolve the edition from the snapshot, write the descriptor and the four locks to <dir>, print the target. The
60
+ // engine is the --engine-image digest, recorded as given. Every first-party digest is the build record's
61
+ // (images-from-record.mjs): no probe and no hash; a subject missing, duplicated or extra in the record refuses.
62
+ // Each satellite must be listed, {name, digest}, by its producer's build record at the commit the snapshot pins
63
+ // (develop-channel-satellites.mjs): the record --selection pinned, read by its own record tag
64
+ // <root>/build-records/<name>:<branch>-<run>-<attempt>, so a channel that moved after selection does not affect
65
+ // the run. The selection must have been selected through --channel. A git-sourced OCI bundle is taken by the
66
+ // digest its tag serves (develop-git-bundles.mjs). The evidence verifier and its data-root pin are not loaded: the
67
+ // descriptor records evidenceTrustPolicy not-armed, stated on stderr (develop-channel-evidence.mjs). The previous
68
+ // seal is the edition's last images lock, held to the sha256 of its bytes and to the id, or --first-seal. Every
69
+ // refusal of the run is reported in one table (develop-resolve-report.mjs): independent checks all run, a
70
+ // dependent one is reported `blocked by <check>`, and when anything is refused nothing is sealed or written. The
71
+ // workflow run is recorded from GITHUB_REPOSITORY / GITHUB_RUN_ID; --local stamps `local` instead.
72
+ // resolve --definition <file> --pins <file> --out <dir> --registry <root>
73
+ // --engine-image <repository>@sha256:<digest> [--source-image <source>=<registry>/<prefix>]...
74
+ // [--catalogue-username <name> --catalogue-token-env <VAR>] [--cosign <absolute-path>] [--local]
75
+ // a CUSTOMER develop edition (official lane): the definition resolved against the develop catalogue pinned in
76
+ // catalogue.pin.json beside it (see catalogue-pin.mjs and resolve-customer-edition.mjs), frozen at the
77
+ // customer's own pins. --pins holds exactly one `sources/<name>` commit per source the definition
78
+ // declares (`freeze-sources --definition` writes it); the catalogue digest is frozen into the descriptor's
79
+ // provenance pins as `catalogue`. The id is the definition's. Takes --data-root, --editions-root,
80
+ // --sources-root, --cache-dir (no --deploy-root, no --kernel: the kernel is the catalogue's, from npm).
81
+ // It takes the customer edition's OWN previous seal, exactly one of --previous-lock <file> --previous-lock-sha256 <hex>
82
+ // (the customer edition's last images lock: sha256 of its bytes, its id the definition's) or --first-seal; it carries
83
+ // the generations of the customer's own composed artifacts forward (vendor rows are the catalogue's, unchanged).
84
+ // resolve --definition-at <owner>/<repo>,<path>,<dataRoot> --offering <owner>/<repo>,<path>,<dataRoot>
85
+ // --pins <file> --out <dir> --registry <root> --engine-repository <repository> --engine-signer <certificate identity>
86
+ // --cosign <absolute-path> --offering-previous-lock <file> --offering-previous-lock-sha256 <hex>
87
+ // (--previous-lock <file> --previous-lock-sha256 <hex> | --first-seal) [--source-image <source>=<registry>/<prefix>]... [--local]
88
+ // a CATALOGUE-FREE customer develop edition (official lane, develop-catalogue-free-resolve.mjs): the vendor
89
+ // offering named by --offering (its directory in the vendor data checkout) is resolved first by the vendor step
90
+ // (develop-catalogue-free-vendor.mjs, a child of this CLI on the vendor roots, NOT the vendor `resolve` verb) on
91
+ // the verified lane: the engine found and cosign-proven from --engine-repository/--engine-signer, the images probed
92
+ // by content hash, the git-sourced bundles cosign-verified, sealed against the OFFERING's previous seal
93
+ // (--offering-previous-lock, held to its sha256 and the offering's id); then the definition, read from its
94
+ // repository at --pins "definition-repository", is resolved against the catalogue that step wrote, on its own
95
+ // roots, carrying the customer's own composed generations from --previous-lock (or --first-seal). --pins is the
96
+ // complete frozen set (the vendor pins, the definition's sources/<name>, "definition-repository"); no head is read
97
+ // and nothing is pushed. The edition pins those and the definition digest, no catalogue (the catalogue's content
98
+ // digest is in the distribution lock only), and records the vendor step's engine. catalogue.pin.json beside the
99
+ // definition is refused. Takes the vendor roots.
100
+ // target --definition <file> --pins <file> --registry <root> --engine-image <repository>@sha256:<digest>
101
+ // [--source-image <source>=<registry>/<prefix>]...
102
+ // the same for a customer edition (id = the definition's; the tag covers the pinned catalogue digest).
103
+ // check-existing --definition <file> --pins <file> --artifact <dir> --engine-image <repository>@sha256:<digest>
104
+ // [--catalogue-username <name> --catalogue-token-env <VAR>] [--source-image <source>=<registry>/<prefix>]...
105
+ // the same for a customer artifact; its descriptor's catalogue provenance pin must also equal the
106
+ // pinned develop catalogue digest, and its own image rows must be the rows the --source-image map composes. It fetches that catalogue (as `resolve --definition` does) to take the
107
+ // kernel from it. Takes --data-root, --cache-dir.
108
+ // freeze-sources --definition <file> --out <file>
109
+ // a CUSTOMER edition: one `sources/<name>` pin per source the definition declares, each the head of
110
+ // refs/heads/develop of that source's repository, resolved once; written exclusively (wx).
111
+ // choose-kernel [--kernel-pin <40-hex>] --out <file>
112
+ // choose the develop kernel commit in the --kernel checkout (full history; its refs/remotes/origin/develop is the develop head):
113
+ // the newest first-parent commit whose declared edition protocol this CLI supports
114
+ // (kernel-protocol-choice.mjs). Writes {chosenSha, headSha, held, reason} (reason: head-supported |
115
+ // head-ahead | explicit-pin); when held, prints `kernel held at v<N> (<sha12>), head at v<M> (<sha12>):
116
+ // raise the CLI` to stdout (never persisted). A pin wins and is still checked; nothing supported is refused.
117
+ // Takes only --kernel (the checkout need not be built).
118
+ // check-overrides --overrides <json>
119
+ // refuse a pins override that would select the tooling this run executes
120
+ // (the edition tooling's own repository); the workflow runs it before any other checkout.
121
+ //
122
+ // Tokens come from the environment (EVIDENCE_REPOSITORIES_TOKEN, XEMA_GHCR_PULL_TOKEN),
123
+ // never argv. Nothing here writes git or publishes a release edition.
124
+
125
+ import { resolve } from 'node:path';
126
+
127
+ import { writeCatalogue } from './catalogue.mjs';
128
+ import { CHANNEL_FLAG, CUSTOMER_ROOTS, DEVELOP_VENDOR_ROOTS, FIRST_SEAL_FLAG, IMAGES_FROM_FLAG, PREVIOUS_LOCK_FLAG, PREVIOUS_LOCK_SHA256_FLAG, SELECTION_FLAG, SELECT_RECORDS_ROOTS, headResolver, parseEditionVerbArgs, runCustomerVerb, writeOnce } from './customer-edition-verbs.mjs';
129
+ import { DEFINITION_FLAG } from './customer-flags.mjs';
130
+ import { resolveCatalogueFree } from './develop-catalogue-free-resolve.mjs';
131
+ import { DEVELOP_CHANNEL_EVIDENCE_TRUST, developResolveEvidenceTrust } from './develop-channel-evidence.mjs';
132
+ import { createChannelSatelliteVerifier } from './develop-channel-satellites.mjs';
133
+ import { developChannelLane, resolveDevelopEdition } from './develop-edition-resolve.mjs';
134
+ import { assertNoToolingOverride, freezeGraph, freezeSources } from './develop-freeze.mjs';
135
+ import { developGraphMembers } from './develop-graph.mjs';
136
+ import { parseSelection, runImageRecord } from './develop-image-record.mjs';
137
+ import { runSelectRecords } from './develop-select-records.mjs';
138
+ import { readSelectionPins } from './develop-selection-pins.mjs';
139
+ import { LOCAL_WORKFLOW, assertExistingArtifactMatches, developEditionTarget, workflowFromEnv, writeEditionArtifact } from './edition-artifact.mjs';
140
+ import { EditionChannel } from './edition-constants.mjs';
141
+ import { WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule, kernelRepoDir, sourcePath } from './edition-workspace.mjs';
142
+ import { ImageBuildRecordError, imageBuildRecordDigest, readImageBuildRecord } from './images-from-record.mjs';
143
+ import { kernelMemberOf } from './kernel-pin-source.mjs';
144
+ import { assertKernelCommitSupported, chooseKernelCommit } from './kernel-protocol-choice.mjs';
145
+ import { loadKernelDistributionSchemas } from './lib.mjs';
146
+ import { kernelPinKey, snapshotPinSource } from './pin-source.mjs';
147
+ import { composeEdition } from './resolve-distribution.mjs';
148
+ import { resolvePreviousSeal } from './resolve-images.mjs';
149
+ import { readStrictJsonFile } from './strict-json.mjs';
150
+
151
+ // The roots a verb takes. A customer form (it names --definition) takes fewer, and never the kernel (the
152
+ // catalogue's kernel is downloaded from npm into the cache root): `target` none, `check-existing` the data
153
+ // root and the cache, `freeze-sources` the data root only, `resolve` all but the deploy root. `check-overrides`
154
+ // takes only the data root in either form: it judges its --overrides JSON against the pin roles the data
155
+ // root declares, so it reads nothing else and refuses every other root flag.
156
+ const DATA_ROOT_ONLY_COMMANDS = Object.freeze(['check-overrides']);
157
+ // `choose-kernel` reads the kernel checkout's git history only, before the kernel is built.
158
+ const KERNEL_ROOT_ONLY_COMMANDS = Object.freeze(['choose-kernel']);
159
+
160
+ if (isEntryModule(import.meta.url)) {
161
+ const [command] = process.argv.slice(2);
162
+ if (DATA_ROOT_ONLY_COMMANDS.includes(command)) {
163
+ configureEditionWorkspaceFromProcess([WorkspaceRoot.Data], { refuseOthers: true });
164
+ } else if (command === 'select-records') {
165
+ configureEditionWorkspaceFromProcess(SELECT_RECORDS_ROOTS, { refuseOthers: true });
166
+ } else if (KERNEL_ROOT_ONLY_COMMANDS.includes(command)) {
167
+ configureEditionWorkspaceFromProcess([WorkspaceRoot.Kernel], { refuseOthers: true });
168
+ } else {
169
+ const customerForm = process.argv.includes(DEFINITION_FLAG);
170
+ configureEditionWorkspaceFromProcess(customerForm ? (CUSTOMER_ROOTS[command] ?? DEVELOP_VENDOR_ROOTS) : DEVELOP_VENDOR_ROOTS, {
171
+ refuseOthers: customerForm && CUSTOMER_ROOTS[command] !== undefined,
172
+ });
173
+ }
174
+ }
175
+
176
+ /** Parse `<command> [<id>] --flag value ...` against the closed vocabulary of the develop command. */
177
+ export const parseDevelopEditionArgs = (argv) => parseEditionVerbArgs(EditionChannel.Develop, argv);
178
+
179
+ function parseOverrides(text) {
180
+ if (text === undefined || text.trim() === '') {return {};}
181
+ const parsed = JSON.parse(text);
182
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
183
+ throw new Error('--overrides must be a JSON object mapping pin key -> 40-hex commit');
184
+ }
185
+ return parsed;
186
+ }
187
+
188
+ /** The target-tag input of `--images-from`: the record's digest, or nothing when the caller names no record. */
189
+ async function imageBuildRecordTagInput(options) {
190
+ return options[IMAGES_FROM_FLAG] === undefined ? {} : { imageBuildRecordDigest: imageBuildRecordDigest(await readImageBuildRecord(options[IMAGES_FROM_FLAG])) };
191
+ }
192
+
193
+ /** The `--selection` the run's image record came from, held to `--channel`. */
194
+ function readChannelSelection(options) {
195
+ const path = options[SELECTION_FLAG];
196
+ const selection = parseSelection(readStrictJsonFile(path), developGraphMembers(), path);
197
+ if (selection.channel !== options[CHANNEL_FLAG]) {
198
+ throw new ImageBuildRecordError(`selection ${path} was selected through channel ${selection.channel}, not ${options[CHANNEL_FLAG]}`);
199
+ }
200
+ return selection;
201
+ }
202
+
203
+ /** The vendor resolve: the channel lane (develop-edition-resolve.mjs `developChannelLane`). */
204
+ async function resolveVendor({ id, options, env }) {
205
+ const pins = readStrictJsonFile(options['--pins']);
206
+ const workflow = options['--local'] ? LOCAL_WORKFLOW : workflowFromEnv(env);
207
+ const evidenceTrustPolicy = developResolveEvidenceTrust();
208
+ const record = await readImageBuildRecord(options[IMAGES_FROM_FLAG]);
209
+ const { catalogue, engineImage, ...resolved } = await resolveDevelopEdition({
210
+ id,
211
+ pins,
212
+ env,
213
+ lane: developChannelLane({
214
+ engineImage: options['--engine-image'],
215
+ record,
216
+ verifySatellites: createChannelSatelliteVerifier({ channel: options[CHANNEL_FLAG], selection: readChannelSelection(options), env }),
217
+ }),
218
+ previousLock: await resolvePreviousSeal({
219
+ distributionId: id,
220
+ previousLockPath: options[PREVIOUS_LOCK_FLAG],
221
+ previousLockSha256: options[PREVIOUS_LOCK_SHA256_FLAG],
222
+ firstSeal: Boolean(options[FIRST_SEAL_FLAG]),
223
+ }),
224
+ });
225
+ const target = developEditionTarget(id, pins, { registry: options['--registry'], engineImage, imageBuildRecordDigest: imageBuildRecordDigest(record) });
226
+ const outDir = resolve(options['--out']);
227
+ writeEditionArtifact({
228
+ outDir,
229
+ channel: EditionChannel.Develop,
230
+ distributionId: id,
231
+ pins,
232
+ workflow,
233
+ evidenceTrustPolicy,
234
+ engineImage,
235
+ ...resolved,
236
+ });
237
+ writeCatalogue(outDir, catalogue);
238
+ console.log(target);
239
+ }
240
+
241
+ async function run(argv, env) {
242
+ const { command, id, options, customer, catalogueFree } = parseDevelopEditionArgs(argv);
243
+ switch (command) {
244
+ case 'target': {
245
+ if (customer) {return runCustomerVerb({ channel: EditionChannel.Develop, command, options, env });}
246
+ console.log(developEditionTarget(id, readStrictJsonFile(options['--pins']), { registry: options['--registry'], engineImage: options['--engine-image'], ...(await imageBuildRecordTagInput(options)) }));
247
+ return;
248
+ }
249
+ case 'resolve': {
250
+ if (customer) {return runCustomerVerb({ channel: EditionChannel.Develop, command, options, env });}
251
+ if (catalogueFree) {
252
+ await resolveCatalogueFree({ options, env });
253
+ return;
254
+ }
255
+ await resolveVendor({ id, options, env });
256
+ return;
257
+ }
258
+ case 'select-records':
259
+ await runSelectRecords({ options, env });
260
+ return;
261
+ case 'image-record':
262
+ await runImageRecord({ id, options, env });
263
+ return;
264
+ case 'check-existing': {
265
+ if (customer) {return runCustomerVerb({ channel: EditionChannel.Develop, command, options, env });}
266
+ assertExistingArtifactMatches({
267
+ artifactDir: resolve(options['--artifact']),
268
+ distributionId: id,
269
+ channel: EditionChannel.Develop,
270
+ evidenceTrustPolicy: DEVELOP_CHANNEL_EVIDENCE_TRUST,
271
+ pins: readStrictJsonFile(options['--pins']),
272
+ engineImage: options['--engine-image'],
273
+ });
274
+ return;
275
+ }
276
+ case 'check-overrides': {
277
+ assertNoToolingOverride(parseOverrides(options['--overrides']));
278
+ return;
279
+ }
280
+ case 'choose-kernel': {
281
+ const { choice, notice } = chooseKernelCommit({ repoDir: kernelRepoDir(), pin: options['--kernel-pin'] });
282
+ writeOnce(options['--out'], choice);
283
+ if (notice !== undefined) {console.log(notice);}
284
+ return;
285
+ }
286
+ case 'freeze-graph': {
287
+ const channel = EditionChannel.Develop;
288
+ const kernelMember = kernelMemberOf({ members: developGraphMembers(), kernelRepoDir: kernelRepoDir(), sourcePath }).repo;
289
+ const pins = freezeGraph({
290
+ channel,
291
+ dataSha: options['--data-sha'],
292
+ kernelMember,
293
+ kernelSha: options['--kernel-sha'],
294
+ deploySha: options['--deploy-sha'],
295
+ editionsRootUrl: options['--editions-root-url'],
296
+ overrides: parseOverrides(options['--overrides']),
297
+ ...(options['--member-pins'] === undefined ? {} : { memberPins: readSelectionPins(options['--member-pins'], '--member-pins', developGraphMembers()) }),
298
+ ...headResolver(EditionChannel.Develop, env),
299
+ });
300
+ const kernelPin = pins[kernelPinKey()];
301
+ assertKernelCommitSupported({ repoDir: kernelRepoDir(), commit: kernelPin, label: `the kernel pin ${kernelPin} (the pin of ${kernelMember})` });
302
+ writeOnce(options['--out'], pins);
303
+ return;
304
+ }
305
+ case 'freeze-sources': {
306
+ if (customer) {return runCustomerVerb({ channel: EditionChannel.Develop, command, options, env });}
307
+ const pins = readStrictJsonFile(options['--pins']);
308
+ const { parseDistribution } = await loadKernelDistributionSchemas();
309
+ const declaredSources = composeEdition(parseDistribution, id, snapshotPinSource(pins)).declaredSources;
310
+ writeOnce(
311
+ options['--out'],
312
+ freezeSources({ pins, declaredSources, overrides: parseOverrides(options['--overrides']), ...headResolver(EditionChannel.Develop, env) }),
313
+ );
314
+ return;
315
+ }
316
+ default:
317
+ throw new Error(`unhandled command ${command}`);
318
+ }
319
+ }
320
+
321
+ if (isEntryModule(import.meta.url)) {
322
+ const argv = process.argv.slice(2);
323
+ run(argv, process.env).then(
324
+ (code) => {
325
+ if (typeof code === 'number') {process.exitCode = code;}
326
+ },
327
+ (error) => {
328
+ console.error(`[develop-edition] ${error.message}`);
329
+ process.exit(1);
330
+ },
331
+ );
332
+ }