@xemahq/xema 0.26.0 → 0.28.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.
Files changed (230) hide show
  1. package/README.md +70 -37
  2. package/dist/assets/distribution-assets.manifest.json +1 -2
  3. package/dist/cli.js +2 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/dev.js +1 -0
  6. package/dist/commands/dev.js.map +1 -1
  7. package/dist/commands/down.d.ts.map +1 -1
  8. package/dist/commands/down.js +7 -1
  9. package/dist/commands/down.js.map +1 -1
  10. package/dist/commands/edition.d.ts +3 -0
  11. package/dist/commands/edition.d.ts.map +1 -0
  12. package/dist/commands/edition.js +46 -0
  13. package/dist/commands/edition.js.map +1 -0
  14. package/dist/commands/init.js +2 -2
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/serve.js +1 -0
  17. package/dist/commands/serve.js.map +1 -1
  18. package/dist/commands/up.js +1 -0
  19. package/dist/commands/up.js.map +1 -1
  20. package/dist/dev/env-defaults.d.ts.map +1 -1
  21. package/dist/dev/env-defaults.js +5 -2
  22. package/dist/dev/env-defaults.js.map +1 -1
  23. package/dist/dev/infra-preflight.d.ts +3 -2
  24. package/dist/dev/infra-preflight.d.ts.map +1 -1
  25. package/dist/dev/infra-preflight.js +18 -8
  26. package/dist/dev/infra-preflight.js.map +1 -1
  27. package/dist/edition/verify-image-evidence.bundle.mjs +54283 -0
  28. package/dist/edition/verify-image-evidence.bundle.provenance.json +2877 -0
  29. package/dist/runtime/config/schema.d.ts +1 -0
  30. package/dist/runtime/config/schema.d.ts.map +1 -1
  31. package/dist/runtime/custody/local-custody.d.ts +12 -0
  32. package/dist/runtime/custody/local-custody.d.ts.map +1 -0
  33. package/dist/runtime/custody/local-custody.js +22 -0
  34. package/dist/runtime/custody/local-custody.js.map +1 -0
  35. package/dist/runtime/custody/openbao-custody-bootstrap.d.ts +18 -0
  36. package/dist/runtime/custody/openbao-custody-bootstrap.d.ts.map +1 -0
  37. package/dist/runtime/custody/openbao-custody-bootstrap.js +50 -0
  38. package/dist/runtime/custody/openbao-custody-bootstrap.js.map +1 -0
  39. package/dist/runtime/custody/openbao-custody-store.d.ts +18 -0
  40. package/dist/runtime/custody/openbao-custody-store.d.ts.map +1 -0
  41. package/dist/runtime/custody/openbao-custody-store.js +55 -0
  42. package/dist/runtime/custody/openbao-custody-store.js.map +1 -0
  43. package/dist/runtime/custody/openbao-http-client.d.ts +34 -0
  44. package/dist/runtime/custody/openbao-http-client.d.ts.map +1 -0
  45. package/dist/runtime/custody/openbao-http-client.js +154 -0
  46. package/dist/runtime/custody/openbao-http-client.js.map +1 -0
  47. package/dist/runtime/custody/openbao-local-constants.d.ts +22 -0
  48. package/dist/runtime/custody/openbao-local-constants.d.ts.map +1 -0
  49. package/dist/runtime/custody/openbao-local-constants.js +48 -0
  50. package/dist/runtime/custody/openbao-local-constants.js.map +1 -0
  51. package/dist/runtime/custody/openbao-local-runner.d.ts +16 -0
  52. package/dist/runtime/custody/openbao-local-runner.d.ts.map +1 -0
  53. package/dist/runtime/custody/openbao-local-runner.js +86 -0
  54. package/dist/runtime/custody/openbao-local-runner.js.map +1 -0
  55. package/dist/runtime/edition/run-entry.d.ts +9 -0
  56. package/dist/runtime/edition/run-entry.d.ts.map +1 -0
  57. package/dist/runtime/edition/run-entry.js +36 -0
  58. package/dist/runtime/edition/run-entry.js.map +1 -0
  59. package/dist/runtime/edition/verb-map.d.ts +60 -0
  60. package/dist/runtime/edition/verb-map.d.ts.map +1 -0
  61. package/dist/runtime/edition/verb-map.js +146 -0
  62. package/dist/runtime/edition/verb-map.js.map +1 -0
  63. package/dist/runtime/env-seeder.d.ts +2 -0
  64. package/dist/runtime/env-seeder.d.ts.map +1 -1
  65. package/dist/runtime/env-seeder.js +18 -3
  66. package/dist/runtime/env-seeder.js.map +1 -1
  67. package/dist/runtime/launch.d.ts +2 -1
  68. package/dist/runtime/launch.d.ts.map +1 -1
  69. package/dist/runtime/launch.js +31 -7
  70. package/dist/runtime/launch.js.map +1 -1
  71. package/dist/runtime/paths.d.ts +1 -2
  72. package/dist/runtime/paths.d.ts.map +1 -1
  73. package/dist/runtime/paths.js +0 -10
  74. package/dist/runtime/paths.js.map +1 -1
  75. package/dist/runtime/runner/chart-local-provenance.d.ts +20 -0
  76. package/dist/runtime/runner/chart-local-provenance.d.ts.map +1 -0
  77. package/dist/runtime/runner/chart-local-provenance.js +114 -0
  78. package/dist/runtime/runner/chart-local-provenance.js.map +1 -0
  79. package/dist/runtime/runner/chart-preflight.d.ts +8 -8
  80. package/dist/runtime/runner/chart-preflight.d.ts.map +1 -1
  81. package/dist/runtime/runner/chart-preflight.js +30 -69
  82. package/dist/runtime/runner/chart-preflight.js.map +1 -1
  83. package/dist/runtime/runner/chart-pull.d.ts +14 -0
  84. package/dist/runtime/runner/chart-pull.d.ts.map +1 -0
  85. package/dist/runtime/runner/chart-pull.js +96 -0
  86. package/dist/runtime/runner/chart-pull.js.map +1 -0
  87. package/dist/runtime/runner/chart-resolver.d.ts +4 -12
  88. package/dist/runtime/runner/chart-resolver.d.ts.map +1 -1
  89. package/dist/runtime/runner/chart-resolver.js +16 -62
  90. package/dist/runtime/runner/chart-resolver.js.map +1 -1
  91. package/dist/runtime/runner/chart-set-lock.d.ts +6 -20
  92. package/dist/runtime/runner/chart-set-lock.d.ts.map +1 -1
  93. package/dist/runtime/runner/chart-set-lock.js +55 -99
  94. package/dist/runtime/runner/chart-set-lock.js.map +1 -1
  95. package/dist/runtime/runner/helm-cli.d.ts +3 -1
  96. package/dist/runtime/runner/helm-cli.d.ts.map +1 -1
  97. package/dist/runtime/runner/helm-cli.js +9 -7
  98. package/dist/runtime/runner/helm-cli.js.map +1 -1
  99. package/dist/runtime/runner/helm-runner.d.ts +4 -4
  100. package/dist/runtime/runner/helm-runner.d.ts.map +1 -1
  101. package/dist/runtime/runner/helm-runner.js +18 -16
  102. package/dist/runtime/runner/helm-runner.js.map +1 -1
  103. package/dist/runtime/sources/kinds/git.d.ts +1 -2
  104. package/dist/runtime/sources/kinds/git.d.ts.map +1 -1
  105. package/dist/runtime/sources/kinds/git.js +8 -5
  106. package/dist/runtime/sources/kinds/git.js.map +1 -1
  107. package/dist/runtime/sources/manifest-walk.d.ts +3 -1
  108. package/dist/runtime/sources/manifest-walk.d.ts.map +1 -1
  109. package/dist/runtime/sources/manifest-walk.js +8 -3
  110. package/dist/runtime/sources/manifest-walk.js.map +1 -1
  111. package/dist/runtime/sources/schema.d.ts +3 -0
  112. package/dist/runtime/sources/schema.d.ts.map +1 -1
  113. package/dist/runtime/sources/schema.js +6 -1
  114. package/dist/runtime/sources/schema.js.map +1 -1
  115. package/dist/runtime/supervisor.d.ts +2 -0
  116. package/dist/runtime/supervisor.d.ts.map +1 -1
  117. package/dist/runtime/supervisor.js +1 -0
  118. package/dist/runtime/supervisor.js.map +1 -1
  119. package/edition/README.md +313 -0
  120. package/edition/assemble-producer-input.mjs +1251 -0
  121. package/edition/authorization-base.mjs +347 -0
  122. package/edition/biome-content.mjs +268 -0
  123. package/edition/build-biome-index.mjs +175 -0
  124. package/edition/build-platform-service-index.mjs +288 -0
  125. package/edition/build-runtime-image-source-index.mjs +821 -0
  126. package/edition/built-verifier.mjs +55 -0
  127. package/edition/catalogue-edition.mjs +49 -0
  128. package/edition/catalogue-fetch.mjs +64 -0
  129. package/edition/catalogue.mjs +135 -0
  130. package/edition/chart-set-source-commit.mjs +92 -0
  131. package/edition/check-committed-image-evidence.mjs +383 -0
  132. package/edition/checkout-indexed-source-repositories.mjs +105 -0
  133. package/edition/comment-blanker.mjs +75 -0
  134. package/edition/connector-source-authority.mjs +60 -0
  135. package/edition/customer-definition.mjs +183 -0
  136. package/edition/customer-flags.mjs +7 -0
  137. package/edition/develop-customer-edition.mjs +99 -0
  138. package/edition/develop-edition-resolve.mjs +65 -0
  139. package/edition/develop-edition.mjs +274 -0
  140. package/edition/develop-freeze.mjs +173 -0
  141. package/edition/develop-image-coordinate.mjs +54 -0
  142. package/edition/develop-seal.mjs +77 -0
  143. package/edition/develop-snapshot-inputs.mjs +104 -0
  144. package/edition/dist-check.mjs +986 -0
  145. package/edition/distribution-evidence-set.mjs +987 -0
  146. package/edition/distribution-paths.mjs +191 -0
  147. package/edition/edition-artifact.mjs +284 -0
  148. package/edition/edition-biome-manifests.mjs +85 -0
  149. package/edition/edition-channel.mjs +6 -0
  150. package/edition/edition-chart-set.mjs +93 -0
  151. package/edition/edition-config.mjs +205 -0
  152. package/edition/edition-constants.mjs +57 -0
  153. package/edition/edition-contract.json +30 -0
  154. package/edition/edition-list.mjs +35 -0
  155. package/edition/edition-locks.mjs +69 -0
  156. package/edition/edition-protocol.mjs +228 -0
  157. package/edition/edition-workspace.mjs +187 -0
  158. package/edition/evidence-checkout.mjs +439 -0
  159. package/edition/evidence-grammars.mjs +16 -0
  160. package/edition/evidence-trust-pin.mjs +304 -0
  161. package/edition/evidence-trust-policy.mjs +294 -0
  162. package/edition/external-source-walk.mjs +463 -0
  163. package/edition/first-party-source-ids.mjs +59 -0
  164. package/edition/fleet-coordinate-finality.mjs +350 -0
  165. package/edition/git-source-auth.mjs +52 -0
  166. package/edition/image-delivery-entitlement-descriptor.mjs +106 -0
  167. package/edition/image-evidence-replay-state.mjs +242 -0
  168. package/edition/image-evidence.mjs +1233 -0
  169. package/edition/kernel-contract-authority.mjs +443 -0
  170. package/edition/lib.mjs +3021 -0
  171. package/edition/no-follow-file.mjs +94 -0
  172. package/edition/oci-evidence-verbs.mjs +392 -0
  173. package/edition/oci-image-source-evidence.mjs +239 -0
  174. package/edition/oras.mjs +648 -0
  175. package/edition/pin-source.mjs +227 -0
  176. package/edition/plan-image-evidence-refresh.mjs +408 -0
  177. package/edition/platform-lock-readability.mjs +892 -0
  178. package/edition/platform-service-class.mjs +18 -0
  179. package/edition/platform-service-source-authority.mjs +105 -0
  180. package/edition/positive-closure.mjs +162 -0
  181. package/edition/produce-distribution-completeness-evidence.mjs +311 -0
  182. package/edition/produce-image-delivery-entitlement-evidence.mjs +294 -0
  183. package/edition/produce-image-source-evidence.mjs +765 -0
  184. package/edition/producer-evidence-bundle.mjs +157 -0
  185. package/edition/registry-credentials.mjs +193 -0
  186. package/edition/release-catalogue.mjs +39 -0
  187. package/edition/release-channel-guard.mjs +109 -0
  188. package/edition/release-edition-artifact.mjs +157 -0
  189. package/edition/remedy-command.mjs +89 -0
  190. package/edition/repin-evidence-trust.mjs +101 -0
  191. package/edition/required-resource-provider.mjs +58 -0
  192. package/edition/resolve-chart-set.mjs +415 -0
  193. package/edition/resolve-customer-edition.mjs +288 -0
  194. package/edition/resolve-distribution.mjs +2318 -0
  195. package/edition/resolve-edition.mjs +186 -0
  196. package/edition/resolve-images.mjs +1606 -0
  197. package/edition/seal-image-digests.mjs +855 -0
  198. package/edition/sealed-evidence-accept.mjs +82 -0
  199. package/edition/source-authority.mjs +725 -0
  200. package/edition/source-closure.mjs +116 -0
  201. package/edition/strict-json.mjs +121 -0
  202. package/edition/string-order.mjs +5 -0
  203. package/edition/trust-policy-arming.mjs +120 -0
  204. package/edition/trusted-executable.mjs +166 -0
  205. package/edition/verifier-binding.mjs +45 -0
  206. package/edition/verifier-files.mjs +8 -0
  207. package/edition/verifier-kernel-schemas.mjs +22 -0
  208. package/edition/verifier-provenance.mjs +64 -0
  209. package/edition/verify-image-evidence.mjs +458 -0
  210. package/edition/verify-image-platforms.mjs +495 -0
  211. package/edition/why.mjs +56 -0
  212. package/edition/workspace-sections.mjs +195 -0
  213. package/examples/06-biome-sources.yaml +1 -0
  214. package/package.json +14 -6
  215. package/dist/commands/dist.d.ts +0 -16
  216. package/dist/commands/dist.d.ts.map +0 -1
  217. package/dist/commands/dist.js +0 -182
  218. package/dist/commands/dist.js.map +0 -1
  219. package/dist/runtime/distribution/available-biome.d.ts +0 -10
  220. package/dist/runtime/distribution/available-biome.d.ts.map +0 -1
  221. package/dist/runtime/distribution/available-biome.js +0 -156
  222. package/dist/runtime/distribution/available-biome.js.map +0 -1
  223. package/dist/runtime/distribution/compose.d.ts +0 -23
  224. package/dist/runtime/distribution/compose.d.ts.map +0 -1
  225. package/dist/runtime/distribution/compose.js +0 -48
  226. package/dist/runtime/distribution/compose.js.map +0 -1
  227. package/dist/runtime/runner/chart-trust.d.ts +0 -57
  228. package/dist/runtime/runner/chart-trust.d.ts.map +0 -1
  229. package/dist/runtime/runner/chart-trust.js +0 -270
  230. package/dist/runtime/runner/chart-trust.js.map +0 -1
