| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- import { createHash } from 'node:crypto'
- import { readdir, readFile, stat } from 'node:fs/promises'
- import { dirname, join, resolve } from 'node:path'
- import { spawnSync } from 'node:child_process'
- import { fileURLToPath } from 'node:url'
- const scriptDir = dirname(fileURLToPath(import.meta.url))
- const sourceRoot = resolve(scriptDir, '..')
- const args = process.argv.slice(2)
- function readArg(name, fallback) {
- const index = args.indexOf(name)
- return index >= 0 ? args[index + 1] : fallback
- }
- const publicRepoDir = resolve(
- sourceRoot,
- readArg('--public-repo-dir', process.env.FILE_VIEWER_PUBLIC_REPO_DIR || '../file-viewer-public')
- )
- const artifactsDir = join(publicRepoDir, 'artifacts')
- const manifestPath = join(artifactsDir, 'release-manifest.json')
- const manifest = JSON.parse(await readFile(manifestPath, 'utf8'))
- const tag = readArg('--tag', `v${manifest.version}`)
- const repo = readArg('--repo', 'flyfish-dev/file-viewer')
- function runGh(commandArgs) {
- const result = spawnSync('gh', commandArgs, {
- cwd: sourceRoot,
- encoding: 'utf8',
- stdio: 'pipe',
- timeout: 30_000
- })
- if (result.status !== 0) {
- throw new Error((result.stderr || result.stdout || '').trim() || `gh ${commandArgs.join(' ')} failed`)
- }
- return result.stdout
- }
- async function sha256(path) {
- const data = await readFile(path)
- return createHash('sha256').update(data).digest('hex')
- }
- async function expectedAssets() {
- const names = (await readdir(artifactsDir))
- .filter(name => !name.startsWith('.'))
- .sort()
- const entries = []
- for (const name of names) {
- const path = join(artifactsDir, name)
- const info = await stat(path)
- if (!info.isFile()) {
- continue
- }
- entries.push({
- name,
- size: info.size,
- digest: `sha256:${await sha256(path)}`
- })
- }
- return entries
- }
- const release = JSON.parse(
- runGh(['release', 'view', tag, '--repo', repo, '--json', 'tagName,assets'])
- )
- const expected = await expectedAssets()
- const actual = new Map(release.assets.map(asset => [asset.name, asset]))
- const expectedNames = new Set(expected.map(asset => asset.name))
- const failures = []
- for (const asset of expected) {
- const uploaded = actual.get(asset.name)
- if (!uploaded) {
- failures.push(`missing asset ${asset.name}`)
- continue
- }
- if (uploaded.state !== 'uploaded') {
- failures.push(`${asset.name} state is ${uploaded.state}`)
- }
- if (uploaded.size !== asset.size) {
- failures.push(`${asset.name} size ${uploaded.size} !== local ${asset.size}`)
- }
- if (uploaded.digest && uploaded.digest !== asset.digest) {
- failures.push(`${asset.name} digest ${uploaded.digest} !== local ${asset.digest}`)
- }
- }
- for (const asset of release.assets) {
- if (!expectedNames.has(asset.name)) {
- failures.push(`unexpected asset ${asset.name}`)
- }
- }
- if (failures.length) {
- throw new Error(`GitHub Release asset verification failed for ${repo}@${tag}:\n${failures.join('\n')}`)
- }
- console.log(
- `Verified ${expected.length} GitHub Release assets for ${repo}@${tag} from ${artifactsDir}.`
- )
|