Files
dotnet__skills/.github/workflows/shared/pat_pool.README.md
Jeff Handley 11c5dfc738 Require environment input to pat_pool shared workflow. (#832)
* Require environment input to pat_pool shared workflow.

- Updates the PAT pool shared workflow to latest version
- Updates the validate-pat-pool workflow to match other repos
- Updates all agentic workflows to use the updated PAT pool import
  with the standard secret names used across other repos
- The new version of the import does not require the 'needs' workaround
  but the issue-triage workflow's 'roles: all' configuration requires
  a different workaround to ensure the pre_activation job exists for
  the pat_pool job to be able to depend on it (necessary for ordering).

* Update markdown bullet style

* Apply Copilot PR feedback

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Bump versions to match gh-aw

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-25 15:35:22 -07:00

226 lines
12 KiB
Markdown

# PAT Pool
Selects a random Copilot PAT from a numbered pool of secrets. This addresses limitations that arise from having a single PAT shared across all agentic workflows, such as rate-limiting.
**This is a stop-gap workaround.** As soon as organization/enterprise billing is available to the dotnet org, this approach will be removed from our workflows.
## Repository Onboarding
To use Agentic Workflows in a dotnet org repository:
1. Follow the instructions for [Configuring Your Repository | Agentic Authoring | GitHub Agentic Workflows][configure-repo]. Use `gh aw` **v0.71.5 or newer**, which supports the agent job dependencies required for this implementation.
2. Copy the `pat_pool.md` and `pat_pool.README.md` files into the repository under `.github/workflows/shared`.
3. Merge those additions into the repository and then follow the instructions for the PAT Creation and Usage below.
**Install or upgrade the `gh aw` CLI and check the version**
```sh
gh extension install github/gh-aw --force
gh aw --version
```
### Environment
Create an environment for the agentic workflows:
- _Configuring these settings requires repo admin permission_
- https://github.com/dotnet/{repo}/settings/environments
- Recommended Name: **copilot-pat-pool**
- Recommended Deployment branches and tags: **Protected branches only**
This environment is used for all agentic workflows, restricting agentic workflows to the repo's protected branches and preventing the workflows from accessing secrets defined for other environments.
## PAT Management
Team members provide PATs into the pool with secret names matching the pattern of `{pool-name}_{0-9}`, such as `COPILOT_PAT_0`.
[Use this link to prefill the PAT creation form with the required settings][create-pat]:
1. **Resource owner** is your **user account**, not an organization.
2. **Copilot Requests (Read)** must be the only permission granted.
3. **8-day expiration** must be used, which enforces a weekly renewal.
4. **Repository access** set to **Public repositories** only.
The **Token Name** _does not_ need to match the secret name and is only visible to the owner of the PAT. It's recommended to use a token name indicating the PAT is used for dotnet org agentic workflows. The **Description** is also only used for your own reference.
Team members providing PATs for workflows should set weekly recurring reminders to regenerate and update their PATs in the PAT pool. With an 8-day expiration, renewal can be done on the same day each week.
## PAT Pool Secrets
For a PAT pool that is specific to an environment, PATs can be added to repositories as **Environment Secrets** for the environment created above. _This requires repo admin permission_.
- **Settings** >
- **Environments** >
- **copilot-pat-pool** (or other environment name) >
- **Add environment secret** (or edit your existing secret)
- Enter your secret name of `COPILOT_PAT_{0-9}` and paste in your PAT
This can also be accomplished using the `gh` CLI, specifying the repo and environment arguments.
```sh
# Register the PAT secret. This will prompt for you to paste the PAT.
gh secret set "<pool_name>_<0-9>" --repo <org>/<repo> --env "copilot-pat-pool"
```
It's also helpful to record who owns each PAT within the pool. To capture which team member is associated with each PAT, a `<pool_name>_<0-9>_<username>` "sidecar secret" can be added alongside the PAT secret to make the username for the PAT pool entry visible. This sidecar secret must have a non-empty value, but it's never consumed, so any value is sufficient.
```sh
# Record a sidecar secret that presents who owns this PAT.
gh secret set "<pool_name>_<0-9>_<username>" --body "<username>" --repo <org>/<repo> --env "copilot-pat-pool"
```
## Workflow Output Attribution
Team members' PATs are _only_ used for the Copilot requests from within the agentic portion of the workflow. All outputs from the workflow use the `github-actions[bot]` account token. Issues, PRs, comments, and all other content generated by the workflow will be attributed to `github-actions[bot]`--not the team member's account or token.
## Usage
The [`pat_pool.md`](./pat_pool.md) workflow import defines a custom job with a `pat_number` output. Consuming workflows need two additions to their frontmatter to import this job and use the PAT number to override the `COPILOT_GITHUB_TOKEN` passed to the workflow's agent job.
```yml
# ###############################################################
# Select a PAT from the pool and override COPILOT_GITHUB_TOKEN.
# Run agentic jobs in an isolated `copilot-pat-pool` environment.
#
# When org-level billing is available, this will be removed.
# See `shared/pat_pool.README.md` for more information.
# ###############################################################
imports:
- uses: shared/pat_pool.md
with:
environment: copilot-pat-pool
environment: copilot-pat-pool
engine:
id: copilot
env:
COPILOT_GITHUB_TOKEN: |
${{ case(
needs.pat_pool.outputs.pat_number == '0', secrets.COPILOT_PAT_0,
needs.pat_pool.outputs.pat_number == '1', secrets.COPILOT_PAT_1,
needs.pat_pool.outputs.pat_number == '2', secrets.COPILOT_PAT_2,
needs.pat_pool.outputs.pat_number == '3', secrets.COPILOT_PAT_3,
needs.pat_pool.outputs.pat_number == '4', secrets.COPILOT_PAT_4,
needs.pat_pool.outputs.pat_number == '5', secrets.COPILOT_PAT_5,
needs.pat_pool.outputs.pat_number == '6', secrets.COPILOT_PAT_6,
needs.pat_pool.outputs.pat_number == '7', secrets.COPILOT_PAT_7,
needs.pat_pool.outputs.pat_number == '8', secrets.COPILOT_PAT_8,
needs.pat_pool.outputs.pat_number == '9', secrets.COPILOT_PAT_9,
'NO COPILOT PAT AVAILABLE')
}}
```
The `COPILOT_GITHUB_TOKEN` expression can be collapsed onto a single line if desired. `gh-aw compile` automatically wires `pat_pool` into the activation and agent jobs' `needs:` graph because of the `needs.pat_pool.` references within the `engine.env` property.
```sh
gh aw compile <workflow-name> --schedule-seed <org>/<repo>
```
### Specifying the environment
The `environment` must be specified both to the `pat_pool.md` import and to the containing workflow to ensure both jobs access the PAT pool from the same environment. The `copilot-pat-pool` environment name is recommended as the isolated environment for agentic workflows that use the PAT pool.
### Customizing the pool
The import declares 10 optional inputs (`COPILOT_PAT_0` through `COPILOT_PAT_9`), each defaulting to `secrets.COPILOT_PAT_#` of the matching number. To point a workflow at a different pool of repository secrets, use the parameterized `uses`/`with` form when importing and pass the substitute secrets as the `COPILOT_PAT_#` inputs:
```yml
imports:
- uses: shared/pat_pool.md
with:
COPILOT_PAT_0: ${{ secrets.MY_TEAM_PAT_0 }}
COPILOT_PAT_1: ${{ secrets.MY_TEAM_PAT_1 }}
# Unspecified inputs default to `secrets.COPILOT_PAT_#` lookups
```
The secrets passed via `with:` must match the secrets referenced in the consuming workflow's `case` expression that overrides `COPILOT_GITHUB_TOKEN`--both sides need to agree on which secret backs each `COPILOT_PAT_#` slot. Update the `case` expression accordingly:
```yml
engine:
id: copilot
env:
COPILOT_GITHUB_TOKEN: ${{ case(needs.pat_pool.outputs.pat_number == '0', secrets.MY_TEAM_PAT_0, needs.pat_pool.outputs.pat_number == '1', secrets.MY_TEAM_PAT_1, ..., 'NO COPILOT PAT AVAILABLE') }}
```
This approach aligns with GitHub's documented guidance for [passing secrets][passing-secrets] between workflows, where the `pat_pool` job returns a PAT number and the `case` statement acts as a secret store to look the PAT secret up based on the selected number.
## Design / Security
There are several details of this implementation that keep our workflows and repositories safe.
1. **Secrets adhere to existing trust boundaries.** The pool of PAT secrets is
provided to a dedicated step within the `pat_pool` job. That job runs
after `pre_activation` and contains only the trusted checkout and action
steps--no untrusted context or input is within scope. The
`select-pat-number` action only references the secret values to determine
which are non-empty, filtering the secret numbers to those with values.
1. **The `pat_pool` job emits only a number, never a secret.** Its sole output,
`pat_number`, is the 0-9 index of the selected PAT (or empty when the pool
is empty). The actual secret materializes only later, in the activation
job's `engine.env` mapping, where the `case()` expression resolves the
number to the matching secret. This follows GitHub's guidance for
[passing secrets][passing-secrets] between jobs or workflows, with the
`case` statement acting as a very simple secret store.
1. **The `select-pat-number` action does not require any permissions.** It
reads only the `COPILOT_PAT_#` environment variables passed to it and writes
only to `GITHUB_OUTPUT`. The job that hosts it sets `permissions:` to the
workflow defaults (no elevated scopes).
1. **The implementation uses supported Agentic Workflow extensibility hooks.**
Defining a custom job inside an [imported workflow file][imports] is
supported by `gh aw compile`. gh-aw automatically
wires `pat_pool` into the activation job's `needs:` graph based on the
`needs.pat_pool.outputs.pat_number` references in `engine.env`. The
[secret override][secret-override] capability supplies the `COPILOT_GITHUB_TOKEN`
value via `engine.env` rather than the default secret of the same name.
Each of the references below contributed to the design and implementation to ensure a secure and reliable design.
## Known Issues
The `pat_pool` import integration requires that the workflow's compilation results in a `pre_activation` job. If nothing in your workflow definition produces a `pre_activation` job, a compilation error will be received.
```text
✗ Failed workflows:
✗ <workflow-name>.md
.github\workflows\<workflow-name>.md:1:1: error: failed to generate YAML: failed to build and validate jobs: job dependency validation failed: job 'pat_pool' depends on non-existent job 'pre_activation'
```
To work around this, add `on.permissions: {}` (or another `on.*` setting that forces `pre_activation`, such as `skip-if-no-match`) to your workflow, which forces a no-op `pre_activation` job to be generated.
```yml
on:
permissions: {}
```
See: [Activation 'needs' does not incorporate jobs in engine.env expressions (github/gh-aw#30790)](https://github.com/github/gh-aw/issues/30790)
## References
- [Agentic Workflows CLI Extension][cli-setup]
- [Agentic Authoring][configure-repo]
- [Authentication][authentication]
- [Agentic Workflow Imports][imports]
- [Custom Steps][steps]
- [Custom Jobs][jobs]
- [Job Outputs][job-outputs]
- [Engine Configuration][engine]
- [Engine Environment Variables][engine-vars]
- [Update agentic engine token handling to use user-provided secrets (github/gh-aw#18017)][secret-override]
- [Case Function in Workflow Expressions][case-expression]
- [Passing a secret between jobs or workflows][passing-secrets]
[cli-setup]: https://github.github.com/gh-aw/setup/cli/
[configure-repo]: https://github.github.com/gh-aw/guides/agentic-authoring/#configuring-your-repository
[authentication]: https://github.github.com/gh-aw/reference/auth/
[create-pat]: https://github.com/settings/personal-access-tokens/new?name=dotnet%20org%20agentic%20workflows&description=GitHub+Agentic+Workflows+-+Copilot+engine+authentication.++Used+for+dotnet+org+workflows.+MUST+be+configured+with+only+Copilot+Requests+permissions+and+user+account+as+resource+owner.+Weekly+expiration+and+required+renewal.&user_copilot_requests=read&expires_in=8
[imports]: https://github.github.com/gh-aw/reference/imports/
[steps]: https://github.github.com/gh-aw/reference/frontmatter/#custom-steps-steps
[jobs]: https://github.github.com/gh-aw/reference/frontmatter/#custom-jobs-jobs
[job-outputs]: https://github.github.com/gh-aw/reference/frontmatter/#job-outputs
[engine]: https://github.github.com/gh-aw/reference/frontmatter/#ai-engine-engine
[engine-vars]: https://github.github.com/gh-aw/reference/engines/#engine-environment-variables
[secret-override]: https://github.com/github/gh-aw/pull/18017
[case-expression]: https://docs.github.com/actions/reference/workflows-and-actions/expressions#case
[passing-secrets]: https://docs.github.com/actions/reference/workflows-and-actions/workflow-commands#example-masking-and-passing-a-secret-between-jobs-or-workflows