@xemahq/xema 0.28.0 → 0.29.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/dist/commands/edition.d.ts.map +1 -1
- package/dist/commands/edition.js +8 -7
- package/dist/commands/edition.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +88 -11
- package/dist/edition/verify-image-evidence.bundle.provenance.json +48 -42
- package/dist/runtime/edition/verb-map.d.ts +2 -1
- package/dist/runtime/edition/verb-map.d.ts.map +1 -1
- package/dist/runtime/edition/verb-map.js +2 -0
- package/dist/runtime/edition/verb-map.js.map +1 -1
- package/dist/runtime/runner/chart-set-lock.d.ts +4 -1
- package/dist/runtime/runner/chart-set-lock.d.ts.map +1 -1
- package/dist/runtime/runner/chart-set-lock.js +39 -4
- package/dist/runtime/runner/chart-set-lock.js.map +1 -1
- package/edition/README.md +69 -32
- package/edition/build-biome-index.mjs +1 -1
- package/edition/build-platform-service-index.mjs +1 -1
- package/edition/build-runtime-image-source-index.mjs +64 -20
- package/edition/catalogue-fetch.mjs +36 -9
- package/edition/catalogue-pin.mjs +71 -0
- package/edition/catalogue.mjs +11 -0
- package/edition/chart-set-source-commit.mjs +17 -7
- package/edition/checkout-indexed-source-repositories.mjs +17 -17
- package/edition/customer-flags.mjs +0 -1
- package/edition/develop-customer-edition.mjs +23 -5
- package/edition/develop-edition.mjs +147 -48
- package/edition/develop-freeze.mjs +69 -25
- package/edition/develop-seal.mjs +39 -25
- package/edition/dist-check.mjs +1 -1
- package/edition/edition-artifact.mjs +65 -18
- package/edition/edition-biome-manifests.mjs +2 -3
- package/edition/edition-chart-set.mjs +59 -6
- package/edition/edition-config.mjs +28 -2
- package/edition/edition-constants.mjs +10 -4
- package/edition/edition-contract-parse.mjs +52 -0
- package/edition/edition-contract.json +10 -1
- package/edition/edition-workspace.mjs +12 -9
- package/edition/evidence-trust-policy.mjs +26 -0
- package/edition/external-source-walk.mjs +48 -61
- package/edition/git-source-auth.mjs +104 -25
- package/edition/image-evidence.mjs +20 -10
- package/edition/kernel-contract-authority.mjs +23 -1
- package/edition/lib.mjs +13 -24
- package/edition/pin-source.mjs +6 -1
- package/edition/registry-credentials.mjs +11 -1
- package/edition/release-edition-artifact.mjs +3 -3
- package/edition/resolve-chart-set.mjs +50 -30
- package/edition/resolve-customer-edition.mjs +63 -7
- package/edition/resolve-distribution.mjs +1 -1
- package/edition/resolve-edition.mjs +12 -11
- package/edition/resolve-images.mjs +36 -15
- package/edition/seal-image-digests.mjs +31 -7
- package/edition/source-closure.mjs +1 -2
- package/edition/trust-policy-arming-verb.mjs +68 -0
- package/package.json +3 -3
|
@@ -114,7 +114,9 @@ import { join } from 'node:path';
|
|
|
114
114
|
|
|
115
115
|
import { sealLock, verifyBiomeContentHashes } from './biome-content.mjs';
|
|
116
116
|
import { fetchCatalogue } from './catalogue-fetch.mjs';
|
|
117
|
-
import {
|
|
117
|
+
import { readCataloguePin } from './catalogue-pin.mjs';
|
|
118
|
+
import { CATALOGUE_TOKEN_ENV_FLAG, CATALOGUE_USERNAME_FLAG, DEFINITION_FLAG } from './customer-flags.mjs';
|
|
119
|
+
import { EditionChannel } from './edition-constants.mjs';
|
|
118
120
|
import { WorkspaceRoot, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule, runtimeImageSourceIndexPath } from './edition-workspace.mjs';
|
|
119
121
|
import {
|
|
120
122
|
assertCoordinatesFinal,
|
|
@@ -134,7 +136,20 @@ import {
|
|
|
134
136
|
} from './verify-image-platforms.mjs';
|
|
135
137
|
|
|
136
138
|
if (isEntryModule(import.meta.url)) {
|
|
137
|
-
|
|
139
|
+
// The customer form (it names --definition) reads exactly these roots - the data root (the vendor's
|
|
140
|
+
// runtime-image-source-index.json), the editions root, the sources root (the readability check of the
|
|
141
|
+
// vendor repositories), the kernel and the cache (external source trees) - and refuses any other.
|
|
142
|
+
let namesDefinition;
|
|
143
|
+
try {
|
|
144
|
+
namesDefinition = parseSealArgs(process.argv.slice(2)).catalogueFlags.has(DEFINITION_FLAG);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
process.stderr.write(`${error.message}\n`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
configureEditionWorkspaceFromProcess(
|
|
150
|
+
[WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.Cache],
|
|
151
|
+
{ refuseOthers: namesDefinition },
|
|
152
|
+
);
|
|
138
153
|
}
|
|
139
154
|
|
|
140
155
|
const SHA256_DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
@@ -393,12 +408,19 @@ export function isNotPublishedYet(error) {
|
|
|
393
408
|
*/
|
|
394
409
|
export function parseSealArgs(args) {
|
|
395
410
|
const MODES = new Set(['--check', '--if-published', '--publication-verdict']);
|
|
396
|
-
const CATALOGUE_VALUE_FLAGS = new Set([
|
|
411
|
+
const CATALOGUE_VALUE_FLAGS = new Set([DEFINITION_FLAG, CATALOGUE_USERNAME_FLAG, CATALOGUE_TOKEN_ENV_FLAG]);
|
|
397
412
|
const positional = [];
|
|
398
413
|
const catalogueFlags = new Map();
|
|
399
414
|
for (let i = 0; i < args.length; i += 1) {
|
|
400
415
|
const arg = args[i];
|
|
401
416
|
if (MODES.has(arg)) {continue;}
|
|
417
|
+
const eq = arg.indexOf('=');
|
|
418
|
+
if (eq > 0 && CATALOGUE_VALUE_FLAGS.has(arg.slice(0, eq))) {
|
|
419
|
+
const value = arg.slice(eq + 1);
|
|
420
|
+
if (value === '' || value.startsWith('--')) {throw new Error(`${arg.slice(0, eq)} takes a value`);}
|
|
421
|
+
catalogueFlags.set(arg.slice(0, eq), value);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
402
424
|
if (CATALOGUE_VALUE_FLAGS.has(arg)) {
|
|
403
425
|
if (args[i + 1] === undefined || args[i + 1].startsWith('--')) {throw new Error(`${arg} takes a value`);}
|
|
404
426
|
catalogueFlags.set(arg, args[i + 1]);
|
|
@@ -530,7 +552,7 @@ async function main() {
|
|
|
530
552
|
throw new Error(
|
|
531
553
|
'usage: xema edition seal <distributionId> ' +
|
|
532
554
|
`[--check | --if-published | --publication-verdict] ` +
|
|
533
|
-
`[--
|
|
555
|
+
`[--definition <file> [--catalogue-username <name> --catalogue-token-env <VAR>]]`,
|
|
534
556
|
);
|
|
535
557
|
}
|
|
536
558
|
// The modes answer different questions and combining them would make the
|
|
@@ -621,12 +643,14 @@ async function main() {
|
|
|
621
643
|
// `authTokenEnv` and the test reads which variable the refusal names, because
|
|
622
644
|
// no structural check can tell a threaded argument from an omitted one.
|
|
623
645
|
const root = editionsRoot();
|
|
624
|
-
// CUSTOMER MODE: the digest-pinned catalogue
|
|
646
|
+
// CUSTOMER MODE (`--definition`): the digest-pinned RELEASE catalogue of catalogue.pin.json beside the
|
|
647
|
+
// definition, the one the edition was resolved against.
|
|
625
648
|
// Its vendor biomes' tree ids are inputs on ITS authority (biome-content.mjs).
|
|
626
649
|
const fetched =
|
|
627
|
-
catalogueFlags.has(
|
|
650
|
+
catalogueFlags.has(DEFINITION_FLAG)
|
|
628
651
|
? await fetchCatalogue({
|
|
629
|
-
ref: catalogueFlags.get(
|
|
652
|
+
ref: readCataloguePin({ definitionFile: catalogueFlags.get(DEFINITION_FLAG), channel: EditionChannel.Release }),
|
|
653
|
+
channel: EditionChannel.Release,
|
|
630
654
|
username: catalogueFlags.get(CATALOGUE_USERNAME_FLAG),
|
|
631
655
|
tokenEnv: catalogueFlags.get(CATALOGUE_TOKEN_ENV_FLAG),
|
|
632
656
|
})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// `xema edition source-closure`: the source tree hash of one directory of a
|
|
3
3
|
// source repository at an exact commit, with its workspace-package closure.
|
|
4
4
|
//
|
|
5
|
-
// usage: xema edition source-closure --repo <repo> --path <dir> --commit <40 hex> --sources-root <dir> --
|
|
5
|
+
// usage: xema edition source-closure --repo <repo> --path <dir> --commit <40 hex> --sources-root <dir> --kernel <dir>
|
|
6
6
|
//
|
|
7
7
|
// These are the facts a content coordinate is derived from. A consumer frames
|
|
8
8
|
// them into its own coordinate; it never reads a git tree, a workspace graph
|
|
@@ -85,7 +85,6 @@ export function readSourceClosure({ repoDir, repo, directory, commit }) {
|
|
|
85
85
|
function main() {
|
|
86
86
|
const rest = configureEditionWorkspaceFromArgv(process.argv.slice(2), [
|
|
87
87
|
WorkspaceRoot.Sources,
|
|
88
|
-
WorkspaceRoot.SourceHash,
|
|
89
88
|
// The hash implementation is held to the kernel it was built for.
|
|
90
89
|
WorkspaceRoot.Kernel,
|
|
91
90
|
]);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `xema edition arming`: is the data root's declared evidence trust policy ARMED
|
|
3
|
+
// on one channel?
|
|
4
|
+
//
|
|
5
|
+
// usage: xema edition arming --channel <release|develop> --data-root <dir> --kernel <dir>
|
|
6
|
+
// stdout: {"channel":"<channel>","state":"armed|unarmed|partial","unmet":["…"]}
|
|
7
|
+
// exit: 0 only when the state is armed; 1 for any other state (each unmet
|
|
8
|
+
// entry on stderr) or a policy that does not hold to its pin; 2 for a
|
|
9
|
+
// usage error. A data root that declares no policy is `unarmed`.
|
|
10
|
+
// It reads the policy the way `xema edition check` does and never writes.
|
|
11
|
+
|
|
12
|
+
import { EditionChannel } from './edition-channel.mjs';
|
|
13
|
+
import {
|
|
14
|
+
WorkspaceRoot,
|
|
15
|
+
configureEditionWorkspaceFromArgv,
|
|
16
|
+
isEntryModule,
|
|
17
|
+
} from './edition-workspace.mjs';
|
|
18
|
+
import { assessDeclaredTrustPolicyArming } from './evidence-trust-policy.mjs';
|
|
19
|
+
import { TrustPolicyArmingState } from './trust-policy-arming.mjs';
|
|
20
|
+
|
|
21
|
+
export const ARMING_USAGE = `usage: xema edition arming --channel <${Object.values(EditionChannel).join('|')}> --data-root <dir> --kernel <dir>`;
|
|
22
|
+
|
|
23
|
+
/** The process exit codes of the verb. */
|
|
24
|
+
export const ArmingExit = Object.freeze({ Armed: 0, NotArmed: 1, Usage: 2 });
|
|
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
|
+
/**
|
|
35
|
+
* Judge the configured data root's policy on `channel`, writing the one JSON
|
|
36
|
+
* line to `stdout` and each unmet entry to `stderr`. Returns the exit code.
|
|
37
|
+
*/
|
|
38
|
+
export async function runArming({ channel, locateVerifier, stdout = process.stdout, stderr = process.stderr }) {
|
|
39
|
+
let verdict;
|
|
40
|
+
try {
|
|
41
|
+
verdict = await assessDeclaredTrustPolicyArming({ channel, locateVerifier });
|
|
42
|
+
} catch (error) {
|
|
43
|
+
stderr.write(`arming: ${error.message}\n`);
|
|
44
|
+
return ArmingExit.NotArmed;
|
|
45
|
+
}
|
|
46
|
+
const { state, unmet } = verdict;
|
|
47
|
+
stdout.write(`${JSON.stringify({ channel, state, unmet })}\n`);
|
|
48
|
+
if (state === TrustPolicyArmingState.ARMED) {return ArmingExit.Armed;}
|
|
49
|
+
for (const entry of unmet) {stderr.write(`${entry}\n`);}
|
|
50
|
+
return ArmingExit.NotArmed;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function main() {
|
|
54
|
+
let channel;
|
|
55
|
+
try {
|
|
56
|
+
const rest = configureEditionWorkspaceFromArgv(process.argv.slice(2), [WorkspaceRoot.Data, WorkspaceRoot.Kernel]);
|
|
57
|
+
({ channel } = parseArmingArgs(rest));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
process.stderr.write(`arming: ${error.message}\n${ARMING_USAGE}\n`);
|
|
60
|
+
process.exitCode = ArmingExit.Usage;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
process.exitCode = await runArming({ channel });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (isEntryModule(import.meta.url)) {
|
|
67
|
+
await main();
|
|
68
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/xema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/xema-dev/xema-cli.git",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"@nestjs/platform-express": "^11.1.28",
|
|
52
52
|
"@xemahq/biome-sdk": "^18.1.0",
|
|
53
53
|
"@xemahq/biome-supply-chain": "^0.5.0",
|
|
54
|
+
"@xemahq/distribution-source-hash": "^0.3.0",
|
|
54
55
|
"@xemahq/dsl": "^0.23.0",
|
|
55
56
|
"@xemahq/identity-client": "^0.16.0",
|
|
56
57
|
"@xemahq/contracts": "^0.76.0",
|
|
@@ -68,7 +69,6 @@
|
|
|
68
69
|
"@types/jest": "^30.0.0",
|
|
69
70
|
"@types/node": "25.2.3",
|
|
70
71
|
"@xemahq/biome-host-api-client": "^0.3.31",
|
|
71
|
-
"@xemahq/distribution-source-hash": "^0.3.0",
|
|
72
72
|
"@xemahq/eslint-config": "^0.9.2",
|
|
73
73
|
"esbuild": "0.28.2",
|
|
74
74
|
"eslint": "^9.39.4",
|
|
@@ -92,6 +92,6 @@
|
|
|
92
92
|
"typecheck": "tsc --noEmit"
|
|
93
93
|
},
|
|
94
94
|
"xemaRelease": {
|
|
95
|
-
"sourceDigest": "sha256:
|
|
95
|
+
"sourceDigest": "sha256:a0dc6a3fdb7cce15b031dcd04476f19924d13d36924070b1527af1fa613b258e"
|
|
96
96
|
}
|
|
97
97
|
}
|