@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,175 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// build-biome-index — walk every `xema-biome.json` in the monorepo and emit the
|
|
3
|
+
// global biome index (`distributions/biome-index.json`) that the distribution
|
|
4
|
+
// resolver reads. The index is the single source of truth for "which biomes
|
|
5
|
+
// exist, at what tier/target/origin/version, and what they require". It carries
|
|
6
|
+
// PACKAGING-relevant fields only.
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// node build-biome-index.mjs [--check] [--source published|workspace] [--unpinned]
|
|
10
|
+
//
|
|
11
|
+
// THE COMMITTED PINS ARE THE AUTHORITY, and this index is a CONSUMER of them.
|
|
12
|
+
// Each repository's commit comes from `runtime-image-source-index.json`'s
|
|
13
|
+
// `repositoryPins`; nothing here asks a repository what its newest built commit
|
|
14
|
+
// is. `--unpinned` is the one way to reach a live resolution and it is a POLICY
|
|
15
|
+
// operation — see `pinAuthority` below.
|
|
16
|
+
//
|
|
17
|
+
// Fail-fast on any manifest missing id/scope/target or with an unknown tier
|
|
18
|
+
// (see manifestToIndexEntry). mjs tooling — clocks are fine; the index itself
|
|
19
|
+
// carries no timestamp so it is reproducible.
|
|
20
|
+
|
|
21
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import { dirname } from 'node:path';
|
|
23
|
+
|
|
24
|
+
import { WorkspaceRoot, biomeIndexPath, configureEditionWorkspaceFromProcess, isEntryModule, platformServiceIndexPath } from './edition-workspace.mjs';
|
|
25
|
+
import {
|
|
26
|
+
buildBiomeIndex,
|
|
27
|
+
distributionBiomeSourceRepositories,
|
|
28
|
+
loadKernelDistributionSchemas,
|
|
29
|
+
serializeJson,
|
|
30
|
+
} from './lib.mjs';
|
|
31
|
+
import { commandForSelf } from './remedy-command.mjs';
|
|
32
|
+
import {
|
|
33
|
+
DistributionSourceMode,
|
|
34
|
+
committedRepositoryPins,
|
|
35
|
+
createPinnedSourceAuthority,
|
|
36
|
+
createSourceAuthority,
|
|
37
|
+
parseSourceModeArgs,
|
|
38
|
+
} from './source-authority.mjs';
|
|
39
|
+
|
|
40
|
+
if (isEntryModule(import.meta.url)) {
|
|
41
|
+
configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.SourceHash]);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* WHY THIS INDEX READS THE PINS INSTEAD OF RESOLVING REPOSITORIES ITSELF.
|
|
46
|
+
*
|
|
47
|
+
* This index and `runtime-image-source-index.json` used to be derived from the
|
|
48
|
+
* same fact and DERIVE IT SEPARATELY: each called `commitForRepository`, which
|
|
49
|
+
* asks the world a live question whose answer moves whenever a build lands.
|
|
50
|
+
* `resolve-images` refuses any disagreement between them — it re-reads each
|
|
51
|
+
* biome manifest at the RUNTIME pin and fails with "sourceManifestHash does not
|
|
52
|
+
* match" — so two derivations minutes apart could produce two artifacts that
|
|
53
|
+
* cannot resolve together.
|
|
54
|
+
*
|
|
55
|
+
* A `--repo <repository>` scope was added to narrow the second derivation to
|
|
56
|
+
* one repository. It narrowed the COLLATERAL and left the RACE: the scoped
|
|
57
|
+
* repository was still resolved live, so a build landing between
|
|
58
|
+
* `build-runtime-image-source-index --repo X` and `build-biome-index --repo X`
|
|
59
|
+
* split the two artifacts on exactly the repository the operator was refreshing.
|
|
60
|
+
*
|
|
61
|
+
* So the scope is gone and the derivation with it. There is ONE artifact whose
|
|
62
|
+
* job is to decide what commit a repository is at — the runtime image source
|
|
63
|
+
* index — and everything downstream reads its record. A scoped refresh of this
|
|
64
|
+
* index is not a mode here any more: it is what you GET, because advancing one
|
|
65
|
+
* pin is the only thing that can move one repository's entries.
|
|
66
|
+
*
|
|
67
|
+
* `--unpinned` is the escape, and it is a POLICY operation rather than a
|
|
68
|
+
* correctness one. Two cases need it and no third has appeared:
|
|
69
|
+
*
|
|
70
|
+
* - BOOTSTRAP — a repository that has JOINED the source set has no pin yet,
|
|
71
|
+
* and `createPinnedSourceAuthority` refuses it rather than resolving it
|
|
72
|
+
* live. That refusal is right (a repository joining is a full rebuild, not
|
|
73
|
+
* a scoped refresh) and this is how the full rebuild is asked for.
|
|
74
|
+
* - WORKSPACE — `--source workspace` snapshots a dirty local tree, which has
|
|
75
|
+
* no pin by construction. It therefore IMPLIES `--unpinned`, rather than
|
|
76
|
+
* making the operator state both.
|
|
77
|
+
*/
|
|
78
|
+
function parseUnpinned(argv) {
|
|
79
|
+
const index = argv.indexOf('--unpinned');
|
|
80
|
+
if (index === -1) {return { unpinned: false, remaining: argv };}
|
|
81
|
+
return {
|
|
82
|
+
unpinned: true,
|
|
83
|
+
remaining: [...argv.slice(0, index), ...argv.slice(index + 1)],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function main() {
|
|
88
|
+
const { unpinned: unpinnedFlag, remaining: afterUnpinned } = parseUnpinned(
|
|
89
|
+
process.argv.slice(2),
|
|
90
|
+
);
|
|
91
|
+
const { mode, remaining } = parseSourceModeArgs(afterUnpinned);
|
|
92
|
+
const check = remaining.includes('--check');
|
|
93
|
+
if (remaining.some((argument) => argument !== '--check')) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
'usage: build-biome-index.mjs [--check] [--source published|workspace] ' +
|
|
96
|
+
'[--unpinned]',
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
// A workspace snapshot has no pin and never will — see `parseUnpinned`.
|
|
100
|
+
const unpinned = unpinnedFlag || mode === DistributionSourceMode.Workspace;
|
|
101
|
+
const schemas = await loadKernelDistributionSchemas();
|
|
102
|
+
const platformServiceIndex = schemas.parsePlatformServiceSourceIndex(
|
|
103
|
+
JSON.parse(readFileSync(platformServiceIndexPath(), 'utf8')),
|
|
104
|
+
);
|
|
105
|
+
const sourceRepositories = distributionBiomeSourceRepositories();
|
|
106
|
+
const sourceAuthority = unpinned
|
|
107
|
+
? createSourceAuthority({
|
|
108
|
+
mode,
|
|
109
|
+
knownRepositories: sourceRepositories.map(({ repo }) => repo),
|
|
110
|
+
})
|
|
111
|
+
: // NO DELEGATE, deliberately. A consumer artifact has nothing to resolve,
|
|
112
|
+
// and passing a live authority it "will not use" makes that a claim about
|
|
113
|
+
// this call site rather than a property of the object. Without one, any
|
|
114
|
+
// repository missing from the record REFUSES by name instead of quietly
|
|
115
|
+
// resolving to whatever its tip happens to be today.
|
|
116
|
+
createPinnedSourceAuthority({ pins: committedRepositoryPins() });
|
|
117
|
+
const index = await buildBiomeIndex(
|
|
118
|
+
platformServiceIndex,
|
|
119
|
+
sourceRepositories,
|
|
120
|
+
sourceAuthority,
|
|
121
|
+
);
|
|
122
|
+
const serialized = serializeJson(index);
|
|
123
|
+
|
|
124
|
+
if (check) {
|
|
125
|
+
let committed;
|
|
126
|
+
try {
|
|
127
|
+
committed = readFileSync(biomeIndexPath(), 'utf8');
|
|
128
|
+
} catch {
|
|
129
|
+
console.error(
|
|
130
|
+
`[dist:index --check] ${biomeIndexPath()} does not exist. Run \`${commandForSelf(import.meta.url)}\`.`,
|
|
131
|
+
);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
if (committed !== serialized) {
|
|
135
|
+
const regenerateCommand =
|
|
136
|
+
mode === DistributionSourceMode.Workspace
|
|
137
|
+
? `${commandForSelf(import.meta.url, '--source', 'workspace')} (local-only output; do not publish it)`
|
|
138
|
+
: commandForSelf(import.meta.url);
|
|
139
|
+
// WHICH AUTHORITY DISAGREED is the first thing the reader needs, because
|
|
140
|
+
// the two have different remedies: a PINNED divergence means the index
|
|
141
|
+
// does not match the pins and regenerating fixes it, while an UNPINNED one
|
|
142
|
+
// means the fleet has moved and regenerating MOVES EVERY PIN. Saying which
|
|
143
|
+
// ran is what keeps the second from being done by reflex.
|
|
144
|
+
console.error(
|
|
145
|
+
`[dist:index --check] authority: ${
|
|
146
|
+
unpinned
|
|
147
|
+
? 'UNPINNED — every repository was resolved LIVE, so this verdict ' +
|
|
148
|
+
'is about the fleet right now and regenerating will move pins'
|
|
149
|
+
: `PINNED — ${
|
|
150
|
+
committedRepositoryPins().length
|
|
151
|
+
} committed pin(s), no live resolution`
|
|
152
|
+
}.`,
|
|
153
|
+
);
|
|
154
|
+
console.error(
|
|
155
|
+
`[dist:index --check] biome-index.json is out of date. Run \`${regenerateCommand}\`.`,
|
|
156
|
+
);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
console.log(
|
|
160
|
+
`[dist:index --check] biome-index.json is up to date (${index.length} biomes).`,
|
|
161
|
+
);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
mkdirSync(dirname(biomeIndexPath()), { recursive: true });
|
|
166
|
+
writeFileSync(biomeIndexPath(), serialized);
|
|
167
|
+
console.log(
|
|
168
|
+
`[dist:index] wrote ${biomeIndexPath()} (${index.length} biomes).`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
main().catch((error) => {
|
|
173
|
+
console.error(`[dist:index] ${error.message}`);
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
});
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
|
|
6
|
+
import { platformServiceIndexPath , WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule, sourcePath } from './edition-workspace.mjs';
|
|
7
|
+
import {
|
|
8
|
+
canonicalPublicHttpsRepositoryUrl,
|
|
9
|
+
loadKernelDistributionSchemas,
|
|
10
|
+
readGitJsonAtCommit,
|
|
11
|
+
serializeJson,
|
|
12
|
+
sha256Hex,
|
|
13
|
+
} from './lib.mjs';
|
|
14
|
+
import {
|
|
15
|
+
comparablePlatformService,
|
|
16
|
+
declaredInstallationClass,
|
|
17
|
+
} from './platform-service-class.mjs';
|
|
18
|
+
import {
|
|
19
|
+
platformServiceSourceDeclaration,
|
|
20
|
+
platformServiceSourceDeclarations,
|
|
21
|
+
platformServiceSourceRepositories,
|
|
22
|
+
} from './platform-service-source-authority.mjs';
|
|
23
|
+
import {
|
|
24
|
+
DistributionSourceMode,
|
|
25
|
+
committedRepositoryPins,
|
|
26
|
+
createPinnedSourceAuthority,
|
|
27
|
+
createSourceAuthority,
|
|
28
|
+
parseSourceModeArgs,
|
|
29
|
+
} from './source-authority.mjs';
|
|
30
|
+
import { readStrictJsonFile } from './strict-json.mjs';
|
|
31
|
+
|
|
32
|
+
if (isEntryModule(import.meta.url)) {
|
|
33
|
+
configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.SourceHash]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const TRUSTED_WRITE_FLAG = '--trusted-write';
|
|
37
|
+
const CHECK_FLAG = '--check';
|
|
38
|
+
const CHECK_SOURCES_FLAG = '--check-sources';
|
|
39
|
+
const UNPINNED_FLAG = '--unpinned';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* THE LAST INDEX IN THIS PLANE THAT COULD NOT BE SCOPED, and the one that
|
|
43
|
+
* measurably fell behind because of it.
|
|
44
|
+
*
|
|
45
|
+
* `build-biome-index.mjs` and `build-runtime-image-source-index.mjs` could both
|
|
46
|
+
* be narrowed to one repository. This one could not: every run resolved all
|
|
47
|
+
* eight platform-service source repositories live. So advancing the two services
|
|
48
|
+
* sourced from `repos/xema-base` REQUIRED moving `event-hub-api`,
|
|
49
|
+
* `identity-api`, `llm-gateway-api` and `xema-host-web` with them — measured
|
|
50
|
+
* 2026-09-23, four collateral repositories that then disagreed with the runtime
|
|
51
|
+
* pins a scoped refresh had just held.
|
|
52
|
+
*
|
|
53
|
+
* It did not stay theoretical. Measured on `origin/main` the same day, the two
|
|
54
|
+
* committed indexes pinned `repos/xema-base` at commits 164 APART —
|
|
55
|
+
* `platform-service-index.json` at `e1987a11` (2026-09-17) against
|
|
56
|
+
* `runtime-image-source-index.json` at `fcde2a83` (2026-09-20) — because the
|
|
57
|
+
* pinnable index had been scope-refreshed and this one could only move by
|
|
58
|
+
* moving everything. Two committed artifacts, one repository, two commits.
|
|
59
|
+
*
|
|
60
|
+
* The fix is not a `--repo` flag here. A flag would have made this index a
|
|
61
|
+
* SECOND place that decides what commit a repository is at, which is the defect
|
|
62
|
+
* wearing a narrower costume. It reads the pins instead, like every other
|
|
63
|
+
* consumer, so a scoped refresh reaches it for free and there is nothing to
|
|
64
|
+
* keep in step.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
function readJson(path) {
|
|
68
|
+
return readStrictJsonFile(path);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function git(repo, args) {
|
|
72
|
+
return execFileSync('git', ['-C', repo, ...args], {
|
|
73
|
+
encoding: 'utf8',
|
|
74
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
75
|
+
}).trim();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Every platform service the data root declares (`platformServiceSources`) — the
|
|
80
|
+
* platform floor. NOT filtered by tier: the kernel distribution-lock schema
|
|
81
|
+
* requires an exact source identity on EVERY `platformServices[]` entry, so an
|
|
82
|
+
* index that describes only one tier makes every edition unresolvable. It did:
|
|
83
|
+
* filtering to `infra` left `xema-kernel-server`, `docs-api`,
|
|
84
|
+
* `opencode-image-prepuller` and `xema-host-web` with no identity at all.
|
|
85
|
+
*
|
|
86
|
+
* The index is derived from the declarations alone: no edition is read, so a
|
|
87
|
+
* data root that has no edition named `core` builds and checks it the same.
|
|
88
|
+
*/
|
|
89
|
+
function loadPlatformServiceDeclarations() {
|
|
90
|
+
return platformServiceSourceDeclarations()
|
|
91
|
+
.slice()
|
|
92
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function trustedSourceAuthority(sourceMode, unpinned) {
|
|
96
|
+
return unpinned
|
|
97
|
+
? createSourceAuthority({
|
|
98
|
+
mode: sourceMode,
|
|
99
|
+
knownRepositories: platformServiceSourceRepositories(),
|
|
100
|
+
})
|
|
101
|
+
: // No delegate: this artifact decides no commits, so it must not be able
|
|
102
|
+
// to ask for one. A platform-service repository with no committed pin
|
|
103
|
+
// refuses by name rather than resolving to its tip.
|
|
104
|
+
createPinnedSourceAuthority({ pins: committedRepositoryPins() });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildTrustedIndex(sourceAuthority) {
|
|
108
|
+
const declarations = loadPlatformServiceDeclarations();
|
|
109
|
+
const services = [];
|
|
110
|
+
for (const declaration of declarations) {
|
|
111
|
+
const binding = platformServiceSourceDeclaration(declaration.name);
|
|
112
|
+
const repoDirectory = sourcePath(binding.repo);
|
|
113
|
+
// Read through the one explicit source authority. Published mode names a
|
|
114
|
+
// fetchable origin/main commit; workspace mode names a deterministic local
|
|
115
|
+
// snapshot commit containing tracked and untracked non-ignored files.
|
|
116
|
+
const before = sourceAuthority.commitForRepository(binding.repo);
|
|
117
|
+
const sourceManifest = readGitJsonAtCommit(
|
|
118
|
+
repoDirectory,
|
|
119
|
+
binding.manifestPath,
|
|
120
|
+
before,
|
|
121
|
+
);
|
|
122
|
+
if (sourceManifest.name !== binding.manifestName) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`${binding.path} at ${before} declares name "${String(sourceManifest.name)}", ` +
|
|
125
|
+
`but platform service "${declaration.name}" is declared to be sourced from ` +
|
|
126
|
+
`"${binding.manifestName}"`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (
|
|
130
|
+
typeof sourceManifest.version !== 'string' ||
|
|
131
|
+
sourceManifest.version.length === 0
|
|
132
|
+
) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`${binding.path} has no exact version at ${before}`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
const repositoryUrl = canonicalPublicHttpsRepositoryUrl(
|
|
138
|
+
git(repoDirectory, ['remote', 'get-url', 'origin']),
|
|
139
|
+
`${binding.repo} origin`,
|
|
140
|
+
);
|
|
141
|
+
services.push({
|
|
142
|
+
name: declaration.name,
|
|
143
|
+
tier: declaration.tier,
|
|
144
|
+
...declaredInstallationClass(declaration),
|
|
145
|
+
repositoryUrl,
|
|
146
|
+
repo: binding.repo,
|
|
147
|
+
path: binding.path,
|
|
148
|
+
sourceCommit: before,
|
|
149
|
+
version: sourceManifest.version,
|
|
150
|
+
sourceManifestHash: sha256Hex(serializeJson(sourceManifest)),
|
|
151
|
+
imageKey: binding.imageKey,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return { schemaVersion: 1, services };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function validateOfflineIndex(index) {
|
|
158
|
+
const declarations = loadPlatformServiceDeclarations();
|
|
159
|
+
const expected = declarations.map(comparablePlatformService);
|
|
160
|
+
const actual = index.services.map(comparablePlatformService);
|
|
161
|
+
if (serializeJson(actual) !== serializeJson(expected)) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
'platform-service-index.json does not exactly match the platformServiceSources declared in edition-workspace.json',
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
for (const service of index.services) {
|
|
167
|
+
const canonicalRepositoryUrl = canonicalPublicHttpsRepositoryUrl(
|
|
168
|
+
service.repositoryUrl,
|
|
169
|
+
`platform service "${service.name}" repositoryUrl`,
|
|
170
|
+
);
|
|
171
|
+
if (canonicalRepositoryUrl !== service.repositoryUrl) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
`platform service "${service.name}" repositoryUrl is not canonical`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
const expectedRepoName = service.repo.split('/').at(-1);
|
|
177
|
+
// Offline, and still exact: the declared source location is the authority,
|
|
178
|
+
// so a committed index that names a different repo, manifest or image key
|
|
179
|
+
// than the declaration fails here without touching Git.
|
|
180
|
+
const binding = platformServiceSourceDeclaration(service.name);
|
|
181
|
+
if (service.repo !== binding.repo || service.path !== binding.path) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`platform service "${service.name}" source ${service.repo}:${service.path} ` +
|
|
184
|
+
`does not match its declared source ${binding.repo}:${binding.path}`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (service.imageKey !== binding.imageKey) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`platform service "${service.name}" imageKey "${service.imageKey}" ` +
|
|
190
|
+
`does not match its declared image key "${binding.imageKey}"`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (!service.path.startsWith(`${service.repo}/`)) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`platform service "${service.name}" source path is outside its repo`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (
|
|
199
|
+
!service.repositoryUrl.endsWith(`/${expectedRepoName}.git`) &&
|
|
200
|
+
!service.repositoryUrl.endsWith(`/${expectedRepoName}`)
|
|
201
|
+
) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`platform service "${service.name}" repositoryUrl does not identify ${service.repo}`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return index;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The index as `--trusted-write` produces and validates it, over an explicit
|
|
212
|
+
* source authority. The develop edition builds its snapshot index here, with a
|
|
213
|
+
* pinned authority whose pins are the snapshot's, so the release and the
|
|
214
|
+
* develop lane share one builder and one validation.
|
|
215
|
+
*/
|
|
216
|
+
export function buildValidatedPlatformServiceIndex(schemas, sourceAuthority) {
|
|
217
|
+
const parsed = schemas.parsePlatformServiceSourceIndex(
|
|
218
|
+
buildTrustedIndex(sourceAuthority),
|
|
219
|
+
);
|
|
220
|
+
return validateOfflineIndex(parsed);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function main() {
|
|
224
|
+
const { mode, remaining } = parseSourceModeArgs(process.argv.slice(2));
|
|
225
|
+
const flags = new Set(remaining);
|
|
226
|
+
const unpinnedFlag = flags.delete(UNPINNED_FLAG);
|
|
227
|
+
const unknown = [...flags].filter(
|
|
228
|
+
(flag) =>
|
|
229
|
+
flag !== TRUSTED_WRITE_FLAG &&
|
|
230
|
+
flag !== CHECK_FLAG &&
|
|
231
|
+
flag !== CHECK_SOURCES_FLAG,
|
|
232
|
+
);
|
|
233
|
+
if (unknown.length > 0 || flags.size !== 1) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`usage: node build-platform-service-index.mjs (${CHECK_FLAG} | ${CHECK_SOURCES_FLAG} | ${TRUSTED_WRITE_FLAG}) [--source published|workspace] [${UNPINNED_FLAG}]`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
// A workspace snapshot has no committed pin by construction, so it IMPLIES
|
|
239
|
+
// the live authority rather than requiring the operator to state both.
|
|
240
|
+
const unpinned = unpinnedFlag || mode === DistributionSourceMode.Workspace;
|
|
241
|
+
const schemas = await loadKernelDistributionSchemas();
|
|
242
|
+
if (flags.has(CHECK_FLAG)) {
|
|
243
|
+
if (!existsSync(platformServiceIndexPath())) {
|
|
244
|
+
throw new Error(`${platformServiceIndexPath()} does not exist`);
|
|
245
|
+
}
|
|
246
|
+
const parsed = schemas.parsePlatformServiceSourceIndex(
|
|
247
|
+
readJson(platformServiceIndexPath()),
|
|
248
|
+
);
|
|
249
|
+
validateOfflineIndex(parsed);
|
|
250
|
+
console.log(
|
|
251
|
+
`[platform-service-index] offline check passed for ${parsed.services.length} services`,
|
|
252
|
+
);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const parsed = buildValidatedPlatformServiceIndex(schemas, trustedSourceAuthority(mode, unpinned));
|
|
256
|
+
if (flags.has(CHECK_SOURCES_FLAG)) {
|
|
257
|
+
if (!existsSync(platformServiceIndexPath())) {
|
|
258
|
+
throw new Error(`${platformServiceIndexPath()} does not exist`);
|
|
259
|
+
}
|
|
260
|
+
const committed = readFileSync(platformServiceIndexPath(), 'utf8');
|
|
261
|
+
if (committed !== serializeJson(parsed)) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
`platform-service-index.json does not match the exact trusted clean sources ` +
|
|
264
|
+
`(authority: ${
|
|
265
|
+
unpinned
|
|
266
|
+
? 'UNPINNED — every repository resolved LIVE, so this names the ' +
|
|
267
|
+
'fleet as it is now and regenerating will move pins'
|
|
268
|
+
: 'PINNED — the committed repositoryPins, no live resolution'
|
|
269
|
+
}).`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
console.log(
|
|
273
|
+
`[platform-service-index] trusted source check passed for ${parsed.services.length} services`,
|
|
274
|
+
);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
writeFileSync(platformServiceIndexPath(), serializeJson(parsed));
|
|
278
|
+
console.log(
|
|
279
|
+
`[platform-service-index] wrote ${platformServiceIndexPath()} from ${parsed.services.length} clean trusted sources`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (isEntryModule(import.meta.url)) {
|
|
284
|
+
main().catch((error) => {
|
|
285
|
+
console.error(`[platform-service-index] ${error.message}`);
|
|
286
|
+
process.exitCode = 1;
|
|
287
|
+
});
|
|
288
|
+
}
|