release: formalize HoloLake 0.5.0 numbered root

This commit is contained in:
冰朔 2026-08-18 23:37:32 +08:00
commit 40b8c49324
10 changed files with 401 additions and 6 deletions

View file

@ -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)
})

View file

@ -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')
}

View file

@ -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/)