@xemahq/xema 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -389
- package/dist/commands/edition.js +3 -3
- package/dist/commands/edition.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +8715 -807
- package/dist/edition/verify-image-evidence.bundle.provenance.json +873 -405
- package/edition/README.md +53 -273
- package/edition/adapter-bundled-versions.mjs +107 -0
- package/edition/adapter-host-versions.mjs +41 -0
- package/edition/authorization-base.mjs +18 -9
- package/edition/build-runtime-image-source-index.mjs +77 -4
- package/edition/catalogue.mjs +15 -12
- package/edition/customer-definition.mjs +3 -20
- package/edition/develop-customer-edition.mjs +2 -2
- package/edition/develop-edition-resolve.mjs +10 -2
- package/edition/develop-edition.mjs +56 -62
- package/edition/develop-freeze.mjs +14 -3
- package/edition/dist-check.mjs +1 -0
- package/edition/distribution-evidence-set.mjs +32 -5
- package/edition/edition-channel.mjs +15 -1
- package/edition/edition-locks.mjs +16 -6
- package/edition/edition-protocol.mjs +30 -8
- package/edition/edition-workspace.mjs +8 -0
- package/edition/image-evidence.mjs +133 -25
- package/edition/kernel-package.mjs +193 -0
- package/edition/lib.mjs +73 -1
- package/edition/oci-evidence-verbs.mjs +2 -2
- package/edition/oras.mjs +32 -10
- package/edition/plan-image-evidence-refresh.mjs +2 -2
- package/edition/produce-distribution-completeness-evidence.mjs +5 -0
- package/edition/produce-image-delivery-entitlement-evidence.mjs +11 -1
- package/edition/release-catalogue.mjs +10 -3
- package/edition/resolve-chart-set.mjs +1 -1
- package/edition/resolve-customer-edition.mjs +29 -33
- package/edition/resolve-distribution.mjs +5 -1
- package/edition/resolve-edition.mjs +3 -2
- package/edition/seal-image-digests.mjs +16 -4
- package/edition/trust-policy-arming-verb.mjs +2 -10
- package/edition/verify-image-evidence.mjs +1 -0
- package/examples/README.md +3 -2
- package/package.json +3 -3
|
@@ -18,6 +18,7 @@ import { mkdirSync, writeFileSync } from 'node:fs';
|
|
|
18
18
|
import { join } from 'node:path';
|
|
19
19
|
import { isDeepStrictEqual } from 'node:util';
|
|
20
20
|
|
|
21
|
+
import { adapterHostRow, assertAdapterBundlesMatchHost, fetchRegistryManifest } from './adapter-bundled-versions.mjs';
|
|
21
22
|
import { AUTHORIZATION_LOCK_FILENAME, writeAuthorizationLock } from './authorization-base.mjs';
|
|
22
23
|
import { CATALOGUE_FILE, CatalogueError } from './catalogue.mjs';
|
|
23
24
|
import {
|
|
@@ -29,8 +30,8 @@ import {
|
|
|
29
30
|
import { assertRosteredChartSetLock } from './edition-chart-set.mjs';
|
|
30
31
|
import { EditionLockFile } from './edition-constants.mjs';
|
|
31
32
|
import { editionsRoot } from './edition-workspace.mjs';
|
|
32
|
-
import {
|
|
33
|
-
import { compareStrings,
|
|
33
|
+
import { adoptCatalogueKernel } from './kernel-package.mjs';
|
|
34
|
+
import { compareStrings, serializeJson, validateImmutableImageLock } from './lib.mjs';
|
|
34
35
|
import { pinnedSource, snapshotPinSource } from './pin-source.mjs';
|
|
35
36
|
import { composeAuthorizationBase, composeManifest, resolveDistributionWithIndex, UnknownSelectionError } from './resolve-distribution.mjs';
|
|
36
37
|
import { resolveImageRows } from './resolve-images.mjs';
|
|
@@ -63,11 +64,22 @@ function catalogueCeiling(catalogue, schemas) {
|
|
|
63
64
|
return { trustPolicy: read('trustPolicy', schemas.DistributionTrustPolicySchema), trustCeiling: read('trustCeiling', schemas.ProcessTrustPolicySchema) };
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
/** A catalogue whose ceiling states no trust-domain assignments reserves no domain: the kernel's required ceiling argument is the empty policy. */
|
|
68
|
+
const NO_CATALOGUE_TRUST_CEILING = Object.freeze({ assignments: [] });
|
|
69
|
+
|
|
70
|
+
/** The kernel's `customerLockCeilingIssues` is the one check of a customer lock against the catalogue's ceiling. */
|
|
71
|
+
function assertLockWithinCatalogueCeiling({ catalogue, definition, lock, schemas }) {
|
|
72
|
+
const issues = schemas.customerLockCeilingIssues(catalogueCeiling(catalogue, schemas).trustCeiling, lock.biomes);
|
|
73
|
+
if (issues.length > 0) {
|
|
74
|
+
throw new CatalogueCeilingError(`catalogue "${catalogue.offeringId}" (${catalogue.channel}): "${definition.id}" is outside the catalogue's trustCeiling: ${issues.join('; ')}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
/**
|
|
67
79
|
* The ceiling policy the customer's lock is written under: the catalogue's assignments, then
|
|
68
80
|
* the definition's for what the catalogue leaves open. An assignment of the definition that
|
|
69
81
|
* names a domain the catalogue assigns, or a biome the catalogue places, is not carried: the
|
|
70
|
-
* catalogue's reading stands, and `
|
|
82
|
+
* catalogue's reading stands, and the kernel's `customerLockCeilingIssues` refuses the difference in the lock.
|
|
71
83
|
*/
|
|
72
84
|
function ceilingUnderCatalogue(catalogueTrustCeiling, definitionTrustCeiling) {
|
|
73
85
|
if (catalogueTrustCeiling === undefined) {return definitionTrustCeiling;}
|
|
@@ -80,25 +92,6 @@ function ceilingUnderCatalogue(catalogueTrustCeiling, definitionTrustCeiling) {
|
|
|
80
92
|
return { assignments: [...catalogueTrustCeiling.assignments, ...open] };
|
|
81
93
|
}
|
|
82
94
|
|
|
83
|
-
/**
|
|
84
|
-
* Every customer-source biome sits in the domain its definition names, and that is the domain
|
|
85
|
-
* the catalogue's ceiling admits for it: the lock row carries the kernel's derivation
|
|
86
|
-
* (`deriveTrustCeiling`) under `ceilingUnderCatalogue`, so a definition that reaches into a
|
|
87
|
-
* domain the catalogue assigns, or moves a biome the catalogue places, differs from it here.
|
|
88
|
-
*/
|
|
89
|
-
function assertWithinCatalogueCeiling({ catalogue, definition, lock, schemas }) {
|
|
90
|
-
const stated = new Map((definition.trustCeiling?.assignments ?? []).flatMap((assignment) => assignment.biomes.map((biome) => [biome, assignment.domain])));
|
|
91
|
-
for (const biome of lock.biomes.filter((candidate) => candidate.source !== undefined)) {
|
|
92
|
-
const admitted = biome.trustCeiling ?? schemas.defaultTrustDomain(biome.id, biome.origin);
|
|
93
|
-
if (stated.get(biome.id) !== admitted) {
|
|
94
|
-
throw new CatalogueCeilingError(
|
|
95
|
-
`catalogue "${catalogue.offeringId}" (${catalogue.channel}): "${definition.id}" assigns biome "${biome.id}" to trust domain ` +
|
|
96
|
-
`"${stated.get(biome.id)}", and the catalogue's trustCeiling admits "${admitted}" for it`,
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
95
|
/** `{name -> {imageRegistry, imageNamePrefix}}` from repeated `<source>=<registry>/<prefix>` values; the prefix is the text after the last "/". */
|
|
103
96
|
export function parseSourceImages(values) {
|
|
104
97
|
const byName = new Map();
|
|
@@ -247,8 +240,10 @@ export async function resolveCustomerImages({ id, catalogue, lock, composed, sch
|
|
|
247
240
|
if (ownBiomes.length > 0) {
|
|
248
241
|
const ownLock = { ...lock, biomes: ownBiomes, platformServices: [] };
|
|
249
242
|
const runtimeImageSourceIndex = {
|
|
243
|
+
schemaVersion: 2,
|
|
250
244
|
contractAuthority: catalogue.contractAuthority,
|
|
251
245
|
repositoryPins: [],
|
|
246
|
+
producerWorkflowPins: [],
|
|
252
247
|
platformServiceBindings: [],
|
|
253
248
|
supplementalSubjects: [],
|
|
254
249
|
crossRepoBuildInputs: [],
|
|
@@ -278,14 +273,9 @@ export async function resolveCustomerImages({ id, catalogue, lock, composed, sch
|
|
|
278
273
|
return merged;
|
|
279
274
|
}
|
|
280
275
|
|
|
281
|
-
/** The kernel at --kernel must carry the contract the catalogue's offering was resolved under. */
|
|
282
|
-
export async function assertKernelAtCatalogueAuthority(catalogue, schemas) {
|
|
283
|
-
await validateKernelContractAuthorityFromGit({ authority: catalogue.contractAuthority, schemas, requireHeadMatch: false });
|
|
284
|
-
}
|
|
285
|
-
|
|
286
276
|
/**
|
|
287
277
|
* The distribution and authorization locks of the customer edition `rawDefinition`
|
|
288
|
-
* against `catalogue`. `catalogueDigest` is the digest of `catalogue`, written into the lock. `
|
|
278
|
+
* against `catalogue`. `catalogueDigest` is the digest of `catalogue`, written into the lock. `adoptKernel` (the catalogue's kernel from npm, proved its contract authority; returns the kernel schemas) and `materialize` are injectable for the test suites.
|
|
289
279
|
*/
|
|
290
280
|
export async function resolveCustomerDistribution({
|
|
291
281
|
rawDefinition,
|
|
@@ -294,10 +284,11 @@ export async function resolveCustomerDistribution({
|
|
|
294
284
|
sourceImages,
|
|
295
285
|
pins,
|
|
296
286
|
materialize,
|
|
297
|
-
|
|
287
|
+
adoptKernel = adoptCatalogueKernel,
|
|
288
|
+
fetchManifest = fetchRegistryManifest,
|
|
298
289
|
}) {
|
|
299
290
|
if (catalogueDigest === undefined) {throw new CatalogueError('a customer resolve names the digest of its catalogue');}
|
|
300
|
-
const schemas = await
|
|
291
|
+
const schemas = await adoptKernel(catalogue);
|
|
301
292
|
const { definition, mirror } = parseCustomerDefinition(schemas, rawDefinition);
|
|
302
293
|
const composed = composeCustomerEdition({ schemas, definition, mirror, catalogue, sourceImages, pins });
|
|
303
294
|
const id = composed.leaf.id;
|
|
@@ -312,9 +303,14 @@ export async function resolveCustomerDistribution({
|
|
|
312
303
|
throw error;
|
|
313
304
|
}
|
|
314
305
|
const { lock, index } = resolved;
|
|
315
|
-
assertTrustCeilingAssignsCustomerBiomes(
|
|
316
|
-
|
|
317
|
-
|
|
306
|
+
assertTrustCeilingAssignsCustomerBiomes(
|
|
307
|
+
schemas,
|
|
308
|
+
definition,
|
|
309
|
+
lock.biomes.filter((biome) => biome.source !== undefined).map((biome) => biome.id),
|
|
310
|
+
catalogueCeiling(catalogue, schemas).trustCeiling ?? NO_CATALOGUE_TRUST_CEILING,
|
|
311
|
+
);
|
|
312
|
+
assertLockWithinCatalogueCeiling({ catalogue, definition, lock, schemas });
|
|
313
|
+
await assertAdapterBundlesMatchHost({ lock, hostRow: adapterHostRow(catalogue.biomes), fetchManifest });
|
|
318
314
|
const authorizationLock = await composeAuthorizationBase(id, lock, index);
|
|
319
315
|
// A customer lock names the catalogue it was resolved against; seal and verify refuse any other.
|
|
320
316
|
return { id, schemas, composed, inputs, lock: { ...lock, catalogueDigest }, authorizationLock };
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
64
64
|
import { join } from 'node:path';
|
|
65
65
|
|
|
66
|
+
import { adapterHostRow, assertAdapterBundlesMatchHost, fetchRegistryManifest } from './adapter-bundled-versions.mjs';
|
|
66
67
|
import {
|
|
67
68
|
adoptedFromLock,
|
|
68
69
|
editionAuthorizationModuleInputs,
|
|
@@ -2212,8 +2213,9 @@ export async function resolveDistributionWithIndex(id, { materialize, inputs } =
|
|
|
2212
2213
|
* this exact writer so an in-process refresh cannot drift from the standalone
|
|
2213
2214
|
* command's serialization or diagnostics.
|
|
2214
2215
|
*/
|
|
2215
|
-
export async function writeResolvedDistribution(id, { log = true, materialize } = {}) {
|
|
2216
|
+
export async function writeResolvedDistribution(id, { log = true, materialize, fetchManifest = fetchRegistryManifest } = {}) {
|
|
2216
2217
|
const { lock, index } = await resolveDistributionWithIndex(id, { materialize });
|
|
2218
|
+
await assertAdapterBundlesMatchHost({ lock, hostRow: adapterHostRow(index), fetchManifest });
|
|
2217
2219
|
const outPath = join(editionsRoot(), id, 'distribution.lock.json');
|
|
2218
2220
|
assertReleaseContent(lock, `the distribution lock for "${id}"`);
|
|
2219
2221
|
assertReleaseEditionTree(join(editionsRoot(), id));
|
|
@@ -2285,12 +2287,14 @@ export async function composeAuthorizationBase(id, lock, index) {
|
|
|
2285
2287
|
const kernel = await loadKernelAuthorizationComposer({ kernelPackageDir: kernelPackageDir() });
|
|
2286
2288
|
// Refuses an edition that carries a module under a pin with no composer.
|
|
2287
2289
|
const inputs = editionAuthorizationModuleInputs(lock.biomes, new Map(index.map((e) => [e.id, e])), kernel);
|
|
2290
|
+
const { biomeOriginsOfLock } = await loadKernelDistributionSchemas();
|
|
2288
2291
|
let sealed = null;
|
|
2289
2292
|
if (kernel !== null) {
|
|
2290
2293
|
sealed = sealEditionAuthorizationBase({
|
|
2291
2294
|
distributionId: id,
|
|
2292
2295
|
inputs,
|
|
2293
2296
|
kernel,
|
|
2297
|
+
biomeOrigins: biomeOriginsOfLock(lock),
|
|
2294
2298
|
adopted: adoptedFromLock(readAuthorizationLock(editionDir)),
|
|
2295
2299
|
runTests: fgaComposedModelTestRunner(resolveFgaCli()),
|
|
2296
2300
|
});
|
|
@@ -50,14 +50,15 @@ import { withoutSealedDigests } from './seal-image-digests.mjs';
|
|
|
50
50
|
import { readStrictJsonFile } from './strict-json.mjs';
|
|
51
51
|
|
|
52
52
|
// A customer resolve (`--definition`) has no vendor editions root: `--out` IS its
|
|
53
|
-
// editions root, the directory its `<id>/` locks are written under.
|
|
53
|
+
// editions root, the directory its `<id>/` locks are written under. It takes no kernel root:
|
|
54
|
+
// the kernel is the catalogue's, downloaded from npm into the cache root.
|
|
54
55
|
if (isEntryModule(import.meta.url)) {
|
|
55
56
|
if (process.argv.includes(DEFINITION_FLAG)) {
|
|
56
57
|
const at = process.argv.indexOf('--out');
|
|
57
58
|
if (at < 0 || process.argv[at + 1] === undefined) {throw new Error(`--out <dir> is required with ${DEFINITION_FLAG}`);}
|
|
58
59
|
mkdirSync(process.argv[at + 1], { recursive: true });
|
|
59
60
|
process.argv.splice(at, 1, '--editions-root');
|
|
60
|
-
configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.
|
|
61
|
+
configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Cache], { refuseOthers: true });
|
|
61
62
|
} else {
|
|
62
63
|
configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.Cache]);
|
|
63
64
|
}
|
|
@@ -112,18 +112,20 @@
|
|
|
112
112
|
import { readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
113
113
|
import { join } from 'node:path';
|
|
114
114
|
|
|
115
|
+
import { adapterHostRow, assertAdapterBundlesMatchHost, fetchRegistryManifest, lockMountsAdapters } from './adapter-bundled-versions.mjs';
|
|
115
116
|
import { sealLock, verifyBiomeContentHashes } from './biome-content.mjs';
|
|
116
117
|
import { fetchCatalogue } from './catalogue-fetch.mjs';
|
|
117
118
|
import { readCataloguePin } from './catalogue-pin.mjs';
|
|
118
119
|
import { CATALOGUE_TOKEN_ENV_FLAG, CATALOGUE_USERNAME_FLAG, DEFINITION_FLAG } from './customer-flags.mjs';
|
|
119
120
|
import { EditionChannel } from './edition-constants.mjs';
|
|
120
|
-
import { WorkspaceRoot, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule, runtimeImageSourceIndexPath } from './edition-workspace.mjs';
|
|
121
|
+
import { WorkspaceRoot, biomeIndexPath, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule, runtimeImageSourceIndexPath } from './edition-workspace.mjs';
|
|
121
122
|
import {
|
|
122
123
|
assertCoordinatesFinal,
|
|
123
124
|
collectFleetPlatformRequirements,
|
|
124
125
|
isNotFinalYet,
|
|
125
126
|
readFleetImageLocks,
|
|
126
127
|
} from './fleet-coordinate-finality.mjs';
|
|
128
|
+
import { adoptCatalogueKernel } from './kernel-package.mjs';
|
|
127
129
|
import { loadKernelDistributionSchemas, serializeJson } from './lib.mjs';
|
|
128
130
|
import { assertPlatformCanParseLock } from './platform-lock-readability.mjs';
|
|
129
131
|
import { assertReleaseEditionTree } from './release-channel-guard.mjs';
|
|
@@ -138,7 +140,8 @@ import {
|
|
|
138
140
|
if (isEntryModule(import.meta.url)) {
|
|
139
141
|
// The customer form (it names --definition) reads exactly these roots - the data root (the vendor's
|
|
140
142
|
// runtime-image-source-index.json), the editions root, the sources root (the readability check of the
|
|
141
|
-
// vendor repositories)
|
|
143
|
+
// vendor repositories) and the cache (external source trees, and the catalogue's kernel from npm) - and
|
|
144
|
+
// refuses any other, the kernel root included. A vendor seal takes the kernel root too.
|
|
142
145
|
let namesDefinition;
|
|
143
146
|
try {
|
|
144
147
|
namesDefinition = parseSealArgs(process.argv.slice(2)).catalogueFlags.has(DEFINITION_FLAG);
|
|
@@ -147,7 +150,9 @@ if (isEntryModule(import.meta.url)) {
|
|
|
147
150
|
process.exit(1);
|
|
148
151
|
}
|
|
149
152
|
configureEditionWorkspaceFromProcess(
|
|
150
|
-
|
|
153
|
+
namesDefinition
|
|
154
|
+
? [WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Cache]
|
|
155
|
+
: [WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.Cache],
|
|
151
156
|
{ refuseOthers: namesDefinition },
|
|
152
157
|
);
|
|
153
158
|
}
|
|
@@ -479,7 +484,7 @@ function replaceLocksTogether(documents) {
|
|
|
479
484
|
* can leave (images sealed, distribution unsealed) is refused by every reader of
|
|
480
485
|
* a sealed edition.
|
|
481
486
|
*/
|
|
482
|
-
export async function sealEditionLocks({ distributionId, root, digests = new Map(), check = false, label = 'dist:seal-digests', vendoredCommit, catalogue, catalogueDigest }) {
|
|
487
|
+
export async function sealEditionLocks({ distributionId, root, digests = new Map(), check = false, label = 'dist:seal-digests', vendoredCommit, catalogue, catalogueDigest, fetchManifest = fetchRegistryManifest }) {
|
|
483
488
|
const schemas = await loadKernelDistributionSchemas();
|
|
484
489
|
const lockPath = join(root, distributionId, 'distribution.lock.json');
|
|
485
490
|
const imagesPath = join(root, distributionId, 'images.lock.json');
|
|
@@ -505,6 +510,11 @@ export async function sealEditionLocks({ distributionId, root, digests = new Map
|
|
|
505
510
|
const imageLock = schemas.parseImageLock(rawImages);
|
|
506
511
|
const pinned = vendoredCommit ?? ((repo) => createPinnedSourceAuthority({ pins: committedRepositoryPins() }).vendoredSourceCommitForRepository(repo));
|
|
507
512
|
const lockDocument = raw !== null && typeof raw === 'object' && 'unsealedSchema' in raw ? schemas.parseUnsealedDistributionLock(raw) : schemas.parseDistributionLock(raw);
|
|
513
|
+
if (lockMountsAdapters(lockDocument)) {
|
|
514
|
+
// The adapter host's row: the verified catalogue's for a customer, the data root's biome index for the vendor.
|
|
515
|
+
const rows = catalogue === undefined ? JSON.parse(readFileSync(biomeIndexPath(), 'utf8')) : catalogue.biomes;
|
|
516
|
+
await assertAdapterBundlesMatchHost({ lock: lockDocument, hostRow: adapterHostRow(rows), fetchManifest });
|
|
517
|
+
}
|
|
508
518
|
const result = await sealOrVerifyLock({
|
|
509
519
|
schemas,
|
|
510
520
|
lock: lockDocument,
|
|
@@ -657,6 +667,8 @@ async function main() {
|
|
|
657
667
|
: undefined;
|
|
658
668
|
const catalogue = fetched?.catalogue;
|
|
659
669
|
const catalogueDigest = fetched?.digest;
|
|
670
|
+
// A customer seal reads the kernel of its catalogue (from npm), proved its contract authority, before any kernel schema is loaded.
|
|
671
|
+
if (catalogue !== undefined) {await adoptCatalogueKernel(catalogue);}
|
|
660
672
|
const imageLock = await loadImageLockForVerification(distributionId, root);
|
|
661
673
|
|
|
662
674
|
// Throws on ANY unresolvable subject or missing required platform, before a
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// usage error. A data root that declares no policy is `unarmed`.
|
|
10
10
|
// It reads the policy the way `xema edition check` does and never writes.
|
|
11
11
|
|
|
12
|
-
import { EditionChannel } from './edition-channel.mjs';
|
|
12
|
+
import { EditionChannel, parseChannelArgs } from './edition-channel.mjs';
|
|
13
13
|
import {
|
|
14
14
|
WorkspaceRoot,
|
|
15
15
|
configureEditionWorkspaceFromArgv,
|
|
@@ -23,14 +23,6 @@ export const ARMING_USAGE = `usage: xema edition arming --channel <${Object.valu
|
|
|
23
23
|
/** The process exit codes of the verb. */
|
|
24
24
|
export const ArmingExit = Object.freeze({ Armed: 0, NotArmed: 1, Usage: 2 });
|
|
25
25
|
|
|
26
|
-
/** The channel of `--channel <channel>` among `rest`; anything else is a usage error. */
|
|
27
|
-
export function parseArmingArgs(rest) {
|
|
28
|
-
if (rest.length !== 2 || rest[0] !== '--channel' || !Object.values(EditionChannel).includes(rest[1])) {
|
|
29
|
-
throw new Error(`--channel <${Object.values(EditionChannel).join('|')}> is required and nothing else is accepted (got ${rest.length === 0 ? 'nothing' : JSON.stringify(rest.join(' '))})`);
|
|
30
|
-
}
|
|
31
|
-
return { channel: rest[1] };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
26
|
/**
|
|
35
27
|
* Judge the configured data root's policy on `channel`, writing the one JSON
|
|
36
28
|
* line to `stdout` and each unmet entry to `stderr`. Returns the exit code.
|
|
@@ -54,7 +46,7 @@ async function main() {
|
|
|
54
46
|
let channel;
|
|
55
47
|
try {
|
|
56
48
|
const rest = configureEditionWorkspaceFromArgv(process.argv.slice(2), [WorkspaceRoot.Data, WorkspaceRoot.Kernel]);
|
|
57
|
-
({ channel } =
|
|
49
|
+
({ channel } = parseChannelArgs(rest));
|
|
58
50
|
} catch (error) {
|
|
59
51
|
process.stderr.write(`arming: ${error.message}\n${ARMING_USAGE}\n`);
|
|
60
52
|
process.exitCode = ArmingExit.Usage;
|
|
@@ -397,6 +397,7 @@ export async function verifyEvidence({ argv, env, bundlePath }) {
|
|
|
397
397
|
completenessStatementDigest: result.completenessStatementDigest,
|
|
398
398
|
customerId: options.customerId,
|
|
399
399
|
distributionId: options.distributionId,
|
|
400
|
+
distributionLock: result.distributionLock,
|
|
400
401
|
channel,
|
|
401
402
|
mode: options.mode,
|
|
402
403
|
targetRegistry: options.targetRegistry,
|
package/examples/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# `xema.config.yaml` examples
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
Copy one of these to `xema.config.yaml` (or `~/.xema/config.yaml`) and edit:
|
|
4
5
|
|
|
5
6
|
```bash
|
|
@@ -14,6 +15,6 @@ xema up
|
|
|
14
15
|
| [`03-byo-infra.yaml`](./03-byo-infra.yaml) | Bring-your-own Postgres / Keycloak / object store; launcher runs only the rest. |
|
|
15
16
|
| [`04-subset-knowledge.yaml`](./04-subset-knowledge.yaml) | Run only a slice (tiers / services) to save resources. |
|
|
16
17
|
| [`05-appliance-helm.yaml`](./05-appliance-helm.yaml) | Deploy to Kubernetes / an appliance via Helm. |
|
|
18
|
+
| [`06-biome-sources.yaml`](./06-biome-sources.yaml) | Pull biomes from workspace, distribution and git sources. |
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
built-in defaults. Every value shown is the default unless noted.
|
|
20
|
+
Config precedence: `--config <path>`, then `./xema.config.yaml`, then `~/.xema/config.yaml`, then built-in defaults.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/xema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/xema-dev/xema-cli.git",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@xemahq/distribution-source-hash": "^0.3.0",
|
|
55
55
|
"@xemahq/dsl": "^0.23.0",
|
|
56
56
|
"@xemahq/identity-client": "^0.16.0",
|
|
57
|
-
"@xemahq/contracts": "^0.
|
|
57
|
+
"@xemahq/contracts": "^0.78.0",
|
|
58
58
|
"@xemahq/kernel-state-runtime": "^0.8.0",
|
|
59
59
|
"@xemahq/license-verifier": "^0.2.0",
|
|
60
60
|
"@xemahq/service-registry-nest": "^0.12.2",
|
|
@@ -92,6 +92,6 @@
|
|
|
92
92
|
"typecheck": "tsc --noEmit"
|
|
93
93
|
},
|
|
94
94
|
"xemaRelease": {
|
|
95
|
-
"sourceDigest": "sha256:
|
|
95
|
+
"sourceDigest": "sha256:d594a2838a0fbee09de46b9b6637afd9f9437a2989a2bfe30f2336ddc20f0523"
|
|
96
96
|
}
|
|
97
97
|
}
|