Skip to content

fix: detect mid-push auth rotation and abort cleanly#463

Merged
tomasz-tomczyk merged 1 commit intomainfrom
fix/452-auth-rotation-mid-push
May 5, 2026
Merged

fix: detect mid-push auth rotation and abort cleanly#463
tomasz-tomczyk merged 1 commit intomainfrom
fix/452-auth-rotation-mid-push

Conversation

@tomasz-tomczyk
Copy link
Copy Markdown
Owner

Summary

  • crit push now detects HTTP 401 from gh api calls and aborts the rest of the push loop instead of spamming per-item errors. Each per-call failure is wrapped with errGHAuthFailed so the loop can short-circuit via errors.Is.
  • On auth failure, push prints Pushed K of N comments before auth failed. Run 'gh auth refresh' then re-run 'crit push' to post the rest. and exits non-zero.
  • runPushLive was refactored from os.Exit(1) to returning an int so the new test can drive it without terminating the process; only call site is runPush.
  • 401 detection is anchored to gh's canonical error-line shapes (gh: HTTP 401: and HTTP/x 401 at line start) rather than loose substring match — avoids false-positives when comment bodies in the PR happen to contain "HTTP 401" or "Bad credentials".
  • Dedup safety verified: re-running push after gh auth refresh only retries comments where github_id == 0 (and items still in the pending-deletes queue) — no double-post of already-pushed work.

Review

  • Code review: passed (Phase 3 surfaced a false-positive risk in the matcher; fixed before pushing)
  • Parity audit: N/A (server-side push, no review-page surface)

Test plan

  • New push_auth_rotation_test.go: stubs gh with a fake binary that returns 201 then gh: HTTP 401: Bad credentials. Asserts non-zero exit, K-of-N message in stderr, fake gh invoked exactly twice (loop aborted), only the first reply got github_id set. Then re-runs with healthy fake — only the un-pushed replies are retried, already-pushed github_id is unchanged.
  • New github_auth_test.go: table-driven coverage of the matcher — positives (gh: HTTP 401:, all HTTP/x 401 include shapes) and negatives (HTTP 401 mid-line, Bad credentials mid-line, 200/404 status lines, indented variants).
  • go test -race -count=1 ./... green.

Closes #452

🤖 Generated with Claude Code

When `gh`'s auth token rotated or expired partway through `crit push`,
the per-item gh-api loop kept firing — each subsequent call returned
HTTP 401, and the user got a wall of generic transport errors with no
clear recovery path. The successful and failed posts were also
interleaved enough to make a clean retry risky.

This change introduces an `errGHAuthFailed` sentinel and an
`isGHAuthFailure` parser that recognises gh's canonical 401 surfaces
("HTTP 401", "HTTP/2 401", "Bad credentials"). The four push-loop
gh-api callers (`createGHReview`, `postGHReply`, `patchGHComment`,
`deleteGHComment`) wrap their failures with the sentinel when 401 is
detected; the inner push helpers (`postPushReplies`, `pushEditedBodies`,
`pushDeletedComments`) check for it via `errors.Is` and break out of
their loops on first sight. `runPushLive` then short-circuits the
remaining steps, prints

  Pushed K of N comments before auth failed.
  Run 'gh auth refresh' then re-run 'crit push' to post the rest.

to stderr, and exits non-zero. The K-of-N totals cover postable
top-level comments, replies, edits and pending deletes attempted before
the abort.

Dedup safety on retry is preserved by the existing review-file
contract: only items where `github_id == 0` are considered candidates
for posting (`collectNewRepliesForPush`, `bucketCommentsForPush`), and
`PendingGitHubDeletes` is only drained for IDs that returned a
non-auth-failure status. So after `gh auth refresh` a re-run of
`crit push` posts only the (N-K) items that hadn't landed — never a
duplicate of one that did.

`runPushLive` now returns an exit code instead of calling `os.Exit`
directly, so the new test can drive it end-to-end without terminating
the test process.

Closes #452
@tomasz-tomczyk tomasz-tomczyk merged commit 5e6bd6b into main May 5, 2026
6 checks passed
@tomasz-tomczyk tomasz-tomczyk deleted the fix/452-auth-rotation-mid-push branch May 5, 2026 16:08
@codecov
Copy link
Copy Markdown

codecov Bot commented May 5, 2026

Codecov Report

❌ Patch coverage is 66.98113% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.04%. Comparing base (5175895) to head (af7c318).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
main.go 67.05% 27 Missing and 1 partial ⚠️
github.go 66.66% 6 Missing and 1 partial ⚠️

❌ Your patch status has failed because the patch coverage (66.98%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #463      +/-   ##
==========================================
+ Coverage   68.72%   69.04%   +0.31%     
==========================================
  Files          36       36              
  Lines       10634    10698      +64     
==========================================
+ Hits         7308     7386      +78     
+ Misses       2764     2748      -16     
- Partials      562      564       +2     
Flag Coverage Δ
e2e 32.53% <0.00%> (-0.20%) ⬇️
unit 66.55% <66.98%> (+0.33%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

crit push: undefined behavior when gh auth token rotates mid-push

1 participant