@@ -0,0 +1,183 @@
1
+ // customer-definition — reading a CUSTOMER edition definition, and the image
2
+ // registry mirror it may declare.
3
+ //
4
+ // A definition resolved against a catalogue is a customer edition. Its rules
5
+ // live in the kernel (`parseCustomerEditionDefinition`, `customerTrustCeilingIssues`);
6
+ // this module calls them and owns only the `imageRegistryMirror`, which is CLI
7
+ // configuration the kernel document does not carry.
8
+
9
+ import { SOURCE_IMAGE_FLAG } from './customer-flags.mjs';
10
+
11
+ export class CustomerDefinitionError extends Error {
12
+ constructor(message, options) {
13
+ super(message, options);
14
+ this.name = 'CustomerDefinitionError';
15
+ }
16
+ }
17
+
18
+ /** The mirror's refusals (overlap, ambiguity, partial segment, tag/digest alteration, self-mapping, uncovered row). */
19
+ export class ImageRegistryMirrorError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = 'ImageRegistryMirrorError';
23
+ }
24
+ }
25
+
26
+ export const MIRROR_KEY = 'imageRegistryMirror';
27
+
28
+ /**
29
+ * Parse a raw customer definition. The RULES are the kernel's, in one place:
30
+ * `parseCustomerEditionDefinition` (a closed vocabulary - visibility, extends,
31
+ * exclude and sweeping selectors are not in it; `trustCeiling` required iff
32
+ * `sources`; `sources[].ref` a branch or a tag, never a commit; git-only
33
+ * sources; the one `{biome}` selector grammar). The CLI adds nothing to them
34
+ * except what the kernel document does not carry: it removes the mirror (CLI
35
+ * configuration, parsed by `parseImageRegistryMirror`) before the kernel sees
36
+ * the document, and refuses a source that states the registry its images come
37
+ * from, because that is handed in with `--source-image` and has no default.
38
+ * Returns the kernel's parsed definition and the raw mirror.
39
+ */
40
+ export function parseCustomerDefinition(schemas, raw) {
41
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
42
+ throw new CustomerDefinitionError('a customer definition must be a JSON object');
43
+ }
44
+ const { [MIRROR_KEY]: mirror, ...document } = raw;
45
+ for (const source of Array.isArray(document.sources) ? document.sources : []) {
46
+ const stated = ['imageRegistry', 'imageNamePrefix'].find((key) => source !== null && typeof source === 'object' && key in source);
47
+ if (stated !== undefined) {
48
+ throw new CustomerDefinitionError(
49
+ `customer definition "${raw.id}": source "${source.name}" states ${stated}; the registry is configuration, ` +
50
+ `handed in with ${SOURCE_IMAGE_FLAG} ${source.name}=<registry>/<prefix>`,
51
+ );
52
+ }
53
+ }
54
+ let definition;
55
+ try {
56
+ definition = schemas.parseCustomerEditionDefinition(document);
57
+ } catch (error) {
58
+ throw new CustomerDefinitionError(error.message, { cause: error });
59
+ }
60
+ assertNoVendorReservedDomain(schemas, definition);
61
+ return { definition, mirror };
62
+ }
63
+
64
+ /** The trust domains only the vendor places a biome in: the kernel's first-party domain. */
65
+ export const vendorReservedTrustDomains = (schemas) => [schemas.FIRST_PARTY_TRUST_DOMAIN];
66
+
67
+ /** A customer's `trustCeiling` assigns its own biomes and never into a vendor-reserved domain. */
68
+ function assertNoVendorReservedDomain(schemas, definition) {
69
+ const reserved = new Set(vendorReservedTrustDomains(schemas));
70
+ for (const assignment of definition.trustCeiling?.assignments ?? []) {
71
+ if (reserved.has(assignment.domain)) {
72
+ throw new CustomerDefinitionError(
73
+ `customer definition "${definition.id}": \`trustCeiling\` assigns ${assignment.biomes.map((biome) => `biome "${biome}"`).join(', ')} ` +
74
+ `to trust domain "${assignment.domain}", which is reserved to the vendor; a customer's biomes are assigned a domain of the customer's own`,
75
+ );
76
+ }
77
+ }
78
+ }
79
+
80
+ /**
81
+ * The trustCeiling must assign every customer-source biome (a biome the resolved lock draws
82
+ * from the definition's own `sources[]`) and no other: the kernel's `customerTrustCeilingIssues`.
83
+ */
84
+ export function assertTrustCeilingAssignsCustomerBiomes(schemas, definition, customerBiomeIds) {
85
+ const issues = schemas.customerTrustCeilingIssues(definition, customerBiomeIds);
86
+ if (issues.length > 0) {
87
+ throw new CustomerDefinitionError(`customer definition "${definition.id}": ${issues.join('; ')}`);
88
+ }
89
+ }
90
+
91
+ const HOST_PATH = /^[a-z0-9.-]+(:[0-9]+)?(\/[a-z0-9._-]+)*$/;
92
+ const segmentsOf = (prefix) => prefix.split('/');
93
+ const isSegmentPrefix = (shorter, longer) => {
94
+ const a = segmentsOf(shorter);
95
+ const b = segmentsOf(longer);
96
+ return a.length <= b.length && a.every((segment, index) => segment === b[index]);
97
+ };
98
+
99
+ /** The strict parse of `imageRegistryMirror`: `{complete: boolean, map: [{source, target}]}`, no defaults. */
100
+ export function parseImageRegistryMirror(value) {
101
+ if (value === undefined) {return undefined;}
102
+ const exact = (candidate, keys, where) => {
103
+ if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate) || Object.keys(candidate).sort().join() !== [...keys].sort().join()) {
104
+ throw new ImageRegistryMirrorError(`${where} must be exactly {${keys.join(', ')}}`);
105
+ }
106
+ };
107
+ if (value === null || typeof value !== 'object' || !('complete' in value)) {
108
+ throw new ImageRegistryMirrorError(`${MIRROR_KEY}.complete is required and must be a boolean (no default)`);
109
+ }
110
+ exact(value, ['complete', 'map'], MIRROR_KEY);
111
+ if (typeof value.complete !== 'boolean') {
112
+ throw new ImageRegistryMirrorError(`${MIRROR_KEY}.complete is required and must be a boolean (no default)`);
113
+ }
114
+ if (!Array.isArray(value.map) || value.map.length === 0) {
115
+ throw new ImageRegistryMirrorError(`${MIRROR_KEY}.map must be a non-empty array of {source, target}`);
116
+ }
117
+ for (const entry of value.map) {
118
+ exact(entry, ['source', 'target'], `${MIRROR_KEY}.map entry`);
119
+ for (const side of ['source', 'target']) {
120
+ if (typeof entry[side] !== 'string' || !HOST_PATH.test(entry[side]) || !entry[side].includes('/')) {
121
+ throw new ImageRegistryMirrorError(
122
+ `${MIRROR_KEY}.map ${side} ${JSON.stringify(entry[side])} is not <host>/<path-prefix> ` +
123
+ '(lowercase, whole path segments, no tag, no digest, no wildcard)',
124
+ );
125
+ }
126
+ }
127
+ if (entry.source === entry.target) {
128
+ throw new ImageRegistryMirrorError(`${MIRROR_KEY}.map maps ${entry.source} to itself`);
129
+ }
130
+ }
131
+ const sources = value.map.map((entry) => entry.source);
132
+ sources.forEach((source, index) => {
133
+ sources.slice(index + 1).forEach((other) => {
134
+ if (isSegmentPrefix(source, other) || isSegmentPrefix(other, source)) {
135
+ throw new ImageRegistryMirrorError(
136
+ source === other
137
+ ? `${MIRROR_KEY}.map lists source ${source} twice`
138
+ : `${MIRROR_KEY}.map sources ${source} and ${other} overlap (one is a path-segment prefix of the other); a ref would match both`,
139
+ );
140
+ }
141
+ });
142
+ });
143
+ return { complete: value.complete, map: value.map.map(({ source, target }) => ({ source, target })) };
144
+ }
145
+
146
+ const REF_NAME = /^[a-z0-9.-]+(:[0-9]+)?(\/[a-z0-9._-]+)+$/;
147
+
148
+ /**
149
+ * Re-home one image ref (name only, as the images lock records it) under the
150
+ * mirror. Whole path segments only; the remaining path is untouched; a ref that
151
+ * carries a tag or digest is refused. Returns the ref unchanged when no entry
152
+ * covers it (the caller decides whether that is an error: `complete`).
153
+ */
154
+ export function mirrorImageRef(imageRef, mirror) {
155
+ if (typeof imageRef !== 'string' || !REF_NAME.test(imageRef)) {
156
+ throw new ImageRegistryMirrorError(`image ref ${JSON.stringify(imageRef)} is not a bare <host>/<path> name (a tag or digest cannot be rewritten)`);
157
+ }
158
+ const partial = mirror.map.find((entry) => imageRef.startsWith(entry.source) && !isSegmentPrefix(entry.source, imageRef));
159
+ if (partial !== undefined) {
160
+ throw new ImageRegistryMirrorError(
161
+ `${MIRROR_KEY} source ${partial.source} matches ${imageRef} only in part of a path segment; prefixes match whole segments`,
162
+ );
163
+ }
164
+ const matches = mirror.map.filter((entry) => isSegmentPrefix(entry.source, imageRef) && entry.source !== imageRef);
165
+ if (matches.length === 0) {return { imageRef, covered: false };}
166
+ const [{ source, target }] = matches;
167
+ const rest = segmentsOf(imageRef).slice(segmentsOf(source).length).join('/');
168
+ return { imageRef: `${target}/${rest}`, covered: true };
169
+ }
170
+
171
+ /** Re-home the vendor image rows; `complete: true` refuses a row no entry covers. */
172
+ export function mirrorVendorSubjects(subjects, mirror) {
173
+ if (mirror === undefined) {return subjects;}
174
+ return subjects.map((subject) => {
175
+ const { imageRef, covered } = mirrorImageRef(subject.imageRef, mirror);
176
+ if (!covered && mirror.complete) {
177
+ throw new ImageRegistryMirrorError(
178
+ `${MIRROR_KEY} is complete:true but no map entry covers ${subject.kind}:${subject.id} (${subject.imageRef})`,
179
+ );
180
+ }
181
+ return { ...subject, imageRef };
182
+ });
183
+ }
@@ -0,0 +1,7 @@
1
+ // The flags that make a resolve a CUSTOMER resolve (against a catalogue).
2
+ export const DEFINITION_FLAG = '--definition';
3
+ export const CATALOGUE_FLAG = '--catalogue';
4
+ export const CHANNEL_FLAG = '--channel';
5
+ export const SOURCE_IMAGE_FLAG = '--source-image';
6
+ export const CATALOGUE_USERNAME_FLAG = '--catalogue-username';
7
+ export const CATALOGUE_TOKEN_ENV_FLAG = '--catalogue-token-env';
@@ -0,0 +1,99 @@
1
+ // develop-customer-edition — a DEVELOP customer edition: a customer definition
2
+ // resolved against a catalogue, frozen at the customer's OWN repository pins.
3
+ //
4
+ // The pins file holds one `sources/<name>` commit per source the definition
5
+ // declares and nothing else (the vendor repositories are the catalogue's, not
6
+ // the customer's). The digest of the catalogue artifact the edition was
7
+ // resolved against is frozen into the descriptor's provenance pins as
8
+ // `catalogue`; the snapshot hash (the artifact tag) covers it. The vendor image
9
+ // rows arrive sealed from the catalogue; only the customer's own image rows are
10
+ // sealed against the develop image repositories.
11
+
12
+ import { sealLock } from './biome-content.mjs';
13
+ import { sealDevelopImages } from './develop-seal.mjs';
14
+ import { CATALOGUE_PIN_KEY, EditionChannel } from './edition-constants.mjs';
15
+ import { loadKernelDistributionSchemas } from './lib.mjs';
16
+ import { snapshotPinNamespace, SnapshotPinNamespace, sortedSnapshotPins, sourcePinKey } from './pin-source.mjs';
17
+ import { resolveCustomerEdition } from './resolve-customer-edition.mjs';
18
+ import { externalSourceTrees, sourceTreeLocator } from './resolve-images.mjs';
19
+
20
+ const SHA_PATTERN = /^[0-9a-f]{40}$/;
21
+
22
+ export class CustomerDevelopPinsError extends Error {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = 'CustomerDevelopPinsError';
26
+ }
27
+ }
28
+
29
+ /**
30
+ * The pins of a develop customer edition: `sources/<name>` commits for exactly
31
+ * the definition's declared sources, plus `catalogue` = the digest of the
32
+ * catalogue artifact. A `catalogue` entry in the file must equal that digest.
33
+ */
34
+ export function customerDevelopPins({ pins, declaredSourceNames, catalogueDigest }) {
35
+ if (pins === null || typeof pins !== 'object' || Array.isArray(pins)) {
36
+ throw new CustomerDevelopPinsError('--pins must be an object mapping sources/<name> -> 40-hex commit');
37
+ }
38
+ const own = {};
39
+ for (const [key, value] of Object.entries(pins)) {
40
+ if (key === CATALOGUE_PIN_KEY) {
41
+ if (value !== catalogueDigest) {
42
+ throw new CustomerDevelopPinsError(`--pins "${CATALOGUE_PIN_KEY}" is ${JSON.stringify(value)}, but --catalogue is ${catalogueDigest}`);
43
+ }
44
+ continue;
45
+ }
46
+ if (snapshotPinNamespace(key) !== SnapshotPinNamespace.Source) {
47
+ throw new CustomerDevelopPinsError(`--pins "${key}" is not one of the customer's own repositories: a customer develop pins only sources/<name> (the vendor's pins are the catalogue's)`);
48
+ }
49
+ if (typeof value !== 'string' || !SHA_PATTERN.test(value)) {
50
+ throw new CustomerDevelopPinsError(`--pins "${key}" is not a 40-hex commit SHA: ${JSON.stringify(value)}`);
51
+ }
52
+ own[key] = value;
53
+ }
54
+ const declared = new Set(declaredSourceNames.map(sourcePinKey));
55
+ for (const key of declared) {
56
+ if (!(key in own)) {throw new CustomerDevelopPinsError(`--pins is missing "${key}": the definition declares that source`);}
57
+ }
58
+ for (const key of Object.keys(own)) {
59
+ if (!declared.has(key)) {throw new CustomerDevelopPinsError(`--pins names "${key}", but the definition declares no such source`);}
60
+ }
61
+ return sortedSnapshotPins({ ...own, [CATALOGUE_PIN_KEY]: catalogueDigest });
62
+ }
63
+
64
+ /** The source names a raw definition declares (the pins are checked before the definition is composed). */
65
+ export function declaredSourceNamesOf(rawDefinition) {
66
+ const sources = rawDefinition?.sources;
67
+ return Array.isArray(sources) ? sources.map((source) => source?.name) : [];
68
+ }
69
+
70
+ /** Resolve and seal the develop customer edition; returns the four locks and the frozen provenance pins. */
71
+ export async function resolveCustomerDevelopEdition({ rawDefinition, catalogue, catalogueDigest, pins, sourceImages, env = process.env, sealOptions = {}, verifyAuthority, materialize }) {
72
+ const frozen = customerDevelopPins({ pins, declaredSourceNames: declaredSourceNamesOf(rawDefinition), catalogueDigest });
73
+ const resolved = await resolveCustomerEdition({
74
+ rawDefinition,
75
+ catalogue,
76
+ channel: EditionChannel.Develop,
77
+ sourceImages,
78
+ pins: frozen,
79
+ catalogueDigest,
80
+ ...(verifyAuthority === undefined ? {} : { verifyAuthority }),
81
+ ...(materialize === undefined ? {} : { materialize }),
82
+ sealOwn: (imageLock) => sealDevelopImages({ imageLock, env, ...sealOptions }),
83
+ });
84
+ // The customer edition is sealed here: every biomeContentHash is recomputed through the
85
+ // same code as the vendor's, vendor biomes on the CATALOGUE's authority (biome-content.mjs).
86
+ const schemas = await loadKernelDistributionSchemas();
87
+ const distributionLock = await sealLock({
88
+ schemas,
89
+ unsealed: resolved.distributionLock,
90
+ imageLock: resolved.imageLock,
91
+ locate: sourceTreeLocator(externalSourceTrees(resolved.distributionLock)),
92
+ vendoredCommit: (repo) => {
93
+ throw new Error(`customer biome source "${repo}" is first-party to no checkout; vendor biomes are taken from the catalogue`);
94
+ },
95
+ catalogue,
96
+ catalogueDigest,
97
+ });
98
+ return { ...resolved, distributionLock, pins: frozen };
99
+ }
@@ -0,0 +1,65 @@
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, over the snapshot's
4
+ // injected inputs: distribution lock -> authorization base (composed, never
5
+ // written) -> images lock -> develop seal, plus the chart-set lock: the frozen
6
+ // xema-deploy charts' registry manifest digests, in the release roster shape. Nothing here writes to the editions
7
+ // trees; the caller writes the artifact (edition-artifact.mjs).
8
+
9
+ import { biomeSourceTreeIds, sealLock } from './biome-content.mjs';
10
+ import { buildCatalogue } from './catalogue.mjs';
11
+ import { sealDevelopImages } from './develop-seal.mjs';
12
+ import { assertFrozenCheckouts, buildSnapshotInputs } from './develop-snapshot-inputs.mjs';
13
+ import { EditionChannel } from './edition-constants.mjs';
14
+ import { deployRoot } from './edition-workspace.mjs';
15
+ import { loadKernelDistributionSchemas } from './lib.mjs';
16
+ import { deployPinKey } from './pin-source.mjs';
17
+ import { resolveDevelopChartSet } from './resolve-chart-set.mjs';
18
+ import { composeAuthorizationBase, resolveDistributionWithIndex } from './resolve-distribution.mjs';
19
+ import { externalSourceTrees, resolveImages, sourceTreeLocator } from './resolve-images.mjs';
20
+ import { createPinnedSourceAuthority } from './source-authority.mjs';
21
+
22
+ export async function resolveDevelopEdition({ id, pins, env = process.env, sealOptions = {}, chartDigestLookup }) {
23
+ const schemas = await loadKernelDistributionSchemas();
24
+ assertFrozenCheckouts(pins);
25
+ const inputs = await buildSnapshotInputs({ id, pins, schemas });
26
+ const { lock, index } = await resolveDistributionWithIndex(id, { inputs });
27
+ const authorizationLock = await composeAuthorizationBase(id, lock, index);
28
+ const { imageLock } = await resolveImages(id, { inputs, lock });
29
+ const sealedImageLock = await sealDevelopImages({ imageLock, env, ...sealOptions });
30
+ // The unsealed lock becomes the one v5 grammar only here, once the images it
31
+ // owns are sealed: each biomeContentHash is recomputed from the frozen sources.
32
+ const authority = createPinnedSourceAuthority({ pins: inputs.runtimeImageSourceIndex.repositoryPins });
33
+ const sealedLock = await sealLock({
34
+ schemas,
35
+ unsealed: lock,
36
+ imageLock: sealedImageLock,
37
+ locate: sourceTreeLocator(externalSourceTrees(lock)),
38
+ vendoredCommit: (repo) => authority.vendoredSourceCommitForRepository(repo),
39
+ });
40
+ const chartSetLock = await resolveDevelopChartSet({
41
+ xemaDeployDir: deployRoot(),
42
+ xemaDeploySha: pins[deployPinKey()],
43
+ lookup: chartDigestLookup,
44
+ });
45
+ const sourceTreeIds = await biomeSourceTreeIds({
46
+ schemas,
47
+ lock,
48
+ imageLock: sealedImageLock,
49
+ locate: sourceTreeLocator(externalSourceTrees(lock)),
50
+ vendoredCommit: (repo) => authority.vendoredSourceCommitForRepository(repo),
51
+ });
52
+ const catalogue = buildCatalogue({
53
+ sourceTreeIds,
54
+ offeringId: id,
55
+ channel: EditionChannel.Develop,
56
+ distributionLock: lock,
57
+ imageLock: sealedImageLock,
58
+ chartSetLock,
59
+ biomeIndex: inputs.biomeIndex,
60
+ platformServiceIndex: inputs.platformServiceIndex,
61
+ contractAuthority: inputs.runtimeImageSourceIndex.contractAuthority,
62
+ leaf: inputs.composed.leaf,
63
+ });
64
+ return { distributionLock: sealedLock, imageLock: sealedImageLock, authorizationLock, chartSetLock, catalogue };
65
+ }
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env node
2
+ // develop-edition — the develop edition's command line.
3
+ //
4
+ // resolve <id> --pins <file> --out <dir> --registry <namespace>
5
+ // --engine-image <repository>@sha256:<digest> [--local]
6
+ // resolve the edition from the snapshot, seal it against the develop image
7
+ // repositories, write the descriptor and the four locks to <dir>, print the target.
8
+ // The workflow run is recorded from GITHUB_REPOSITORY / GITHUB_RUN_ID;
9
+ // --local stamps `local` instead, for a resolve outside GitHub Actions.
10
+ // resolve --definition <file> --catalogue oci://<repo>@sha256:<digest> --pins <file> --out <dir> --registry <namespace>
11
+ // --engine-image <repository>@sha256:<digest> [--source-image <source>=<registry>/<prefix>]...
12
+ // [--catalogue-username <name> --catalogue-token-env <VAR>] [--local]
13
+ // a CUSTOMER develop edition: the definition resolved against the catalogue (see resolve-customer-edition.mjs),
14
+ // frozen at the customer's own pins. --pins holds exactly one `sources/<name>` commit per source the definition
15
+ // declares; the catalogue digest is frozen into the descriptor's provenance pins as `catalogue`. The id is the
16
+ // definition's. Takes --data-root, --editions-root, --sources-root, --kernel, --cache-dir (no --deploy-root).
17
+ // target <id> --pins <file> --registry <namespace>
18
+ // print <namespace>/xema-edition-<id>:<snapshot hash>. The namespace is always the caller's
19
+ // (e.g. registry.example.com/acme); the repository prefix comes from
20
+ // edition-contract.json, and no namespace is implied.
21
+ // freeze-graph --data-sha <sha> --kernel-sha <sha>
22
+ // --deploy-sha <sha> [--editions-root-url <https url>]
23
+ // [--overrides <json>] --out <file>
24
+ // resolve refs/heads/develop once per graph repository (and the editions
25
+ // root) from this checkout's runtime-image-source-index.json; the three checked-out
26
+ // repositories are pinned at what the workflow checked out; write the pins.
27
+ // check-existing <id> --pins <file> --artifact <dir> --engine-image <repository>@sha256:<digest>
28
+ // refuse to reuse a pulled artifact whose descriptor disagrees with this snapshot.
29
+ // check-overrides --overrides <json>
30
+ // refuse a pins override that would select the tooling this run executes
31
+ // (the edition tooling's own repository); the workflow runs it before any other checkout.
32
+ // freeze-sources <id> --pins <file> [--overrides <json>] --out <file>
33
+ // add one pin per source the edition's chain declares (run after the editions
34
+ // root is checked out and passed as --editions-root); write the final pins.
35
+ //
36
+ // Tokens come from the environment (EVIDENCE_REPOSITORIES_TOKEN, XEMA_GHCR_PULL_TOKEN),
37
+ // never argv. Nothing here writes git or publishes a release edition.
38
+
39
+ import { writeFileSync } from 'node:fs';
40
+ import { resolve } from 'node:path';
41
+
42
+ import { fetchCatalogue } from './catalogue-fetch.mjs';
43
+ import { writeCatalogue } from './catalogue.mjs';
44
+ import { CATALOGUE_FLAG, CATALOGUE_TOKEN_ENV_FLAG, CATALOGUE_USERNAME_FLAG, DEFINITION_FLAG, SOURCE_IMAGE_FLAG } from './customer-flags.mjs';
45
+ import { resolveCustomerDevelopEdition } from './develop-customer-edition.mjs';
46
+ import { resolveDevelopEdition } from './develop-edition-resolve.mjs';
47
+ import { assertNoToolingOverride, freezeGraph, freezeSources, lsRemoteDevelopHead } from './develop-freeze.mjs';
48
+ import { assertCanonicalDistributionId } from './distribution-paths.mjs';
49
+ import { LOCAL_WORKFLOW, assertEngineImage, assertExistingArtifactMatches, developEditionTarget, workflowFromEnv, writeEditionArtifact } from './edition-artifact.mjs';
50
+ import { editionWorkspaceConfig } from './edition-config.mjs';
51
+ import { EditionChannel } from './edition-constants.mjs';
52
+ import { WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule, runtimeImageSourceIndexPath } from './edition-workspace.mjs';
53
+ import { descriptorEvidenceTrust } from './evidence-trust-policy.mjs';
54
+ import { loadKernelDistributionSchemas, serializeJson } from './lib.mjs';
55
+ import { snapshotPinSource } from './pin-source.mjs';
56
+ import { parseSourceImages } from './resolve-customer-edition.mjs';
57
+ import { composeEdition } from './resolve-distribution.mjs';
58
+ import { readStrictJsonFile } from './strict-json.mjs';
59
+
60
+ if (isEntryModule(import.meta.url)) {
61
+ configureEditionWorkspaceFromProcess(
62
+ process.argv.includes(DEFINITION_FLAG)
63
+ ? [WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.Cache, WorkspaceRoot.SourceHash]
64
+ : [WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.Deploy, WorkspaceRoot.Cache, WorkspaceRoot.SourceHash],
65
+ );
66
+ }
67
+
68
+ // The flags only a customer develop resolve takes, and the one of them that repeats.
69
+ const CUSTOMER_VALUES = Object.freeze([DEFINITION_FLAG, CATALOGUE_FLAG, CATALOGUE_USERNAME_FLAG, CATALOGUE_TOKEN_ENV_FLAG, SOURCE_IMAGE_FLAG]);
70
+ const CUSTOMER_REQUIRED = Object.freeze([DEFINITION_FLAG, CATALOGUE_FLAG]);
71
+ const REPEATABLE = Object.freeze([SOURCE_IMAGE_FLAG]);
72
+
73
+ const COMMANDS = Object.freeze({
74
+ resolve: { positional: true, values: ['--pins', '--out', '--registry', '--engine-image', ...CUSTOMER_VALUES], switches: ['--local'] },
75
+ target: { positional: true, values: ['--pins', '--registry'], switches: [] },
76
+ 'freeze-graph': {
77
+ positional: false,
78
+ values: ['--data-sha', '--kernel-sha', '--deploy-sha', '--editions-root-url', '--overrides', '--out'],
79
+ switches: [],
80
+ },
81
+ 'freeze-sources': { positional: true, values: ['--pins', '--overrides', '--out'], switches: [] },
82
+ 'check-overrides': { positional: false, values: ['--overrides'], switches: [] },
83
+ 'check-existing': { positional: true, values: ['--pins', '--artifact', '--engine-image'], switches: [] },
84
+ });
85
+ const REQUIRED = Object.freeze({
86
+ resolve: ['--pins', '--out', '--registry', '--engine-image'],
87
+ target: ['--pins', '--registry'],
88
+ 'freeze-graph': ['--data-sha', '--kernel-sha', '--deploy-sha', '--out'],
89
+ 'freeze-sources': ['--pins', '--out'],
90
+ 'check-overrides': ['--overrides'],
91
+ 'check-existing': ['--pins', '--artifact', '--engine-image'],
92
+ });
93
+
94
+ /** Parse `<command> [<id>] --flag value ...` against the closed vocabulary of the command. */
95
+ export function parseDevelopEditionArgs(argv) {
96
+ const [command, ...rest] = argv;
97
+ const spec = COMMANDS[command];
98
+ if (spec === undefined) {
99
+ throw new Error(`unknown command ${JSON.stringify(command)}; expected ${Object.keys(COMMANDS).join(' | ')}`);
100
+ }
101
+ const options = {};
102
+ const positional = [];
103
+ for (let i = 0; i < rest.length; i += 1) {
104
+ const arg = rest[i];
105
+ if (spec.switches.includes(arg)) {
106
+ options[arg] = true;
107
+ } else if (spec.values.includes(arg)) {
108
+ const value = rest[i + 1];
109
+ if (value === undefined || (value.startsWith('--') && value.length > 2)) {
110
+ throw new Error(`${arg} requires a value`);
111
+ }
112
+ if (REPEATABLE.includes(arg)) {
113
+ options[arg] = [...(options[arg] ?? []), value];
114
+ } else if (options[arg] !== undefined) {
115
+ throw new Error(`${arg} given twice`);
116
+ } else {
117
+ options[arg] = value;
118
+ }
119
+ i += 1;
120
+ } else if (arg.startsWith('--')) {
121
+ throw new Error(`unknown argument ${arg} for ${command}`);
122
+ } else {
123
+ positional.push(arg);
124
+ }
125
+ }
126
+ const customer = command === 'resolve' && CUSTOMER_VALUES.some((flag) => options[flag] !== undefined);
127
+ if (customer) {
128
+ for (const required of CUSTOMER_REQUIRED) {
129
+ if (options[required] === undefined) {throw new Error(`a customer develop resolve requires ${required}`);}
130
+ }
131
+ if (positional.length !== 0) {throw new Error('a customer develop resolve takes no <distribution-id>: the id is the definition\'s');}
132
+ } else if (spec.positional ? positional.length !== 1 : positional.length !== 0) {
133
+ throw new Error(`${command} takes ${spec.positional ? 'exactly one <distribution-id>' : 'no positional argument'}`);
134
+ }
135
+ for (const required of REQUIRED[command]) {
136
+ if (options[required] === undefined) {throw new Error(`${command} requires ${required}`);}
137
+ }
138
+ if (spec.positional && !customer) {assertCanonicalDistributionId(positional[0]);}
139
+ if (options['--engine-image'] !== undefined) {assertEngineImage(options['--engine-image']);}
140
+ return { command, id: positional[0], options };
141
+ }
142
+
143
+ function parseOverrides(text) {
144
+ if (text === undefined || text.trim() === '') {return {};}
145
+ const parsed = JSON.parse(text);
146
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
147
+ throw new Error('--overrides must be a JSON object mapping pin key -> 40-hex commit');
148
+ }
149
+ return parsed;
150
+ }
151
+
152
+ function writeOnce(path, value) {
153
+ writeFileSync(path, serializeJson(value), { flag: 'wx' });
154
+ }
155
+
156
+ function headResolver(env) {
157
+ const token = env.EVIDENCE_REPOSITORIES_TOKEN;
158
+ const username = editionWorkspaceConfig().sourceAuthUsername;
159
+ return (url) => lsRemoteDevelopHead(url, { username, token });
160
+ }
161
+
162
+ /** A customer develop resolve: the artifact (descriptor and four locks, no catalogue: it is not an offering) goes to --out. */
163
+ async function resolveCustomer(options, env) {
164
+ const { catalogue, digest } = await fetchCatalogue({
165
+ ref: options[CATALOGUE_FLAG],
166
+ username: options[CATALOGUE_USERNAME_FLAG],
167
+ tokenEnv: options[CATALOGUE_TOKEN_ENV_FLAG],
168
+ env,
169
+ });
170
+ const rawDefinition = readStrictJsonFile(options[DEFINITION_FLAG]);
171
+ const evidenceTrustPolicy = await descriptorEvidenceTrust({ channel: EditionChannel.Develop });
172
+ const { id, pins, ...resolved } = await resolveCustomerDevelopEdition({
173
+ rawDefinition,
174
+ catalogue,
175
+ catalogueDigest: digest,
176
+ pins: readStrictJsonFile(options['--pins']),
177
+ sourceImages: parseSourceImages(options[SOURCE_IMAGE_FLAG] ?? []),
178
+ env,
179
+ });
180
+ const target = developEditionTarget(id, pins, { registry: options['--registry'] });
181
+ writeEditionArtifact({
182
+ outDir: resolve(options['--out']),
183
+ channel: EditionChannel.Develop,
184
+ distributionId: id,
185
+ pins,
186
+ workflow: options['--local'] ? LOCAL_WORKFLOW : workflowFromEnv(env),
187
+ evidenceTrustPolicy,
188
+ engineImage: options['--engine-image'],
189
+ distributionLock: resolved.distributionLock,
190
+ imageLock: resolved.imageLock,
191
+ authorizationLock: resolved.authorizationLock,
192
+ chartSetLock: resolved.chartSetLock,
193
+ });
194
+ console.log(target);
195
+ }
196
+
197
+ async function run(argv, env) {
198
+ const { command, id, options } = parseDevelopEditionArgs(argv);
199
+ switch (command) {
200
+ case 'target': {
201
+ console.log(developEditionTarget(id, readStrictJsonFile(options['--pins']), { registry: options['--registry'] }));
202
+ return;
203
+ }
204
+ case 'resolve': {
205
+ if (options[DEFINITION_FLAG] !== undefined) {return resolveCustomer(options, env);}
206
+ const pins = readStrictJsonFile(options['--pins']);
207
+ const workflow = options['--local'] ? LOCAL_WORKFLOW : workflowFromEnv(env);
208
+ const target = developEditionTarget(id, pins, { registry: options['--registry'] });
209
+ const evidenceTrustPolicy = await descriptorEvidenceTrust({ channel: EditionChannel.Develop });
210
+ const { catalogue, ...resolved } = await resolveDevelopEdition({ id, pins, env });
211
+ const outDir = resolve(options['--out']);
212
+ writeEditionArtifact({
213
+ outDir,
214
+ channel: EditionChannel.Develop,
215
+ distributionId: id,
216
+ pins,
217
+ workflow,
218
+ evidenceTrustPolicy,
219
+ engineImage: options['--engine-image'],
220
+ ...resolved,
221
+ });
222
+ writeCatalogue(outDir, catalogue);
223
+ console.log(target);
224
+ return;
225
+ }
226
+ case 'check-existing': {
227
+ assertExistingArtifactMatches({
228
+ artifactDir: resolve(options['--artifact']),
229
+ distributionId: id,
230
+ channel: EditionChannel.Develop,
231
+ evidenceTrustPolicy: await descriptorEvidenceTrust({ channel: EditionChannel.Develop }),
232
+ pins: readStrictJsonFile(options['--pins']),
233
+ engineImage: options['--engine-image'],
234
+ });
235
+ return;
236
+ }
237
+ case 'check-overrides': {
238
+ assertNoToolingOverride(parseOverrides(options['--overrides']));
239
+ return;
240
+ }
241
+ case 'freeze-graph': {
242
+ const pins = freezeGraph({
243
+ runtimeSourceIndex: readStrictJsonFile(runtimeImageSourceIndexPath()),
244
+ dataSha: options['--data-sha'],
245
+ kernelSha: options['--kernel-sha'],
246
+ deploySha: options['--deploy-sha'],
247
+ editionsRootUrl: options['--editions-root-url'],
248
+ overrides: parseOverrides(options['--overrides']),
249
+ resolveHead: headResolver(env),
250
+ });
251
+ writeOnce(options['--out'], pins);
252
+ return;
253
+ }
254
+ case 'freeze-sources': {
255
+ const pins = readStrictJsonFile(options['--pins']);
256
+ const { parseDistribution } = await loadKernelDistributionSchemas();
257
+ const declaredSources = composeEdition(parseDistribution, id, snapshotPinSource(pins)).declaredSources;
258
+ writeOnce(
259
+ options['--out'],
260
+ freezeSources({ pins, declaredSources, overrides: parseOverrides(options['--overrides']), resolveHead: headResolver(env) }),
261
+ );
262
+ return;
263
+ }
264
+ default:
265
+ throw new Error(`unhandled command ${command}`);
266
+ }
267
+ }
268
+
269
+ if (isEntryModule(import.meta.url)) {
270
+ run(process.argv.slice(2), process.env).catch((error) => {
271
+ console.error(`[develop-edition] ${error.message}`);
272
+ process.exit(1);
273
+ });
274
+ }