fix: verify final gate against remote main

This commit is contained in:
KoalaDev
2026-08-25 02:26:13 +02:00
parent 26c5a1a08f
commit 0f3da9659d
3 changed files with 18 additions and 2 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ To release a new version (e.g., `v2.5.1`), follow these steps:
git commit -m "release: prepare v2.5.1"
npm run release:gate -- 2.5.1 --candidate
```
2. Commit the release notes and prepared version changes, open a pull request,
2. Push the already committed and candidate-gated changes, open a pull request,
and wait for required `verify`, `node20`, and `e2e` checks.
3. After the PR is merged, fast-forward local `main`, wait for `verify`,
`node20`, and `e2e` on the merge commit, then run the final local gate. It
+9 -1
View File
@@ -58,6 +58,12 @@ export function linuxGateCommand() {
].join(' && ');
}
export function parseRemoteMain(text) {
const match = /^([a-f0-9]{40})\trefs\/heads\/main\s*$/u.exec(String(text));
if (!match) throw new Error(`could not resolve origin main from: ${String(text).trim() || '<empty>'}`);
return match[1];
}
function assertCleanTree() {
const status = capture('git', ['status', '--porcelain=v1']);
if (status) throw new Error(`release gate requires a clean working tree:\n${status}`);
@@ -67,7 +73,9 @@ function assertFinalMainChecks() {
const branch = capture('git', ['branch', '--show-current']);
if (branch !== 'main') throw new Error(`final release gate requires branch main, found ${branch || '<detached>'}`);
const head = capture('git', ['rev-parse', 'HEAD']);
const remoteMain = capture('git', ['rev-parse', 'origin/main']);
const remoteMain = parseRemoteMain(capture('git', [
'ls-remote', '--exit-code', 'origin', 'refs/heads/main'
]));
if (head !== remoteMain) throw new Error(`HEAD ${head} does not match origin/main ${remoteMain}`);
const checksText = capture('gh', [
+8
View File
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
import {
linuxGateCommand,
parseGateArgs,
parseRemoteMain,
playwrightImageFromLock
} from './release-local-gate.mjs';
@@ -20,6 +21,13 @@ describe('local release gate contract', () => {
expect(() => playwrightImageFromLock({ packages: {} })).toThrow('must pin');
});
it('extracts main only from the exact remote branch record', () => {
const sha = '0123456789abcdef0123456789abcdef01234567';
expect(parseRemoteMain(`${sha}\trefs/heads/main\n`)).toBe(sha);
expect(() => parseRemoteMain('')).toThrow('could not resolve origin main');
expect(() => parseRemoteMain(`${sha}\trefs/heads/not-main`)).toThrow('could not resolve origin main');
});
it('runs the complete CI-equivalent dependency, verify, and browser sequence', () => {
expect(linuxGateCommand()).toBe([
'git clone --no-local /src /work',