Rotate Copilot tokens for the gh-aw agentic workflows - #13525
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces Copilot token rotation for the repository’s scheduled GitHub Agentic Workflows so runs can spread load across multiple volunteer fine-grained PATs instead of consuming a single person’s Copilot quota.
Changes:
- Add a
pick_copilot_tokenjob to the two agentic workflow sources to select a “live” token alias (with fallbacks) based on day-of-year rotation. - Update the compiled
*.lock.ymlworkflows so the agent step and log-redaction step use the rotated token secret (falling back to the base secret). - Add supporting maintenance tooling/docs: a manual token health-check workflow, a post-compile patch script, and contributor documentation for adding/removing tokens.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/daily-repo-status.md | Adds token-pick job in the gh-aw source workflow. |
| .github/workflows/daily-repo-status.lock.yml | Wires agent execution + redaction to the rotated secret and adds dependency on token-pick job. |
| .github/workflows/daily-issue-triage.md | Adds token-pick job in the gh-aw source workflow (staggered rotation slot). |
| .github/workflows/daily-issue-triage.lock.yml | Wires agent execution + redaction to the rotated secret and adds dependency on token-pick job. |
| .github/workflows/copilot-token-health.yml | New manual workflow to report token liveness per alias. |
| .github/scripts/post-compile.sh | New post-processing script to re-apply wiring after gh aw compile. |
| .github/COPILOT_TOKENS.md | New documentation for contributing and maintaining the token pool. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #13525 +/- ##
============================================
+ Coverage 18.93% 19.64% +0.70%
- Complexity 18474 19787 +1313
============================================
Files 6221 6368 +147
Lines 560039 574889 +14850
Branches 68285 70353 +2068
============================================
+ Hits 106066 112951 +6885
- Misses 442346 449666 +7320
- Partials 11627 12272 +645
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The scheduled agentic workflows ran on a single COPILOT_GITHUB_TOKEN, which burns through one person's Copilot quota. Volunteers can now add their own fine-grained PATs as COPILOT_GITHUB_TOKEN_<alias> secrets and register the alias in the GH_AW_COPILOT_TOKEN_NAMES repo variable. Runs pick a token by day of year, skip dead ones, and fall back to the base secret when nothing in the pool is live. The two workflows start half the pool apart so they don't drain the same volunteer on the same day. Strict mode forbids reading secrets in the agent job, so each workflow defines a pick_copilot_token job that outputs only the chosen alias; the agent job resolves the secret itself. gh aw compile doesn't know about this wiring, so .github/scripts/post-compile.sh re-applies it to the lock files after every compile, including pointing the log redaction step at the rotated token. A manual copilot-token-health workflow reports which pool tokens are still live. See .github/COPILOT_TOKENS.md for how to contribute a token. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2215290 to
c8eba3e
Compare
…w workflows Disable report-failure-as-issue, missing-tool, and report-incomplete issue creation on daily-repo-status and daily-issue-triage so agentic run failures no longer file GitHub issues. Pin daily-repo-status to gpt-5.6-luna (cheapest lightweight model, sufficient for a formulaic report) and daily-issue-triage to claude-sonnet-5 (frontier agentic model, cheaper than the claude-sonnet-4.6 default it was silently falling back to).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
Suppressed comments (7)
.github/workflows/daily-repo-status.md:76
- The token liveness check hard-codes
https://api.github.com/user, which will break on GHES/GHEC or any non-github.com instance. Use the built-inGITHUB_API_URLinstead so the workflow remains portable.
live() {
[ -n "$1" ] && [ "$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $1" https://api.github.com/user || echo 000)" = "200" ]
.github/workflows/daily-issue-triage.md:77
- The token liveness check hard-codes
https://api.github.com/user, which will fail on GHES/GHEC. UseGITHUB_API_URLso the rotation logic works on any GitHub instance.
live() {
[ -n "$1" ] && [ "$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $1" https://api.github.com/user || echo 000)" = "200" ]
.github/workflows/copilot-token-health.yml:59
- This health check also hard-codes
https://api.github.com/user, which won't work on GHES/GHEC. PreferGITHUB_API_URLso the workflow can run against the current GitHub instance.
code=$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $TOKEN" https://api.github.com/user || echo 000)
.github/workflows/daily-repo-status.lock.yml:140
- This lock file pins
actions/checkoutto v6.0.2, while the rest of the repo workflows pin v6.0.3 (e.g..github/workflows/ci.yml:32). If this downgrade wasn't intentional (security/bugfixes in patch releases), consider regenerating the lock file or updating allactions/checkoutpins back to v6.0.3 consistently.
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
.github/workflows/daily-issue-triage.lock.yml:139
- This lock file pins
actions/checkoutto v6.0.2, but other workflows in the repo use v6.0.3 (e.g..github/workflows/ci.yml:32). If the downgrade is accidental, regenerate the lock file or bump allactions/checkoutoccurrences back to v6.0.3 for consistency and patch-level fixes.
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
.github/workflows/daily-repo-status.lock.yml:1350
- The generated lock file still hard-codes
https://api.github.com/userfor token liveness. If these workflows are ever run on GHES/GHEC, rotation will incorrectly treat all tokens as dead. Update to useGITHUB_API_URLand re-run the compile/post-compile steps so the lock stays in sync with the.mdsource.
live() {
[ -n "$1" ] && [ "$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $1" https://api.github.com/user || echo 000)" = "200" ]
.github/workflows/daily-issue-triage.lock.yml:1354
- The generated lock file still hard-codes
https://api.github.com/userfor token liveness, so it won't work on GHES/GHEC. UseGITHUB_API_URLand re-run compile/post-compile so the lock file matches the source workflow.
live() {
[ -n "$1" ] && [ "$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $1" https://api.github.com/user || echo 000)" = "200" ]
Switch the repo-status schedule from daily to a fixed cron at 12:00 UTC on Sundays (fixed rather than gh-aw's fuzzy "weekly" shorthand, which scatters the run time). Rename daily-repo-status to weekly-repo-status across the workflow, its lock file, post-compile.sh, COPILOT_TOKENS.md, and the rotation comment in daily-issue-triage; the report label becomes weekly-status. The [repo-status] title-prefix is kept so close-older-issues still closes the previous daily report issues.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Suppressed comments (9)
.github/COPILOT_TOKENS.md:72
- The rotation docs imply the whole workflow uses the rotated token, but the compiled workflows keep the detection phase on the base COPILOT_GITHUB_TOKEN (see post-compile script comment). Call this out so maintainers understand why base must remain valid and why some quota may still be consumed by that token.
The agent job resolves the secret itself via
`secrets[format('COPILOT_GITHUB_TOKEN_{0}', needs.pick_copilot_token.outputs.name)]` and falls
back to the base `COPILOT_GITHUB_TOKEN` when the pick job outputs an empty name. Keep the base
secret set to one reliable token.
.github/workflows/weekly-repo-status.lock.yml:1244
- The detection phase still uses the base COPILOT_GITHUB_TOKEN. If the goal is to spread Copilot quota across the volunteer pool, rotate the token for detection as well (and ensure any log redaction/upload steps cover it).
.github/workflows/daily-issue-triage.lock.yml:1249 - The detection phase still uses the base COPILOT_GITHUB_TOKEN. If the goal is to spread Copilot quota across the volunteer pool, rotate the token for detection as well (and ensure any log redaction/upload steps cover it).
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
.github/workflows/weekly-repo-status.lock.yml:44
- This lock file now pins github/gh-aw-actions/setup to v0.76.1. If this was not an intentional downgrade (previous lock files often pin newer setup/checkouts for fixes), consider recompiling/updating pins to the desired versions to avoid regressing behavior or missing security updates.
This issue also appears on line 1244 of the same file.
.github/workflows/daily-issue-triage.md:78
- The liveness check hardcodes https://api.github.com and has no curl timeouts, which breaks on GHES/GHEC custom API URLs and can hang the job on network stalls. Use $GITHUB_API_URL and set connect/overall timeouts.
live() {
[ -n "$1" ] && [ "$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $1" https://api.github.com/user || echo 000)" = "200" ]
}
.github/workflows/weekly-repo-status.md:79
- The liveness check hardcodes https://api.github.com and has no curl timeouts, which breaks on GHES/GHEC custom API URLs and can hang the job on network stalls. Use $GITHUB_API_URL and set connect/overall timeouts.
live() {
[ -n "$1" ] && [ "$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $1" https://api.github.com/user || echo 000)" = "200" ]
}
.github/COPILOT_TOKENS.md:65
- The docs describe a
GET /userliveness check, but the scripts currently hardcode api.github.com. Updating the docs to reference $GITHUB_API_URL/user matches the implementation once it’s made GHES-compatible.
This issue also appears on line 69 of the same file.
reading secrets there. It picks today's alias by day-of-year mod N, checks the token is live
(`GET /user` returns 200, otherwise it moves on to the next candidate) and outputs the chosen
alias. The token value itself never crosses jobs. The two workflows use different
.github/workflows/copilot-token-health.yml:59
- The health check hardcodes https://api.github.com and has no curl timeouts. Using $GITHUB_API_URL makes this work on GHES/GHEC, and timeouts prevent hangs.
code=$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $TOKEN" https://api.github.com/user || echo 000)
.github/workflows/daily-issue-triage.lock.yml:43
- This lock file now pins github/gh-aw-actions/setup to v0.76.1. If this was not an intentional downgrade (previous lock files often pin newer setup/checkouts for fixes), consider recompiling/updating pins to the desired versions to avoid regressing behavior or missing security updates.
This issue also appears on line 1249 of the same file.
# - github/gh-aw-actions/setup@46d564922b082d0db93244972e8005ea6904ee5f # v0.76.1
DaanHoogland
left a comment
There was a problem hiding this comment.
looks ok'ish for an AI bit of work ;) , won’t impede any production code and will help us with better triaging 👍
Description
The scheduled agentic workflows ran on a single COPILOT_GITHUB_TOKEN, which burns through one person's Copilot quota. Volunteers can now add their own fine-grained PATs as COPILOT_GITHUB_TOKEN_ secrets and register the alias in the GH_AW_COPILOT_TOKEN_NAMES repo variable. Runs pick a token by day of year, skip dead ones, and fall back to the base secret when nothing in the pool is live. The two workflows start half the pool apart so they don't drain the same volunteer on the same day.
Strict mode forbids reading secrets in the agent job, so each workflow defines a pick_copilot_token job that outputs only the chosen alias; the agent job resolves the secret itself. gh aw compile doesn't know about this wiring, so .github/scripts/post-compile.sh re-applies it to the lock files after every compile, including pointing the log redaction step at the rotated token. A manual copilot-token-health workflow reports which pool tokens are still live.
See .github/COPILOT_TOKENS.md for how to contribute a token.
Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
How did you try to break this feature and the system with this change?