release: formalize HoloLake 0.5.0 numbered root
This commit is contained in:
parent
5d01607459
commit
40b8c49324
10 changed files with 401 additions and 6 deletions
|
|
@ -0,0 +1,39 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
const catalog = JSON.parse(readFileSync(new URL('../contracts/module-donor-admission-registry.json', import.meta.url), 'utf8'))
|
||||
const numbered = JSON.parse(readFileSync(new URL('../contracts/numbered-ipc-registry.json', import.meta.url), 'utf8'))
|
||||
|
||||
test('chaotic donors are read-only candidates and never a bulk merge source', () => {
|
||||
assert.equal(catalog.state, 'DONORS_QUARANTINED_CANDIDATES_NUMBERED_NOT_ADMITTED')
|
||||
assert.equal(catalog.root_rule.repair_old_application_in_place, false)
|
||||
assert.equal(catalog.root_rule.bulk_merge_or_wholesale_copy_allowed, false)
|
||||
assert.equal(catalog.root_rule.one_candidate_per_admission_cycle, true)
|
||||
assert.ok(catalog.donors.every((donor) => donor.state.startsWith('READ_ONLY')))
|
||||
})
|
||||
|
||||
test('candidate coordinates are unique but are not permanent runtime module numbers', () => {
|
||||
const coordinates = catalog.candidates.map((candidate) => candidate.candidate_number)
|
||||
assert.equal(new Set(coordinates).size, coordinates.length)
|
||||
assert.ok(catalog.candidates.every((candidate) => candidate.state.startsWith('QUARANTINED')))
|
||||
assert.equal(catalog.root_rule.candidate_number_is_runtime_module_number, false)
|
||||
assert.equal(catalog.root_rule.permanent_module_number_assignment_before_acceptance, false)
|
||||
})
|
||||
|
||||
test('legacy numbering donor is rejected and the numbered IPC root remains singular', () => {
|
||||
const legacy = catalog.rejected_inputs.find((candidate) => candidate.name === 'legacy_numbered_operation_runtime')
|
||||
assert.equal(legacy.state, 'REJECTED_SUPERSEDED')
|
||||
assert.equal(numbered.runtime.public_tauri_command, 'numbered_ipc')
|
||||
assert.equal(numbered.runtime.legacy_direct_commands_allowed, false)
|
||||
assert.ok(catalog.admission_gate.includes('ALLOCATE_NUMBERED_IPC_MODULE_TARGET_AND_OPERATION_COORDINATES'))
|
||||
assert.ok(catalog.admission_gate.includes('IMPLEMENT_ADAPTER_WITHOUT_RAW_TAURI_INVOKE'))
|
||||
})
|
||||
|
||||
test('hot installation is artifact, permission, self-test and rollback backed', () => {
|
||||
assert.equal(catalog.hot_install_boundary.source_repository_is_directly_executable, false)
|
||||
assert.equal(catalog.hot_install_boundary.immutable_signed_artifact_required, true)
|
||||
assert.equal(catalog.hot_install_boundary.compatibility_manifest_required, true)
|
||||
assert.equal(catalog.hot_install_boundary.rollback_on_self_test_failure, true)
|
||||
assert.equal(catalog.hot_install_boundary.user_data_survives_unmount, true)
|
||||
})
|
||||
|
|
@ -106,12 +106,30 @@ export function validateCredentialEnvironment(env) {
|
|||
if (missing.length) fail(`HOLOLAKE_RELEASE_PIPELINE_CREDENTIALS_MISSING:${[...new Set(missing)].sort().join(',')}`)
|
||||
}
|
||||
|
||||
export function materializeUpdaterPrivateKey(env, readText = (file) => fs.readFileSync(file, 'utf8')) {
|
||||
if (typeof env.TAURI_SIGNING_PRIVATE_KEY === 'string' && env.TAURI_SIGNING_PRIVATE_KEY.trim()) {
|
||||
return { ...env }
|
||||
}
|
||||
const privateKeyPath = requireText(
|
||||
env.TAURI_SIGNING_PRIVATE_KEY_PATH,
|
||||
'HOLOLAKE_RELEASE_PIPELINE_UPDATER_PRIVATE_KEY_PATH_REQUIRED',
|
||||
)
|
||||
const privateKey = requireText(
|
||||
readText(privateKeyPath),
|
||||
'HOLOLAKE_RELEASE_PIPELINE_UPDATER_PRIVATE_KEY_EMPTY',
|
||||
)
|
||||
return {
|
||||
...env,
|
||||
TAURI_SIGNING_PRIVATE_KEY: privateKey,
|
||||
}
|
||||
}
|
||||
|
||||
function run(program, args, options = {}) {
|
||||
return execFileSync(program, args, {
|
||||
cwd: root,
|
||||
encoding: 'utf8',
|
||||
stdio: options.capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',
|
||||
env: process.env,
|
||||
env: options.env || process.env,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -194,6 +212,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|||
const trustFacts = validateReleaseTrust(trust)
|
||||
const input = validateReleaseInput(readJson(inputPath), trustFacts)
|
||||
validateCredentialEnvironment(process.env)
|
||||
const credentialEnvironment = materializeUpdaterPrivateKey(process.env)
|
||||
requireCleanImmutableSource(input)
|
||||
if (process.platform !== 'darwin' || process.arch !== 'arm64') fail('HOLOLAKE_RELEASE_PIPELINE_BUILD_HOST_MISMATCH')
|
||||
if (!process.env.APPLE_SIGNING_IDENTITY.includes(input.appleTeamIdentifier)) fail('HOLOLAKE_RELEASE_PIPELINE_SIGNING_TEAM_MISMATCH')
|
||||
|
|
@ -205,7 +224,9 @@ export async function main(argv = process.argv.slice(2)) {
|
|||
const buildConfig = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-config-')), 'tauri.release.json')
|
||||
fs.writeFileSync(buildConfig, `${JSON.stringify({ bundle: { createUpdaterArtifacts: true } })}\n`, { mode: 0o600 })
|
||||
try {
|
||||
run('npm', ['run', 'tauri', '--', 'build', '--ci', '--config', buildConfig])
|
||||
run('npm', ['run', 'tauri', '--', 'build', '--ci', '--config', buildConfig], {
|
||||
env: credentialEnvironment,
|
||||
})
|
||||
} finally {
|
||||
fs.rmSync(path.dirname(buildConfig), { recursive: true, force: true })
|
||||
}
|
||||
|
|
@ -217,6 +238,18 @@ export async function main(argv = process.argv.slice(2)) {
|
|||
const updater = findOne(bundleRoot, (file) => file.endsWith('.app.tar.gz'), 'HOLOLAKE_RELEASE_PIPELINE_UPDATER_NOT_UNIQUE')
|
||||
const updaterSignature = `${updater}.sig`
|
||||
if (!fs.statSync(updaterSignature, { throwIfNoEntry: false })?.isFile()) fail('HOLOLAKE_RELEASE_PIPELINE_UPDATER_SIGNATURE_MISSING')
|
||||
run('cargo', [
|
||||
'run',
|
||||
'--quiet',
|
||||
'--manifest-path',
|
||||
'src-tauri/Cargo.toml',
|
||||
'--example',
|
||||
'verify_updater_signature',
|
||||
'--',
|
||||
'src-tauri/release-trust.json',
|
||||
updater,
|
||||
updaterSignature,
|
||||
])
|
||||
if (decodeURIComponent(input.packageUrl.pathname.split('/').pop()) !== path.basename(updater)) {
|
||||
fail('HOLOLAKE_RELEASE_PIPELINE_PACKAGE_URL_FILENAME_MISMATCH')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs'
|
|||
import test from 'node:test'
|
||||
|
||||
import {
|
||||
materializeUpdaterPrivateKey,
|
||||
validateCredentialEnvironment,
|
||||
validateReleaseInput,
|
||||
validateReleaseTrust,
|
||||
|
|
@ -76,6 +77,25 @@ test('release pipeline requires updater signing, Developer ID and Apple notariza
|
|||
}))
|
||||
})
|
||||
|
||||
test('release pipeline materializes a protected updater key path only inside the build environment', () => {
|
||||
const source = {
|
||||
TAURI_SIGNING_PRIVATE_KEY_PATH: '/protected/hololake-updater.key',
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: 'provided-at-runtime',
|
||||
}
|
||||
const hydrated = materializeUpdaterPrivateKey(source, (file) => {
|
||||
assert.equal(file, source.TAURI_SIGNING_PRIVATE_KEY_PATH)
|
||||
return 'encrypted-private-key-material'
|
||||
})
|
||||
assert.equal(hydrated.TAURI_SIGNING_PRIVATE_KEY, 'encrypted-private-key-material')
|
||||
assert.equal(source.TAURI_SIGNING_PRIVATE_KEY, undefined)
|
||||
})
|
||||
|
||||
test('release pipeline verifies the updater signature against embedded product trust before broadcast', () => {
|
||||
const source = readFileSync(new URL('./release-pipeline.mjs', import.meta.url), 'utf8')
|
||||
assert.match(source, /--example',\s*'verify_updater_signature'/)
|
||||
assert.match(source, /src-tauri\/release-trust\.json/)
|
||||
})
|
||||
|
||||
test('Windows updater keeps signed installation but never claims the macOS rollback boundary', () => {
|
||||
const source = readFileSync(new URL('../src-tauri/src/release_update.rs', import.meta.url), 'utf8')
|
||||
assert.match(source, /SIGNED_PACKAGE_VERIFIED_INSTALLING_NO_LOCAL_ROLLBACK/)
|
||||
|
|
|
|||
Loading…
Reference in a new issue