Skip to content

Add retry-on-401 for GitHub API calls#13

Open
zyla wants to merge 1 commit intomainfrom
add-retry-on-401
Open

Add retry-on-401 for GitHub API calls#13
zyla wants to merge 1 commit intomainfrom
add-retry-on-401

Conversation

@zyla
Copy link
Collaborator

@zyla zyla commented Mar 6, 2026

Summary

CI runs are getting long enough that GitHub App installation tokens (1-hour lifetime) expire mid-build. The existing proactive refresh mechanism checks expiresAt before each API call, but doesn't handle cases where GitHub rejects a token the client still considers valid (clock skew, early revocation, network delays).

This PR adds reactive token refresh: when any GitHub API call returns 401, force-refresh the token and retry once.

Changes

src/CommitStatus.hs

  • forceRefreshClient — acquires the file lock and always refreshes the token, bypassing both in-memory and file caches
  • withFreshClient — calls getClient, executes the request; if response is 401, logs a warning, calls forceRefreshClient, and retries once
  • Refactored updateCommitStatus and checkExistingStatus to use withFreshClient

test/FakeGithubApi.hs

  • Mock server now issues unique tokens, tracks their actual expiry, and validates them on API calls
  • Added tokenExpirationOffset to simulate clock skew (server reports a longer-than-actual token lifetime)

test/Spec.hs

  • Added # github token expiration offset N test directive

New test: test/t/slow/github-token-retry-on-401

  • Token lifetime=2s, expiration offset=5s, threshold=0
  • Server issues token valid for 2s but reports 7s to client
  • After 3s sleep, client's token is expired server-side but client thinks it's valid
  • withFreshClient catches the 401, force-refreshes, and retries successfully

Testing

All existing GitHub tests continue to pass. New test verified:

stack test --test-arguments "--pattern github"
# All 8 tests passed

When a GitHub API call returns 401 (e.g. due to clock skew, early token
revocation, or network delays), force-refresh the token and retry once.

Changes:
- Add forceRefreshClient to bypass both in-memory and file caches
- Add withFreshClient wrapper that catches 401 and retries with fresh token
- Refactor updateCommitStatus and checkExistingStatus to use withFreshClient
- Add token validation to FakeGithubApi mock server (unique tokens, expiry
  tracking, clock skew simulation via expiration offset)
- Add '# github token expiration offset N' test directive
- Add golden test for 401 retry scenario
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.

1 participant