mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
11c5dfc738
* 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>
226 lines
12 KiB
Markdown
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
|