@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.
- package/README.md +70 -37
- package/dist/assets/distribution-assets.manifest.json +1 -2
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/dev.js +1 -0
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +7 -1
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/edition.d.ts +3 -0
- package/dist/commands/edition.d.ts.map +1 -0
- package/dist/commands/edition.js +46 -0
- package/dist/commands/edition.js.map +1 -0
- package/dist/commands/init.js +2 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/serve.js +1 -0
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/up.js +1 -0
- package/dist/commands/up.js.map +1 -1
- package/dist/dev/env-defaults.d.ts.map +1 -1
- package/dist/dev/env-defaults.js +5 -2
- package/dist/dev/env-defaults.js.map +1 -1
- package/dist/dev/infra-preflight.d.ts +3 -2
- package/dist/dev/infra-preflight.d.ts.map +1 -1
- package/dist/dev/infra-preflight.js +18 -8
- package/dist/dev/infra-preflight.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +54283 -0
- package/dist/edition/verify-image-evidence.bundle.provenance.json +2877 -0
- package/dist/runtime/config/schema.d.ts +1 -0
- package/dist/runtime/config/schema.d.ts.map +1 -1
- package/dist/runtime/custody/local-custody.d.ts +12 -0
- package/dist/runtime/custody/local-custody.d.ts.map +1 -0
- package/dist/runtime/custody/local-custody.js +22 -0
- package/dist/runtime/custody/local-custody.js.map +1 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.d.ts +18 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.js +50 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.js.map +1 -0
- package/dist/runtime/custody/openbao-custody-store.d.ts +18 -0
- package/dist/runtime/custody/openbao-custody-store.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-custody-store.js +55 -0
- package/dist/runtime/custody/openbao-custody-store.js.map +1 -0
- package/dist/runtime/custody/openbao-http-client.d.ts +34 -0
- package/dist/runtime/custody/openbao-http-client.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-http-client.js +154 -0
- package/dist/runtime/custody/openbao-http-client.js.map +1 -0
- package/dist/runtime/custody/openbao-local-constants.d.ts +22 -0
- package/dist/runtime/custody/openbao-local-constants.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-local-constants.js +48 -0
- package/dist/runtime/custody/openbao-local-constants.js.map +1 -0
- package/dist/runtime/custody/openbao-local-runner.d.ts +16 -0
- package/dist/runtime/custody/openbao-local-runner.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-local-runner.js +86 -0
- package/dist/runtime/custody/openbao-local-runner.js.map +1 -0
- package/dist/runtime/edition/run-entry.d.ts +9 -0
- package/dist/runtime/edition/run-entry.d.ts.map +1 -0
- package/dist/runtime/edition/run-entry.js +36 -0
- package/dist/runtime/edition/run-entry.js.map +1 -0
- package/dist/runtime/edition/verb-map.d.ts +60 -0
- package/dist/runtime/edition/verb-map.d.ts.map +1 -0
- package/dist/runtime/edition/verb-map.js +146 -0
- package/dist/runtime/edition/verb-map.js.map +1 -0
- package/dist/runtime/env-seeder.d.ts +2 -0
- package/dist/runtime/env-seeder.d.ts.map +1 -1
- package/dist/runtime/env-seeder.js +18 -3
- package/dist/runtime/env-seeder.js.map +1 -1
- package/dist/runtime/launch.d.ts +2 -1
- package/dist/runtime/launch.d.ts.map +1 -1
- package/dist/runtime/launch.js +31 -7
- package/dist/runtime/launch.js.map +1 -1
- package/dist/runtime/paths.d.ts +1 -2
- package/dist/runtime/paths.d.ts.map +1 -1
- package/dist/runtime/paths.js +0 -10
- package/dist/runtime/paths.js.map +1 -1
- package/dist/runtime/runner/chart-local-provenance.d.ts +20 -0
- package/dist/runtime/runner/chart-local-provenance.d.ts.map +1 -0
- package/dist/runtime/runner/chart-local-provenance.js +114 -0
- package/dist/runtime/runner/chart-local-provenance.js.map +1 -0
- package/dist/runtime/runner/chart-preflight.d.ts +8 -8
- package/dist/runtime/runner/chart-preflight.d.ts.map +1 -1
- package/dist/runtime/runner/chart-preflight.js +30 -69
- package/dist/runtime/runner/chart-preflight.js.map +1 -1
- package/dist/runtime/runner/chart-pull.d.ts +14 -0
- package/dist/runtime/runner/chart-pull.d.ts.map +1 -0
- package/dist/runtime/runner/chart-pull.js +96 -0
- package/dist/runtime/runner/chart-pull.js.map +1 -0
- package/dist/runtime/runner/chart-resolver.d.ts +4 -12
- package/dist/runtime/runner/chart-resolver.d.ts.map +1 -1
- package/dist/runtime/runner/chart-resolver.js +16 -62
- package/dist/runtime/runner/chart-resolver.js.map +1 -1
- package/dist/runtime/runner/chart-set-lock.d.ts +6 -20
- package/dist/runtime/runner/chart-set-lock.d.ts.map +1 -1
- package/dist/runtime/runner/chart-set-lock.js +55 -99
- package/dist/runtime/runner/chart-set-lock.js.map +1 -1
- package/dist/runtime/runner/helm-cli.d.ts +3 -1
- package/dist/runtime/runner/helm-cli.d.ts.map +1 -1
- package/dist/runtime/runner/helm-cli.js +9 -7
- package/dist/runtime/runner/helm-cli.js.map +1 -1
- package/dist/runtime/runner/helm-runner.d.ts +4 -4
- package/dist/runtime/runner/helm-runner.d.ts.map +1 -1
- package/dist/runtime/runner/helm-runner.js +18 -16
- package/dist/runtime/runner/helm-runner.js.map +1 -1
- package/dist/runtime/sources/kinds/git.d.ts +1 -2
- package/dist/runtime/sources/kinds/git.d.ts.map +1 -1
- package/dist/runtime/sources/kinds/git.js +8 -5
- package/dist/runtime/sources/kinds/git.js.map +1 -1
- package/dist/runtime/sources/manifest-walk.d.ts +3 -1
- package/dist/runtime/sources/manifest-walk.d.ts.map +1 -1
- package/dist/runtime/sources/manifest-walk.js +8 -3
- package/dist/runtime/sources/manifest-walk.js.map +1 -1
- package/dist/runtime/sources/schema.d.ts +3 -0
- package/dist/runtime/sources/schema.d.ts.map +1 -1
- package/dist/runtime/sources/schema.js +6 -1
- package/dist/runtime/sources/schema.js.map +1 -1
- package/dist/runtime/supervisor.d.ts +2 -0
- package/dist/runtime/supervisor.d.ts.map +1 -1
- package/dist/runtime/supervisor.js +1 -0
- package/dist/runtime/supervisor.js.map +1 -1
- package/edition/README.md +313 -0
- package/edition/assemble-producer-input.mjs +1251 -0
- package/edition/authorization-base.mjs +347 -0
- package/edition/biome-content.mjs +268 -0
- package/edition/build-biome-index.mjs +175 -0
- package/edition/build-platform-service-index.mjs +288 -0
- package/edition/build-runtime-image-source-index.mjs +821 -0
- package/edition/built-verifier.mjs +55 -0
- package/edition/catalogue-edition.mjs +49 -0
- package/edition/catalogue-fetch.mjs +64 -0
- package/edition/catalogue.mjs +135 -0
- package/edition/chart-set-source-commit.mjs +92 -0
- package/edition/check-committed-image-evidence.mjs +383 -0
- package/edition/checkout-indexed-source-repositories.mjs +105 -0
- package/edition/comment-blanker.mjs +75 -0
- package/edition/connector-source-authority.mjs +60 -0
- package/edition/customer-definition.mjs +183 -0
- package/edition/customer-flags.mjs +7 -0
- package/edition/develop-customer-edition.mjs +99 -0
- package/edition/develop-edition-resolve.mjs +65 -0
- package/edition/develop-edition.mjs +274 -0
- package/edition/develop-freeze.mjs +173 -0
- package/edition/develop-image-coordinate.mjs +54 -0
- package/edition/develop-seal.mjs +77 -0
- package/edition/develop-snapshot-inputs.mjs +104 -0
- package/edition/dist-check.mjs +986 -0
- package/edition/distribution-evidence-set.mjs +987 -0
- package/edition/distribution-paths.mjs +191 -0
- package/edition/edition-artifact.mjs +284 -0
- package/edition/edition-biome-manifests.mjs +85 -0
- package/edition/edition-channel.mjs +6 -0
- package/edition/edition-chart-set.mjs +93 -0
- package/edition/edition-config.mjs +205 -0
- package/edition/edition-constants.mjs +57 -0
- package/edition/edition-contract.json +30 -0
- package/edition/edition-list.mjs +35 -0
- package/edition/edition-locks.mjs +69 -0
- package/edition/edition-protocol.mjs +228 -0
- package/edition/edition-workspace.mjs +187 -0
- package/edition/evidence-checkout.mjs +439 -0
- package/edition/evidence-grammars.mjs +16 -0
- package/edition/evidence-trust-pin.mjs +304 -0
- package/edition/evidence-trust-policy.mjs +294 -0
- package/edition/external-source-walk.mjs +463 -0
- package/edition/first-party-source-ids.mjs +59 -0
- package/edition/fleet-coordinate-finality.mjs +350 -0
- package/edition/git-source-auth.mjs +52 -0
- package/edition/image-delivery-entitlement-descriptor.mjs +106 -0
- package/edition/image-evidence-replay-state.mjs +242 -0
- package/edition/image-evidence.mjs +1233 -0
- package/edition/kernel-contract-authority.mjs +443 -0
- package/edition/lib.mjs +3021 -0
- package/edition/no-follow-file.mjs +94 -0
- package/edition/oci-evidence-verbs.mjs +392 -0
- package/edition/oci-image-source-evidence.mjs +239 -0
- package/edition/oras.mjs +648 -0
- package/edition/pin-source.mjs +227 -0
- package/edition/plan-image-evidence-refresh.mjs +408 -0
- package/edition/platform-lock-readability.mjs +892 -0
- package/edition/platform-service-class.mjs +18 -0
- package/edition/platform-service-source-authority.mjs +105 -0
- package/edition/positive-closure.mjs +162 -0
- package/edition/produce-distribution-completeness-evidence.mjs +311 -0
- package/edition/produce-image-delivery-entitlement-evidence.mjs +294 -0
- package/edition/produce-image-source-evidence.mjs +765 -0
- package/edition/producer-evidence-bundle.mjs +157 -0
- package/edition/registry-credentials.mjs +193 -0
- package/edition/release-catalogue.mjs +39 -0
- package/edition/release-channel-guard.mjs +109 -0
- package/edition/release-edition-artifact.mjs +157 -0
- package/edition/remedy-command.mjs +89 -0
- package/edition/repin-evidence-trust.mjs +101 -0
- package/edition/required-resource-provider.mjs +58 -0
- package/edition/resolve-chart-set.mjs +415 -0
- package/edition/resolve-customer-edition.mjs +288 -0
- package/edition/resolve-distribution.mjs +2318 -0
- package/edition/resolve-edition.mjs +186 -0
- package/edition/resolve-images.mjs +1606 -0
- package/edition/seal-image-digests.mjs +855 -0
- package/edition/sealed-evidence-accept.mjs +82 -0
- package/edition/source-authority.mjs +725 -0
- package/edition/source-closure.mjs +116 -0
- package/edition/strict-json.mjs +121 -0
- package/edition/string-order.mjs +5 -0
- package/edition/trust-policy-arming.mjs +120 -0
- package/edition/trusted-executable.mjs +166 -0
- package/edition/verifier-binding.mjs +45 -0
- package/edition/verifier-files.mjs +8 -0
- package/edition/verifier-kernel-schemas.mjs +22 -0
- package/edition/verifier-provenance.mjs +64 -0
- package/edition/verify-image-evidence.mjs +458 -0
- package/edition/verify-image-platforms.mjs +495 -0
- package/edition/why.mjs +56 -0
- package/edition/workspace-sections.mjs +195 -0
- package/examples/06-biome-sources.yaml +1 -0
- package/package.json +14 -6
- package/dist/commands/dist.d.ts +0 -16
- package/dist/commands/dist.d.ts.map +0 -1
- package/dist/commands/dist.js +0 -182
- package/dist/commands/dist.js.map +0 -1
- package/dist/runtime/distribution/available-biome.d.ts +0 -10
- package/dist/runtime/distribution/available-biome.d.ts.map +0 -1
- package/dist/runtime/distribution/available-biome.js +0 -156
- package/dist/runtime/distribution/available-biome.js.map +0 -1
- package/dist/runtime/distribution/compose.d.ts +0 -23
- package/dist/runtime/distribution/compose.d.ts.map +0 -1
- package/dist/runtime/distribution/compose.js +0 -48
- package/dist/runtime/distribution/compose.js.map +0 -1
- package/dist/runtime/runner/chart-trust.d.ts +0 -57
- package/dist/runtime/runner/chart-trust.d.ts.map +0 -1
- package/dist/runtime/runner/chart-trust.js +0 -270
- package/dist/runtime/runner/chart-trust.js.map +0 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// resolve-customer-edition — resolve a CUSTOMER edition against a catalogue.
|
|
2
|
+
//
|
|
3
|
+
// resolve-edition.mjs <root flags> --definition <file> --catalogue oci://<repo>@sha256:<digest>
|
|
4
|
+
// --channel release|develop --out <dir> [--source-image <source>=<registry>/<prefix>]...
|
|
5
|
+
// [--catalogue-username <name> --catalogue-token-env <VAR>]
|
|
6
|
+
//
|
|
7
|
+
// The catalogue (catalogue.mjs) is the CEILING: it supplies availability, pins,
|
|
8
|
+
// image digests, chart-set rows and the contract authority, and never adds a
|
|
9
|
+
// component. The resolved set is the customer's positive selections, every
|
|
10
|
+
// installationClass core entry of the catalogue and the transitive declared
|
|
11
|
+
// dependencies; a component the offering gains later stays out unless selected.
|
|
12
|
+
// Vendor image rows are COPIED from the catalogue (re-homed under the declared
|
|
13
|
+
// imageRegistryMirror, digests untouched); the customer's own sources resolve
|
|
14
|
+
// as they always have.
|
|
15
|
+
|
|
16
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
|
|
19
|
+
import { AUTHORIZATION_LOCK_FILENAME, writeAuthorizationLock } from './authorization-base.mjs';
|
|
20
|
+
import { CATALOGUE_FILE, CatalogueError } from './catalogue.mjs';
|
|
21
|
+
import {
|
|
22
|
+
assertTrustCeilingAssignsCustomerBiomes,
|
|
23
|
+
mirrorVendorSubjects,
|
|
24
|
+
parseCustomerDefinition,
|
|
25
|
+
parseImageRegistryMirror,
|
|
26
|
+
} from './customer-definition.mjs';
|
|
27
|
+
import { assertRosteredChartSetLock } from './edition-chart-set.mjs';
|
|
28
|
+
import { EditionLockFile } from './edition-constants.mjs';
|
|
29
|
+
import { editionsRoot } from './edition-workspace.mjs';
|
|
30
|
+
import { validateKernelContractAuthorityFromGit } from './kernel-contract-authority.mjs';
|
|
31
|
+
import { compareStrings, loadKernelDistributionSchemas, serializeJson, validateImmutableImageLock } from './lib.mjs';
|
|
32
|
+
import { pinnedSource, snapshotPinSource } from './pin-source.mjs';
|
|
33
|
+
import { composeAuthorizationBase, composeManifest, resolveDistributionWithIndex, UnknownSelectionError } from './resolve-distribution.mjs';
|
|
34
|
+
import { resolveImages } from './resolve-images.mjs';
|
|
35
|
+
|
|
36
|
+
/** A selection or dependency the catalogue does not offer: the catalogue is the ceiling. */
|
|
37
|
+
export class CatalogueCeilingError extends Error {
|
|
38
|
+
constructor(message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = 'CatalogueCeilingError';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The catalogue's declared ceiling, read by the kernel's two grammars: `trustPolicy` is the
|
|
46
|
+
* offering's origin allow list, `trustCeiling` its trust-domain assignments. `null` states none.
|
|
47
|
+
*/
|
|
48
|
+
function catalogueCeiling(catalogue, schemas) {
|
|
49
|
+
const read = (key, schema) => {
|
|
50
|
+
const value = catalogue.ceiling[key];
|
|
51
|
+
if (value === null) {return undefined;}
|
|
52
|
+
const parsed = schema.safeParse(value);
|
|
53
|
+
if (!parsed.success) {
|
|
54
|
+
throw new CatalogueError(
|
|
55
|
+
`${CATALOGUE_FILE}: ceiling.${key} of "${catalogue.offeringId}" is not the kernel's shape: ` +
|
|
56
|
+
parsed.error.issues.map((issue) => `[${issue.path.join('.')}] ${issue.message}`).join('; '),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return parsed.data;
|
|
60
|
+
};
|
|
61
|
+
return { trustPolicy: read('trustPolicy', schemas.DistributionTrustPolicySchema), trustCeiling: read('trustCeiling', schemas.ProcessTrustPolicySchema) };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The ceiling policy the customer's lock is written under: the catalogue's assignments, then
|
|
66
|
+
* the definition's for what the catalogue leaves open. An assignment of the definition that
|
|
67
|
+
* names a domain the catalogue assigns, or a biome the catalogue places, is not carried: the
|
|
68
|
+
* catalogue's reading stands, and `assertWithinCatalogueCeiling` refuses the difference.
|
|
69
|
+
*/
|
|
70
|
+
function ceilingUnderCatalogue(catalogueTrustCeiling, definitionTrustCeiling) {
|
|
71
|
+
if (catalogueTrustCeiling === undefined) {return definitionTrustCeiling;}
|
|
72
|
+
const domains = new Set(catalogueTrustCeiling.assignments.map((assignment) => assignment.domain));
|
|
73
|
+
const placed = new Set(catalogueTrustCeiling.assignments.flatMap((assignment) => assignment.biomes));
|
|
74
|
+
const open = (definitionTrustCeiling?.assignments ?? [])
|
|
75
|
+
.filter((assignment) => !domains.has(assignment.domain))
|
|
76
|
+
.map((assignment) => ({ domain: assignment.domain, biomes: assignment.biomes.filter((biome) => !placed.has(biome)) }))
|
|
77
|
+
.filter((assignment) => assignment.biomes.length > 0);
|
|
78
|
+
return { assignments: [...catalogueTrustCeiling.assignments, ...open] };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Every customer-source biome sits in the domain its definition names, and that is the domain
|
|
83
|
+
* the catalogue's ceiling admits for it: the lock row carries the kernel's derivation
|
|
84
|
+
* (`deriveTrustCeiling`) under `ceilingUnderCatalogue`, so a definition that reaches into a
|
|
85
|
+
* domain the catalogue assigns, or moves a biome the catalogue places, differs from it here.
|
|
86
|
+
*/
|
|
87
|
+
function assertWithinCatalogueCeiling({ catalogue, definition, lock, schemas }) {
|
|
88
|
+
const stated = new Map((definition.trustCeiling?.assignments ?? []).flatMap((assignment) => assignment.biomes.map((biome) => [biome, assignment.domain])));
|
|
89
|
+
for (const biome of lock.biomes.filter((candidate) => candidate.source !== undefined)) {
|
|
90
|
+
const admitted = biome.trustCeiling ?? schemas.defaultTrustDomain(biome.id, biome.origin);
|
|
91
|
+
if (stated.get(biome.id) !== admitted) {
|
|
92
|
+
throw new CatalogueCeilingError(
|
|
93
|
+
`catalogue "${catalogue.offeringId}" (${catalogue.channel}): "${definition.id}" assigns biome "${biome.id}" to trust domain ` +
|
|
94
|
+
`"${stated.get(biome.id)}", and the catalogue's trustCeiling admits "${admitted}" for it`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** `{name -> {imageRegistry, imageNamePrefix}}` from repeated `<source>=<registry>/<prefix>` values; the prefix is the text after the last "/". */
|
|
101
|
+
export function parseSourceImages(values) {
|
|
102
|
+
const byName = new Map();
|
|
103
|
+
for (const value of values) {
|
|
104
|
+
const eq = value.indexOf('=');
|
|
105
|
+
const slash = value.lastIndexOf('/');
|
|
106
|
+
if (eq <= 0 || slash < eq + 2) {
|
|
107
|
+
throw new Error(`--source-image ${JSON.stringify(value)} is not <source>=<registry>/<prefix> (the prefix may be empty: "<source>=<registry>/")`);
|
|
108
|
+
}
|
|
109
|
+
const name = value.slice(0, eq);
|
|
110
|
+
if (byName.has(name)) {throw new Error(`--source-image names source "${name}" twice`);}
|
|
111
|
+
byName.set(name, { imageRegistry: value.slice(eq + 1, slash), imageNamePrefix: value.slice(slash + 1) });
|
|
112
|
+
}
|
|
113
|
+
return byName;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The definition's sources with the configured registry/prefix injected; every declared source needs its --source-image.
|
|
118
|
+
* With `pins` (a develop customer edition) each source's ref is the frozen commit of its `sources/<name>` pin.
|
|
119
|
+
*/
|
|
120
|
+
function withSourceImages(definition, sourceImages, pins) {
|
|
121
|
+
const declared = new Set((definition.sources ?? []).map((source) => source.name));
|
|
122
|
+
for (const name of sourceImages.keys()) {
|
|
123
|
+
if (!declared.has(name)) {throw new Error(`--source-image names source "${name}", which the definition does not declare (declared: ${[...declared].join(', ') || 'none'})`);}
|
|
124
|
+
}
|
|
125
|
+
return (definition.sources ?? []).map((source) => {
|
|
126
|
+
const image = sourceImages.get(source.name);
|
|
127
|
+
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`);}
|
|
128
|
+
const pinned = pins === undefined ? source : pinnedSource(source, snapshotPinSource(pins));
|
|
129
|
+
return { ...pinned, ...image };
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** The composition of a customer definition over the catalogue. */
|
|
134
|
+
export function composeCustomerEdition({ schemas, definition, mirror, catalogue, sourceImages, pins }) {
|
|
135
|
+
const ceiling = catalogueCeiling(catalogue, schemas);
|
|
136
|
+
const manifest = schemas.parseDistribution({ ...definition, ...(definition.sources === undefined ? {} : { sources: withSourceImages(definition, sourceImages, pins) }) });
|
|
137
|
+
if (definition.sources === undefined && sourceImages.size > 0) {
|
|
138
|
+
throw new Error('--source-image given, but the definition declares no sources');
|
|
139
|
+
}
|
|
140
|
+
const composed = composeManifest(manifest);
|
|
141
|
+
return {
|
|
142
|
+
...composed,
|
|
143
|
+
// The catalogue is the ceiling: its origin allow list is the one this resolve applies
|
|
144
|
+
// (a definition states none), and its domain assignments come before the definition's.
|
|
145
|
+
trustPolicy: ceiling.trustPolicy,
|
|
146
|
+
trustCeiling: ceilingUnderCatalogue(ceiling.trustCeiling, composed.trustCeiling),
|
|
147
|
+
platformServices: new Map(
|
|
148
|
+
catalogue.platformServices.map((row) => [row.name, { name: row.name, tier: row.tier, installationClass: row.installationClass }]),
|
|
149
|
+
),
|
|
150
|
+
webProducerRepos: [...new Set(catalogue.images.webBiomes.map((biome) => biome.repo))],
|
|
151
|
+
mirror: parseImageRegistryMirror(mirror),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const subjectKey = (subject) => `${subject.kind}\0${subject.id}`;
|
|
156
|
+
const compareSubjects = (a, b) => compareStrings(a.kind, b.kind) || compareStrings(a.id, b.id);
|
|
157
|
+
|
|
158
|
+
/** The vendor image rows of the customer's lock: copied from the catalogue, owners narrowed to the selection. */
|
|
159
|
+
export function projectVendorImages(catalogue, lock, enums) {
|
|
160
|
+
const biomes = new Set(lock.biomes.filter((biome) => biome.source === undefined).map((biome) => biome.id));
|
|
161
|
+
const services = new Set(lock.platformServices.map((service) => service.name));
|
|
162
|
+
const subjects = [];
|
|
163
|
+
for (const subject of catalogue.images.subjects) {
|
|
164
|
+
const owners = subject.owners.filter((owner) =>
|
|
165
|
+
owner.kind === enums.RuntimeImageOwnerKind.Biome ? biomes.has(owner.biomeId) : services.has(owner.platformService),
|
|
166
|
+
);
|
|
167
|
+
if (owners.length > 0) {subjects.push({ ...subject, owners });}
|
|
168
|
+
}
|
|
169
|
+
const have = new Set(subjects.filter((s) => s.owners.some((o) => o.kind === enums.RuntimeImageOwnerKind.Biome)).flatMap((s) => s.owners.map((o) => `${o.biomeId}\0${s.kind}\0${s.id}`)));
|
|
170
|
+
for (const biome of lock.biomes.filter((b) => b.source === undefined && b.target !== 'web')) {
|
|
171
|
+
const shared = new Set(Object.keys(biome.workerImages ?? {}));
|
|
172
|
+
const wanted = [
|
|
173
|
+
...(biome.services ?? []).filter((id) => !shared.has(id)).map((id) => [enums.RuntimeImageSubjectKind.Service, id]),
|
|
174
|
+
...(biome.runtimeComponents ?? []).map((id) => [enums.RuntimeImageSubjectKind.RuntimeComponent, id]),
|
|
175
|
+
];
|
|
176
|
+
for (const [kind, id] of wanted) {
|
|
177
|
+
if (!have.has(`${biome.id}\0${kind}\0${id}`)) {
|
|
178
|
+
throw new CatalogueCeilingError(`the catalogue carries no image row for ${kind}:${id} of selected biome "${biome.id}"`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
subjects,
|
|
184
|
+
platformServiceBindings: catalogue.images.platformServiceBindings.filter((binding) => services.has(binding.platformService)),
|
|
185
|
+
webBiomes: catalogue.images.webBiomes.filter((biome) => biomes.has(biome.biomeId)),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The customer's image lock: vendor rows copied (and mirrored), own rows resolved. */
|
|
190
|
+
export async function resolveCustomerImages({ id, catalogue, lock, composed, schemas, inputs, sealOwn }) {
|
|
191
|
+
const vendor = projectVendorImages(catalogue, lock, schemas);
|
|
192
|
+
const vendorSubjects = mirrorVendorSubjects(vendor.subjects, composed.mirror);
|
|
193
|
+
const ownBiomes = lock.biomes.filter((biome) => biome.source !== undefined);
|
|
194
|
+
let own = { subjects: [], platformServiceBindings: [], webBiomes: [] };
|
|
195
|
+
if (ownBiomes.length > 0) {
|
|
196
|
+
const ownLock = { ...lock, biomes: ownBiomes, platformServices: [] };
|
|
197
|
+
const runtimeImageSourceIndex = {
|
|
198
|
+
contractAuthority: catalogue.contractAuthority,
|
|
199
|
+
repositoryPins: [],
|
|
200
|
+
platformServiceBindings: [],
|
|
201
|
+
supplementalSubjects: [],
|
|
202
|
+
crossRepoBuildInputs: [],
|
|
203
|
+
buildStepContextDirectories: [],
|
|
204
|
+
};
|
|
205
|
+
own = (await resolveImages(id, { inputs: { ...inputs, runtimeImageSourceIndex, composed }, lock: ownLock })).imageLock;
|
|
206
|
+
if (sealOwn !== undefined) {own = await sealOwn(own);}
|
|
207
|
+
}
|
|
208
|
+
const seen = new Set(vendorSubjects.map(subjectKey));
|
|
209
|
+
const clash = own.subjects.find((subject) => seen.has(subjectKey(subject)));
|
|
210
|
+
if (clash !== undefined) {throw new Error(`the customer's own image ${clash.kind}:${clash.id} collides with a catalogue image of the same identity`);}
|
|
211
|
+
return validateImmutableImageLock(
|
|
212
|
+
schemas.parseImageLock({
|
|
213
|
+
schemaVersion: 3,
|
|
214
|
+
sourceHashAlgorithm: catalogue.images.sourceHashAlgorithm,
|
|
215
|
+
distributionId: id,
|
|
216
|
+
contractAuthority: catalogue.contractAuthority,
|
|
217
|
+
subjects: [...vendorSubjects, ...own.subjects].sort(compareSubjects),
|
|
218
|
+
platformServiceBindings: [...vendor.platformServiceBindings, ...own.platformServiceBindings].sort((a, b) => compareStrings(a.platformService, b.platformService)),
|
|
219
|
+
webBiomes: [...vendor.webBiomes, ...own.webBiomes].sort((a, b) => compareStrings(a.biomeId, b.biomeId)),
|
|
220
|
+
}),
|
|
221
|
+
schemas,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** The kernel at --kernel must carry the contract the catalogue's offering was resolved under. */
|
|
226
|
+
export async function assertKernelAtCatalogueAuthority(catalogue, schemas) {
|
|
227
|
+
await validateKernelContractAuthorityFromGit({ authority: catalogue.contractAuthority, schemas, requireHeadMatch: false });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The distribution and authorization locks of the customer edition `rawDefinition`
|
|
232
|
+
* against `catalogue`. `catalogueDigest` is the digest of `catalogue`, written into the lock. `verifyAuthority` and `materialize` are injectable for the test suites.
|
|
233
|
+
*/
|
|
234
|
+
export async function resolveCustomerDistribution({
|
|
235
|
+
rawDefinition,
|
|
236
|
+
catalogue,
|
|
237
|
+
catalogueDigest,
|
|
238
|
+
sourceImages,
|
|
239
|
+
pins,
|
|
240
|
+
materialize,
|
|
241
|
+
verifyAuthority = assertKernelAtCatalogueAuthority,
|
|
242
|
+
}) {
|
|
243
|
+
if (catalogueDigest === undefined) {throw new CatalogueError('a customer resolve names the digest of its catalogue');}
|
|
244
|
+
const schemas = await loadKernelDistributionSchemas();
|
|
245
|
+
const { definition, mirror } = parseCustomerDefinition(schemas, rawDefinition);
|
|
246
|
+
const composed = composeCustomerEdition({ schemas, definition, mirror, catalogue, sourceImages, pins });
|
|
247
|
+
const id = composed.leaf.id;
|
|
248
|
+
const inputs = { composed, biomeIndex: catalogue.biomes, platformServiceIndex: { schemaVersion: 1, services: catalogue.platformServices } };
|
|
249
|
+
let resolved;
|
|
250
|
+
try {
|
|
251
|
+
resolved = await resolveDistributionWithIndex(id, { inputs, ...(materialize === undefined ? {} : { materialize }) });
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (error instanceof UnknownSelectionError) {
|
|
254
|
+
throw new CatalogueCeilingError(`catalogue "${catalogue.offeringId}" (${catalogue.channel}) does not offer what "${id}" needs: ${error.message}`);
|
|
255
|
+
}
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
const { lock, index } = resolved;
|
|
259
|
+
assertTrustCeilingAssignsCustomerBiomes(schemas, definition, lock.biomes.filter((biome) => biome.source !== undefined).map((biome) => biome.id));
|
|
260
|
+
assertWithinCatalogueCeiling({ catalogue, definition, lock, schemas });
|
|
261
|
+
await verifyAuthority(catalogue, schemas);
|
|
262
|
+
const authorizationLock = await composeAuthorizationBase(id, lock, index);
|
|
263
|
+
// A customer lock names the catalogue it was resolved against; seal and verify refuse any other.
|
|
264
|
+
return { id, schemas, composed, inputs, lock: { ...lock, catalogueDigest }, authorizationLock };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Resolve the customer edition of `channel` against `catalogue`; returns the four locks. `sealOwn` seals the customer's OWN image rows only (a develop edition); vendor rows arrive sealed. */
|
|
268
|
+
export async function resolveCustomerEdition(request) {
|
|
269
|
+
const { id, schemas, composed, inputs, lock, authorizationLock } = await resolveCustomerDistribution(request);
|
|
270
|
+
const { catalogue } = request;
|
|
271
|
+
const imageLock = await resolveCustomerImages({ id, catalogue, lock, composed, schemas, inputs, sealOwn: request.sealOwn });
|
|
272
|
+
// The chart-set is copied whole, so it carries the coordinates of the channel it was published on:
|
|
273
|
+
// the edition being written states its channel, and a chart-set of the other one is refused here.
|
|
274
|
+
const chartSetLock = assertRosteredChartSetLock(catalogue.chartSet, `the catalogue's chart-set of "${catalogue.offeringId}"`, request.channel);
|
|
275
|
+
return { id, distributionLock: lock, imageLock, authorizationLock, chartSetLock };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Write the four locks to `<out>/<id>/` (the layout of an editions root). */
|
|
279
|
+
export function writeCustomerEdition(out, { id, distributionLock, imageLock, authorizationLock, chartSetLock }) {
|
|
280
|
+
if (out !== editionsRoot()) {throw new CatalogueError(`the customer edition is written into the editions root (${editionsRoot()}), not ${out}`);}
|
|
281
|
+
const dir = join(out, id);
|
|
282
|
+
mkdirSync(dir, { recursive: true });
|
|
283
|
+
writeFileSync(join(dir, EditionLockFile.Distribution), serializeJson(distributionLock));
|
|
284
|
+
writeFileSync(join(dir, EditionLockFile.Images), serializeJson(imageLock));
|
|
285
|
+
writeFileSync(join(dir, EditionLockFile.ChartSet), serializeJson(chartSetLock));
|
|
286
|
+
writeAuthorizationLock(dir, authorizationLock, serializeJson);
|
|
287
|
+
return { dir, authorization: AUTHORIZATION_LOCK_FILENAME };
|
|
288
|
+
}
|