-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add PATs rotation to agentic workflow(s) #13496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
1077f88
Recompile agentic workflows with gh-aw v0.67.1
JanKrivanek 8286af9
Add select-copilot-pat action for PAT pool rotation
JanKrivanek 8495fd2
Add Copilot PAT pool rotation to close-stale-prs workflow
JanKrivanek fa13415
Fix naming convention in README.md to use COPILOT_GITHUB_TOKEN(_#) in…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| # Select Copilot PAT | ||
|
|
||
| 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 offered for agentic workflows, 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]. | ||
| 2. Copy this `select-copilot-pat` folder into the repository under `.github/actions/select-copilot-pat`, including both the `README.md` and `action.yml`. | ||
| 3. Merge those additions into the repository and then follow the instructions for the PAT Creation and Usage below. | ||
|
|
||
| > **Optional:** If you plan to manage secrets or workflows from the command line (e.g., `gh aw secrets set`), [install the `gh aw` CLI extension][cli-setup]: | ||
| > | ||
| > ```sh | ||
| > gh extension install github/gh-aw | ||
| > ``` | ||
|
|
||
| ## PAT Management | ||
|
|
||
| Team members provide PATs into the pools for the repository by adding them as repository secrets 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 repository secrets. With an 8-day expiration, renewal can be done on the same day each week. | ||
|
|
||
| PATs are added to repositories through the **Settings > Secrets and variables > Actions** UI, saved as **Repository secrets** and matching the `<pool_name>_<0-9>` naming convention. This can also be done using the GitHub CLI. | ||
|
|
||
| ```sh | ||
| gh aw secrets set "<pool_name>_<0-9>" --value "<your-github-pat>" --repo dotnet/<repo> | ||
| ``` | ||
|
|
||
| ## 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 | ||
|
|
||
| Add the following frontmatter at the top-level of an agentic workflow. These elements are not supported through [imports][imports], so they must be copied into all workflows. | ||
|
|
||
| Up to 10 `SECRET_#` environment variables can be passed to the action, numbered 0-9. Different workflows can use different pools of PATs if desired. Change the `secrets.COPILOT_PAT_0` through `secrets.COPILOT_PAT_9` secret names in both the `select-copilot-pat` step `env` values and in the `case` expression under the `engine: env` configuration. | ||
|
|
||
| ```yml | ||
| on: | ||
| # Add the pre-activation step of selecting a random PAT from the supplied secrets | ||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| name: Checkout the select-copilot-pat action folder | ||
| with: | ||
| persist-credentials: false | ||
| sparse-checkout: .github/actions/select-copilot-pat | ||
| sparse-checkout-cone-mode: true | ||
| fetch-depth: 1 | ||
|
|
||
| - id: select-copilot-pat | ||
| name: Select Copilot token from pool | ||
| uses: ./.github/actions/select-copilot-pat | ||
| env: | ||
| # If the secret names are changed here, they must also be changed | ||
| # in the `engine: env` case expression | ||
| SECRET_0: ${{ secrets.COPILOT_PAT_0 }} | ||
| SECRET_1: ${{ secrets.COPILOT_PAT_1 }} | ||
| SECRET_2: ${{ secrets.COPILOT_PAT_2 }} | ||
| SECRET_3: ${{ secrets.COPILOT_PAT_3 }} | ||
| SECRET_4: ${{ secrets.COPILOT_PAT_4 }} | ||
| SECRET_5: ${{ secrets.COPILOT_PAT_5 }} | ||
| SECRET_6: ${{ secrets.COPILOT_PAT_6 }} | ||
| SECRET_7: ${{ secrets.COPILOT_PAT_7 }} | ||
| SECRET_8: ${{ secrets.COPILOT_PAT_8 }} | ||
| SECRET_9: ${{ secrets.COPILOT_PAT_9 }} | ||
|
|
||
| # Add the pre-activation output of the randomly selected PAT | ||
| jobs: | ||
| pre-activation: | ||
| outputs: | ||
| copilot_pat_number: ${{ steps.select-copilot-pat.outputs.copilot_pat_number }} | ||
|
|
||
| # Override the COPILOT_GITHUB_TOKEN expression used in the activation job | ||
| # Consume the PAT number from the pre-activation step and select the corresponding secret | ||
| engine: | ||
| id: copilot | ||
| env: | ||
| # We cannot use line breaks in this expression as it leads to a syntax error in the compiled workflow | ||
| # If none of the `COPILOT_PAT_#` secrets were selected, then the default COPILOT_GITHUB_TOKEN is used | ||
| COPILOT_GITHUB_TOKEN: ${{ case(needs.pre_activation.outputs.copilot_pat_number == '0', secrets.COPILOT_PAT_0, needs.pre_activation.outputs.copilot_pat_number == '1', secrets.COPILOT_PAT_1, needs.pre_activation.outputs.copilot_pat_number == '2', secrets.COPILOT_PAT_2, needs.pre_activation.outputs.copilot_pat_number == '3', secrets.COPILOT_PAT_3, needs.pre_activation.outputs.copilot_pat_number == '4', secrets.COPILOT_PAT_4, needs.pre_activation.outputs.copilot_pat_number == '5', secrets.COPILOT_PAT_5, needs.pre_activation.outputs.copilot_pat_number == '6', secrets.COPILOT_PAT_6, needs.pre_activation.outputs.copilot_pat_number == '7', secrets.COPILOT_PAT_7, needs.pre_activation.outputs.copilot_pat_number == '8', secrets.COPILOT_PAT_8, needs.pre_activation.outputs.copilot_pat_number == '9', secrets.COPILOT_PAT_9, secrets.COPILOT_GITHUB_TOKEN) }} | ||
| ``` | ||
|
|
||
| ## 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 the `select-copilot-pat` action within the `pre_activation` | ||
| job, which is a deterministic and trusted portion of the workflow. No | ||
| untrusted context or input is within scope during this job. The action step | ||
| runs within that job, and the secrets do not get passed across contexts. The | ||
| `select-copilot-pat` action only references the secret values to determine | ||
| which values are non-empty, filtering the secret numbers to those with | ||
| values. | ||
| 1. **The `select-copilot-pat` action does not require any permissions.** It | ||
| merely selects a random number from the pool of non-empty secrets and | ||
| returns the _number_ (**not the secret**). The consuming workflow uses the | ||
| returned secret number to provide the corresponding PAT to the agent job. | ||
| 1. **The implementation uses existing extensibility hooks in Agentic | ||
| Workflows.** Everything is supported by `gh aw compile` in this approach, | ||
| and no hand-editing of the compiled output is required. The `pre_activation` | ||
| job is designed for this type of extensibility, and the | ||
| [secret override][secret-override] capability was added to support using a | ||
| secret with a name different from the default `COPILOT_GITHUB_TOKEN`. | ||
|
|
||
| Each of the references below contributed to the design and implementation to ensure a secure and reliable design. | ||
|
|
||
| ## 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] | ||
| - [Case Function in Workflow Expressions][case-expression] | ||
| - [Update agentic engine token handling to use user-provided secrets (github/gh-aw#18017)][secret-override] | ||
|
|
||
| [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 | ||
| [case-expression]: https://docs.github.com/en/actions/reference/workflows-and-actions/expressions#case | ||
| [secret-override]: https://github.com/github/gh-aw/pull/18017 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| --- | ||
| name: "Select Copilot PAT from Pool" | ||
| description: >- | ||
| Selects a random Copilot PAT from a numbered pool of secrets. Secrets are | ||
| passed as environment variables SECRET_0 through SECRET_9 by the calling | ||
| workflow step. | ||
|
|
||
| inputs: | ||
| random-seed: | ||
| description: >- | ||
| A seed number to use for the random PAT selection, for deterministic | ||
| selection if needed. | ||
| required: false | ||
| default: "" | ||
|
|
||
| outputs: | ||
| copilot_pat_number: | ||
| description: >- | ||
| The 0-9 secret number selected from the pool of specified secrets | ||
| value: ${{ steps.select-pat-number.outputs.copilot_pat_number }} | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
| - id: select-pat-number | ||
| shell: bash | ||
| env: | ||
| RANDOM_SEED: ${{ inputs.random-seed }} | ||
| run: | | ||
| # Collect numbers with non-empty secrets from SECRET_0..SECRET_9. | ||
| PAT_NUMBERS=() | ||
| for i in $(seq 0 9); do | ||
| var="SECRET_${i}" | ||
| val="${!var}" | ||
| if [ -n "$val" ]; then | ||
| PAT_NUMBERS+=(${i}) | ||
JanKrivanek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| fi | ||
| done | ||
|
|
||
| # If none of the secrets in the pool have values, emit a warning | ||
| # and do not set an output value. The consumer can then fall back | ||
| # to using COPILOT_GITHUB_TOKEN. | ||
| if [ ${#PAT_NUMBERS[@]} -eq 0 ]; then | ||
| warning_message="::warning::None of the specified secrets had values " | ||
| warning_message+="(checked SECRET_0 through SECRET_9)" | ||
| echo "$warning_message" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Select a random index using the seed if specified | ||
| if [ -n "$RANDOM_SEED" ]; then | ||
| RANDOM=$RANDOM_SEED | ||
| fi | ||
|
|
||
| PAT_INDEX=$(( RANDOM % ${#PAT_NUMBERS[@]} )) | ||
| PAT_NUMBER="${PAT_NUMBERS[$PAT_INDEX]}" | ||
| selection_message="Selected token ${PAT_NUMBER}" | ||
| selection_details="(index: ${PAT_INDEX}; pool size: ${#PAT_NUMBERS[@]})" | ||
| echo "${selection_message} ${selection_details}" | ||
|
|
||
| # Set the PAT number as the output | ||
| echo "copilot_pat_number=${PAT_NUMBER}" >> "$GITHUB_OUTPUT" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.