Improve evaluate-pr-tests workflow: fork support, access gating, dry-run#34678
Improve evaluate-pr-tests workflow: fork support, access gating, dry-run#34678
Conversation
- Change trigger from pull_request to pull_request_target so fork PRs have access to secrets (COPILOT_GITHUB_TOKEN) - Add roles: all to allow fork contributors (who have read permission) to trigger the workflow - Remove forks: ["*"] (not needed with pull_request_target) - Remove ready_for_review type (not supported by gh-aw for pull_request_target) - Update if condition and gate step to reference pull_request_target Validated on PureWeen/maui: - Same-repo PR: all green (run 23603776593) - Fork PR via workflow_dispatch: all green (run 23605610535) - Fork PR via pull_request_target: all green (run 23606033617) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34678Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34678" |
There was a problem hiding this comment.
Pull request overview
Updates the gh-aw Evaluate PR Tests workflow triggers so fork PRs can be evaluated (by switching from pull_request to pull_request_target) while keeping the workflow’s gating/conditions aligned with the new event.
Changes:
- Switched workflow trigger from
pull_requesttopull_request_targetand updated relatedif:conditions. - Updated the gate step condition to run under
pull_request_target. - Updated the compiled
.lock.ymlto reflect the new trigger configuration.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
.github/workflows/copilot-evaluate-tests.md |
Moves evaluation to pull_request_target and updates workflow conditions/gate accordingly. |
.github/workflows/copilot-evaluate-tests.lock.yml |
Regenerated compiled workflow reflecting the trigger/condition changes. |
| roles: all | ||
|
|
There was a problem hiding this comment.
roles: all is indented under on: (same level as workflow_dispatch), so the gh-aw compiler treats it as part of the event config instead of a top-level workflow setting. In the compiled lock file this ends up commented out and the membership/role gate is effectively removed, meaning anyone (including arbitrary /evaluate-tests issue comments) can trigger a pull_request_target run that has access to repository secrets. Unindent roles: all to the top level (aligned with on:/if:) and re-run gh aw compile so the intended role gating is applied.
| roles: all | |
| roles: all |
There was a problem hiding this comment.
This suggestion is incorrect. The gh aw compile command explicitly requires roles under on: — placing it at the top level produces:
error: Unknown property: roles. Did you mean: bots, jobs, tools. 'roles' belongs under 'on'
The lock file comment # roles: all # Roles processed as role check in pre-activation job is the compiler's standard format. With roles: all, the pre_activation role check is intentionally omitted (everyone is allowed). This was validated on PureWeen/maui where a fork contributor with read permission successfully triggered the workflow (run 23606033617).
There was a problem hiding this comment.
@PureWeen's explanation is correct. The reviewer's suggestion has two issues:
-
roles: allplacement: The gh-aw compiler requiresrolesunderon:— placing it at the top level produces a compile error. The lock file line# roles: all # Roles processed as role check in pre-activation jobis the compiler's standard format confirming the setting was processed. Withroles: all, the compiler intentionally skips the pre-activation role gate (because everyone is allowed), which is exactly the intended behavior for fork contributors. -
The fork guard suggestion (
head.repo.fork == falsein the activationif:): This would completely defeat the purpose of this PR, which is specifically to allow fork PRs to be evaluated.
The security model is sound:
pull_request_targetruns the workflow from the base branch — forks cannot alter it- The agent container has all credentials scrubbed
safe-outputslimits the agent to posting 1 comment- The workflow is read-only (evaluate tests, post a comment)
No changes needed here.
| pull_request_target: | ||
| paths: | ||
| - src/**/tests/** | ||
| - src/**/test/** | ||
| types: | ||
| - opened | ||
| - synchronize | ||
| - reopened | ||
| - ready_for_review | ||
| # roles: all # Roles processed as role check in pre-activation job | ||
| workflow_dispatch: |
There was a problem hiding this comment.
The compiled lock file shows roles: all commented out under the on: block, which indicates the roles setting was not applied. This also coincides with the removal of the pre_activation/membership check job, so the workflow no longer gates who can run it. Fix by moving roles: to the correct top-level location in the .md frontmatter and re-compiling so the lock file includes the intended role check behavior.
| needs: pre_activation | ||
| if: > | ||
| (needs.pre_activation.outputs.activated == 'true') && ((github.event_name == 'pull_request' && github.event.pull_request.draft == false) || github.event_name == 'workflow_dispatch' || (github.event_name == 'issue_comment' && | ||
| (github.event_name == 'pull_request_target' && github.event.pull_request.draft == false) || github.event_name == 'workflow_dispatch' || (github.event_name == 'issue_comment' && |
There was a problem hiding this comment.
jobs.activation no longer depends on a role/membership gate (needs: pre_activation and the needs.pre_activation.outputs.activated check are gone). With pull_request_target, this means the workflow can run with secrets for any matching PR/comment, which is a significant security/cost exposure. After fixing the roles placement in the .md, ensure the compiled lock restores the gating (or add an explicit guard) before allowing activation to proceed.
| (github.event_name == 'pull_request_target' && github.event.pull_request.draft == false) || github.event_name == 'workflow_dispatch' || (github.event_name == 'issue_comment' && | |
| (github.event_name == 'pull_request_target' && github.event.pull_request.draft == false && github.event.pull_request.head.repo.fork == false) || github.event_name == 'workflow_dispatch' || (github.event_name == 'issue_comment' && |
The workflow_dispatch step runs with GITHUB_TOKEN and checks out PR code. Restrict it to only process PRs from authors with write/maintain/admin access, preventing checkout of untrusted fork code in a privileged context. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the PR author permission check from inline workflow bash into the shared Checkout-GhAwPr.ps1 script. Any gh-aw workflow using this script now automatically gates on the PR author having write/maintain/admin access before checking out code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fork PRs are handled by pull_request_target (platform checkout in sandboxed container). The workflow_dispatch path should only process same-repo PRs from authors with write access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restoring only skills/, instructions/, and copilot-instructions.md left other .github/ subdirs (pr-review/, scripts/, workflows/) from the PR branch. Restore the entire .github/ directory for complete coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of deleting .github/ and restoring from main, merge the base branch into the PR branch after checkout. This produces the same state as a pull_request merge commit: PR changes + latest main. If the PR modifies a skill, the PR version wins; otherwise main's version is used. This lets contributors iterate on skills via workflow_dispatch while keeping everything else current. On merge conflict, falls back to the PR branch as-is with a warning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- pull_request_target: only auto-runs for OWNER/MEMBER/COLLABORATOR - issue_comment: /evaluate-tests only accepted from OWNER/MEMBER/COLLABORATOR - workflow_dispatch: unchanged - External PRs require maintainer /evaluate-tests comment to trigger Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert merge strategy to targeted git checkout (works in shallow clones) - Remove roles:all, restore gh-aw pre_activation with write-level checks - Remove author_association from if: (gh-aw handles access gating) - Update fork fallback message to remove stale workflow_dispatch advice Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add suppress_comment input for workflow_dispatch dry-run (evaluate without posting comment) - Add explicit noop guidance so the agent uses it instead of silently exiting - Update posting results section to respect dry-run mode Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prevents silent fork check bypass when gh returns empty/malformed JSON — $null.isFork evaluates to $false in PowerShell, which would let the fork check pass incorrectly. Note: ready_for_review cannot be added to pull_request_target types yet — gh-aw compiler doesn't include it in the allowed type list. Filed as a known gap. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Overhauls the
copilot-evaluate-testsgh-aw workflow to properly support fork PRs, add access gating, and improve the agent experience.Problem
Fork PRs triggered via
pull_requestdo not have access to repository secrets (COPILOT_GITHUB_TOKEN). This is a GitHub Actions platform limitation — secrets are scrubbed for forkpull_requestevents to prevent exfiltration. Theforks: ["*"]compiler flag only removes the activation gate but does not provision secrets.Solution
Switch to
pull_request_target(runs in base repo context with secrets), gate on author access level, and add dry-run/noop flows.Trigger Behavior
pull_request_targetsrc/**/tests/**issue_comment/evaluate-testscomment on a PRworkflow_dispatchAccess Matrix
pull_request_target/evaluate-testscommentworkflow_dispatchpre_activationblockspre_activationblockspre_activationblockspre_activationblockspre_activationblocksExternal contributors' PRs require a maintainer to comment
/evaluate-teststo trigger evaluation.Fork Handling by Flow
pull_request_targetpre_activationgates on write access; platform checks out PR in sandboxed containerissue_commentpre_activationgates on commenter's write access; platform handles checkout in sandboxworkflow_dispatchisCrossRepositorycheck) + verifies author write access; restores agent infrastructure from base branchChanges
.mdpull_request→pull_request_target.mdforks: ["*"]pull_request_target(no fork gate to remove).mdready_for_reviewtrigger typepull_request_target; draft PRs are already filtered by thedraft == falsecondition.mdsuppress_commentinput.mdnoopwith reason instead of silently exiting.mdworkflow_dispatchrecommendation for fork users.ps1workflow_dispatchpath.ps1Security Model
pull_request_targetruns the workflow from the base branch — fork PRs cannot alter the workflow filepre_activationgates all triggers onadmin/maintainer/writerolesworkflow_dispatchhas additional defense-in-depth: PS1 rejects forks + checks author permissionsValidation
Tested on PureWeen/maui with all three trigger paths:
workflow_dispatch(#23)pull_request_target(#23)