Skip to content

Fix Copilot PAT rotation for gh-aw v0.77.5 runtime - #736

Merged
JanKrivanek merged 5 commits into
mainfrom
dev/jankrivanek/fix-pat-rotation
Jun 9, 2026
Merged

Fix Copilot PAT rotation for gh-aw v0.77.5 runtime#736
JanKrivanek merged 5 commits into
mainfrom
dev/jankrivanek/fix-pat-rotation

Conversation

@JanKrivanek

Copy link
Copy Markdown
Member

What & why

The Copilot PAT-rotation stop-gap was silently broken in the agent job, and the newer gh-aw v0.77.5 compiler surfaces it as a warning.

Root cause

The rotation wires the selected token into engine.env via
COPILOT_GITHUB_TOKEN: ${{ case(needs.pre_activation.outputs.copilot_pat_number ...) }}.
But the compiled agent job only depends on activation, not on the built-in pre_activation. GitHub Actions' needs context only exposes direct dependencies, so inside the agent job needs.pre_activation.outputs.copilot_pat_number evaluates to an empty string — the case() falls through to the default COPILOT_GITHUB_TOKEN, and rotation never reaches the agent (the early validation step in the activation job worked, which masked the bug). gh-aw v0.77.5 added a compiler warning that detects exactly this.

This is the same warning that led dotnet/msbuild#13919 to remove PAT rotation entirely. This PR fixes it instead.

The fix (close to the original, fully supported)

Replace the pre_activation step-injection with a small select_copilot_pat custom job wired via on.needs:

  • Same select-copilot-pat action, same secret pool, same case() expression — only pre_activationselect_copilot_pat.
  • Because it is a user-defined (non-built-in) job referenced in engine.env, the compiler makes it a direct dependency of the agent job, so needs.select_copilot_pat.outputs.copilot_pat_number resolves correctly in both the activation and agent jobs. No built-in reference → no warning → rotation actually reaches the agent.

Applied to all 8 agentic workflows and recompiled with gh-aw v0.77.5.

Validation

A temporary test-pat-rotation workflow (pull_request-triggered, non-destructive) selects a pool token and asserts in the agent job that needs.select_copilot_pat.outputs.copilot_pat_number is non-empty — the exact value that was silently empty before. It will be removed before merge.

Security review (compilation manifest)

  • Secrets: none added/removed. Same pool already used by every workflow (COPILOT_GITHUB_TOKEN, COPILOT_GITHUB_TOKEN_2..8). The pool secrets are only read by the select-copilot-pat action to pick a number (never the secret value), in a deterministic job with no untrusted input; fork PRs receive no secrets.
  • Actions: no new third-party actions. Runtime version bumps only (gh-aw-managed): github/gh-aw-actions/setup@v0.77.5, actions/github-script@v9.0.0, firewall 0.25.58, mcp-gateway v0.3.22, github-mcp-server v1.1.0. actions/checkout and the local select-copilot-pat action are unchanged.
  • Redirects/domains: unchanged.

Note

Draft for verification of the PAT-rotation fix. Once the test workflow run is green, I'll drop the test workflow and mark ready.

The PAT-rotation stop-gap wired the rotated token into engine.env via needs.pre_activation.outputs.copilot_pat_number. Because the compiled agent job depends only on 'activation' (not the built-in 'pre_activation'), that needs reference evaluated to an empty string in the agent job, so the case() fell through to the default COPILOT_GITHUB_TOKEN and rotation never reached the agent. gh-aw v0.77.5 surfaces this as a compiler warning.

Replace the pre_activation step-injection with a 'select_copilot_pat' custom job wired via on.needs. As a user-defined job referenced in engine.env, the compiler makes it a direct dependency of the agent job, so needs.select_copilot_pat.outputs.copilot_pat_number resolves correctly in both the activation and agent jobs. Same action, same secret pool, same case() expression. Recompiled all workflows with gh-aw v0.77.5.
Non-destructive pull_request-triggered workflow that selects a pool token and asserts, in the agent job, that needs.select_copilot_pat.outputs.copilot_pat_number is non-empty (the exact value that was silently empty with the old pre_activation wiring). To be removed after validation.
gh-aw v0.77.5 emits the top-level frontmatter `if:` on the built-in
pre_activation job WITHOUT a ${{ }} wrapper. When the condition starts with
`!` (the fork guards), the emitted `if: !(...)` is invalid YAML (a leading `!`
starts a YAML tag), which GitHub rejects as a workflow-file startup failure.
v0.68.3 wrapped it (valid); v0.77.5 does not. Confirmed independent of the
PAT-rotation change via a minimal probe.

