import test from 'brittle'
import { readFile } from 'node:fs/promises'
import {
buildLocalReleaseCandidateManifest,
calculateManifestDigest,
normalizeDeclaredBlockers,
verifyLocalReleaseManifestDigest
} from '../../scripts/lib/local-release-candidate-manifest.mjs'
const SOURCE_COMMIT = 'a'.repeat(40)
const SOURCE_TREE = 'b'.repeat(40)
const IMAGE_DIGEST = 'sha256:' + 'c'.repeat(64)
const BASE_DIGEST = 'sha256:' + 'd'.repeat(64)
test('local release candidate manifest separates passing local preflight from external release authority', (t) => {
const snapshot = fixtureSnapshot()
const first = buildLocalReleaseCandidateManifest(snapshot, {
declaredBlockers: ['GLOBAL_PG2_IN_PROGRESS', 'D6_STORE_FORMAT_BLOCKED', 'GLOBAL_PG2_IN_PROGRESS']
})
const second = buildLocalReleaseCandidateManifest(snapshot, {
declaredBlockers: ['D6_STORE_FORMAT_BLOCKED', 'GLOBAL_PG2_IN_PROGRESS']
})
t.alike(first, second)
t.is(first.release.localPreflight, 'passed')
t.is(first.release.releaseReady, false)
t.is(first.release.promotionAuthority, false)
t.is(first.release.authorizesRelease, false)
t.is(first.claimClass, 'LOCAL_OFFLINE_DRAFT_ONLY')
t.is(first.image.metadataVersionAligned, true)
t.is(first.image.sourceBound, false)
t.is(first.image.sourceBindingPending, true)
t.alike(first.declaredBlockers, ['D6_STORE_FORMAT_BLOCKED', 'GLOBAL_PG2_IN_PROGRESS'])
t.is(first.blockers.includes('BLIND_WORKSPACES_DRAFT_ONLY'), false)
t.ok(first.blockers.includes('EXTERNAL_RELEASE_EVIDENCE_NOT_ATTACHED'))
t.ok(first.blockers.includes('LOCAL_MANIFEST_NO_PROMOTION_AUTHORITY'))
t.ok(first.blockers.includes('PINNED_IMAGE_NOT_SOURCE_BOUND'))
t.is(first.appliances.communityStore.attached, true)
t.is(first.manifestDigest, calculateManifestDigest(first))
t.ok(verifyLocalReleaseManifestDigest(first))
})
test('local release candidate manifest fails closed on tag, image, base, workflow, and Umbrel gaps', (t) => {
const snapshot = fixtureSnapshot()
snapshot.source.versionTagCommit = 'e'.repeat(40)
snapshot.build.baseImages[0].digestPinned = false
snapshot.build.baseImages[0].ref = 'node:22-bookworm-slim'
snapshot.build.sbomConfigured = false
snapshot.build.provenanceConfigured = false
snapshot.build.splitSourceWorkflow = true
snapshot.appliances.umbrel.submission = 'https://github.com/getumbrel/umbrel-apps/pull/PENDING'
snapshot.communityStore.packageVersion = '9.9.8'
const manifest = buildLocalReleaseCandidateManifest(snapshot)
t.is(manifest.release.localPreflight, 'blocked')
t.is(manifest.release.releaseReady, false)
t.is(manifest.image.sourceBound, false)
for (const blocker of [
'SOURCE_VERSION_TAG_NOT_EXACT',
'PINNED_IMAGE_NOT_SOURCE_BOUND',
'CONTAINER_BASE_IMAGE_DIGESTS_UNPINNED',
'RELEASE_WORKFLOW_SBOM_MISSING',
'RELEASE_WORKFLOW_PROVENANCE_MISSING',
'RELEASE_WORKFLOW_SPLIT_SOURCE',
'UMBREL_OFFICIAL_SUBMISSION_PENDING',
'COMMUNITY_UMBREL_STORE_EXACT_PAIR_MISMATCH'
]) {
t.ok(manifest.blockers.includes(blocker), blocker)
}
})
test('local release candidate manifest digest rejects tampering', (t) => {
const manifest = buildLocalReleaseCandidateManifest(fixtureSnapshot())
manifest.source.commit = 'f'.repeat(40)
t.exception(() => verifyLocalReleaseManifestDigest(manifest), /digest mismatch/)
})
test('local release candidate manifest validates declared blocker identifiers', (t) => {
t.alike(
normalizeDeclaredBlockers(['Z_BLOCKER', 'A_BLOCKER', 'Z_BLOCKER']),
['A_BLOCKER', 'Z_BLOCKER']
)
t.exception(() => normalizeDeclaredBlockers(['not-safe']), /uppercase identifier/)
})
test('local release candidate manifest blocks when community-store source is not attached', (t) => {
const snapshot = fixtureSnapshot()
snapshot.communityStore = null
const manifest = buildLocalReleaseCandidateManifest(snapshot)
const binding = manifest.requiredExternalEvidence.find(item => item.id === 'community-umbrel-store-source-and-package-binding')
t.is(manifest.appliances.communityStore.attached, false)
t.ok(manifest.blockers.includes('COMMUNITY_UMBREL_STORE_NOT_BOUND'))
t.is(binding.status, 'not-attached')
})
test('release workflow explicitly requests SBOM and max-mode provenance attestations', async (t) => {
const workflow = await readFile('.github/workflows/release-surfaces.yml', 'utf8')
t.ok(workflow.includes('--sbom=true'))
t.ok(workflow.includes('--provenance=mode=max'))
t.ok(workflow.includes('--platform linux/amd64,linux/arm64'))
})
function fixtureSnapshot () {
const semver = '9.9.9'
return {
source: {
repository: 'bigdestiny2/P2P-Hiverelay',
commit: SOURCE_COMMIT,
tree: SOURCE_TREE,
describe: 'v9.9.9-0-gaaaaaaa',
dirty: false,
expectedVersionTag: 'v9.9.9',
versionTagCommit: SOURCE_COMMIT,
tagsAtSource: ['v9.9.9']
},
semver,
publicPackages: [
['packages/core/package.json', 'p2p-hiverelay'],
['packages/client/package.json', 'p2p-hiverelay-client'],
['packages/verifier/package.json', 'p2p-hiverelay-verifier'],
['packages/services/package.json', 'p2p-hiveservices']
].map(([path, name]) => ({ path, name, version: semver })),
blindPackages: [
'blind-protocol',
'blind-ipc',
'blind-client',
'blind-peercred',
'blind-edge',
'blind-daemon'
].map(name => ({
path: `packages/${name}/package.json`,
name: `@hiverelay/${name}`,
version: semver,
private: true
})),
appliances: {
umbrel: {
version: semver,
imageRef: `ghcr.io/bigdestiny2/p2p-hiverelay:${semver}@${IMAGE_DIGEST}`,
image: {
ref: `ghcr.io/bigdestiny2/p2p-hiverelay:${semver}@${IMAGE_DIGEST}`,
name: 'ghcr.io/bigdestiny2/p2p-hiverelay',
tag: semver,
digest: IMAGE_DIGEST
},
submission: 'https://github.com/getumbrel/umbrel-apps/pull/123'
},
startos: {
id: 'blindspark',
version: semver,
packageFormat: 'startos-0.3.5.x',
artifactName: 'blindspark.s9pk'
},
startos04: {
version: semver,
versionWithRevision: `${semver}:0`,
imageRef: `ghcr.io/bigdestiny2/p2p-hiverelay:${semver}`,
packageFormat: 'startos-0.4'
}
},
fleetChannels: {
stable: `v${semver}`,
canary: `v${semver}`
},
build: {
imageName: 'ghcr.io/bigdestiny2/p2p-hiverelay',
platforms: ['linux/amd64', 'linux/arm64'],
baseImages: [{ ref: `node:22-bookworm-slim@${BASE_DIGEST}`, digestPinned: true }],
sbomConfigured: true,
provenanceConfigured: true,
sourceBindingConfigured: true,
splitSourceWorkflow: false
},
communityStore: {
source: {
repository: 'bigdestiny2/blindspark-umbrel-store',
commit: '3'.repeat(40),
tree: '4'.repeat(40),
dirty: false
},
packageVersion: semver,
umbrelVersion: semver,
imageRef: `ghcr.io/bigdestiny2/p2p-hiverelay:${semver}@${IMAGE_DIGEST}`,
image: {
ref: `ghcr.io/bigdestiny2/p2p-hiverelay:${semver}@${IMAGE_DIGEST}`,
name: 'ghcr.io/bigdestiny2/p2p-hiverelay',
tag: semver,
digest: IMAGE_DIGEST
},
inputs: [{
path: 'package.json',
bytes: 42,
sha256: '5'.repeat(64),
gitBlob: '6'.repeat(40)
}]
},
inputs: [
{
path: 'package.json',
bytes: 123,
sha256: '1'.repeat(64),
gitBlob: '2'.repeat(40)
}
]
}
}