#!/usr/bin/env node import { createHash, createPrivateKey, createPublicKey, sign } from 'node:crypto' import { mkdir, readFile, writeFile } from 'node:fs/promises' import { basename, join, resolve } from 'node:path' import { canonicalJson } from '../lib/canonical-json.mjs' import { parseStable } from '../lib/contracts.mjs' const [configArg, privateKeyArg, outputArg] = process.argv.slice(2) if (configArg === undefined || privateKeyArg === undefined || outputArg === undefined) { console.error('usage: build-release.mjs ') process.exit(64) } const config = JSON.parse(await readFile(resolve(configArg), 'utf8')) const privateKey = createPrivateKey(await readFile(resolve(privateKeyArg))) if (privateKey.asymmetricKeyType !== 'ed25519') throw new Error('Release private key must be Ed25519') const publicDer = createPublicKey(privateKey).export({ format: 'der', type: 'spki' }) const keyId = createHash('sha256').update(publicDer).digest('hex') if (keyId !== config.currentKeyId) throw new Error('Release private key is not the configured current key') const artifacts = [] for (const artifact of config.artifacts) { const bytes = await readFile(resolve(artifact.path)) artifacts.push({ id: artifact.id, mediaType: artifact.mediaType, sha256: createHash('sha256').update(bytes).digest('hex'), size: bytes.byteLength, url: new URL(basename(artifact.path), config.artifactBaseUrl).href, }) } let issuedAt if (process.env.SOURCE_DATE_EPOCH === undefined) issuedAt = new Date().toISOString() else { const sourceDateEpoch = Number(process.env.SOURCE_DATE_EPOCH) if (!Number.isSafeInteger(sourceDateEpoch) || sourceDateEpoch < 0) throw new Error('SOURCE_DATE_EPOCH must be a non-negative safe integer') issuedAt = new Date(sourceDateEpoch * 1000).toISOString() } const stable = canonicalJson({ schema: 1, updateApi: 1, keyringGeneration: config.keyringGeneration, targetSequence: config.targetSequence, issuedAt, artifacts, desired: config.desired, officialDshPolicy: config.officialDshPolicy, }) parseStable(stable) const signature = canonicalJson({ schema: 1, algorithm: 'Ed25519', keyId, signature: sign(null, stable, privateKey).toString('base64'), }) const output = resolve(outputArg) await mkdir(output, { recursive: true }) await writeFile(join(output, 'stable.json'), stable, { flag: 'wx' }) await writeFile(join(output, 'stable.sig.json'), signature, { flag: 'wx' })