@xemahq/xema 0.31.2 → 0.33.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/biome.js +1 -1
- package/dist/commands/biome.js.map +1 -1
- package/dist/commands/edition.d.ts.map +1 -1
- package/dist/commands/edition.js +3 -1
- package/dist/commands/edition.js.map +1 -1
- package/dist/commands/installation.js +3 -3
- package/dist/commands/installation.js.map +1 -1
- package/dist/commands/objects.js +1 -1
- package/dist/commands/objects.js.map +1 -1
- package/dist/dev/forked-worker.d.ts.map +1 -1
- package/dist/dev/forked-worker.js +9 -6
- package/dist/dev/forked-worker.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +243 -160
- package/dist/edition/verify-image-evidence.bundle.provenance.json +50 -38
- package/dist/lib/control-plane.js +2 -2
- package/dist/lib/control-plane.js.map +1 -1
- package/dist/lib/plan-format.js.map +1 -1
- package/dist/lib/promise-from-sync.d.ts +2 -0
- package/dist/lib/promise-from-sync.d.ts.map +1 -0
- package/dist/lib/promise-from-sync.js +9 -0
- package/dist/lib/promise-from-sync.js.map +1 -0
- package/dist/runtime/biome-local-install.js +1 -1
- package/dist/runtime/biome-local-install.js.map +1 -1
- package/dist/runtime/boot-graph-loader.js.map +1 -1
- package/dist/runtime/config/sources-config.js.map +1 -1
- package/dist/runtime/edition/verb-map.d.ts +3 -1
- package/dist/runtime/edition/verb-map.d.ts.map +1 -1
- package/dist/runtime/edition/verb-map.js +4 -0
- package/dist/runtime/edition/verb-map.js.map +1 -1
- package/dist/runtime/infra/infra-catalog.d.ts +3 -1
- package/dist/runtime/infra/infra-catalog.d.ts.map +1 -1
- package/dist/runtime/infra/infra-catalog.js.map +1 -1
- package/dist/runtime/installation/lifecycle.d.ts.map +1 -1
- package/dist/runtime/installation/lifecycle.js +7 -6
- package/dist/runtime/installation/lifecycle.js.map +1 -1
- package/dist/runtime/installation/state-store.d.ts.map +1 -1
- package/dist/runtime/installation/state-store.js +2 -1
- package/dist/runtime/installation/state-store.js.map +1 -1
- package/dist/runtime/read-only-no-follow-flags.d.ts +2 -0
- package/dist/runtime/read-only-no-follow-flags.d.ts.map +1 -0
- package/dist/runtime/read-only-no-follow-flags.js +6 -0
- package/dist/runtime/read-only-no-follow-flags.js.map +1 -0
- package/dist/runtime/roster.d.ts.map +1 -1
- package/dist/runtime/roster.js +3 -5
- package/dist/runtime/roster.js.map +1 -1
- package/dist/runtime/run-manifest.d.ts.map +1 -1
- package/dist/runtime/run-manifest.js +1 -3
- package/dist/runtime/run-manifest.js.map +1 -1
- package/dist/runtime/secrets/managed-local-files.d.ts.map +1 -1
- package/dist/runtime/secrets/managed-local-files.js +2 -1
- package/dist/runtime/secrets/managed-local-files.js.map +1 -1
- package/dist/runtime/secrets/managed-local-key-provider.d.ts.map +1 -1
- package/dist/runtime/secrets/managed-local-key-provider.js +13 -10
- package/dist/runtime/secrets/managed-local-key-provider.js.map +1 -1
- package/dist/runtime/secrets/resolver.d.ts.map +1 -1
- package/dist/runtime/secrets/resolver.js +2 -1
- package/dist/runtime/secrets/resolver.js.map +1 -1
- package/dist/runtime/sources/credentials.d.ts.map +1 -1
- package/dist/runtime/sources/credentials.js +6 -9
- package/dist/runtime/sources/credentials.js.map +1 -1
- package/dist/runtime/store-client.d.ts.map +1 -1
- package/dist/runtime/store-client.js.map +1 -1
- package/dist/runtime/supervisor.js +2 -2
- package/dist/runtime/supervisor.js.map +1 -1
- package/edition/README.md +23 -0
- package/edition/build-ledger-completeness.mjs +67 -0
- package/edition/build-ledger.mjs +107 -0
- package/edition/checkout-indexed-source-repositories.mjs +57 -2
- package/edition/customer-definition.mjs +8 -0
- package/edition/develop-snapshot-inputs.mjs +4 -15
- package/edition/dist-check.mjs +11 -7
- package/edition/edition-biome-manifests.mjs +3 -2
- package/edition/edition-pin-verbs.mjs +183 -0
- package/edition/edition-pin.mjs +17 -0
- package/edition/edition-pinned-inputs.mjs +116 -0
- package/edition/edition-source-pins.mjs +209 -0
- package/edition/edition-unpin.mjs +16 -0
- package/edition/image-evidence.mjs +13 -7
- package/edition/indexes-at-pins.mjs +29 -0
- package/edition/release-catalogue.mjs +9 -8
- package/edition/release-edition-artifact.mjs +3 -2
- package/edition/resolve-distribution.mjs +16 -25
- package/edition/resolve-images.mjs +25 -11
- package/edition/seal-image-digests.mjs +12 -5
- package/edition/source-authority.mjs +8 -21
- package/package.json +3 -3
|
@@ -21,13 +21,14 @@ import { assertCanonicalDistributionId } from './distribution-paths.mjs';
|
|
|
21
21
|
import { LOCAL_WORKFLOW, assertEngineImage, editionTarget, workflowFromEnv, writeEditionArtifact } from './edition-artifact.mjs';
|
|
22
22
|
import { assertRosteredChartSetLock } from './edition-chart-set.mjs';
|
|
23
23
|
import { EditionChannel, EditionLockFile } from './edition-constants.mjs';
|
|
24
|
+
import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
|
|
24
25
|
import { WorkspaceRoot, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule } from './edition-workspace.mjs';
|
|
25
26
|
import { descriptorEvidenceTrust } from './evidence-trust-policy.mjs';
|
|
26
27
|
import { loadKernelDistributionSchemas, validateImmutableImageLock } from './lib.mjs';
|
|
27
28
|
import { releaseCatalogue } from './release-catalogue.mjs';
|
|
28
29
|
import { assertReleaseEditionTree } from './release-channel-guard.mjs';
|
|
29
30
|
import { externalSourceTrees, sourceTreeLocator } from './resolve-images.mjs';
|
|
30
|
-
import {
|
|
31
|
+
import { createPinnedSourceAuthority } from './source-authority.mjs';
|
|
31
32
|
|
|
32
33
|
if (isEntryModule(import.meta.url)) {
|
|
33
34
|
configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel]);
|
|
@@ -110,7 +111,7 @@ export async function readCommittedEdition(id, root = editionsRoot()) {
|
|
|
110
111
|
{ cause: error },
|
|
111
112
|
);
|
|
112
113
|
}
|
|
113
|
-
const authority = createPinnedSourceAuthority({ pins:
|
|
114
|
+
const authority = createPinnedSourceAuthority({ pins: (await editionPinnedInputs(id, { schemas })).repositoryPins });
|
|
114
115
|
await verifyBiomeContentHashes({
|
|
115
116
|
schemas,
|
|
116
117
|
lock: sealed,
|
|
@@ -77,7 +77,9 @@ import {
|
|
|
77
77
|
import { resolveDispatchedRuntimeComponents } from './dispatched-runtime-components.mjs';
|
|
78
78
|
import { fleetChartSetLockPath, writeEditionChartSet } from './edition-chart-set.mjs';
|
|
79
79
|
import { workspaceSection } from './edition-config.mjs';
|
|
80
|
-
import {
|
|
80
|
+
import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
|
|
81
|
+
import { splitEditionDefinition } from './edition-source-pins.mjs';
|
|
82
|
+
import { WorkspaceRoot, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule, kernelPackageDir } from './edition-workspace.mjs';
|
|
81
83
|
import { walkDeclaredSources } from './external-source-walk.mjs';
|
|
82
84
|
import {
|
|
83
85
|
biomeTierOrder,
|
|
@@ -260,7 +262,7 @@ export function loadEditionManifest(parseDistribution, id) {
|
|
|
260
262
|
"(core class, the classes the edition's include names, and services a shipped biome's API requires)",
|
|
261
263
|
);
|
|
262
264
|
}
|
|
263
|
-
const manifest = parseDistribution(raw);
|
|
265
|
+
const manifest = parseDistribution(splitEditionDefinition(raw, id).document);
|
|
264
266
|
if (manifest.id !== id) {
|
|
265
267
|
throw new Error(`distribution at ${path} declares id "${manifest.id}", but it was addressed as "${id}"`);
|
|
266
268
|
}
|
|
@@ -314,26 +316,6 @@ export function composeManifest(manifest, pinSource = PUBLISHED_PIN_SOURCE) {
|
|
|
314
316
|
};
|
|
315
317
|
}
|
|
316
318
|
|
|
317
|
-
function readBiomeIndex() {
|
|
318
|
-
if (!existsSync(biomeIndexPath())) {
|
|
319
|
-
throw new Error(
|
|
320
|
-
`${biomeIndexPath()} not found — run \`${commandFor('build-biome-index.mjs')}\` first`,
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
return JSON.parse(readFileSync(biomeIndexPath(), 'utf8'));
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function readPlatformServiceIndex(parsePlatformServiceSourceIndex) {
|
|
327
|
-
if (!existsSync(platformServiceIndexPath())) {
|
|
328
|
-
throw new Error(
|
|
329
|
-
`${platformServiceIndexPath()} not found — run the trusted platform-service index generator first`,
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
return parsePlatformServiceSourceIndex(
|
|
333
|
-
JSON.parse(readFileSync(platformServiceIndexPath(), 'utf8')),
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
319
|
/** Unique key for an index/lock entry (id+target+repo distinguishes cultivar/reference + web/server pairs). */
|
|
338
320
|
function entryKey(e) {
|
|
339
321
|
return JSON.stringify([e.id, e.target, e.repo]);
|
|
@@ -1473,6 +1455,9 @@ export function resolveDistributionFromInputs(
|
|
|
1473
1455
|
// lose its UI. A web joins once EVERY server it requires is in the edition
|
|
1474
1456
|
// (the last of them to be processed completes it), so a UI is never pulled
|
|
1475
1457
|
// in ahead of the capability it administers.
|
|
1458
|
+
// An operator-audience web is skipped here on purpose: operator biomes enter an edition only by explicit
|
|
1459
|
+
// selection (selectors skip them in applySelectors; naming one forces it in; a required service owned by
|
|
1460
|
+
// one must be selected explicitly). Pulling one in implicitly from a server biome would bypass that rule.
|
|
1476
1461
|
const webBiomesByRequiredServer = new Map();
|
|
1477
1462
|
for (const candidate of resourceProviderCandidates) {
|
|
1478
1463
|
if (
|
|
@@ -2226,8 +2211,14 @@ export async function resolveDistributionWithIndex(id, { materialize, inputs, re
|
|
|
2226
2211
|
// THE RELEASE READS THESE FROM DISK; A DEVELOP SNAPSHOT INJECTS THEM
|
|
2227
2212
|
// (`develop-snapshot-inputs.mjs`). Either way they pass through the one
|
|
2228
2213
|
// validation below, so there is a single resolve path and two sources.
|
|
2229
|
-
|
|
2230
|
-
|
|
2214
|
+
// The edition's own indexes (editionPinnedInputs): the committed ones, or
|
|
2215
|
+
// rebuilt at the repositories it holds. Read only when the caller injected
|
|
2216
|
+
// neither.
|
|
2217
|
+
const pinned =
|
|
2218
|
+
inputs?.platformServiceIndex === undefined || inputs?.biomeIndex === undefined
|
|
2219
|
+
? await editionPinnedInputs(id, { schemas })
|
|
2220
|
+
: undefined;
|
|
2221
|
+
const claimedPlatformServiceIndex = inputs?.platformServiceIndex ?? pinned.platformServiceIndex;
|
|
2231
2222
|
// OWNERSHIP IS THE SERVICE'S OWN STATEMENT: each entry's capability domains
|
|
2232
2223
|
// are re-derived from its pinned source manifest (hash-verified), so a
|
|
2233
2224
|
// hand-edited index cannot move where grants land. Only the customer resolve
|
|
@@ -2239,7 +2230,7 @@ export async function resolveDistributionWithIndex(id, { materialize, inputs, re
|
|
|
2239
2230
|
? claimedPlatformServiceIndex
|
|
2240
2231
|
: verifiedPlatformServiceIndex(claimedPlatformServiceIndex, { readManifest });
|
|
2241
2232
|
const platformIndex = validateServiceDependencyOwnerParity(
|
|
2242
|
-
validateImmutableBiomeIndex(inputs?.biomeIndex ??
|
|
2233
|
+
validateImmutableBiomeIndex(inputs?.biomeIndex ?? pinned.biomeIndex),
|
|
2243
2234
|
platformServiceIndex,
|
|
2244
2235
|
);
|
|
2245
2236
|
const { entries, sourceRecords } = await walkDeclaredSources({
|
|
@@ -36,6 +36,8 @@ import { execFileSync } from 'node:child_process';
|
|
|
36
36
|
import { writeFileSync } from 'node:fs';
|
|
37
37
|
import { join, relative, sep } from 'node:path';
|
|
38
38
|
|
|
39
|
+
import { assertBuildLedgerComplete } from './build-ledger-completeness.mjs';
|
|
40
|
+
import { BUILD_LEDGER_FILE, readBuildLedger } from './build-ledger.mjs';
|
|
39
41
|
import { resolveDispatchedRuntimeComponents } from './dispatched-runtime-components.mjs';
|
|
40
42
|
import {
|
|
41
43
|
assertCanonicalDistributionId,
|
|
@@ -43,7 +45,8 @@ import {
|
|
|
43
45
|
readBoundDistributionFile,
|
|
44
46
|
} from './distribution-paths.mjs';
|
|
45
47
|
import { editionWorkspaceConfig } from './edition-config.mjs';
|
|
46
|
-
import {
|
|
48
|
+
import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
|
|
49
|
+
import { dataRoot, editionsRoot, WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule, sourcePath, sourcesRoot } from './edition-workspace.mjs';
|
|
47
50
|
import {
|
|
48
51
|
externalSourceCacheDir,
|
|
49
52
|
materializeGitSource,
|
|
@@ -66,7 +69,6 @@ import {
|
|
|
66
69
|
} from './lib.mjs';
|
|
67
70
|
import { assertReleaseContent } from './release-channel-guard.mjs';
|
|
68
71
|
import { composeEdition } from './resolve-distribution.mjs';
|
|
69
|
-
import { readStrictJsonFile } from './strict-json.mjs';
|
|
70
72
|
|
|
71
73
|
if (isEntryModule(import.meta.url)) {
|
|
72
74
|
configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.Cache]);
|
|
@@ -162,10 +164,6 @@ const FIRST_PARTY_TREES = (repo) => ({ dir: sourcePath(repo), base: sourcesRoot(
|
|
|
162
164
|
const imageBuildInputHashByIdentity = new Map();
|
|
163
165
|
const sourceManifestHashByIdentity = new Map();
|
|
164
166
|
|
|
165
|
-
function readJson(path) {
|
|
166
|
-
return readStrictJsonFile(path);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
167
|
/**
|
|
170
168
|
* Memoized image build-input closure hash.
|
|
171
169
|
*
|
|
@@ -1487,8 +1485,13 @@ export function resolveCrossRepoInputsForRoster({
|
|
|
1487
1485
|
return crossRepoInputsByConsumer;
|
|
1488
1486
|
}
|
|
1489
1487
|
|
|
1490
|
-
/**
|
|
1491
|
-
|
|
1488
|
+
/**
|
|
1489
|
+
* The release's inputs: the COMMITTED lock, and the edition's own indexes
|
|
1490
|
+
* (`editionPinnedInputs`: the committed ones, or rebuilt at the repositories the
|
|
1491
|
+
* edition holds).
|
|
1492
|
+
*/
|
|
1493
|
+
async function releaseImageInputs(schemas, id) {
|
|
1494
|
+
const pinned = await editionPinnedInputs(id, { schemas });
|
|
1492
1495
|
return {
|
|
1493
1496
|
lock: validateImmutableDistributionLock(
|
|
1494
1497
|
readBoundDistributionFile({
|
|
@@ -1499,8 +1502,8 @@ function releaseImageInputs(schemas, id) {
|
|
|
1499
1502
|
parse: (raw) => parseResolvedDistributionLock(schemas, raw),
|
|
1500
1503
|
}).value,
|
|
1501
1504
|
),
|
|
1502
|
-
runtimeSourceIndex:
|
|
1503
|
-
biomeIndex:
|
|
1505
|
+
runtimeSourceIndex: pinned.runtimeImageSourceIndex,
|
|
1506
|
+
biomeIndex: pinned.biomeIndex,
|
|
1504
1507
|
requiredPlatforms: resolveDistributionRequiredPlatforms(schemas, id),
|
|
1505
1508
|
declaredSources: resolveDistributionDeclaredSources(schemas, id),
|
|
1506
1509
|
};
|
|
@@ -1542,7 +1545,7 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
|
|
|
1542
1545
|
biomeIndex,
|
|
1543
1546
|
requiredPlatforms,
|
|
1544
1547
|
declaredSources,
|
|
1545
|
-
} = inputs === undefined ? releaseImageInputs(schemas, id) : snapshotImageInputs(schemas, { inputs, lock });
|
|
1548
|
+
} = inputs === undefined ? await releaseImageInputs(schemas, id) : snapshotImageInputs(schemas, { inputs, lock });
|
|
1546
1549
|
// Materialise every EXTERNAL source at the commit the lock pins, and resolve
|
|
1547
1550
|
// every `repo` handle through one locator from here down.
|
|
1548
1551
|
const locate = sourceTreeLocator(externalSourceTrees(distributionLock));
|
|
@@ -1652,6 +1655,17 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
|
|
|
1652
1655
|
),
|
|
1653
1656
|
};
|
|
1654
1657
|
});
|
|
1658
|
+
// Release lane only: a develop snapshot or customer overlay is not covered by
|
|
1659
|
+
// the committed ledger.
|
|
1660
|
+
if (inputs === undefined) {
|
|
1661
|
+
assertBuildLedgerComplete({
|
|
1662
|
+
ledger: readBuildLedger(),
|
|
1663
|
+
distributionId: id,
|
|
1664
|
+
subjects,
|
|
1665
|
+
requiredPlatforms,
|
|
1666
|
+
expectedAt: join(dataRoot(), BUILD_LEDGER_FILE),
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1655
1669
|
const webBiomes = structure.webBiomes.map((biome) => ({
|
|
1656
1670
|
...biome,
|
|
1657
1671
|
sourceCommit: commitsBefore.get(biome.repo),
|
|
@@ -118,6 +118,7 @@ import { fetchCatalogue } from './catalogue-fetch.mjs';
|
|
|
118
118
|
import { readCataloguePin } from './catalogue-pin.mjs';
|
|
119
119
|
import { CATALOGUE_TOKEN_ENV_FLAG, CATALOGUE_USERNAME_FLAG, DEFINITION_FLAG } from './customer-flags.mjs';
|
|
120
120
|
import { EditionChannel } from './edition-constants.mjs';
|
|
121
|
+
import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
|
|
121
122
|
import { WorkspaceRoot, biomeIndexPath, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule, runtimeImageSourceIndexPath } from './edition-workspace.mjs';
|
|
122
123
|
import {
|
|
123
124
|
assertCoordinatesFinal,
|
|
@@ -508,11 +509,14 @@ export async function sealEditionLocks({ distributionId, root, digests = new Map
|
|
|
508
509
|
}
|
|
509
510
|
const raw = JSON.parse(readFileSync(lockPath, 'utf8'));
|
|
510
511
|
const imageLock = schemas.parseImageLock(rawImages);
|
|
511
|
-
|
|
512
|
+
// A release seal reads the EDITION's indexes and pins (a held repository is sealed at the held commit); a
|
|
513
|
+
// customer seal (catalogue) and a develop seal (injected `vendoredCommit`) are not vendor release editions.
|
|
514
|
+
const editionInputs = catalogue === undefined && vendoredCommit === undefined ? await editionPinnedInputs(distributionId, { schemas }) : undefined;
|
|
515
|
+
const pinned = vendoredCommit ?? ((repo) => createPinnedSourceAuthority({ pins: editionInputs?.repositoryPins ?? committedRepositoryPins() }).vendoredSourceCommitForRepository(repo));
|
|
512
516
|
const lockDocument = raw !== null && typeof raw === 'object' && 'unsealedSchema' in raw ? schemas.parseUnsealedDistributionLock(raw) : schemas.parseDistributionLock(raw);
|
|
513
517
|
if (lockMountsAdapters(lockDocument)) {
|
|
514
518
|
// The adapter host's row: the verified catalogue's for a customer, the data root's biome index for the vendor.
|
|
515
|
-
const rows = catalogue
|
|
519
|
+
const rows = catalogue !== undefined ? catalogue.biomes : (editionInputs?.biomeIndex ?? JSON.parse(readFileSync(biomeIndexPath(), 'utf8')));
|
|
516
520
|
await assertAdapterBundlesMatchHost({ lock: lockDocument, hostRow: adapterHostRow(rows), fetchManifest });
|
|
517
521
|
}
|
|
518
522
|
const result = await sealOrVerifyLock({
|
|
@@ -762,9 +766,12 @@ async function main() {
|
|
|
762
766
|
),
|
|
763
767
|
imageLock,
|
|
764
768
|
distributionId,
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
769
|
+
// A vendor edition grades the pins it RESOLVES at (a held repository is graded at the held commit);
|
|
770
|
+
// a customer seal grades the data root's committed index.
|
|
771
|
+
runtimeSourceIndex:
|
|
772
|
+
catalogue === undefined
|
|
773
|
+
? (await editionPinnedInputs(distributionId, { schemas: await loadKernelDistributionSchemas() })).runtimeImageSourceIndex
|
|
774
|
+
: JSON.parse(readFileSync(runtimeImageSourceIndexPath(), 'utf8')),
|
|
768
775
|
});
|
|
769
776
|
|
|
770
777
|
const { writes, unchanged } = planDigestSeal({
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { tmpdir } from 'node:os';
|
|
10
10
|
import { join, relative, resolve, sep } from 'node:path';
|
|
11
11
|
|
|
12
|
+
import { BUILD_LEDGER_FILE, readBuildLedger } from './build-ledger.mjs';
|
|
12
13
|
import { dataRoot, sourcesRoot } from './edition-workspace.mjs';
|
|
13
14
|
|
|
14
15
|
export const PUBLISHED_SOURCE_REF = 'refs/remotes/origin/main';
|
|
@@ -91,7 +92,7 @@ const BUILT_COMMIT_SEARCH_LIMIT = 500;
|
|
|
91
92
|
* A pin frozen eight days in the past is what kept a fix for a production
|
|
92
93
|
* crashloop out of every image built from it.
|
|
93
94
|
*/
|
|
94
|
-
export
|
|
95
|
+
export { BUILD_LEDGER_FILE };
|
|
95
96
|
|
|
96
97
|
/**
|
|
97
98
|
* Read the committed ledger. A caller that resolves many repositories should
|
|
@@ -106,25 +107,11 @@ export const BUILD_LEDGER_FILE = 'build-ledger.json';
|
|
|
106
107
|
export function loadBuildLedger(
|
|
107
108
|
distributionsDir = dataRoot(),
|
|
108
109
|
) {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (error?.code === 'ENOENT') {return null;}
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
if (
|
|
118
|
-
raw === null ||
|
|
119
|
-
typeof raw !== 'object' ||
|
|
120
|
-
raw.schemaVersion !== 1 ||
|
|
121
|
-
typeof raw.repositories !== 'object' ||
|
|
122
|
-
raw.repositories === null ||
|
|
123
|
-
Array.isArray(raw.repositories)
|
|
124
|
-
) {
|
|
125
|
-
throw new Error(`${BUILD_LEDGER_FILE} is not strict schema v1`);
|
|
126
|
-
}
|
|
127
|
-
return raw.repositories;
|
|
110
|
+
const ledger = readBuildLedger(distributionsDir);
|
|
111
|
+
if (ledger === null) {return null;}
|
|
112
|
+
return Object.fromEntries(
|
|
113
|
+
Object.entries(ledger.repositories).map(([repo, entry]) => [repo, entry.commits]),
|
|
114
|
+
);
|
|
128
115
|
}
|
|
129
116
|
|
|
130
117
|
/**
|
|
@@ -197,7 +184,7 @@ function ledgerCommits(repo, ledger) {
|
|
|
197
184
|
* positive — each is a record that a build actually happened — while either
|
|
198
185
|
* alone has been measured missing whole repositories.
|
|
199
186
|
*/
|
|
200
|
-
function builtCommits(directory, ref, repo, ledger) {
|
|
187
|
+
export function builtCommits(directory, ref, repo, ledger) {
|
|
201
188
|
const fromTree = evidenceTreeCommits(directory, ref);
|
|
202
189
|
const fromLedger = ledgerCommits(repo, ledger);
|
|
203
190
|
// Null from BOTH means neither record has ever heard of this repository, so
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/xema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/xema-dev/xema-cli.git",
|
|
@@ -85,13 +85,13 @@
|
|
|
85
85
|
"verify:pack": "node ./scripts/verify-packed-assets.cjs",
|
|
86
86
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
87
87
|
"start": "node ./dist/cli.js",
|
|
88
|
-
"lint": "eslint .",
|
|
88
|
+
"lint": "eslint . --max-warnings 0",
|
|
89
89
|
"lint:fix": "eslint . --fix",
|
|
90
90
|
"test": "jest --config jest.config.cjs && pnpm run test:edition",
|
|
91
91
|
"test:edition": "node --test \"edition/*.test.mjs\" \"edition/*-contract-tests.mjs\"",
|
|
92
92
|
"typecheck": "tsc --noEmit"
|
|
93
93
|
},
|
|
94
94
|
"xemaRelease": {
|
|
95
|
-
"sourceDigest": "sha256:
|
|
95
|
+
"sourceDigest": "sha256:ec5750fec69809f0c18307f59b655b145349cffd670741ac33dbb1aac0029d62"
|
|
96
96
|
}
|
|
97
97
|
}
|