ship-app: the commit step pushes HEAD to the branch the run was given and only there; the CI dispatch and the behind-check follow that branch

6 October 2026: from a release worktree, `git push origin master` pushed the shared checkout's unchanged local master ref and the tool reported "pushed to origin/master" from its own arithmetic; the Windows build was then looked for on master. Master merges are main's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-06 19:41:48 +00:00
parent 21819915f4
commit 449871b3c7

View file

@ -279,8 +279,9 @@ async function preflight() {
const otherDirty = dirty.filter(p => !versionPaths.has(p));
if (otherDirty.length) problems.push(`this tree has ${otherDirty.length} modified tracked file(s) besides the version files; commit or stash them first:\n ${otherDirty.slice(0, 8).join('\n ')}`);
if (!DRY) { const f = git(['fetch', 'origin', 'master', '--quiet']); if (f.code !== 0) problems.push(`git fetch origin failed: ${f.out}`); }
const behind = git(['rev-list', '--count', 'HEAD..origin/master']).out;
if (behind !== '0') problems.push(`this tree is ${behind} commit(s) behind origin/master; git pull first`);
const upstream = `origin/${flags.branch || 'master'}`;
const behind = git(['rev-parse', '--verify', '-q', upstream]).code === 0 ? git(['rev-list', '--count', `HEAD..${upstream}`]).out : '0';
if (behind !== '0') problems.push(`this tree is ${behind} commit(s) behind ${upstream}; git pull first`);
const headSha = git(['rev-parse', 'HEAD']).out;
// the fork
if (!existsSync(join(NODE_DIR, '.git'))) problems.push(`--node ${NODE_DIR} is not a git worktree`);
@ -382,21 +383,25 @@ async function commit() {
const changed = git(['status', '--porcelain', '--', ...paths]).out.split('\n').filter(Boolean);
const msg = `Igneum Miner ${VERSION}: ${NOTES}`;
if (changed.length) {
if (DRY) return done('commit', 'would', `git commit ${paths.length} files as "${msg}" and push origin master`);
if (DRY) return done('commit', 'would', `git commit ${paths.length} files as "${msg}" and push origin ${flags.branch || 'master'}`);
const a = git(['add', '--', ...paths]); if (a.code !== 0) throw new Error(`git add failed: ${a.out}`);
const cm = git(['commit', '-m', msg]); if (cm.code !== 0) throw new Error(`git commit failed: ${cm.out}`);
} else if (DRY) { done('commit', 'would', 'nothing to commit (the files are committed); push if origin/master lacks HEAD'); return; }
} else if (DRY) { done('commit', 'would', `nothing to commit (the files are committed); push if origin/${flags.branch || 'master'} lacks HEAD`); return; }
const sha = git(['rev-parse', 'HEAD']).out;
const subject = git(['log', '-1', '--format=%s']).out;
if (!subject.startsWith(`Igneum Miner ${VERSION}`)) say(` note: HEAD is "${subject.slice(0, 80)}", not the version commit; the Windows build runs on whatever master is`);
const pushed = git(['merge-base', '--is-ancestor', sha, 'origin/master']).code === 0;
// the push goes to the branch this run was given and only there (6 October 2026: `git push origin master` from a release
// worktree pushed the shared checkout's local master ref, said "pushed to origin/master" from its own arithmetic, and the
// Windows build was then looked for on master; master merges are main's)
const target = flags.branch || 'master';
const pushed = git(['rev-parse', '--verify', '-q', `origin/${target}`]).code === 0 && git(['merge-base', '--is-ancestor', sha, `origin/${target}`]).code === 0;
if (!pushed) {
const sw = runSync('gh', ['auth', 'switch', '--user', GH_USER]); if (sw.code !== 0) throw new Error(`gh auth switch failed: ${sw.out}`);
const p = await run('git', ['push', 'origin', 'master']);
if (p.code !== 0) throw new Error(`git push origin master exited ${p.code}; retry: gh auth switch --user ${GH_USER} && git push origin master`);
const p = await run('git', ['push', 'origin', `HEAD:refs/heads/${target}`]);
if (p.code !== 0) throw new Error(`git push origin HEAD:${target} exited ${p.code}; retry: gh auth switch --user ${GH_USER} && git push origin HEAD:refs/heads/${target}`);
}
state.sha = sha; saveState();
done('commit', pushed && !changed.length ? 'already' : 'ok', `${sha.slice(0, 12)} ${pushed ? 'was on' : 'pushed to'} origin/master`);
done('commit', pushed && !changed.length ? 'already' : 'ok', `${sha.slice(0, 12)} ${pushed ? 'was on' : 'pushed to'} origin/${target}`);
}
async function ci() {
@ -412,8 +417,8 @@ async function ci() {
say(` no ${WORKFLOW} run for ${sha.slice(0, 12)} yet; waiting up to 3 minutes for the push to start one`);
for (let i = 0; i < 12 && !pick; i++) { await sleep(15000); runs = await list(); pick = runs[0]; }
if (!pick) {
say(` dispatching: gh workflow run ${WORKFLOW} --ref master`);
const d = await gh(['workflow', 'run', WORKFLOW, '--repo', REPO, '--ref', 'master']); if (d.code !== 0) throw new Error(`gh workflow run failed: ${d.out.trim()}`);
say(` dispatching: gh workflow run ${WORKFLOW} --ref ${flags.branch || 'master'}`);
const d = await gh(['workflow', 'run', WORKFLOW, '--repo', REPO, '--ref', flags.branch || 'master']); if (d.code !== 0) throw new Error(`gh workflow run failed: ${d.out.trim()}`);
for (let i = 0; i < 12 && !pick; i++) { await sleep(15000); runs = await list(); pick = runs[0]; }
if (!pick) throw new Error(`no run appeared for ${sha.slice(0, 12)}; check https://github.com/${REPO}/actions and retry: ${retryCmd('ci')}`);
}