67 lines
2.7 KiB
JavaScript
67 lines
2.7 KiB
JavaScript
|
|
#!/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`)
|
||
|
|
}
|
||
|
|
|