codeburn/app/scripts/verify-windows-installer.mjs
2026-08-13 20:01:00 +05:30

101 lines
3.5 KiB
JavaScript

#!/usr/bin/env node
import { readFileSync, readdirSync } from 'node:fs'
import { basename, join, resolve } from 'node:path'
import { rootFromModuleUrl } from './windows-installer-paths.mjs'
function fail(message) {
console.error(`Windows installer manifest invalid: ${message}`)
process.exitCode = 1
}
function option(name, fallback) {
const index = process.argv.indexOf(name)
if (index === -1) return fallback
if (!process.argv[index + 1]) throw new Error(`${name} requires a value`)
return process.argv[index + 1]
}
function packageVersion(path) {
return JSON.parse(readFileSync(path, 'utf8')).version
}
function filesBelow(directory) {
return readdirSync(directory, { withFileTypes: true })
.filter(entry => entry.isFile())
.map(entry => basename(entry.name))
}
function releaseVersion(tag) {
const match = /^desktop-v(.+)$/.exec(tag)
if (!match) throw new Error(`${tag || '(missing tag)'} is not a desktop release tag`)
return match[1]
}
function verifyLiveRelease(tag, assetPath) {
const version = releaseVersion(tag)
const assets = JSON.parse(readFileSync(assetPath, 'utf8'))
if (!Array.isArray(assets) || assets.some(asset => typeof asset !== 'string')) {
throw new Error('release asset manifest must be a JSON array of names')
}
const required = [
`CodeBurn-${version}-arm64.dmg`,
`CodeBurn-${version}.dmg`,
`CodeBurn-${version}-arm64-mac.zip`,
`CodeBurn-${version}-mac.zip`,
`CodeBurn-${version}.AppImage`,
`codeburn-desktop_${version}_amd64.deb`,
`codeburn-desktop-${version}.x86_64.rpm`,
`CodeBurn-Setup-${version}.exe`,
`CodeBurn-Setup-${version}.exe.blockmap`,
]
for (const expected of required) {
const count = assets.filter(asset => asset === expected).length
if (count === 0) fail(`live release is missing ${expected}`)
if (count > 1) fail(`live release contains ${count} copies of ${expected}`)
}
if (!process.exitCode) console.log(`Live desktop release assets verified for ${version}`)
}
try {
const tag = option('--tag', '')
const releaseAssets = option('--release-assets', '')
if (releaseAssets) {
verifyLiveRelease(tag, resolve(releaseAssets))
} else {
const root = resolve(option('--root', rootFromModuleUrl(import.meta.url)))
const artifacts = resolve(option('--artifacts', join(root, 'app', 'release')))
const rootVersion = packageVersion(join(root, 'package.json'))
const appVersion = packageVersion(join(root, 'app', 'package.json'))
if (rootVersion !== appVersion) {
fail(`root version ${rootVersion} does not match app version ${appVersion}`)
}
if (tag && tag !== `desktop-v${appVersion}`) {
fail(`${tag} does not match app version ${appVersion}`)
}
const files = filesBelow(artifacts)
const expectedArtifacts = [
`CodeBurn-Setup-${appVersion}.exe`,
`CodeBurn-Setup-${appVersion}.exe.blockmap`,
]
for (const expected of expectedArtifacts) {
const count = files.filter(file => file === expected).length
if (count !== 1) fail(`expected exactly one ${expected}, found ${count}`)
}
const installerArtifacts = files.filter(file => /^CodeBurn-Setup-.*\.exe(?:\.blockmap)?$/.test(file))
const unexpected = installerArtifacts.filter(file => !expectedArtifacts.includes(file))
if (unexpected.length > 0) {
fail(`unexpected Windows installer artifacts: ${unexpected.join(', ')}`)
}
if (!process.exitCode) {
console.log(`Windows installer manifest verified for ${appVersion}`)
}
}
} catch (error) {
fail(error instanceof Error ? error.message : String(error))
}