@dsh-enhanced/plugin-control-plane 0.1.48 → 0.1.53
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 +187 -15
- package/bin/dsh-live-qualification-authority.js +15 -0
- package/bin/dsh-local-release-adapter.js +331 -9
- package/bin/dsh-systemd-host-attestor.js +359 -9
- package/bin/dsh-systemd-host-authority.js +12 -0
- package/lib/adoption-coordinator.d.ts +3 -1
- package/lib/adoption-coordinator.d.ts.map +1 -1
- package/lib/adoption-coordinator.js +24 -9
- package/lib/adoption-coordinator.js.map +1 -1
- package/lib/cli.d.ts.map +1 -1
- package/lib/cli.js +33 -14
- package/lib/cli.js.map +1 -1
- package/lib/creation-capability-feedback.d.ts +23 -0
- package/lib/creation-capability-feedback.d.ts.map +1 -0
- package/lib/creation-capability-feedback.js +132 -0
- package/lib/creation-capability-feedback.js.map +1 -0
- package/lib/creation-capability-journal.d.ts +66 -0
- package/lib/creation-capability-journal.d.ts.map +1 -0
- package/lib/creation-capability-journal.js +775 -0
- package/lib/creation-capability-journal.js.map +1 -0
- package/lib/creation-capability-runtime.d.ts +63 -0
- package/lib/creation-capability-runtime.d.ts.map +1 -0
- package/lib/creation-capability-runtime.js +578 -0
- package/lib/creation-capability-runtime.js.map +1 -0
- package/lib/creation-capability-source.d.ts +37 -0
- package/lib/creation-capability-source.d.ts.map +1 -0
- package/lib/creation-capability-source.js +328 -0
- package/lib/creation-capability-source.js.map +1 -0
- package/lib/creation-capability-types.d.ts +265 -0
- package/lib/creation-capability-types.d.ts.map +1 -0
- package/lib/creation-capability-types.js +2 -0
- package/lib/creation-capability-types.js.map +1 -0
- package/lib/deployment-readiness.d.ts +16 -0
- package/lib/deployment-readiness.d.ts.map +1 -1
- package/lib/deployment-readiness.js +48 -1
- package/lib/deployment-readiness.js.map +1 -1
- package/lib/effect-blocked-replay.js +2 -2
- package/lib/effect-blocked-replay.js.map +1 -1
- package/lib/foreground-deployment-runtime.d.ts +3 -0
- package/lib/foreground-deployment-runtime.d.ts.map +1 -1
- package/lib/foreground-deployment-runtime.js +55 -13
- package/lib/foreground-deployment-runtime.js.map +1 -1
- package/lib/foreground-deployment.d.ts.map +1 -1
- package/lib/foreground-deployment.js +5 -0
- package/lib/foreground-deployment.js.map +1 -1
- package/lib/host-attestor.d.ts +2 -0
- package/lib/host-attestor.d.ts.map +1 -1
- package/lib/host-attestor.js +8 -1
- package/lib/host-attestor.js.map +1 -1
- package/lib/host-inputs.d.ts +13 -0
- package/lib/host-inputs.d.ts.map +1 -0
- package/lib/host-inputs.js +162 -0
- package/lib/host-inputs.js.map +1 -0
- package/lib/host-maintenance.d.ts +61 -0
- package/lib/host-maintenance.d.ts.map +1 -0
- package/lib/host-maintenance.js +158 -0
- package/lib/host-maintenance.js.map +1 -0
- package/lib/host-readiness.d.ts +5 -0
- package/lib/host-readiness.d.ts.map +1 -0
- package/lib/host-readiness.js +22 -0
- package/lib/host-readiness.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +7 -0
- package/lib/index.js.map +1 -1
- package/lib/live-qualification-authority.d.ts +32 -0
- package/lib/live-qualification-authority.d.ts.map +1 -0
- package/lib/live-qualification-authority.js +280 -0
- package/lib/live-qualification-authority.js.map +1 -0
- package/lib/live-qualification-client.d.ts +4 -0
- package/lib/live-qualification-client.d.ts.map +1 -0
- package/lib/live-qualification-client.js +22 -0
- package/lib/live-qualification-client.js.map +1 -0
- package/lib/live-qualification-runtime.d.ts +78 -0
- package/lib/live-qualification-runtime.d.ts.map +1 -0
- package/lib/live-qualification-runtime.js +367 -0
- package/lib/live-qualification-runtime.js.map +1 -0
- package/lib/live-qualification.d.ts +73 -0
- package/lib/live-qualification.d.ts.map +1 -0
- package/lib/live-qualification.js +164 -0
- package/lib/live-qualification.js.map +1 -0
- package/lib/native-source-build-worker.d.ts +4 -0
- package/lib/native-source-build-worker.d.ts.map +1 -0
- package/lib/native-source-build-worker.js +87 -0
- package/lib/native-source-build-worker.js.map +1 -0
- package/lib/native-source-build.d.ts +36 -0
- package/lib/native-source-build.d.ts.map +1 -0
- package/lib/native-source-build.js +143 -0
- package/lib/native-source-build.js.map +1 -0
- package/lib/native-source-pack.d.ts +11 -0
- package/lib/native-source-pack.d.ts.map +1 -0
- package/lib/native-source-pack.js +83 -0
- package/lib/native-source-pack.js.map +1 -0
- package/lib/owner-task-gaps.d.ts +2 -0
- package/lib/owner-task-gaps.d.ts.map +1 -1
- package/lib/owner-task-gaps.js +9 -1
- package/lib/owner-task-gaps.js.map +1 -1
- package/lib/runtime-epoch.d.ts +69 -0
- package/lib/runtime-epoch.d.ts.map +1 -0
- package/lib/runtime-epoch.js +139 -0
- package/lib/runtime-epoch.js.map +1 -0
- package/lib/runtime-observer-protocol.d.ts +3 -3
- package/lib/runtime-observer-protocol.js +5 -5
- package/lib/runtime-observer-protocol.js.map +1 -1
- package/lib/runtime-observer.d.ts.map +1 -1
- package/lib/runtime-observer.js +42 -6
- package/lib/runtime-observer.js.map +1 -1
- package/lib/service.d.ts +248 -4
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +1431 -64
- package/lib/service.js.map +1 -1
- package/lib/source-adoption-authority.d.ts +3 -0
- package/lib/source-adoption-authority.d.ts.map +1 -1
- package/lib/source-adoption-authority.js +13 -3
- package/lib/source-adoption-authority.js.map +1 -1
- package/lib/source-adoption-runner.d.ts +3 -0
- package/lib/source-adoption-runner.d.ts.map +1 -1
- package/lib/source-adoption-runner.js +16 -4
- package/lib/source-adoption-runner.js.map +1 -1
- package/lib/source-approval-authority.d.ts.map +1 -1
- package/lib/source-approval-authority.js +1 -0
- package/lib/source-approval-authority.js.map +1 -1
- package/lib/source-baseline.d.ts +37 -0
- package/lib/source-baseline.d.ts.map +1 -0
- package/lib/source-baseline.js +250 -0
- package/lib/source-baseline.js.map +1 -0
- package/lib/source-build.d.ts +43 -9
- package/lib/source-build.d.ts.map +1 -1
- package/lib/source-build.js +670 -57
- package/lib/source-build.js.map +1 -1
- package/lib/source-context.d.ts +18 -0
- package/lib/source-context.d.ts.map +1 -1
- package/lib/source-context.js +85 -11
- package/lib/source-context.js.map +1 -1
- package/lib/source-creation.d.ts +106 -0
- package/lib/source-creation.d.ts.map +1 -0
- package/lib/source-creation.js +664 -0
- package/lib/source-creation.js.map +1 -0
- package/lib/source-job-types.d.ts +28 -1
- package/lib/source-job-types.d.ts.map +1 -1
- package/lib/source-jobs.d.ts +33 -3
- package/lib/source-jobs.d.ts.map +1 -1
- package/lib/source-jobs.js +420 -43
- package/lib/source-jobs.js.map +1 -1
- package/lib/source-maintenance.d.ts +66 -0
- package/lib/source-maintenance.d.ts.map +1 -0
- package/lib/source-maintenance.js +177 -0
- package/lib/source-maintenance.js.map +1 -0
- package/lib/source-revision.d.ts +57 -0
- package/lib/source-revision.d.ts.map +1 -0
- package/lib/source-revision.js +175 -0
- package/lib/source-revision.js.map +1 -0
- package/lib/source-workspace.d.ts +19 -14
- package/lib/source-workspace.d.ts.map +1 -1
- package/lib/source-workspace.js +90 -17
- package/lib/source-workspace.js.map +1 -1
- package/lib/sqlite.d.ts +13 -3
- package/lib/sqlite.d.ts.map +1 -1
- package/lib/sqlite.js +362 -1
- package/lib/sqlite.js.map +1 -1
- package/lib/store.d.ts +258 -3
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +2257 -96
- package/lib/store.js.map +1 -1
- package/lib/systemd-host-authority.d.ts +68 -0
- package/lib/systemd-host-authority.d.ts.map +1 -0
- package/lib/systemd-host-authority.js +424 -0
- package/lib/systemd-host-authority.js.map +1 -0
- package/lib/task-observation-evidence.d.ts +32 -0
- package/lib/task-observation-evidence.d.ts.map +1 -0
- package/lib/task-observation-evidence.js +62 -0
- package/lib/task-observation-evidence.js.map +1 -0
- package/lib/task-observation-runtime.d.ts +2 -3
- package/lib/task-observation-runtime.d.ts.map +1 -1
- package/lib/task-observation-runtime.js +7 -39
- package/lib/task-observation-runtime.js.map +1 -1
- package/lib/task-observation-store.js +1 -1
- package/lib/trust.d.ts.map +1 -1
- package/lib/trust.js +0 -0
- package/lib/trust.js.map +1 -1
- package/lib/types.d.ts +57 -7
- package/lib/types.d.ts.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +22 -14
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
existsSync,
|
|
26
26
|
fsyncSync,
|
|
27
27
|
lstatSync,
|
|
28
|
+
mkdtempSync,
|
|
28
29
|
mkdirSync,
|
|
29
30
|
openSync,
|
|
30
31
|
readFileSync,
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
readdirSync,
|
|
34
35
|
readSync,
|
|
35
36
|
renameSync,
|
|
37
|
+
rmdirSync,
|
|
36
38
|
rmSync,
|
|
37
39
|
fstatSync,
|
|
38
40
|
statSync,
|
|
@@ -43,6 +45,7 @@ import {
|
|
|
43
45
|
} from 'node:fs'
|
|
44
46
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
45
47
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
48
|
+
import { gunzipSync, gzipSync } from 'node:zlib'
|
|
46
49
|
|
|
47
50
|
export const LOCAL_RELEASE_ADAPTER_VERSION = 'dsh-local-release-adapter-1'
|
|
48
51
|
const PHASES = new Set(['pr', 'review', 'merge', 'build', 'sign', 'publish', 'registry-verify', 'catalog-admission'])
|
|
@@ -411,7 +414,8 @@ function catalogConfig(value, npm = false) {
|
|
|
411
414
|
}
|
|
412
415
|
function buildConfig(value) {
|
|
413
416
|
const item = object(value, 'build config')
|
|
414
|
-
|
|
417
|
+
const expected = ['sandboxExecutable', 'tarExecutable', 'nodeExecutable', 'pnpmExecutable', 'pnpmRoot', 'storeRoot']
|
|
418
|
+
exactKeys(item, item.cacheRoot === undefined ? expected : [...expected, 'cacheRoot'], 'build config')
|
|
415
419
|
const sandboxExecutable = inspectExecutable(item.sandboxExecutable, 'build sandbox')
|
|
416
420
|
const tarExecutable = inspectExecutable(item.tarExecutable, 'tar')
|
|
417
421
|
if (sandboxExecutable.path !== '/usr/bin/bwrap') fail('local build adapter requires the pinned Linux bubblewrap sandbox')
|
|
@@ -419,6 +423,7 @@ function buildConfig(value) {
|
|
|
419
423
|
const pnpmExecutable = inspectExecutable(item.pnpmExecutable, 'pnpm executable')
|
|
420
424
|
const pnpmRoot = pinnedDirectory(item.pnpmRoot, 'pnpm root')
|
|
421
425
|
const storeRoot = pinnedDirectory(item.storeRoot, 'pnpm store')
|
|
426
|
+
const cacheRoot = item.cacheRoot === undefined ? undefined : pinnedDirectory(item.cacheRoot, 'pnpm cache')
|
|
422
427
|
if (nodeExecutable.path !== join(pnpmRoot.path, 'node')) fail('Node executable must be the pinned pnpm root node entrypoint')
|
|
423
428
|
if (pnpmExecutable.path !== join(pnpmRoot.path, 'pnpm')) fail('pnpm executable must be the pinned pnpm root entrypoint')
|
|
424
429
|
let pnpmManifest
|
|
@@ -428,14 +433,40 @@ function buildConfig(value) {
|
|
|
428
433
|
const storeVersion = Number.parseInt(pnpmVersion.split('.')[0], 10)
|
|
429
434
|
const projectsPath = join(storeRoot.path, `v${storeVersion}`, 'projects')
|
|
430
435
|
if (!existsSync(projectsPath) || !lstatSync(projectsPath).isDirectory()) fail('pnpm store project registry is unavailable')
|
|
431
|
-
return { sandboxExecutable, tarExecutable, nodeExecutable, pnpmExecutable, pnpmRoot, storeRoot, storeVersion }
|
|
436
|
+
return { sandboxExecutable, tarExecutable, nodeExecutable, pnpmExecutable, pnpmRoot, storeRoot, cacheRoot, storeVersion }
|
|
432
437
|
}
|
|
433
438
|
function verifyBuildPins(build) {
|
|
434
|
-
|
|
439
|
+
const pinned = [[build.pnpmRoot, 'pnpm root'], [build.storeRoot, 'pnpm store']]
|
|
440
|
+
if (build.cacheRoot !== undefined) pinned.push([build.cacheRoot, 'pnpm cache'])
|
|
441
|
+
for (const [item, label] of pinned) {
|
|
435
442
|
if (directoryInventory(item.path, label).sha256 !== item.sha256) fail(`${label} changed after configuration validation`)
|
|
436
443
|
}
|
|
437
444
|
}
|
|
438
445
|
|
|
446
|
+
export function inspectLocalReleaseBuildEnvironment(value) {
|
|
447
|
+
const input = object(value, 'local release build environment')
|
|
448
|
+
exactKeys(input, input.cacheRoot === undefined ? ['pnpmRoot', 'storeRoot'] : ['pnpmRoot', 'storeRoot', 'cacheRoot'],
|
|
449
|
+
'local release build environment')
|
|
450
|
+
const executable = (path, label) => {
|
|
451
|
+
const pin = { path, sha256: sha256Bytes(stableBytes(path, label)) }
|
|
452
|
+
return inspectExecutable(pin, label)
|
|
453
|
+
}
|
|
454
|
+
const pnpmRoot = directoryInventory(input.pnpmRoot, 'pnpm root')
|
|
455
|
+
const storeRoot = directoryInventory(input.storeRoot, 'pnpm store')
|
|
456
|
+
const cacheRoot = input.cacheRoot === undefined ? undefined : directoryInventory(input.cacheRoot, 'pnpm cache')
|
|
457
|
+
const pins = {
|
|
458
|
+
sandboxExecutable: executable('/usr/bin/bwrap', 'build sandbox'),
|
|
459
|
+
tarExecutable: executable('/usr/bin/tar', 'tar'),
|
|
460
|
+
nodeExecutable: executable(join(pnpmRoot.path, 'node'), 'build Node executable'),
|
|
461
|
+
pnpmExecutable: executable(join(pnpmRoot.path, 'pnpm'), 'pnpm executable'),
|
|
462
|
+
pnpmRoot: { path: pnpmRoot.path, sha256: pnpmRoot.sha256 },
|
|
463
|
+
storeRoot: { path: storeRoot.path, sha256: storeRoot.sha256 },
|
|
464
|
+
...(cacheRoot === undefined ? {} : { cacheRoot: { path: cacheRoot.path, sha256: cacheRoot.sha256 } }),
|
|
465
|
+
}
|
|
466
|
+
buildConfig(pins)
|
|
467
|
+
return pins
|
|
468
|
+
}
|
|
469
|
+
|
|
439
470
|
function command(executable, args, cwd, extraEnvironment = {}, input, inherited = [], hooks = {}) {
|
|
440
471
|
if (process.platform !== 'linux' || !existsSync('/proc/self/fd')) fail('descriptor-pinned commands require Linux /proc/self/fd')
|
|
441
472
|
const pinned = openPinnedFile(executable, `pinned ${basename(executable.path)} executable`, MAX_ARTIFACT_BYTES, true)
|
|
@@ -488,14 +519,17 @@ function openSandboxContext(build, workspace, output, runRoot) {
|
|
|
488
519
|
const storeProjects = join(runRoot, 'store-projects')
|
|
489
520
|
if (!existsSync(storeProjects)) mkdirSync(storeProjects, { mode: 0o700 })
|
|
490
521
|
const toolchainSnapshot = join(runRoot, 'toolchain-snapshot'); const storeSnapshot = join(runRoot, 'store-snapshot')
|
|
522
|
+
const cacheSnapshot = build.cacheRoot === undefined ? undefined : join(runRoot, 'cache-snapshot')
|
|
491
523
|
if (!existsSync(toolchainSnapshot)) copyPinnedTree(build.pnpmRoot, toolchainSnapshot, 'pnpm root')
|
|
492
524
|
if (!existsSync(storeSnapshot)) copyPinnedTree(build.storeRoot, storeSnapshot, 'pnpm store')
|
|
525
|
+
if (cacheSnapshot !== undefined && !existsSync(cacheSnapshot)) copyPinnedTree(build.cacheRoot, cacheSnapshot, 'pnpm cache')
|
|
493
526
|
const pnpmRoot = openPinnedDirectory({ path: toolchainSnapshot, sha256: build.pnpmRoot.sha256 }, 'pnpm snapshot')
|
|
494
527
|
const storeRoot = openPinnedDirectory({ path: storeSnapshot, sha256: build.storeRoot.sha256 }, 'pnpm store snapshot')
|
|
495
528
|
const workspaceRoot = openPinnedDirectory({ path: workspace }, 'build workspace')
|
|
496
529
|
const outputRoot = openPinnedDirectory({ path: output }, 'build output')
|
|
497
530
|
const storeProjectsRoot = openPinnedDirectory({ path: storeProjects }, 'pnpm project registry')
|
|
498
531
|
const mounts = [pnpmRoot, storeRoot, workspaceRoot, outputRoot, storeProjectsRoot]
|
|
532
|
+
if (cacheSnapshot !== undefined) mounts.push(openPinnedDirectory({ path: cacheSnapshot, sha256: build.cacheRoot.sha256 }, 'pnpm cache snapshot'))
|
|
499
533
|
return { mounts, runRoot }
|
|
500
534
|
}
|
|
501
535
|
function closeSandboxContext(context) { for (const item of context.mounts) closePinnedDirectory(item) }
|
|
@@ -504,16 +538,95 @@ function sandboxCommand(build, args, context, hooks = {}) {
|
|
|
504
538
|
for (const item of mounts) verifyPinnedDirectory(item)
|
|
505
539
|
const mountFd = index => String(3 + index)
|
|
506
540
|
const sandboxArgs = ['--unshare-all', '--die-with-parent', '--new-session', '--clearenv',
|
|
507
|
-
'--ro-bind', '/usr', '/usr', '--ro-bind', '/lib', '/lib',
|
|
541
|
+
'--ro-bind', '/usr', '/usr', '--ro-bind', '/lib', '/lib',
|
|
542
|
+
// Debian arm64 keeps its loader under /lib; /lib64 is an x86_64 path.
|
|
543
|
+
...(process.arch === 'arm64' ? [] : ['--ro-bind', '/lib64', '/lib64']),
|
|
544
|
+
'--symlink', 'usr/bin', '/bin',
|
|
508
545
|
'--ro-bind-fd', mountFd(0), '/toolchain', '--ro-bind-fd', mountFd(1), '/store',
|
|
546
|
+
...(build.cacheRoot === undefined ? [] : ['--ro-bind-fd', mountFd(5), '/cache']),
|
|
509
547
|
'--bind-fd', mountFd(4), `/store/v${build.storeVersion}/projects`, '--proc', '/proc', '--dev', '/dev', '--tmpfs', '/tmp', '--dir', '/home',
|
|
510
548
|
'--bind-fd', mountFd(2), '/workspace', '--bind-fd', mountFd(3), '/output',
|
|
511
549
|
'--chdir', '/workspace', '--setenv', 'HOME', '/home', '--setenv', 'TMPDIR', '/tmp', '--setenv', 'SOURCE_DATE_EPOCH', '0',
|
|
512
|
-
'--setenv', 'PATH', '/toolchain:/usr/bin:/bin',
|
|
550
|
+
'--setenv', 'PATH', '/toolchain:/usr/bin:/bin',
|
|
551
|
+
...(build.cacheRoot === undefined ? [] : ['--setenv', 'pnpm_config_cache_dir', '/cache']),
|
|
552
|
+
'/toolchain/pnpm', ...args]
|
|
513
553
|
command(build.sandboxExecutable, sandboxArgs, runRoot, {}, undefined, mounts.map(item => item.descriptor),
|
|
514
554
|
{ beforeSpawn: hooks.beforeSandboxSpawn, afterSpawn: hooks.afterSandboxSpawn, afterFinally: hooks.afterSandboxFinally })
|
|
515
555
|
for (const item of mounts) verifyPinnedDirectory(item)
|
|
516
556
|
}
|
|
557
|
+
|
|
558
|
+
/** Build one frozen local cohort with the release adapter's pinned, offline
|
|
559
|
+
* sandbox. The caller supplies private staging directories, never a live Home.
|
|
560
|
+
* All candidate code sees is a copy without Git metadata or installed modules. */
|
|
561
|
+
export function buildLocalUpdateCohort(value) {
|
|
562
|
+
const input = object(value, 'local update cohort')
|
|
563
|
+
exactKeys(input, ['build', 'workspace', 'output', 'packagePaths'], 'local update cohort')
|
|
564
|
+
const build = buildConfig(input.build)
|
|
565
|
+
const workspace = canonicalPath(input.workspace, 'local update workspace')
|
|
566
|
+
const output = canonicalPath(input.output, 'local update output')
|
|
567
|
+
if (workspace === output || dirname(workspace) !== dirname(output)) fail('local update workspace and output must be private siblings')
|
|
568
|
+
privateDirectory(dirname(workspace), 'local update staging parent')
|
|
569
|
+
privateDirectory(workspace, 'local update workspace')
|
|
570
|
+
privateDirectory(output, 'local update output')
|
|
571
|
+
if (readdirSync(output).length !== 0) fail('local update output must be empty')
|
|
572
|
+
const outputIdentity = statSync(output, { bigint: true })
|
|
573
|
+
if (!Array.isArray(input.packagePaths) || input.packagePaths.length < 1 || input.packagePaths.length > 128) {
|
|
574
|
+
fail('local update package paths exceed their bound')
|
|
575
|
+
}
|
|
576
|
+
const paths = input.packagePaths.map(path => text(path, 'local update package path', /^(?:plugins|packages)\/[a-z0-9][a-z0-9-]{0,63}$/u))
|
|
577
|
+
const slugs = paths.map(path => basename(path))
|
|
578
|
+
if (new Set(paths).size !== paths.length || new Set(slugs).size !== slugs.length) fail('local update package paths are ambiguous')
|
|
579
|
+
verifyBuildPins(build)
|
|
580
|
+
const runRoot = mkdtempSync(join(dirname(workspace), '.local-cohort-build-'))
|
|
581
|
+
try {
|
|
582
|
+
chmodSync(runRoot, 0o700)
|
|
583
|
+
const isolatedWorkspace = join(runRoot, 'workspace')
|
|
584
|
+
copyBuildWorkspace(workspace, isolatedWorkspace)
|
|
585
|
+
normalizeBuildBinModes(isolatedWorkspace)
|
|
586
|
+
const sourceInputs = snapshotBuildInputs(isolatedWorkspace)
|
|
587
|
+
for (const path of paths) {
|
|
588
|
+
const packageRoot = join(isolatedWorkspace, path)
|
|
589
|
+
if (!existsSync(packageRoot) || !lstatSync(packageRoot).isDirectory()
|
|
590
|
+
|| realpathSync(packageRoot) !== resolve(packageRoot)) fail(`local update package is unavailable: ${path}`)
|
|
591
|
+
}
|
|
592
|
+
const packed = join(runRoot, 'packed'); mkdirSync(packed, { mode: 0o700 })
|
|
593
|
+
for (const slug of slugs) mkdirSync(join(packed, slug), { mode: 0o700 })
|
|
594
|
+
const sandbox = openSandboxContext(build, isolatedWorkspace, packed, runRoot)
|
|
595
|
+
try {
|
|
596
|
+
sandboxCommand(build, ['install', '--offline', '--frozen-lockfile', '--frozen-store', '--ignore-scripts',
|
|
597
|
+
'--package-import-method=copy', '--store-dir=/store'], sandbox)
|
|
598
|
+
sandboxCommand(build, ['--workspace-root', '--if-present', 'run', 'build'], sandbox)
|
|
599
|
+
for (const [index, path] of paths.entries()) {
|
|
600
|
+
const packageRoot = join(isolatedWorkspace, path)
|
|
601
|
+
if (!lstatSync(packageRoot).isDirectory() || realpathSync(packageRoot) !== resolve(packageRoot)) {
|
|
602
|
+
fail(`local update package changed before pack: ${path}`)
|
|
603
|
+
}
|
|
604
|
+
sandboxCommand(build, ['--dir', `/workspace/${path}`, 'pack', '--pack-destination', `/output/${slugs[index]}`], sandbox)
|
|
605
|
+
}
|
|
606
|
+
} finally { closeSandboxContext(sandbox) }
|
|
607
|
+
verifyBuildInputs(isolatedWorkspace, sourceInputs)
|
|
608
|
+
verifyBuildPins(build)
|
|
609
|
+
if (readdirSync(packed).sort().join('\0') !== [...slugs].sort().join('\0')) fail('local update pack output has unexpected directories')
|
|
610
|
+
let totalBytes = 0
|
|
611
|
+
for (const slug of slugs) {
|
|
612
|
+
const directory = join(packed, slug)
|
|
613
|
+
privateDirectory(directory, `local update ${slug} output`)
|
|
614
|
+
const names = readdirSync(directory)
|
|
615
|
+
if (names.length !== 1 || !names[0].endsWith('.tgz')) fail(`local update pack output is ambiguous: ${slug}`)
|
|
616
|
+
const tarball = safeRegularFile(join(directory, names[0]), `local update ${slug} tarball`)
|
|
617
|
+
totalBytes += lstatSync(tarball).size
|
|
618
|
+
if (totalBytes > 2_147_483_648) fail('local update cohort output exceeds its bound')
|
|
619
|
+
}
|
|
620
|
+
// Output is a private, empty preparation directory; make the complete
|
|
621
|
+
// validated set visible in one rename, or leave it absent on failure.
|
|
622
|
+
const currentOutput = lstatSync(output, { bigint: true })
|
|
623
|
+
if (!currentOutput.isDirectory() || currentOutput.dev !== outputIdentity.dev
|
|
624
|
+
|| currentOutput.ino !== outputIdentity.ino || readdirSync(output).length !== 0) fail('local update output changed during build')
|
|
625
|
+
rmdirSync(output)
|
|
626
|
+
renameSync(packed, output)
|
|
627
|
+
fsyncDirectory(dirname(output))
|
|
628
|
+
} finally { rmSync(runRoot, { recursive: true, force: true }) }
|
|
629
|
+
}
|
|
517
630
|
function git(gitValue, args, cwd, environment = {}, hooks = {}) {
|
|
518
631
|
return command(gitValue.executable, [...SAFE_GIT_CONFIG, ...args], cwd,
|
|
519
632
|
{ GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null', ...environment }, undefined, [],
|
|
@@ -964,6 +1077,92 @@ function copyBuildWorkspace(source, destination) {
|
|
|
964
1077
|
}
|
|
965
1078
|
inspect(destination)
|
|
966
1079
|
}
|
|
1080
|
+
function normalizeBuildBinModes(root) {
|
|
1081
|
+
for (const collection of ['plugins', 'packages']) {
|
|
1082
|
+
const collectionRoot = join(root, collection)
|
|
1083
|
+
if (!existsSync(collectionRoot)) continue
|
|
1084
|
+
for (const name of readdirSync(collectionRoot)) {
|
|
1085
|
+
const packageRoot = join(collectionRoot, name)
|
|
1086
|
+
if (!lstatSync(packageRoot).isDirectory()) continue
|
|
1087
|
+
const manifestPath = join(packageRoot, 'package.json')
|
|
1088
|
+
if (!existsSync(manifestPath)) continue
|
|
1089
|
+
const manifestFile = lstatSync(manifestPath)
|
|
1090
|
+
if (!manifestFile.isFile() || manifestFile.size > MAX_INPUT_BYTES) fail('local update package manifest is unsafe')
|
|
1091
|
+
let manifest
|
|
1092
|
+
try { manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) }
|
|
1093
|
+
catch { fail('local update package manifest is invalid') }
|
|
1094
|
+
if (manifest === null || typeof manifest !== 'object' || Array.isArray(manifest)) fail('local update package manifest is invalid')
|
|
1095
|
+
const bin = manifest.bin
|
|
1096
|
+
if (bin === undefined) continue
|
|
1097
|
+
const binPaths = typeof bin === 'string' ? [bin]
|
|
1098
|
+
: bin !== null && typeof bin === 'object' && !Array.isArray(bin) ? Object.values(bin) : null
|
|
1099
|
+
if (binPaths === null) fail('local update package bin is invalid')
|
|
1100
|
+
for (const binPath of binPaths) {
|
|
1101
|
+
if (typeof binPath !== 'string' || binPath.length === 0 || binPath.length > 2_000
|
|
1102
|
+
|| binPath.includes('\0') || binPath.includes('\\') || isAbsolute(binPath)
|
|
1103
|
+
|| binPath.split('/').includes('..')) fail('local update bin path is invalid')
|
|
1104
|
+
const path = resolve(packageRoot, binPath)
|
|
1105
|
+
const inside = relative(packageRoot, path)
|
|
1106
|
+
if (!inside || inside === '..' || inside.startsWith(`..${sep}`) || isAbsolute(inside)) fail('local update bin path escapes its package')
|
|
1107
|
+
const parts = inside.split(sep)
|
|
1108
|
+
let current = packageRoot
|
|
1109
|
+
let missing = false
|
|
1110
|
+
for (const [index, part] of parts.entries()) {
|
|
1111
|
+
current = join(current, part)
|
|
1112
|
+
let metadata
|
|
1113
|
+
try { metadata = lstatSync(current) }
|
|
1114
|
+
catch (error) {
|
|
1115
|
+
if (error.code !== 'ENOENT') throw error
|
|
1116
|
+
missing = true
|
|
1117
|
+
break
|
|
1118
|
+
}
|
|
1119
|
+
if (metadata.isSymbolicLink() || (index === parts.length - 1 ? !metadata.isFile() : !metadata.isDirectory())) {
|
|
1120
|
+
fail('local update bin path is unsafe')
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
if (missing) continue
|
|
1124
|
+
if (realpathSync(path) !== path) fail('local update bin path is unsafe')
|
|
1125
|
+
chmodSync(path, 0o755)
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
function snapshotBuildInputs(root) {
|
|
1131
|
+
const directories = [], files = []
|
|
1132
|
+
let entries = 0, bytes = 0
|
|
1133
|
+
const visit = (path, relativePath = '') => {
|
|
1134
|
+
const metadata = lstatSync(path)
|
|
1135
|
+
if (++entries > 100_000) fail('local update source has too many entries')
|
|
1136
|
+
if (metadata.isDirectory() && !metadata.isSymbolicLink()) {
|
|
1137
|
+
directories.push({ path: relativePath, mode: metadata.mode & 0o777 })
|
|
1138
|
+
for (const name of readdirSync(path).sort()) visit(join(path, name), relativePath ? `${relativePath}/${name}` : name)
|
|
1139
|
+
} else if (metadata.isFile() && !metadata.isSymbolicLink()) {
|
|
1140
|
+
bytes += metadata.size
|
|
1141
|
+
if (metadata.size > MAX_ARTIFACT_BYTES || bytes > 4_294_967_296) fail('local update source exceeds its size bound')
|
|
1142
|
+
files.push({ path: relativePath, mode: metadata.mode & 0o777, size: metadata.size,
|
|
1143
|
+
sha256: sha256Bytes(readFileSync(path)) })
|
|
1144
|
+
} else fail('local update source contains an unsupported entry')
|
|
1145
|
+
}
|
|
1146
|
+
visit(root)
|
|
1147
|
+
return { directories, files }
|
|
1148
|
+
}
|
|
1149
|
+
function verifyBuildInputs(root, before) {
|
|
1150
|
+
for (const item of before.directories) {
|
|
1151
|
+
const path = item.path ? join(root, item.path) : root
|
|
1152
|
+
const metadata = lstatSync(path)
|
|
1153
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink() || (metadata.mode & 0o777) !== item.mode) {
|
|
1154
|
+
fail(`local update source directory changed during build: ${item.path}`)
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
for (const item of before.files) {
|
|
1158
|
+
const metadata = lstatSync(join(root, item.path))
|
|
1159
|
+
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size !== item.size
|
|
1160
|
+
|| (metadata.mode & 0o777) !== item.mode
|
|
1161
|
+
|| sha256Bytes(readFileSync(join(root, item.path))) !== item.sha256) {
|
|
1162
|
+
fail(`local update source file changed during build: ${item.path}`)
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
967
1166
|
function fileInventory(root) {
|
|
968
1167
|
const result = []
|
|
969
1168
|
const visit = (directory, prefix = '') => {
|
|
@@ -976,6 +1175,92 @@ function fileInventory(root) {
|
|
|
976
1175
|
}
|
|
977
1176
|
visit(root); return result
|
|
978
1177
|
}
|
|
1178
|
+
const TAR_BLOCK_BYTES = 512
|
|
1179
|
+
const DEPENDENCY_MAPS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']
|
|
1180
|
+
function tarOctal(header, start, length, label) {
|
|
1181
|
+
const field = header.subarray(start, start + length)
|
|
1182
|
+
if (field.some(byte => byte !== 0 && byte !== 32 && (byte < 48 || byte > 55))) {
|
|
1183
|
+
fail(`${label} uses unsupported binary or non-octal tar encoding`)
|
|
1184
|
+
}
|
|
1185
|
+
const octal = field.toString('ascii').replaceAll('\0', ' ').trim()
|
|
1186
|
+
if (!/^[0-7]+$/u.test(octal)) fail(`${label} is not an octal tar field`)
|
|
1187
|
+
const value = Number.parseInt(octal, 8)
|
|
1188
|
+
if (!Number.isSafeInteger(value)) fail(`${label} exceeds the safe tar size`)
|
|
1189
|
+
return value
|
|
1190
|
+
}
|
|
1191
|
+
function tarString(header, start, length) {
|
|
1192
|
+
const field = header.subarray(start, start + length)
|
|
1193
|
+
const terminator = field.indexOf(0)
|
|
1194
|
+
if (terminator !== -1 && field.subarray(terminator).some(byte => byte !== 0)) fail('tar pathname field has nonzero padding')
|
|
1195
|
+
return field.subarray(0, terminator === -1 ? field.length : terminator).toString('utf8')
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
export function normalizePackedDependencyOrder(packed) {
|
|
1199
|
+
if (!Buffer.isBuffer(packed) || packed.length < 1 || packed.length > MAX_ARTIFACT_BYTES) fail('packed archive size is invalid')
|
|
1200
|
+
let archive
|
|
1201
|
+
try { archive = gunzipSync(packed, { maxOutputLength: MAX_ARTIFACT_BYTES }) }
|
|
1202
|
+
catch { fail('packed archive is invalid or exceeds the extraction limit') }
|
|
1203
|
+
if (archive.length < TAR_BLOCK_BYTES * 3 || archive.length % TAR_BLOCK_BYTES !== 0) fail('packed tar block layout is invalid')
|
|
1204
|
+
let manifestOffset; let manifestSize; let offset = 0; let terminated = false
|
|
1205
|
+
while (offset < archive.length) {
|
|
1206
|
+
const header = archive.subarray(offset, offset + TAR_BLOCK_BYTES)
|
|
1207
|
+
if (header.every(byte => byte === 0)) {
|
|
1208
|
+
if (archive.length - offset < TAR_BLOCK_BYTES * 2 || archive.subarray(offset).some(byte => byte !== 0)) {
|
|
1209
|
+
fail('packed tar terminator is invalid')
|
|
1210
|
+
}
|
|
1211
|
+
terminated = true; break
|
|
1212
|
+
}
|
|
1213
|
+
const expectedChecksum = tarOctal(header, 148, 8, 'tar header checksum')
|
|
1214
|
+
let checksum = 0
|
|
1215
|
+
for (let index = 0; index < TAR_BLOCK_BYTES; index += 1) checksum += index >= 148 && index < 156 ? 32 : header[index]
|
|
1216
|
+
if (checksum !== expectedChecksum) fail('packed tar header checksum is invalid')
|
|
1217
|
+
const size = tarOctal(header, 124, 12, 'tar entry size')
|
|
1218
|
+
const type = header[156]
|
|
1219
|
+
if (type !== 0 && type !== 48 && type !== 53) fail('packed tar uses an unsupported entry representation')
|
|
1220
|
+
if (type === 53 && size !== 0) fail('packed tar directory has a payload')
|
|
1221
|
+
const name = tarString(header, 0, 100); const prefix = tarString(header, 345, 155)
|
|
1222
|
+
const entryPath = prefix === '' ? name : `${prefix}/${name}`
|
|
1223
|
+
const logicalPath = type === 53 && entryPath.endsWith('/') ? entryPath.slice(0, -1) : entryPath
|
|
1224
|
+
if ((logicalPath !== 'package' && !logicalPath.startsWith('package/'))
|
|
1225
|
+
|| logicalPath.includes('//') || logicalPath.split('/').some(part => part === '' || part === '.' || part === '..')) {
|
|
1226
|
+
fail('packed tar entry path is not canonical')
|
|
1227
|
+
}
|
|
1228
|
+
const dataOffset = offset + TAR_BLOCK_BYTES
|
|
1229
|
+
const nextOffset = dataOffset + Math.ceil(size / TAR_BLOCK_BYTES) * TAR_BLOCK_BYTES
|
|
1230
|
+
if (nextOffset > archive.length) fail('packed tar entry is truncated')
|
|
1231
|
+
if (entryPath === 'package/package.json') {
|
|
1232
|
+
if (manifestOffset !== undefined || prefix !== '' || (type !== 0 && type !== 48)) {
|
|
1233
|
+
fail('packed manifest has a duplicate or unsupported representation')
|
|
1234
|
+
}
|
|
1235
|
+
manifestOffset = dataOffset; manifestSize = size
|
|
1236
|
+
}
|
|
1237
|
+
offset = nextOffset
|
|
1238
|
+
}
|
|
1239
|
+
if (!terminated || manifestOffset === undefined || manifestSize === undefined) fail('packed tar has no regular package manifest')
|
|
1240
|
+
const sourceBytes = archive.subarray(manifestOffset, manifestOffset + manifestSize)
|
|
1241
|
+
const source = sourceBytes.toString('utf8')
|
|
1242
|
+
if (!Buffer.from(source, 'utf8').equals(sourceBytes)) fail('packed manifest is not valid UTF-8')
|
|
1243
|
+
let manifest
|
|
1244
|
+
try { manifest = JSON.parse(source) } catch { fail('packed manifest is not valid JSON') }
|
|
1245
|
+
if (typeof manifest !== 'object' || manifest === null || Array.isArray(manifest)) fail('packed manifest must be an object')
|
|
1246
|
+
const lineEnding = source.endsWith('\n') ? '\n' : ''
|
|
1247
|
+
if (`${JSON.stringify(manifest, null, 2)}${lineEnding}` !== source) fail('packed manifest is not in the supported canonical JSON format')
|
|
1248
|
+
const normalized = { ...manifest }
|
|
1249
|
+
for (const name of DEPENDENCY_MAPS) {
|
|
1250
|
+
if (!Object.hasOwn(manifest, name)) continue
|
|
1251
|
+
const entries = manifest[name]
|
|
1252
|
+
if (typeof entries !== 'object' || entries === null || Array.isArray(entries)
|
|
1253
|
+
|| Object.values(entries).some(value => typeof value !== 'string')) fail(`packed manifest ${name} is invalid`)
|
|
1254
|
+
normalized[name] = Object.fromEntries(Object.entries(entries).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0))
|
|
1255
|
+
}
|
|
1256
|
+
const normalizedBytes = Buffer.from(`${JSON.stringify(normalized, null, 2)}${lineEnding}`)
|
|
1257
|
+
if (normalizedBytes.length !== manifestSize) fail('packed manifest normalization changed its tar entry size')
|
|
1258
|
+
const normalizedArchive = Buffer.from(archive)
|
|
1259
|
+
normalizedBytes.copy(normalizedArchive, manifestOffset)
|
|
1260
|
+
const result = gzipSync(normalizedArchive, { mtime: 0 })
|
|
1261
|
+
if (result.length > MAX_ARTIFACT_BYTES) fail('normalized packed archive exceeds the artifact limit')
|
|
1262
|
+
return result
|
|
1263
|
+
}
|
|
979
1264
|
function prepareBuildSource(request, context, gitValue, hooks) {
|
|
980
1265
|
const bundle = join(context.directory, 'source.bundle'); rmSync(bundle, { force: true })
|
|
981
1266
|
const temporaryRef = `refs/dsh-release/build/${sha256Bytes(request.operationId).slice(0, 32)}`
|
|
@@ -1012,15 +1297,18 @@ function runIsolatedBuild(request, context, runNumber, gitValue, build, packageP
|
|
|
1012
1297
|
try {
|
|
1013
1298
|
sandboxCommand(build, ['install', '--offline', '--frozen-lockfile', '--frozen-store', '--ignore-scripts',
|
|
1014
1299
|
'--package-import-method=copy', '--store-dir=/store'], sandbox, hooks)
|
|
1300
|
+
sandboxCommand(build, ['--workspace-root', '--if-present', 'run', 'build'], sandbox, hooks)
|
|
1015
1301
|
sandboxCommand(build, ['--dir', `/workspace/${packagePath}`, 'run', 'build'], sandbox, hooks)
|
|
1016
1302
|
sandboxCommand(build, ['--dir', `/workspace/${packagePath}`, 'pack', '--pack-destination', '/output'], sandbox, hooks)
|
|
1017
1303
|
} finally { closeSandboxContext(sandbox) }
|
|
1018
1304
|
const packed = readdirSync(output).filter(name => name.endsWith('.tgz'))
|
|
1019
1305
|
if (packed.length !== 1 || readdirSync(output).length !== 1) fail('pnpm pack must produce exactly one tarball')
|
|
1020
1306
|
const tarball = safeRegularFile(join(output, packed[0]), `isolated build ${runNumber} tarball`)
|
|
1021
|
-
const bytes = readBounded(tarball, `isolated build ${runNumber} tarball`)
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1307
|
+
const bytes = normalizePackedDependencyOrder(readBounded(tarball, `isolated build ${runNumber} tarball`))
|
|
1308
|
+
const normalizedTarball = join(runRoot, 'normalized-package.tgz'); writeSynced(normalizedTarball, bytes, 0o600)
|
|
1309
|
+
const unpacked = unpackPackage(build, normalizedTarball, join(runRoot, 'packed'), runRoot, hooks)
|
|
1310
|
+
return { runRoot, checkout, workspace, packageRoot: unpacked.packageRoot, inventory: unpacked.inventory,
|
|
1311
|
+
tarball: normalizedTarball, bytes, sha256: sha256Bytes(bytes) }
|
|
1024
1312
|
}
|
|
1025
1313
|
function temporaryBuildRef(request) { return `refs/dsh-release/build/${sha256Bytes(request.operationId).slice(0, 32)}` }
|
|
1026
1314
|
function buildPhase(request, config, context, hooks) {
|
|
@@ -1069,7 +1357,8 @@ function buildPhase(request, config, context, hooks) {
|
|
|
1069
1357
|
const sbomPath = join(outputs, 'sbom.cdx.json'); immutableCopy(sbomPath, Buffer.from(`${canonicalReleaseValue(sbom)}\n`), 0o600)
|
|
1070
1358
|
const provenance = { _type: 'https://in-toto.io/Statement/v1', subject: [{ name: packageName, digest: { sha256: tarballSha256 } }],
|
|
1071
1359
|
predicateType: 'https://slsa.dev/provenance/v1', predicate: { buildDefinition: { buildType: 'https://dsh-enhanced.dev/build/local-isolated/v1',
|
|
1072
|
-
externalParameters: { packagePath, packageVersion
|
|
1360
|
+
externalParameters: { packagePath, packageVersion, manifestNormalization: 'sort-direct-dependency-maps-v1' },
|
|
1361
|
+
resolvedDependencies: [{ uri: pathToFileURL(gitValue.remote).href, digest: { gitCommit: mergeCommit } }] },
|
|
1073
1362
|
runDetails: { builder: { id: `dsh-local-release-adapter:${config.authority}:${config.keyId}` }, metadata: { invocationId: request.operationId },
|
|
1074
1363
|
byproducts: [{ name: 'sbom', digest: { sha256: sha256Bytes(readFileSync(sbomPath)) } }] } } }
|
|
1075
1364
|
const provenancePath = join(outputs, 'provenance.intoto.jsonl'); immutableCopy(provenancePath, Buffer.from(`${canonicalReleaseValue(provenance)}\n`), 0o600)
|
|
@@ -1384,6 +1673,39 @@ function persistReviewReceipt(receipt, config) {
|
|
|
1384
1673
|
} else immutableJson(path, receipt, 0o600)
|
|
1385
1674
|
}
|
|
1386
1675
|
|
|
1676
|
+
/** Read-only installation preflight. No request, receipt, lock, subprocess or
|
|
1677
|
+
* release phase is created; return public identity only, never loaded keys. */
|
|
1678
|
+
export function inspectLocalReleaseAdapterConfiguration(environment, phase) {
|
|
1679
|
+
if (!PHASES.has(phase)) fail('adapter inspection phase is invalid')
|
|
1680
|
+
const config = loadConfig(environment, phase)
|
|
1681
|
+
if (config.phase !== phase) fail('adapter config differs from inspection phase')
|
|
1682
|
+
const publicKey = createPublicKey(config.privateKey)
|
|
1683
|
+
if (config.authorizationAuthority.publicKey.equals(publicKey)
|
|
1684
|
+
|| (config.authorizationAuthority.authority === config.authority && config.authorizationAuthority.keyId === config.keyId)) {
|
|
1685
|
+
fail('release authorization and adapter identities must be independent')
|
|
1686
|
+
}
|
|
1687
|
+
if (['pr', 'review', 'merge', 'build'].includes(phase)) {
|
|
1688
|
+
const git = gitConfig(config.git)
|
|
1689
|
+
if (phase === 'review' && (!git.reviewStore || !git.reviewDecisionRoot)) fail('review requires independent decision and receipt stores')
|
|
1690
|
+
if (phase === 'merge' && (!git.reviewStore || !git.reviewAuthority || git.reviewAuthority.publicKey.equals(publicKey))) {
|
|
1691
|
+
fail('merge requires an independent review identity and receipt store')
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
if (phase === 'build') buildConfig(config.build)
|
|
1695
|
+
if (['publish', 'registry-verify', 'catalog-admission'].includes(phase)) {
|
|
1696
|
+
const registry = registryConfig(config.registry)
|
|
1697
|
+
if (!registry.signer) fail('registry requires an artifact signer')
|
|
1698
|
+
if (phase === 'registry-verify' && !registry.downloadRoot) fail('registry verification requires a separate download root')
|
|
1699
|
+
if (registry.signer.publicKey.equals(publicKey)) fail('registry phase and artifact signer must be independent')
|
|
1700
|
+
}
|
|
1701
|
+
if (phase === 'catalog-admission') {
|
|
1702
|
+
const catalog = catalogConfig(config.catalog)
|
|
1703
|
+
const helper = openPinnedFile(catalog.helper, 'catalog helper', 1_048_576, false)
|
|
1704
|
+
try { privateFile(catalog.path, 'catalog file') } finally { closePinnedFile(helper) }
|
|
1705
|
+
}
|
|
1706
|
+
return { id: config.id, phase, executablePath: config.executablePath, authority: config.authority, keyId: config.keyId }
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1387
1709
|
export async function runLocalReleaseAdapter(argv = process.argv.slice(2), environment = process.env, hooks = {}) {
|
|
1388
1710
|
if (argv.length === 1 && argv[0] === '--version') { process.stdout.write(`${LOCAL_RELEASE_ADAPTER_VERSION}\n`); return }
|
|
1389
1711
|
if (argv.length === 1 && argv[0] === '--capabilities') {
|