#!/usr/bin/env node import { execFileSync, spawnSync } from 'node:child_process' function fail(message) { console.error(`release_preflight=FAIL ${message}`) process.exit(1) } function git(args) { try { return execFileSync('git', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], }).trim() } catch (error) { const stderr = error.stderr?.toString().trim() fail(stderr ? `git_error=${JSON.stringify(stderr)}` : 'git command failed') } } const root = git(['rev-parse', '--show-toplevel']) process.chdir(root) for (const args of [['diff', '--quiet'], ['diff', '--cached', '--quiet']]) { if (spawnSync('git', args, { stdio: 'ignore' }).status !== 0) { fail('the worktree must be clean') } } if (git(['ls-files', '--others', '--exclude-standard'])) { fail('the worktree must not contain untracked files') } const sourceSha = git(['rev-parse', '--verify', 'HEAD^{commit}']) if (!/^[0-9a-f]{40}$/.test(sourceSha)) fail('HEAD is not a full Git commit') const mainSha = git(['rev-parse', '--verify', 'refs/remotes/origin/main^{commit}']) if (!/^[0-9a-f]{40}$/.test(mainSha)) fail('origin/main is unavailable') if (spawnSync('git', ['merge-base', '--is-ancestor', sourceSha, mainSha], { stdio: 'ignore' }).status !== 0) { fail('HEAD is not part of origin/main history') } console.log(JSON.stringify({ sourceSha, originMainSha: mainSha }))