Wrap the fork-guard conditions in parentheses so the emitted scalar starts
with `(` instead of `!` (semantically identical). Affects close-stale-prs,
devops-health-check, devops-health-groom, markdown-linter, pr-malicious-scan.
@JanKrivanek
JanKrivanek marked this pull request as ready for review June 9, 2026 16:54
@JanKrivanek
JanKrivanek requested a review from ViktorHofer as a code owner June 9, 2026 16:54
Copilot AI review requested due to automatic review settings June 9, 2026 16:54
@JanKrivanek
JanKrivanek enabled auto-merge (squash) June 9, 2026 16:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR repairs Copilot PAT rotation in agentic GitHub Actions workflows when compiled with gh-aw v0.77.5 by moving token selection into an explicit custom job (select_copilot_pat) that becomes a direct dependency for jobs consuming engine.env.

Changes:

  • Replace pre_activation step-injection with a dedicated select_copilot_pat job and update engine.env to reference needs.select_copilot_pat.outputs.copilot_pat_number.
  • Recompile affected agentic workflows/lockfiles with gh-aw v0.77.5 and update the select-copilot-pat documentation accordingly.
  • Update maintenance/dependabot config to align with the new gh-aw runtime/tooling behavior.
Show a summary per file
File Description
.github/workflows/pr-malicious-scan.agent.md Introduces select_copilot_pat job and updates engine.env to consume its output.
.github/workflows/markdown-linter.md Same PAT selection job wiring and engine.env update.
.github/workflows/issue-triage.md Same PAT selection job wiring and engine.env update.
.github/workflows/issue-investigate.md Same PAT selection job wiring and engine.env update.
.github/workflows/devops-health-investigate.md Same PAT selection job wiring and engine.env update for the worker workflow.
.github/workflows/devops-health-investigate.lock.yml Recompiled lockfile with PAT selection dependency and runtime/tooling updates.
.github/workflows/devops-health-groom.md Same PAT selection job wiring and engine.env update.
.github/workflows/devops-health-check.md Same PAT selection job wiring and engine.env update.
.github/workflows/close-stale-prs.agent.md Same PAT selection job wiring and engine.env update.
.github/workflows/close-stale-prs.agent.lock.yml Recompiled lockfile with PAT selection dependency and runtime/tooling updates.
.github/workflows/agentics-maintenance.yml Recompiled maintenance workflow with gh-aw v0.77.5 and new operations.
.github/dependabot.yml Adjusts dependabot configuration (including ignoring gh-aw managed actions).
.github/aw/actions-lock.json Removes an old action pin entry; file now appears stale vs workflow references.
.github/actions/select-copilot-pat/README.md Updates documentation to recommend select_copilot_pat custom job + on.needs.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (1)

