@xemahq/xema 0.34.0 → 0.35.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.js +1 -1
- package/dist/commands/edition.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.provenance.json +40 -40
- package/edition/README.md +1 -0
- package/edition/catalogue-edition.mjs +4 -4
- package/edition/develop-customer-edition.mjs +34 -3
- package/edition/develop-edition.mjs +30 -16
- package/edition/edition-artifact.mjs +49 -9
- package/edition/pin-source.mjs +1 -6
- package/edition/release-edition-artifact.mjs +9 -15
- package/edition/resolve-customer-edition.mjs +9 -2
- package/edition/seal-image-digests.mjs +20 -18
- package/edition/sealed-distribution-lock.mjs +22 -0
- package/package.json +2 -2
package/dist/commands/edition.js
CHANGED
|
@@ -7,7 +7,7 @@ const verb_map_1 = require("../runtime/edition/verb-map");
|
|
|
7
7
|
const VERB_SUMMARY = {
|
|
8
8
|
[verb_map_1.EditionVerb.Resolve]: 'Resolve the whole release lock set of an edition: xema edition resolve <id> [--check]; or a customer edition against the catalogue pinned in catalogue.pin.json beside the definition: xema edition resolve --definition <file> --channel <release|develop> --out <dir> [--source-image <source>=<registry>/<prefix>]...',
|
|
9
9
|
[verb_map_1.EditionVerb.Seal]: 'Seal the image digests of an edition: xema edition seal <id> [--definition <file>] (a customer edition seals against the release catalogue pinned in catalogue.pin.json beside the definition)',
|
|
10
|
-
[verb_map_1.EditionVerb.Develop]: 'Resolve a develop edition snapshot: xema edition develop <resolve|target|freeze-graph|freeze-sources|check-existing|check-overrides> ...; or a customer develop edition (catalogue pinned in catalogue.pin.json beside the definition): xema edition develop <resolve|target|check-existing|freeze-sources> --definition <file> ... (resolve: --pins <file> --out <dir> --registry <root> --engine-image <repo>@sha256:<digest> [--source-image <source>=<registry>/<prefix>]...)',
|
|
10
|
+
[verb_map_1.EditionVerb.Develop]: 'Resolve a develop edition snapshot: xema edition develop <resolve|target|freeze-graph|freeze-sources|check-existing|check-overrides> ...; or a customer develop edition (catalogue pinned in catalogue.pin.json beside the definition): xema edition develop <resolve|target|check-existing|freeze-sources> --definition <file> ... (resolve: --pins <file> --out <dir> --registry <root> --engine-image <repo>@sha256:<digest> [--source-image <source>=<registry>/<prefix>]...; target: --pins <file> --registry <root> --engine-image <repo>@sha256:<digest> [--source-image ...]; check-existing: --pins <file> --artifact <dir> --engine-image <repo>@sha256:<digest> [--source-image ...]). The develop tag is sha256 of the CLI version, the engine image, the pins and the source-image map.',
|
|
11
11
|
[verb_map_1.EditionVerb.Artifact]: 'Write the OCI artifact files of a release edition from the committed sealed locks: xema edition artifact <id> --channel release --registry <root> --coordinate <tag> --engine-image <repo>@sha256:<digest> --out <dir>',
|
|
12
12
|
[verb_map_1.EditionVerb.Catalogue]: 'Write the catalogue.json of a release offering from its committed sealed locks: xema edition catalogue <id> --out <dir>',
|
|
13
13
|
[verb_map_1.EditionVerb.Index]: `Rebuild a catalogue index: xema edition index <${Object.values(verb_map_1.EditionIndexKind).join('|')}> [--check]; xema edition index runtime-image-source --repin-contract-authority --kernel-commit <40-hex> replaces only the contract authority of the committed index with the kernel checkout's at the stated commit (its HEAD must equal it)`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edition.js","sourceRoot":"","sources":["../../src/commands/edition.ts"],"names":[],"mappings":";;AA6CA,0CAiBC;AA9DD,kDAA8D;AAC9D,4DAA8D;AAC9D,0DAA2H;AAW3H,MAAM,YAAY,GAA0C;IAC1D,CAAC,sBAAW,CAAC,OAAO,CAAC,EACnB,0TAA0T;IAC5T,CAAC,sBAAW,CAAC,IAAI,CAAC,EAAE,gMAAgM;IACpN,CAAC,sBAAW,CAAC,OAAO,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"edition.js","sourceRoot":"","sources":["../../src/commands/edition.ts"],"names":[],"mappings":";;AA6CA,0CAiBC;AA9DD,kDAA8D;AAC9D,4DAA8D;AAC9D,0DAA2H;AAW3H,MAAM,YAAY,GAA0C;IAC1D,CAAC,sBAAW,CAAC,OAAO,CAAC,EACnB,0TAA0T;IAC5T,CAAC,sBAAW,CAAC,IAAI,CAAC,EAAE,gMAAgM;IACpN,CAAC,sBAAW,CAAC,OAAO,CAAC,EAAE,swBAAswB;IAC7xB,CAAC,sBAAW,CAAC,QAAQ,CAAC,EACpB,wNAAwN;IAC1N,CAAC,sBAAW,CAAC,SAAS,CAAC,EAAE,yHAAyH;IAClJ,CAAC,sBAAW,CAAC,KAAK,CAAC,EAAE,kDAAkD,MAAM,CAAC,MAAM,CAAC,2BAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,+OAA+O;IAC/V,CAAC,sBAAW,CAAC,KAAK,CAAC,EAAE,oIAAoI;IACzJ,CAAC,sBAAW,CAAC,GAAG,CAAC,EAAE,qEAAqE;IACxF,CAAC,sBAAW,CAAC,eAAe,CAAC,EAAE,qGAAqG;IACpI,CAAC,sBAAW,CAAC,eAAe,CAAC,EAAE,wEAAwE;IACvG,CAAC,sBAAW,CAAC,kBAAkB,CAAC,EAAE,mIAAmI;IACrK,CAAC,sBAAW,CAAC,cAAc,CAAC,EAC1B,yQAAyQ;IAC3Q,CAAC,sBAAW,CAAC,QAAQ,CAAC,EAAE,+EAA+E,MAAM,CAAC,MAAM,CAAC,8BAAmB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK;IACxJ,CAAC,sBAAW,CAAC,IAAI,CAAC,EAAE,6FAA6F;IACjH,CAAC,sBAAW,CAAC,KAAK,CAAC,EAAE,6JAA6J;IAClL,CAAC,sBAAW,CAAC,QAAQ,CAAC,EAAE,gGAAgG,MAAM,CAAC,MAAM,CAAC,mCAAwB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,qKAAqK;IAC9U,CAAC,sBAAW,CAAC,cAAc,CAAC,EAC1B,6LAA6L;IAC/L,CAAC,sBAAW,CAAC,MAAM,CAAC,EAClB,yMAAyM;IAC3M,CAAC,sBAAW,CAAC,GAAG,CAAC,EACf,mWAAmW;IACrW,CAAC,sBAAW,CAAC,KAAK,CAAC,EACjB,qMAAqM;IACvM,CAAC,sBAAW,CAAC,aAAa,CAAC,EACzB,uMAAuM;CAC1M,CAAC;AAEF,SAAgB,eAAe,CAAC,OAAgB;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC;IACvF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,sBAAW,CAAC,EAAE,CAAC;QAC9C,OAAO;aACJ,OAAO,CAAC,IAAI,CAAC;aACb,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;aAC/B,QAAQ,CAAC,WAAW,EAAE,0DAA0D,CAAC;aACjF,kBAAkB,EAAE;aACpB,MAAM,CAAC,CAAC,IAAc,EAAE,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,0BAAc,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAChD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAA,qBAAQ,EAAC,4BAAe,CAAC,YAAY,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACjG,CAAC;QACH,CAAC,CAAC,CAAC;IACP,CAAC;AACH,CAAC"}
|
|
@@ -2383,229 +2383,229 @@
|
|
|
2383
2383
|
},
|
|
2384
2384
|
{
|
|
2385
2385
|
"package": "@xemahq/xema",
|
|
2386
|
-
"version": "0.
|
|
2386
|
+
"version": "0.35.0",
|
|
2387
2387
|
"path": "edition/adapter-host-versions.mjs",
|
|
2388
2388
|
"sha256": "sha256:1b7ebd3592f4da1225fba74aaba912e7d85fbceeb1626f7dcdac83491cbb4d69"
|
|
2389
2389
|
},
|
|
2390
2390
|
{
|
|
2391
2391
|
"package": "@xemahq/xema",
|
|
2392
|
-
"version": "0.
|
|
2392
|
+
"version": "0.35.0",
|
|
2393
2393
|
"path": "edition/authorization-base.mjs",
|
|
2394
2394
|
"sha256": "sha256:bbeac7ff01376cb89ed2140526d4795d8e559b3f01612b404a701f19aaa24c6a"
|
|
2395
2395
|
},
|
|
2396
2396
|
{
|
|
2397
2397
|
"package": "@xemahq/xema",
|
|
2398
|
-
"version": "0.
|
|
2398
|
+
"version": "0.35.0",
|
|
2399
2399
|
"path": "edition/build-ledger.mjs",
|
|
2400
2400
|
"sha256": "sha256:05202cbdd606aee086c8e9c62d28be68c429653a8b1d73a289ed0fe4ff14da85"
|
|
2401
2401
|
},
|
|
2402
2402
|
{
|
|
2403
2403
|
"package": "@xemahq/xema",
|
|
2404
|
-
"version": "0.
|
|
2404
|
+
"version": "0.35.0",
|
|
2405
2405
|
"path": "edition/connector-source-authority.mjs",
|
|
2406
2406
|
"sha256": "sha256:ea94475f4324737673accd7de1303c44db1cbc4dac61a865a06cd83abe310164"
|
|
2407
2407
|
},
|
|
2408
2408
|
{
|
|
2409
2409
|
"package": "@xemahq/xema",
|
|
2410
|
-
"version": "0.
|
|
2410
|
+
"version": "0.35.0",
|
|
2411
2411
|
"path": "edition/distribution-paths.mjs",
|
|
2412
2412
|
"sha256": "sha256:d78a5c8405bc6c25d36fcec61b0030e1fdacac7d528e698adb96f906f4b70279"
|
|
2413
2413
|
},
|
|
2414
2414
|
{
|
|
2415
2415
|
"package": "@xemahq/xema",
|
|
2416
|
-
"version": "0.
|
|
2416
|
+
"version": "0.35.0",
|
|
2417
2417
|
"path": "edition/edition-artifact.mjs",
|
|
2418
|
-
"sha256": "sha256:
|
|
2418
|
+
"sha256": "sha256:287e4cf042626963e07396c706350e9668adbcacd238a7ad4db1d68bba3858b2"
|
|
2419
2419
|
},
|
|
2420
2420
|
{
|
|
2421
2421
|
"package": "@xemahq/xema",
|
|
2422
|
-
"version": "0.
|
|
2422
|
+
"version": "0.35.0",
|
|
2423
2423
|
"path": "edition/edition-channel.mjs",
|
|
2424
2424
|
"sha256": "sha256:3c0c1564dc50be133cad500452d953353aa5beb703142c05a17bbce8175ba9f3"
|
|
2425
2425
|
},
|
|
2426
2426
|
{
|
|
2427
2427
|
"package": "@xemahq/xema",
|
|
2428
|
-
"version": "0.
|
|
2428
|
+
"version": "0.35.0",
|
|
2429
2429
|
"path": "edition/edition-chart-set.mjs",
|
|
2430
2430
|
"sha256": "sha256:cea98292ffc8bfe2afcc01f327172bc9071a83ef9b62a4e56236f2029d2699a9"
|
|
2431
2431
|
},
|
|
2432
2432
|
{
|
|
2433
2433
|
"package": "@xemahq/xema",
|
|
2434
|
-
"version": "0.
|
|
2434
|
+
"version": "0.35.0",
|
|
2435
2435
|
"path": "edition/edition-config.mjs",
|
|
2436
2436
|
"sha256": "sha256:e3997bd9a0082e02d01760868b2a4f41ed3081e904126b30fdf86e7938dc6137"
|
|
2437
2437
|
},
|
|
2438
2438
|
{
|
|
2439
2439
|
"package": "@xemahq/xema",
|
|
2440
|
-
"version": "0.
|
|
2440
|
+
"version": "0.35.0",
|
|
2441
2441
|
"path": "edition/edition-constants.mjs",
|
|
2442
2442
|
"sha256": "sha256:71ae51028847ab3430ffec9b229b0516a7705b75a6de1fcfa665f5e1abf8e9bb"
|
|
2443
2443
|
},
|
|
2444
2444
|
{
|
|
2445
2445
|
"package": "@xemahq/xema",
|
|
2446
|
-
"version": "0.
|
|
2446
|
+
"version": "0.35.0",
|
|
2447
2447
|
"path": "edition/edition-contract-parse.mjs",
|
|
2448
2448
|
"sha256": "sha256:c575e96b70bda82899931eb0b18567988e2e13e664fa498bcbef96b571dcabb4"
|
|
2449
2449
|
},
|
|
2450
2450
|
{
|
|
2451
2451
|
"package": "@xemahq/xema",
|
|
2452
|
-
"version": "0.
|
|
2452
|
+
"version": "0.35.0",
|
|
2453
2453
|
"path": "edition/edition-contract.json",
|
|
2454
2454
|
"sha256": "sha256:aa1a05f1728813a7bdb1e02d9305e9adf641d695e6767305210c67a1d81b38a7"
|
|
2455
2455
|
},
|
|
2456
2456
|
{
|
|
2457
2457
|
"package": "@xemahq/xema",
|
|
2458
|
-
"version": "0.
|
|
2458
|
+
"version": "0.35.0",
|
|
2459
2459
|
"path": "edition/edition-protocol.mjs",
|
|
2460
2460
|
"sha256": "sha256:c488ac72ff3f23c0f28f8db159bf84d2c86ec5cda3633db1fe91ca3f21b12214"
|
|
2461
2461
|
},
|
|
2462
2462
|
{
|
|
2463
2463
|
"package": "@xemahq/xema",
|
|
2464
|
-
"version": "0.
|
|
2464
|
+
"version": "0.35.0",
|
|
2465
2465
|
"path": "edition/edition-source-pins.mjs",
|
|
2466
2466
|
"sha256": "sha256:b141474fff33b06734fb62cd2552752b991c8267bb1c9bccb22a81dd80e91c6c"
|
|
2467
2467
|
},
|
|
2468
2468
|
{
|
|
2469
2469
|
"package": "@xemahq/xema",
|
|
2470
|
-
"version": "0.
|
|
2470
|
+
"version": "0.35.0",
|
|
2471
2471
|
"path": "edition/edition-workspace.mjs",
|
|
2472
2472
|
"sha256": "sha256:22235719a41c0cf6206f5bcd7489341dccb22150df0b129724ef8ab566b5bff1"
|
|
2473
2473
|
},
|
|
2474
2474
|
{
|
|
2475
2475
|
"package": "@xemahq/xema",
|
|
2476
|
-
"version": "0.
|
|
2476
|
+
"version": "0.35.0",
|
|
2477
2477
|
"path": "edition/evidence-checkout.mjs",
|
|
2478
2478
|
"sha256": "sha256:31fb669769b31de4c6f2b2501bb96959548172ef166e7568edbbfed7f5079a11"
|
|
2479
2479
|
},
|
|
2480
2480
|
{
|
|
2481
2481
|
"package": "@xemahq/xema",
|
|
2482
|
-
"version": "0.
|
|
2482
|
+
"version": "0.35.0",
|
|
2483
2483
|
"path": "edition/evidence-grammars.mjs",
|
|
2484
2484
|
"sha256": "sha256:4dae22b61ade99219a482b971c9780d68a3d37d021e39cdfe860b245364b8b9f"
|
|
2485
2485
|
},
|
|
2486
2486
|
{
|
|
2487
2487
|
"package": "@xemahq/xema",
|
|
2488
|
-
"version": "0.
|
|
2488
|
+
"version": "0.35.0",
|
|
2489
2489
|
"path": "edition/evidence-trust-pin.mjs",
|
|
2490
2490
|
"sha256": "sha256:b9dd5c01903394436530783614efb0cd357d982ed2befd844a39328479773cbc"
|
|
2491
2491
|
},
|
|
2492
2492
|
{
|
|
2493
2493
|
"package": "@xemahq/xema",
|
|
2494
|
-
"version": "0.
|
|
2494
|
+
"version": "0.35.0",
|
|
2495
2495
|
"path": "edition/git-json.mjs",
|
|
2496
2496
|
"sha256": "sha256:d0d25cda0c19007becabc9a2827c3dcf759b295cd0b41596eb4480c9549a1f21"
|
|
2497
2497
|
},
|
|
2498
2498
|
{
|
|
2499
2499
|
"package": "@xemahq/xema",
|
|
2500
|
-
"version": "0.
|
|
2500
|
+
"version": "0.35.0",
|
|
2501
2501
|
"path": "edition/image-delivery-entitlement-descriptor.mjs",
|
|
2502
2502
|
"sha256": "sha256:4726b66116f01bf2523fafe2b7a356591eca8fb5ca6bd8b154dd4821873e921b"
|
|
2503
2503
|
},
|
|
2504
2504
|
{
|
|
2505
2505
|
"package": "@xemahq/xema",
|
|
2506
|
-
"version": "0.
|
|
2506
|
+
"version": "0.35.0",
|
|
2507
2507
|
"path": "edition/image-evidence-replay-state.mjs",
|
|
2508
2508
|
"sha256": "sha256:eba0cf1d38af0ab2f00c5099fa8bc2edc543136ce92f895544d4d284b3d6af0f"
|
|
2509
2509
|
},
|
|
2510
2510
|
{
|
|
2511
2511
|
"package": "@xemahq/xema",
|
|
2512
|
-
"version": "0.
|
|
2512
|
+
"version": "0.35.0",
|
|
2513
2513
|
"path": "edition/image-evidence.mjs",
|
|
2514
2514
|
"sha256": "sha256:433202eb2db84510acd11de6fe5ede81c8d96b8bc72991444940f7f84a48897f"
|
|
2515
2515
|
},
|
|
2516
2516
|
{
|
|
2517
2517
|
"package": "@xemahq/xema",
|
|
2518
|
-
"version": "0.
|
|
2518
|
+
"version": "0.35.0",
|
|
2519
2519
|
"path": "edition/kernel-capability-domain.mjs",
|
|
2520
2520
|
"sha256": "sha256:3ecf887dd8d399e91a8285f6f115f6e418fb59bbf3f004539fe65ec667219356"
|
|
2521
2521
|
},
|
|
2522
2522
|
{
|
|
2523
2523
|
"package": "@xemahq/xema",
|
|
2524
|
-
"version": "0.
|
|
2524
|
+
"version": "0.35.0",
|
|
2525
2525
|
"path": "edition/kernel-contract-authority.mjs",
|
|
2526
2526
|
"sha256": "sha256:20abdde62a7b037c01ea9bbe1ff1e6094e6b0efd0add2f656f1938a04409bf82"
|
|
2527
2527
|
},
|
|
2528
2528
|
{
|
|
2529
2529
|
"package": "@xemahq/xema",
|
|
2530
|
-
"version": "0.
|
|
2530
|
+
"version": "0.35.0",
|
|
2531
2531
|
"path": "edition/lib.mjs",
|
|
2532
2532
|
"sha256": "sha256:9708d6d385f69105bbd77359f5571ffec7b0c7f7fd343c4bbb88b33c4a61e780"
|
|
2533
2533
|
},
|
|
2534
2534
|
{
|
|
2535
2535
|
"package": "@xemahq/xema",
|
|
2536
|
-
"version": "0.
|
|
2536
|
+
"version": "0.35.0",
|
|
2537
2537
|
"path": "edition/no-follow-file.mjs",
|
|
2538
2538
|
"sha256": "sha256:c17bdf5e441f3c079aa06d4b48bbed05cad087855b9ad2a5e6dc43d5ee2b17c3"
|
|
2539
2539
|
},
|
|
2540
2540
|
{
|
|
2541
2541
|
"package": "@xemahq/xema",
|
|
2542
|
-
"version": "0.
|
|
2542
|
+
"version": "0.35.0",
|
|
2543
2543
|
"path": "edition/operation-requirements.mjs",
|
|
2544
2544
|
"sha256": "sha256:ffe6fdc9da6b2974270fc86c11be0c41d0bc6d85c8e3c137d3c3bc00a72fc4f6"
|
|
2545
2545
|
},
|
|
2546
2546
|
{
|
|
2547
2547
|
"package": "@xemahq/xema",
|
|
2548
|
-
"version": "0.
|
|
2548
|
+
"version": "0.35.0",
|
|
2549
2549
|
"path": "edition/pin-source.mjs",
|
|
2550
|
-
"sha256": "sha256:
|
|
2550
|
+
"sha256": "sha256:a23fef4698ef66ba9de4a1aabe3eebe6487d1f105f3fc161bafc5a33b99a2440"
|
|
2551
2551
|
},
|
|
2552
2552
|
{
|
|
2553
2553
|
"package": "@xemahq/xema",
|
|
2554
|
-
"version": "0.
|
|
2554
|
+
"version": "0.35.0",
|
|
2555
2555
|
"path": "edition/source-authority.mjs",
|
|
2556
2556
|
"sha256": "sha256:858b607dde94e2abd00a341f569e18cfa567d4bff139c64a2d60abbb7de4c85e"
|
|
2557
2557
|
},
|
|
2558
2558
|
{
|
|
2559
2559
|
"package": "@xemahq/xema",
|
|
2560
|
-
"version": "0.
|
|
2560
|
+
"version": "0.35.0",
|
|
2561
2561
|
"path": "edition/strict-json.mjs",
|
|
2562
2562
|
"sha256": "sha256:f95778396df7f38b086f55d2ec4f3b6644a8aeff2b32282a62829145fb544562"
|
|
2563
2563
|
},
|
|
2564
2564
|
{
|
|
2565
2565
|
"package": "@xemahq/xema",
|
|
2566
|
-
"version": "0.
|
|
2566
|
+
"version": "0.35.0",
|
|
2567
2567
|
"path": "edition/string-order.mjs",
|
|
2568
2568
|
"sha256": "sha256:b3210cd273e4b2e01fb8b1a91e28734cd211d4010c080d0ce95f5e5ff631cffb"
|
|
2569
2569
|
},
|
|
2570
2570
|
{
|
|
2571
2571
|
"package": "@xemahq/xema",
|
|
2572
|
-
"version": "0.
|
|
2572
|
+
"version": "0.35.0",
|
|
2573
2573
|
"path": "edition/trusted-executable.mjs",
|
|
2574
2574
|
"sha256": "sha256:37d46cdbc999ddd55d4ea8aeed4850ddd1efde89ed7b532f5a2ddec87105d796"
|
|
2575
2575
|
},
|
|
2576
2576
|
{
|
|
2577
2577
|
"package": "@xemahq/xema",
|
|
2578
|
-
"version": "0.
|
|
2578
|
+
"version": "0.35.0",
|
|
2579
2579
|
"path": "edition/verifier-binding.mjs",
|
|
2580
2580
|
"sha256": "sha256:80732658617f5255258c538eeb2383331ec8553b44b58d552b567845a7b916aa"
|
|
2581
2581
|
},
|
|
2582
2582
|
{
|
|
2583
2583
|
"package": "@xemahq/xema",
|
|
2584
|
-
"version": "0.
|
|
2584
|
+
"version": "0.35.0",
|
|
2585
2585
|
"path": "edition/verifier-files.mjs",
|
|
2586
2586
|
"sha256": "sha256:cdd14a20a63f0f194a8abb55680a5a7e3ced1f09f8b210a90d349e655f6de0c0"
|
|
2587
2587
|
},
|
|
2588
2588
|
{
|
|
2589
2589
|
"package": "@xemahq/xema",
|
|
2590
|
-
"version": "0.
|
|
2590
|
+
"version": "0.35.0",
|
|
2591
2591
|
"path": "edition/verifier-provenance.mjs",
|
|
2592
2592
|
"sha256": "sha256:3e2fb8e07cf1f66b8ad6c7ac2555fe3af64ec7abeb07cecbaf1939a18c2b3663"
|
|
2593
2593
|
},
|
|
2594
2594
|
{
|
|
2595
2595
|
"package": "@xemahq/xema",
|
|
2596
|
-
"version": "0.
|
|
2596
|
+
"version": "0.35.0",
|
|
2597
2597
|
"path": "edition/verify-image-evidence.mjs",
|
|
2598
2598
|
"sha256": "sha256:62f83f8bb835f360535402d12657156d2c9d6e651b426c2b30a04a2dc2f8d3e8"
|
|
2599
2599
|
},
|
|
2600
2600
|
{
|
|
2601
2601
|
"package": "@xemahq/xema",
|
|
2602
|
-
"version": "0.
|
|
2602
|
+
"version": "0.35.0",
|
|
2603
2603
|
"path": "edition/workspace-sections.mjs",
|
|
2604
2604
|
"sha256": "sha256:058c849767434e9ba911d9310092bede42e3ef959c75bbd35ff8879dc21ce3ff"
|
|
2605
2605
|
},
|
|
2606
2606
|
{
|
|
2607
2607
|
"package": "@xemahq/xema",
|
|
2608
|
-
"version": "0.
|
|
2608
|
+
"version": "0.35.0",
|
|
2609
2609
|
"path": "scripts/build-evidence-verifier.mjs",
|
|
2610
2610
|
"sha256": "sha256:5a688d20c47dc88aff37aa0c6cc02daf2c370a5bc610fef8446ce7be222d4c0b"
|
|
2611
2611
|
},
|
package/edition/README.md
CHANGED
|
@@ -75,6 +75,7 @@ xema edition list --editions-root <dir>
|
|
|
75
75
|
xema edition locks <id> --channel <release|develop> # validated locks as JSON; an unsealed distribution lock is read on develop only
|
|
76
76
|
xema edition artifact <id> --channel release --registry <ns> --coordinate <tag> --engine-image <repo>@sha256:<digest> --out <dir>
|
|
77
77
|
xema edition develop <freeze-sources|resolve|target|check-existing> ...
|
|
78
|
+
# develop tag = sha256 of { cli, engine, pins, sourceImages }; target and check-existing take --engine-image, and a customer's --source-image
|
|
78
79
|
xema edition develop check-overrides --overrides <json> --data-root <dir> # data root only
|
|
79
80
|
xema edition chart-set <resolve|source-commit> ...
|
|
80
81
|
xema edition arming --channel <release|develop> --data-root <dir> --kernel <dir>
|
|
@@ -24,11 +24,11 @@ if (isEntryModule(import.meta.url)) {
|
|
|
24
24
|
configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel]);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/** `{ id, out }` of
|
|
27
|
+
/** `{ id, out }` of `<distribution-id> --out <dir>` (the argv AFTER the `catalogue` verb, which the edition dispatcher has already stripped); anything else is the usage error. */
|
|
28
28
|
export function parseCatalogueArgs(argv) {
|
|
29
|
-
const [
|
|
30
|
-
if (
|
|
31
|
-
throw new Error('usage: catalogue <distribution-id> --out <dir>');
|
|
29
|
+
const [id, flag, out, ...extra] = argv;
|
|
30
|
+
if (id === undefined || flag !== '--out' || out === undefined || out.startsWith('--') || extra.length > 0) {
|
|
31
|
+
throw new Error('usage: xema edition catalogue <distribution-id> --out <dir>');
|
|
32
32
|
}
|
|
33
33
|
assertCanonicalDistributionId(id);
|
|
34
34
|
return { id, out };
|
|
@@ -5,20 +5,23 @@
|
|
|
5
5
|
// declares and nothing else (the vendor repositories are the catalogue's, not
|
|
6
6
|
// the customer's). The digest of the catalogue artifact the edition was
|
|
7
7
|
// resolved against is frozen into the descriptor's provenance pins as
|
|
8
|
-
// `catalogue`; the
|
|
8
|
+
// `catalogue`; the artifact tag (developEditionTag) covers it. The vendor image
|
|
9
9
|
// rows arrive sealed from the catalogue; only the customer's own image rows are
|
|
10
10
|
// sealed against the develop image repositories.
|
|
11
11
|
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
|
|
12
14
|
import { sealLock } from './biome-content.mjs';
|
|
13
15
|
import { parseCatalogueRef } from './catalogue-fetch.mjs';
|
|
14
16
|
import { readCataloguePin } from './catalogue-pin.mjs';
|
|
17
|
+
import { developImageRef } from './develop-image-coordinate.mjs';
|
|
15
18
|
import { sealDevelopImageSubjects } from './develop-seal.mjs';
|
|
16
19
|
import { assertCanonicalDistributionId } from './distribution-paths.mjs';
|
|
17
|
-
import { CATALOGUE_PIN_KEY, EditionChannel } from './edition-constants.mjs';
|
|
20
|
+
import { CATALOGUE_PIN_KEY, EditionChannel, EditionLockFile } from './edition-constants.mjs';
|
|
18
21
|
import { loadKernelDistributionSchemas } from './lib.mjs';
|
|
19
22
|
import { isSourcePinKey, sortedSnapshotPins, sourcePinKey } from './pin-source.mjs';
|
|
20
23
|
import { resolveCustomerEdition } from './resolve-customer-edition.mjs';
|
|
21
|
-
import { externalSourceTrees, sourceTreeLocator } from './resolve-images.mjs';
|
|
24
|
+
import { externalSourceTrees, imageRefForSubject, sourceTreeLocator } from './resolve-images.mjs';
|
|
22
25
|
import { readStrictJsonFile } from './strict-json.mjs';
|
|
23
26
|
|
|
24
27
|
const SHA_PATTERN = /^[0-9a-f]{40}$/;
|
|
@@ -116,3 +119,31 @@ export function customerDevelopFrame({ definitionFile, pinsFile }) {
|
|
|
116
119
|
const pins = customerDevelopPins({ pins: readStrictJsonFile(pinsFile), declaredSourceNames: declaredSourceNamesOf(rawDefinition), catalogueDigest: digest });
|
|
117
120
|
return { id: rawDefinition.id, rawDefinition, catalogueRef, catalogueDigest: digest, pins };
|
|
118
121
|
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The own image rows of an existing customer develop artifact are the rows this `--source-image` map composes:
|
|
125
|
+
* every subject owned by a biome drawn from a declared source is `<registry>/<prefix><subject>` mapped to its
|
|
126
|
+
* develop repository. The tag covers the map, but a tag is mutable registry state, so the pulled bytes are
|
|
127
|
+
* compared too. Throws naming the first row that differs.
|
|
128
|
+
*/
|
|
129
|
+
export function assertOwnImageRowsMatch({ artifactDir, sourceImages }) {
|
|
130
|
+
const lock = readStrictJsonFile(join(artifactDir, EditionLockFile.Distribution));
|
|
131
|
+
const imageLock = readStrictJsonFile(join(artifactDir, EditionLockFile.Images));
|
|
132
|
+
const sourceOf = new Map((lock.biomes ?? []).filter((biome) => biome.source !== undefined).map((biome) => [biome.id, biome.source.name]));
|
|
133
|
+
for (const subject of imageLock.subjects ?? []) {
|
|
134
|
+
const biomeId = (subject.owners ?? []).find((owner) => sourceOf.has(owner.biomeId))?.biomeId;
|
|
135
|
+
if (biomeId === undefined) {continue;}
|
|
136
|
+
const name = sourceOf.get(biomeId);
|
|
137
|
+
const image = sourceImages.get(name);
|
|
138
|
+
if (image === undefined) {
|
|
139
|
+
throw new Error(`the existing artifact draws biome "${biomeId}" from source "${name}", which has no --source-image; refusing to reuse it`);
|
|
140
|
+
}
|
|
141
|
+
const expected = developImageRef(imageRefForSubject(subject.id, image.imageRegistry, image.imageNamePrefix));
|
|
142
|
+
if (subject.imageRef !== expected) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`the existing artifact's own image row ${subject.kind}:${subject.id} is ${subject.imageRef}, not ${expected} ` +
|
|
145
|
+
'(the --source-image map differs); refusing to reuse it',
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -16,11 +16,14 @@
|
|
|
16
16
|
// declares (`freeze-sources --definition` writes it); the catalogue digest is frozen into the descriptor's
|
|
17
17
|
// provenance pins as `catalogue`. The id is the definition's. Takes --data-root, --editions-root,
|
|
18
18
|
// --sources-root, --cache-dir (no --deploy-root, no --kernel: the kernel is the catalogue's, from npm).
|
|
19
|
-
// target <id> --pins <file> --registry <root>
|
|
20
|
-
// print <root>/<develop segment>/xema-edition-<id>:<
|
|
19
|
+
// target <id> --pins <file> --registry <root> --engine-image <repository>@sha256:<digest>
|
|
20
|
+
// print <root>/<develop segment>/xema-edition-<id>:<tag>, the tag being sha256 of the canonical
|
|
21
|
+
// { cli, engine, pins, sourceImages } (developEditionTag): every input of the bytes the artifact records,
|
|
22
|
+
// so the same pins with another engine, source-image map or CLI version are another artifact. The root is always the caller's
|
|
21
23
|
// registry root (e.g. registry.example.com/acme); the develop namespace segment and the repository
|
|
22
24
|
// prefix come from edition-contract.json, and no root is implied.
|
|
23
|
-
// target --definition <file> --pins <file> --registry <root>
|
|
25
|
+
// target --definition <file> --pins <file> --registry <root> --engine-image <repository>@sha256:<digest>
|
|
26
|
+
// [--source-image <source>=<registry>/<prefix>]...
|
|
24
27
|
// the same for a customer edition (id = the definition's; the tag covers the pinned catalogue digest).
|
|
25
28
|
// freeze-graph --data-sha <sha> --kernel-sha <sha>
|
|
26
29
|
// --deploy-sha <sha> [--editions-root-url <https url>]
|
|
@@ -31,9 +34,9 @@
|
|
|
31
34
|
// check-existing <id> --pins <file> --artifact <dir> --engine-image <repository>@sha256:<digest>
|
|
32
35
|
// refuse to reuse a pulled artifact whose descriptor disagrees with this snapshot.
|
|
33
36
|
// check-existing --definition <file> --pins <file> --artifact <dir> --engine-image <repository>@sha256:<digest>
|
|
34
|
-
// [--catalogue-username <name> --catalogue-token-env <VAR>]
|
|
37
|
+
// [--catalogue-username <name> --catalogue-token-env <VAR>] [--source-image <source>=<registry>/<prefix>]...
|
|
35
38
|
// the same for a customer artifact; its descriptor's catalogue provenance pin must also equal the
|
|
36
|
-
// pinned develop catalogue digest. It fetches that catalogue (as `resolve --definition` does) to take the
|
|
39
|
+
// 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
|
|
37
40
|
// kernel from it. Takes --data-root, --cache-dir.
|
|
38
41
|
// check-overrides --overrides <json>
|
|
39
42
|
// refuse a pins override that would select the tooling this run executes
|
|
@@ -55,7 +58,7 @@ import { fetchCatalogue } from './catalogue-fetch.mjs';
|
|
|
55
58
|
import { readCataloguePin } from './catalogue-pin.mjs';
|
|
56
59
|
import { writeCatalogue } from './catalogue.mjs';
|
|
57
60
|
import { CATALOGUE_TOKEN_ENV_FLAG, CATALOGUE_USERNAME_FLAG, DEFINITION_FLAG, SOURCE_IMAGE_FLAG } from './customer-flags.mjs';
|
|
58
|
-
import { customerDevelopFrame, resolveCustomerDevelopEdition } from './develop-customer-edition.mjs';
|
|
61
|
+
import { assertOwnImageRowsMatch, customerDevelopFrame, resolveCustomerDevelopEdition } from './develop-customer-edition.mjs';
|
|
59
62
|
import { resolveDevelopEdition } from './develop-edition-resolve.mjs';
|
|
60
63
|
import { assertNoToolingOverride, freezeCustomerSources, freezeGraph, freezeSources, lsRemoteDevelopHead } from './develop-freeze.mjs';
|
|
61
64
|
import { assertCanonicalDistributionId } from './distribution-paths.mjs';
|
|
@@ -68,7 +71,7 @@ import { holdSource } from './git-source-auth.mjs';
|
|
|
68
71
|
import { adoptCatalogueKernel } from './kernel-package.mjs';
|
|
69
72
|
import { loadKernelDistributionSchemas, serializeJson } from './lib.mjs';
|
|
70
73
|
import { snapshotPinSource } from './pin-source.mjs';
|
|
71
|
-
import { parseSourceImages } from './resolve-customer-edition.mjs';
|
|
74
|
+
import { assertSourceImagesCoverDefinition, parseSourceImages } from './resolve-customer-edition.mjs';
|
|
72
75
|
import { composeEdition } from './resolve-distribution.mjs';
|
|
73
76
|
import { readStrictJsonFile } from './strict-json.mjs';
|
|
74
77
|
|
|
@@ -111,10 +114,10 @@ const COMMANDS = Object.freeze({
|
|
|
111
114
|
},
|
|
112
115
|
target: {
|
|
113
116
|
positional: true,
|
|
114
|
-
values: ['--pins', '--registry'],
|
|
117
|
+
values: ['--pins', '--registry', '--engine-image'],
|
|
115
118
|
switches: [],
|
|
116
|
-
required: ['--pins', '--registry'],
|
|
117
|
-
customer: { values: [DEFINITION_FLAG], required: ['--pins', '--registry'], refused: [] },
|
|
119
|
+
required: ['--pins', '--registry', '--engine-image'],
|
|
120
|
+
customer: { values: [DEFINITION_FLAG, SOURCE_IMAGE_FLAG], required: ['--pins', '--registry', '--engine-image'], refused: [] },
|
|
118
121
|
},
|
|
119
122
|
'freeze-graph': {
|
|
120
123
|
positional: false,
|
|
@@ -135,7 +138,7 @@ const COMMANDS = Object.freeze({
|
|
|
135
138
|
values: ['--pins', '--artifact', '--engine-image'],
|
|
136
139
|
switches: [],
|
|
137
140
|
required: ['--pins', '--artifact', '--engine-image'],
|
|
138
|
-
customer: { values: [DEFINITION_FLAG, ...CREDENTIAL_FLAGS], required: ['--pins', '--artifact', '--engine-image'], refused: [] },
|
|
141
|
+
customer: { values: [DEFINITION_FLAG, ...CREDENTIAL_FLAGS, SOURCE_IMAGE_FLAG], required: ['--pins', '--artifact', '--engine-image'], refused: [] },
|
|
139
142
|
},
|
|
140
143
|
});
|
|
141
144
|
|
|
@@ -223,6 +226,7 @@ function fetchPinnedDevelopCatalogue(options, env) {
|
|
|
223
226
|
async function resolveCustomer(options, env) {
|
|
224
227
|
const { catalogue, digest } = await fetchPinnedDevelopCatalogue(options, env);
|
|
225
228
|
const rawDefinition = readStrictJsonFile(options[DEFINITION_FLAG]);
|
|
229
|
+
const sourceImages = parseSourceImages(options[SOURCE_IMAGE_FLAG] ?? []);
|
|
226
230
|
// The catalogue's kernel is adopted inside the resolve, before any kernel schema is loaded; the evidence trust
|
|
227
231
|
// state loads kernel schemas, so it is read after.
|
|
228
232
|
const { id, pins, ...resolved } = await resolveCustomerDevelopEdition({
|
|
@@ -230,11 +234,11 @@ async function resolveCustomer(options, env) {
|
|
|
230
234
|
catalogue,
|
|
231
235
|
catalogueDigest: digest,
|
|
232
236
|
pins: readStrictJsonFile(options['--pins']),
|
|
233
|
-
sourceImages
|
|
237
|
+
sourceImages,
|
|
234
238
|
env,
|
|
235
239
|
});
|
|
236
240
|
const evidenceTrustPolicy = await descriptorEvidenceTrust({ channel: EditionChannel.Develop });
|
|
237
|
-
const target = developEditionTarget(id, pins, { registry: options['--registry'] });
|
|
241
|
+
const target = developEditionTarget(id, pins, { registry: options['--registry'], engineImage: options['--engine-image'], sourceImages });
|
|
238
242
|
writeEditionArtifact({
|
|
239
243
|
outDir: resolve(options['--out']),
|
|
240
244
|
channel: EditionChannel.Develop,
|
|
@@ -254,6 +258,7 @@ async function resolveCustomer(options, env) {
|
|
|
254
258
|
/** A customer check-existing: the pinned catalogue is fetched to take its kernel; the digest fetched is the digest the artifact is compared with. */
|
|
255
259
|
export async function checkExistingCustomer(options, env, { adopt = adoptCatalogueKernel } = {}) {
|
|
256
260
|
const frame = customerDevelopFrame({ definitionFile: options[DEFINITION_FLAG], pinsFile: options['--pins'] });
|
|
261
|
+
const sourceImages = customerSourceImages(options, frame.rawDefinition);
|
|
257
262
|
const { catalogue, digest } = await fetchPinnedDevelopCatalogue(options, env);
|
|
258
263
|
if (digest !== frame.catalogueDigest) {
|
|
259
264
|
throw new Error(`the fetched catalogue is ${digest}, not the pinned ${frame.catalogueDigest}`);
|
|
@@ -268,6 +273,14 @@ export async function checkExistingCustomer(options, env, { adopt = adoptCatalog
|
|
|
268
273
|
engineImage: options['--engine-image'],
|
|
269
274
|
catalogueDigest: frame.catalogueDigest,
|
|
270
275
|
});
|
|
276
|
+
assertOwnImageRowsMatch({ artifactDir: resolve(options['--artifact']), sourceImages });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** The parsed --source-image map of a customer verb, covering exactly the definition's declared sources. */
|
|
280
|
+
function customerSourceImages(options, rawDefinition) {
|
|
281
|
+
const sourceImages = parseSourceImages(options[SOURCE_IMAGE_FLAG] ?? []);
|
|
282
|
+
assertSourceImagesCoverDefinition(rawDefinition, sourceImages);
|
|
283
|
+
return sourceImages;
|
|
271
284
|
}
|
|
272
285
|
|
|
273
286
|
async function run(argv, env) {
|
|
@@ -276,17 +289,18 @@ async function run(argv, env) {
|
|
|
276
289
|
case 'target': {
|
|
277
290
|
if (customer) {
|
|
278
291
|
const frame = customerDevelopFrame({ definitionFile: options[DEFINITION_FLAG], pinsFile: options['--pins'] });
|
|
279
|
-
|
|
292
|
+
const sourceImages = customerSourceImages(options, frame.rawDefinition);
|
|
293
|
+
console.log(developEditionTarget(frame.id, frame.pins, { registry: options['--registry'], engineImage: options['--engine-image'], sourceImages }));
|
|
280
294
|
return;
|
|
281
295
|
}
|
|
282
|
-
console.log(developEditionTarget(id, readStrictJsonFile(options['--pins']), { registry: options['--registry'] }));
|
|
296
|
+
console.log(developEditionTarget(id, readStrictJsonFile(options['--pins']), { registry: options['--registry'], engineImage: options['--engine-image'] }));
|
|
283
297
|
return;
|
|
284
298
|
}
|
|
285
299
|
case 'resolve': {
|
|
286
300
|
if (customer) {return resolveCustomer(options, env);}
|
|
287
301
|
const pins = readStrictJsonFile(options['--pins']);
|
|
288
302
|
const workflow = options['--local'] ? LOCAL_WORKFLOW : workflowFromEnv(env);
|
|
289
|
-
const target = developEditionTarget(id, pins, { registry: options['--registry'] });
|
|
303
|
+
const target = developEditionTarget(id, pins, { registry: options['--registry'], engineImage: options['--engine-image'] });
|
|
290
304
|
const evidenceTrustPolicy = await descriptorEvidenceTrust({ channel: EditionChannel.Develop });
|
|
291
305
|
const { catalogue, ...resolved } = await resolveDevelopEdition({ id, pins, env });
|
|
292
306
|
const outDir = resolve(options['--out']);
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
// from edition-contract.json), addressed by digest, whose titled layers are
|
|
6
6
|
// `xema-edition.json` (the descriptor) plus the lock files. It is written to a
|
|
7
7
|
// scratch directory; the workflow pushes it with oras and signs the digest.
|
|
8
|
-
// Tags are for humans only (develop: the
|
|
8
|
+
// Tags are for humans only (develop: the hash of every input, developEditionTag; release: the
|
|
9
9
|
// coordinate). Admission rests on the signature of the manifest digest, never on
|
|
10
10
|
// a channel label: no reader branches on `provenance.channel`.
|
|
11
11
|
//
|
|
12
12
|
// The artifact is never written to git, so the writer REFUSES a target inside
|
|
13
13
|
// either the data tree or the editions root.
|
|
14
14
|
|
|
15
|
-
import { existsSync, mkdirSync, realpathSync, writeFileSync } from 'node:fs';
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
16
16
|
import { basename, dirname, join, relative, resolve, sep, isAbsolute } from 'node:path';
|
|
17
17
|
|
|
18
18
|
import { assertCanonicalDistributionId } from './distribution-paths.mjs';
|
|
@@ -29,8 +29,8 @@ import {
|
|
|
29
29
|
EvidenceTrustState,
|
|
30
30
|
} from './edition-constants.mjs';
|
|
31
31
|
import { dataRoot, editionsRoot } from './edition-workspace.mjs';
|
|
32
|
-
import { serializeJson } from './lib.mjs';
|
|
33
|
-
import {
|
|
32
|
+
import { serializeJson, sha256Hex } from './lib.mjs';
|
|
33
|
+
import { sortedSnapshotPins } from './pin-source.mjs';
|
|
34
34
|
import { readStrictJsonFile } from './strict-json.mjs';
|
|
35
35
|
|
|
36
36
|
/** Which GitHub Actions run produced the artifact; recorded in its descriptor. */
|
|
@@ -217,9 +217,49 @@ export function editionTarget(distributionId, registryRoot, channel, tag) {
|
|
|
217
217
|
return `${editionRepository(distributionId, registryRoot, channel)}:${tag}`;
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
/**
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
/** The version of this CLI, the writer of every descriptor and lock: read lazily, so a bundle that never tags does not need it. */
|
|
221
|
+
let cliVersionMemo;
|
|
222
|
+
export function cliVersion() {
|
|
223
|
+
if (cliVersionMemo === undefined) {
|
|
224
|
+
const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
225
|
+
if (typeof version !== 'string' || version.length === 0) {throw new Error('@xemahq/xema: no "version" in its package.json');}
|
|
226
|
+
cliVersionMemo = version;
|
|
227
|
+
}
|
|
228
|
+
return cliVersionMemo;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** `{ <source>: "<registry>/<prefix>" }` sorted by source: the canonical form of a parsed --source-image map. */
|
|
232
|
+
export function canonicalSourceImages(sourceImages = new Map()) {
|
|
233
|
+
return Object.fromEntries(
|
|
234
|
+
[...sourceImages.entries()]
|
|
235
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
236
|
+
.map(([name, { imageRegistry, imageNamePrefix }]) => [name, `${imageRegistry}/${imageNamePrefix}`]),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A develop edition's tag: sha256 of the canonical `{ cli, engine, pins, sourceImages }`. Every input of
|
|
242
|
+
* the bytes the artifact records is inside it. The pins fix the locks and the descriptor's pins; the
|
|
243
|
+
* descriptor's `engine.image` is not a pin; a customer's `--source-image` map composes its own image rows
|
|
244
|
+
* (`<registry>/<prefix><subject>`) and is not a pin either; and the CLI version is the writer of every
|
|
245
|
+
* byte. A tag over the pins alone named two artifacts that differ in any of the others, and a re-run could
|
|
246
|
+
* only refuse the existing one (`check-existing`) or reuse a stale one. `provenance.workflow` (the run that
|
|
247
|
+
* wrote the bytes) is the one recorded field outside it: a fact about the artifact, not an input to it.
|
|
248
|
+
*/
|
|
249
|
+
export function developEditionTag({ pins, engineImage, sourceImages = new Map(), version = cliVersion() }) {
|
|
250
|
+
return sha256Hex(
|
|
251
|
+
serializeJson({
|
|
252
|
+
cli: version,
|
|
253
|
+
engine: assertEngineImage(engineImage),
|
|
254
|
+
pins: sortedSnapshotPins(pins),
|
|
255
|
+
sourceImages: canonicalSourceImages(sourceImages),
|
|
256
|
+
}),
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** A develop edition's target: the tag is `developEditionTag` of the pins, the engine and the source images. */
|
|
261
|
+
export function developEditionTarget(distributionId, pins, { registry, engineImage, sourceImages }) {
|
|
262
|
+
return editionTarget(distributionId, registry, EditionChannel.Develop, developEditionTag({ pins, engineImage, sourceImages }));
|
|
223
263
|
}
|
|
224
264
|
|
|
225
265
|
function realpathOfPlanned(path) {
|
|
@@ -286,8 +326,8 @@ export function writeEditionArtifact({
|
|
|
286
326
|
}
|
|
287
327
|
|
|
288
328
|
/**
|
|
289
|
-
* An existing
|
|
290
|
-
* is a hash of
|
|
329
|
+
* An existing develop tag is reused only if it IS this snapshot: the tag
|
|
330
|
+
* is a hash of its inputs, but a tag is mutable registry state, so the pulled
|
|
291
331
|
* descriptor must say the same edition, the same channel, exactly the same pins,
|
|
292
332
|
* the same engine image, the same evidence trust state and the same lock set. A customer artifact (`catalogueDigest` given) must also record that digest
|
|
293
333
|
* as its catalogue provenance pin. Throws naming the first difference.
|
package/edition/pin-source.mjs
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// sources/<name> one per `sources[]` entry declared in the chain
|
|
22
22
|
|
|
23
23
|
import { workspaceSection } from './edition-config.mjs';
|
|
24
|
-
import { compareStrings
|
|
24
|
+
import { compareStrings } from './lib.mjs';
|
|
25
25
|
import { PinRole } from './workspace-sections.mjs';
|
|
26
26
|
|
|
27
27
|
export const PinSourceKind = Object.freeze({
|
|
@@ -154,11 +154,6 @@ export function sortedSnapshotPins(pins) {
|
|
|
154
154
|
return Object.fromEntries(Object.entries(pins).sort(([a], [b]) => compareStrings(a, b)));
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
/** sha256 hex of the canonical JSON of the pins sorted by key: the snapshot's identity. */
|
|
158
|
-
export function snapshotHash(pins) {
|
|
159
|
-
return sha256Hex(serializeJson(sortedSnapshotPins(pins)));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
157
|
/** A declared source with its ref replaced by the snapshot pin; identity under a published pin source. */
|
|
163
158
|
export function pinnedSource(source, pinSource = PUBLISHED_PIN_SOURCE) {
|
|
164
159
|
if (pinSource?.kind !== PinSourceKind.Snapshot) {return source;}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// release-edition-artifact — write the OCI artifact of a RELEASE edition from
|
|
3
3
|
// the committed, sealed locks of the editions root.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
5
|
+
// <id> --channel release --registry <root> --coordinate <tag>
|
|
6
6
|
// --engine-image <repository>@sha256:<digest> --out <dir> [--local]
|
|
7
7
|
//
|
|
8
8
|
// The artifact has the layout of every edition (edition-artifact.mjs): the
|
|
@@ -28,6 +28,7 @@ import { loadKernelDistributionSchemas, validateImmutableImageLock } from './lib
|
|
|
28
28
|
import { releaseCatalogue } from './release-catalogue.mjs';
|
|
29
29
|
import { assertReleaseEditionTree } from './release-channel-guard.mjs';
|
|
30
30
|
import { externalSourceTrees, sourceTreeLocator } from './resolve-images.mjs';
|
|
31
|
+
import { readSealedDistributionLock } from './sealed-distribution-lock.mjs';
|
|
31
32
|
import { createPinnedSourceAuthority } from './source-authority.mjs';
|
|
32
33
|
|
|
33
34
|
if (isEntryModule(import.meta.url)) {
|
|
@@ -38,10 +39,9 @@ const VALUE_FLAGS = Object.freeze(['--channel', '--registry', '--coordinate', '-
|
|
|
38
39
|
const REQUIRED_FLAGS = VALUE_FLAGS;
|
|
39
40
|
const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
40
41
|
|
|
41
|
-
/** Parse
|
|
42
|
+
/** Parse `<id> --flag value ...` (the argv AFTER the `artifact` verb, which the edition dispatcher has already stripped); only the release channel is written by this verb. */
|
|
42
43
|
export function parseReleaseArtifactArgs(argv) {
|
|
43
|
-
const
|
|
44
|
-
if (command !== 'artifact') {throw new Error(`unknown command ${JSON.stringify(command)}; expected artifact`);}
|
|
44
|
+
const rest = argv;
|
|
45
45
|
const options = {};
|
|
46
46
|
const positional = [];
|
|
47
47
|
for (let i = 0; i < rest.length; i += 1) {
|
|
@@ -100,17 +100,11 @@ export async function readCommittedEdition(id, root = editionsRoot()) {
|
|
|
100
100
|
const schemas = await loadKernelDistributionSchemas();
|
|
101
101
|
const imageLock = validateImmutableImageLock(schemas.parseImageLock(read(EditionLockFile.Images)), schemas);
|
|
102
102
|
assertSealedImageLock(imageLock, join(dir, EditionLockFile.Images));
|
|
103
|
-
const distributionLock =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
throw new Error(
|
|
109
|
-
`${join(dir, EditionLockFile.Distribution)} is not a sealed distribution lock, so nothing is published from it. ` +
|
|
110
|
-
`Run \`xema edition seal ${id}\` first. (${error.message})`,
|
|
111
|
-
{ cause: error },
|
|
112
|
-
);
|
|
113
|
-
}
|
|
103
|
+
const { raw: distributionLock, sealed } = readSealedDistributionLock({
|
|
104
|
+
schemas,
|
|
105
|
+
lockPath: join(dir, EditionLockFile.Distribution),
|
|
106
|
+
distributionId: id,
|
|
107
|
+
});
|
|
114
108
|
const authority = createPinnedSourceAuthority({ pins: (await editionPinnedInputs(id, { schemas })).repositoryPins });
|
|
115
109
|
await verifyBiomeContentHashes({
|
|
116
110
|
schemas,
|
|
@@ -84,14 +84,21 @@ export function parseSourceImages(values) {
|
|
|
84
84
|
* The definition's sources with the configured registry/prefix injected; every declared source needs its --source-image.
|
|
85
85
|
* With `pins` (a develop customer edition) each source's ref is the frozen commit of its `sources/<name>` pin.
|
|
86
86
|
*/
|
|
87
|
-
|
|
87
|
+
/** Every --source-image names a declared source, and every declared source has one: the registry is configuration and has no default. */
|
|
88
|
+
export function assertSourceImagesCoverDefinition(definition, sourceImages) {
|
|
88
89
|
const declared = new Set((definition.sources ?? []).map((source) => source.name));
|
|
89
90
|
for (const name of sourceImages.keys()) {
|
|
90
91
|
if (!declared.has(name)) {throw new Error(`--source-image names source "${name}", which the definition does not declare (declared: ${[...declared].join(', ') || 'none'})`);}
|
|
91
92
|
}
|
|
93
|
+
for (const name of declared) {
|
|
94
|
+
if (!sourceImages.has(name)) {throw new Error(`source "${name}" has no --source-image ${name}=<registry>/<prefix>: the registry is configuration and has no default`);}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function withSourceImages(definition, sourceImages, pins) {
|
|
99
|
+
assertSourceImagesCoverDefinition(definition, sourceImages);
|
|
92
100
|
return (definition.sources ?? []).map((source) => {
|
|
93
101
|
const image = sourceImages.get(source.name);
|
|
94
|
-
if (image === undefined) {throw new Error(`source "${source.name}" has no --source-image ${source.name}=<registry>/<prefix>: the registry is configuration and has no default`);}
|
|
95
102
|
const pinned = pins === undefined ? source : pinnedSource(source, snapshotPinSource(pins));
|
|
96
103
|
return { ...pinned, ...image };
|
|
97
104
|
});
|
|
@@ -98,8 +98,9 @@
|
|
|
98
98
|
// state a pin advance leaves, owner ruling 2026-09-29), as
|
|
99
99
|
// ONE line on stdout, exit 0. A malformed, empty or
|
|
100
100
|
// PARTIALLY sealed lock exits 1: a broken artifact.
|
|
101
|
-
// OFFLINE and structural: it reads the
|
|
102
|
-
// else
|
|
101
|
+
// OFFLINE and structural: it reads the locks and nothing
|
|
102
|
+
// else (the distribution lock through the built kernel,
|
|
103
|
+
// `--kernel`, only when the images lock is PUBLISHABLE). That is deliberate rather than a shortcut — a
|
|
103
104
|
// publication gate that needs a registry credential can
|
|
104
105
|
// fail for reasons that have nothing to do with the artifact
|
|
105
106
|
// being published, and a gate people learn to re-run is a
|
|
@@ -133,6 +134,7 @@ import { assertPlatformCanParseLock } from './platform-lock-readability.mjs';
|
|
|
133
134
|
import { assertReleaseEditionTree } from './release-channel-guard.mjs';
|
|
134
135
|
import { commandForSelf } from './remedy-command.mjs';
|
|
135
136
|
import { externalSourceTrees, sourceTreeLocator } from './resolve-images.mjs';
|
|
137
|
+
import { readSealedDistributionLock } from './sealed-distribution-lock.mjs';
|
|
136
138
|
import { committedRepositoryPins, createPinnedSourceAuthority } from './source-authority.mjs';
|
|
137
139
|
import { readStrictJsonFile } from './strict-json.mjs';
|
|
138
140
|
import {
|
|
@@ -306,10 +308,10 @@ export const PublicationVerdict = Object.freeze({
|
|
|
306
308
|
* some services by digest and silently leave the rest un-pinned.
|
|
307
309
|
*
|
|
308
310
|
* IT CHECKS SHAPE AS WELL AS PRESENCE. This gate reads the lock with
|
|
309
|
-
* `JSON.parse
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
311
|
+
* `JSON.parse`, so the kernel's digest regex is not upstream of it and this
|
|
312
|
+
* file's own `SHA256_DIGEST` is the only rule on the images lock. (The sealed
|
|
313
|
+
* grammar of the distribution lock is the kernel's, asked of it separately once
|
|
314
|
+
* the images lock reads PUBLISHABLE.)
|
|
313
315
|
*/
|
|
314
316
|
export function classifyPublicationState({
|
|
315
317
|
distributionId,
|
|
@@ -598,19 +600,12 @@ async function main() {
|
|
|
598
600
|
);
|
|
599
601
|
}
|
|
600
602
|
|
|
601
|
-
// PUBLICATION GATE — offline and structural.
|
|
602
|
-
//
|
|
603
|
-
//
|
|
603
|
+
// PUBLICATION GATE — offline (no network, no credential) and structural. The images lock is
|
|
604
|
+
// classified with no kernel at all. A PUBLISHABLE verdict then also reads the distribution lock
|
|
605
|
+
// through the BUILT kernel (`readSealedDistributionLock`, the predicate `xema edition artifact`
|
|
606
|
+
// uses), so the publication workflow provisions the built kernel before it asks (`--kernel`).
|
|
607
|
+
// It returns BEFORE `loadImageLockForVerification`, which needs sibling repositories and the registry.
|
|
604
608
|
//
|
|
605
|
-
// The distinction is the IMPORT versus the CALL, and getting it wrong once
|
|
606
|
-
// cost a needless refactor: importing `verify-image-platforms.mjs`,
|
|
607
|
-
// `oras.mjs` and `lib.mjs` is all FINE without a kernel —
|
|
608
|
-
// measured, all three load in a flat checkout — because
|
|
609
|
-
// `loadKernelDistributionSchemas()` is invoked at USE time, not at module
|
|
610
|
-
// load. What fails in the publication checkout is CALLING
|
|
611
|
-
// `loadImageLockForVerification`. So the fix is to not call it, which is what
|
|
612
|
-
// this early return does; making the imports dynamic bought nothing and was
|
|
613
|
-
// reverted.
|
|
614
609
|
// EVERY mode, the offline publication verdict included: a release edition
|
|
615
610
|
// never carries develop content, whatever the sealer is asked to do with it.
|
|
616
611
|
assertReleaseEditionTree(join(editionsRoot(), distributionId));
|
|
@@ -627,6 +622,13 @@ async function main() {
|
|
|
627
622
|
// including an empty stdout from a crash — is a failure there, never a
|
|
628
623
|
// withhold. The human explanation goes to stderr.
|
|
629
624
|
if (state.verdict === PublicationVerdict.Publishable) {
|
|
625
|
+
// The artifact verb reads the distribution lock through this same predicate; a verdict that
|
|
626
|
+
// passed an unsealed one would be the first thing `xema edition artifact` contradicts.
|
|
627
|
+
readSealedDistributionLock({
|
|
628
|
+
schemas: await loadKernelDistributionSchemas(),
|
|
629
|
+
lockPath: join(editionsRoot(), distributionId, 'distribution.lock.json'),
|
|
630
|
+
distributionId,
|
|
631
|
+
});
|
|
630
632
|
console.error(
|
|
631
633
|
`[dist:seal-digests --publication-verdict] "${distributionId}": all ` +
|
|
632
634
|
`${state.subjectCount} subject(s) carry a canonical imageDigest; publishable. ` +
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// sealed-distribution-lock — the one predicate for "this committed distribution.lock.json is the SEALED grammar".
|
|
2
|
+
// `xema edition artifact` and the `xema edition seal --publication-verdict` gate both ask it, so the verdict can
|
|
3
|
+
// never say PUBLISHABLE for a lock the artifact verb then refuses.
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse the committed distribution lock at `lockPath` as the sealed grammar; an unsealed (or otherwise
|
|
9
|
+
* unparseable) lock is refused, naming the file. Returns the parsed sealed lock.
|
|
10
|
+
*/
|
|
11
|
+
export function readSealedDistributionLock({ schemas, lockPath, distributionId }) {
|
|
12
|
+
const raw = JSON.parse(readFileSync(lockPath, 'utf8'));
|
|
13
|
+
try {
|
|
14
|
+
return { raw, sealed: schemas.parseDistributionLock(raw) };
|
|
15
|
+
} catch (error) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`${lockPath} is not a sealed distribution lock, so nothing is published from it. ` +
|
|
18
|
+
`Run \`xema edition seal ${distributionId}\` first. (${error.message})`,
|
|
19
|
+
{ cause: error },
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/xema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/xema-dev/xema-cli.git",
|
|
@@ -92,6 +92,6 @@
|
|
|
92
92
|
"typecheck": "tsc --noEmit"
|
|
93
93
|
},
|
|
94
94
|
"xemaRelease": {
|
|
95
|
-
"sourceDigest": "sha256:
|
|
95
|
+
"sourceDigest": "sha256:5a8392acd12bf89ba47fdff3d9cbb4ec49b8194b218af9ebbfaea8d6ce16114f"
|
|
96
96
|
}
|
|
97
97
|
}
|