hololake-system-architecture/product-source/hololake-clean-desktop/scripts/build-update-manifest.mjs

67 lines
2.7 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
import crypto from 'node:crypto'
import fs from 'node:fs'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
function required(value, name) {
if (!value) throw new Error(`${name}_REQUIRED`)
return value
}
export function buildManifest({ version, artifact, signatureFile, baseUrl, notes, publishedAt }) {
if (!/^\d+\.\d+\.\d+$/.test(required(version, 'VERSION'))) throw new Error('VERSION_INVALID')
const artifactPath = path.resolve(required(artifact, 'ARTIFACT'))
const signaturePath = path.resolve(required(signatureFile, 'SIGNATURE'))
if (!fs.statSync(artifactPath, { throwIfNoEntry: false })?.isFile()) throw new Error('ARTIFACT_NOT_READABLE')
if (!fs.statSync(signaturePath, { throwIfNoEntry: false })?.isFile()) throw new Error('SIGNATURE_NOT_READABLE')
const signature = fs.readFileSync(signaturePath, 'utf8').trim()
if (signature.length < 32) throw new Error('SIGNATURE_INVALID')
const root = new URL(required(baseUrl, 'BASE_URL'))
if (root.protocol !== 'https:') throw new Error('HTTPS_BASE_URL_REQUIRED')
if (root.username || root.password || root.search || root.hash) throw new Error('BASE_URL_MUST_NOT_CONTAIN_CREDENTIALS_OR_QUERY')
const bytes = fs.readFileSync(artifactPath)
const fileName = path.basename(artifactPath)
const url = new URL(fileName, root.href.endsWith('/') ? root : new URL(`${root.href}/`)).href
return {
version,
notes: notes || 'HoloLake signed update',
pub_date: publishedAt || new Date().toISOString(),
platforms: {
'darwin-aarch64': {
signature,
url,
sha256: crypto.createHash('sha256').update(bytes).digest('hex'),
bytes: bytes.length,
},
},
}
}
function parseArgs(values) {
const result = {}
for (let index = 0; index < values.length; index += 2) {
if (!values[index]?.startsWith('--') || values[index + 1] === undefined) throw new Error('ARGUMENTS_INVALID')
result[values[index].slice(2)] = values[index + 1]
}
return result
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
const args = parseArgs(process.argv.slice(2))
const manifest = buildManifest({
version: args.version,
artifact: args.artifact,
signatureFile: args.signature,
baseUrl: args['base-url'],
notes: args.notes,
publishedAt: args['published-at'],
})
const output = path.resolve(required(args.output, 'OUTPUT'))
fs.mkdirSync(path.dirname(output), { recursive: true })
const temporary = `${output}.tmp`
fs.writeFileSync(temporary, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o644 })
fs.renameSync(temporary, output)
process.stdout.write(`${JSON.stringify({ outcome: 'MANIFEST_WRITTEN', output, version: manifest.version, platform: 'darwin-aarch64' })}\n`)
}