#!/usr/bin/env node import fs from 'node:fs'; import path from 'node:path'; import { execFileSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; const scriptDirectory = path.dirname(fileURLToPath(import.meta.url)); const defaultRepositoryRoot = path.resolve(scriptDirectory, '..'); const SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/; function readJson(file) { return JSON.parse(fs.readFileSync(file, 'utf8')); } function exactVersionTag(extensionRoot, expectedTag) { try { const tags = execFileSync( 'git', ['-C', extensionRoot, 'tag', '--points-at', 'HEAD', '--list', '--', expectedTag], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] } ) .split(/\r?\n/) .map(tag => tag.trim()) .find(tag => tag === expectedTag); return tags || null; } catch { return null; } } export function verifyCompanionRelease({ repositoryRoot = defaultRepositoryRoot, resolveExactTag = exactVersionTag, } = {}) { const extensionRoot = path.join(repositoryRoot, 'Extensions', 'Browser'); const packagePath = path.join(extensionRoot, 'package.json'); const manifestPath = path.join(extensionRoot, 'manifest.json'); if (!fs.existsSync(packagePath) || !fs.existsSync(manifestPath)) { throw new Error('Companion package.json and manifest.json must both exist.'); } const packageVersion = readJson(packagePath).version; const manifestVersion = readJson(manifestPath).version; if (!packageVersion || packageVersion !== manifestVersion) { throw new Error( `Companion versions do not agree: package.json=${packageVersion || 'missing'}, ` + `manifest.json=${manifestVersion || 'missing'}.` ); } if (typeof packageVersion !== 'string' || !SEMVER.test(packageVersion)) { throw new Error(`Companion version ${String(packageVersion)} is not a valid semantic version.`); } const expectedTag = `v${packageVersion}`; const tag = resolveExactTag(extensionRoot, expectedTag); if (!tag) { throw new Error( `Companion HEAD is not exactly tagged ${expectedTag}; publish the Companion release first.` ); } if (tag !== expectedTag) { throw new Error(`Companion HEAD tag ${tag} does not match ${expectedTag}.`); } return { tag, version: packageVersion }; } function main() { try { const { tag, version } = verifyCompanionRelease(); console.log(`Companion release ${version} matches exact tag ${tag}.`); } catch (error) { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; } } if (path.resolve(process.argv[1] || '') === fileURLToPath(import.meta.url)) { main(); }