.github/aw/actions-lock.json:32

  • .github/aw/actions-lock.json no longer contains any entry for the github/gh-aw-actions/* actions that the workflows now reference (for example github/gh-aw-actions/setup@v0.77.5). It also still contains an entry for github/gh-aw/actions/setup@v0.71.5, which doesn’t appear to be used anywhere in .github/workflows. This file looks stale/inconsistent with the workflow pins and should be updated (or removed) so it accurately reflects the action refs used by the repo.
    "github/gh-aw/actions/setup@v0.71.5": {
      "repo": "github/gh-aw/actions/setup",
      "version": "v0.71.5",
      "sha": "19ac811a4a85389c33b15128e1d7b7d4507f814a"
    }
  • Files reviewed: 20/20 changed files
  • Comments generated: 3

Comment thread .github/workflows/agentics-maintenance.yml
Comment thread .github/workflows/devops-health-investigate.lock.yml
Comment thread .github/workflows/close-stale-prs.agent.lock.yml
@github-actions github-actions Bot added the waiting-on-author PR state label label Jun 9, 2026
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

👋 @JanKrivanek — this PR has 3 unresolved review thread(s). When you're ready, please address the feedback and push an update; the triage bot will pick up the next state automatically. (Add the no-stale label to silence further pings.)

@JanKrivanek
JanKrivanek merged commit cc0621e into main Jun 9, 2026
36 checks passed
@JanKrivanek
JanKrivanek deleted the dev/jankrivanek/fix-pat-rotation branch June 9, 2026 17:58
JanKrivanek added a commit that referenced this pull request Jun 9, 2026
Adopt the shared workflow import pattern from dotnet/runtime PR #127946,
replacing the per-workflow select-copilot-pat action + custom job (shipped in
#736) with a reusable shared/pat_pool.md import.

What changed:
- Add .github/workflows/shared/pat_pool.md: an import that defines a `pat_pool`
  job (inline bash, no separate action) exposing a `pat_number` output, plus an
  import-schema mapping COPILOT_PAT_0..7 to this repo's pool secrets
  (COPILOT_GITHUB_TOKEN, COPILOT_GITHUB_TOKEN_2..8).
- Add .github/workflows/shared/pat_pool.README.md documenting the pattern.
- Convert all 8 agentic workflows to `imports: - shared/pat_pool.md` +
  `engine.env` `case(needs.pat_pool.outputs.pat_number ...)`.
- Delete the now-unused .github/actions/select-copilot-pat action.
- Add .github/workflows/validate-pat-pool.yml: a daily standalone workflow that
  validates each pool PAT with a Copilot CLI request and summarizes pool health.

Wiring note (adaptation from runtime): consuming workflows declare
`on.needs: [pat_pool]` instead of runtime's `needs: [pre_activation]` +
`on.permissions: {}`. This wires pat_pool ahead of the pre_activation and
activation jobs so the selected PAT is validated by the activation job and used
by the agent, and it works for `roles: all` workflows (issue-triage), which do
not produce a pre_activation job for the runtime workaround to attach to.

Compiled with gh-aw v0.77.5. Verified end-to-end with a temporary
test-pat-rotation workflow (since removed): a run selected token #2 of the
3-token pool and the agent job observed pat_number='2', confirming the rotated
PAT reaches the agent.
JanKrivanek added a commit that referenced this pull request Jun 10, 2026
Adopt the shared workflow import pattern from dotnet/runtime PR #127946,
replacing the per-workflow select-copilot-pat action + custom job (shipped in
#736) with a reusable shared/pat_pool.md import.

What changed:
- Add .github/workflows/shared/pat_pool.md: an import that defines a `pat_pool`
  job (inline bash, no separate action) exposing a `pat_number` output, plus an
  import-schema mapping COPILOT_PAT_0..7 to this repo's pool secrets
  (COPILOT_GITHUB_TOKEN, COPILOT_GITHUB_TOKEN_2..8).
- Add .github/workflows/shared/pat_pool.README.md documenting the pattern.
- Convert all 8 agentic workflows to `imports: - shared/pat_pool.md` +
  `engine.env` `case(needs.pat_pool.outputs.pat_number ...)`.
- Delete the now-unused .github/actions/select-copilot-pat action.
- Add .github/workflows/validate-pat-pool.yml: a daily standalone workflow that
  validates each pool PAT with a Copilot CLI request and summarizes pool health.

Wiring note (adaptation from runtime): consuming workflows declare
`on.needs: [pat_pool]` instead of runtime's `needs: [pre_activation]` +
`on.permissions: {}`. This wires pat_pool ahead of the pre_activation and
activation jobs so the selected PAT is validated by the activation job and used
by the agent, and it works for `roles: all` workflows (issue-triage), which do
not produce a pre_activation job for the runtime workaround to attach to.

Compiled with gh-aw v0.77.5. Verified end-to-end with a temporary
test-pat-rotation workflow (since removed): a run selected token #2 of the
3-token pool and the agent job observed pat_number='2', confirming the rotated
PAT reaches the agent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on-author PR state label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants