Skip to content

Commit 9e915b3

Browse files
moonbox3Copilot
andauthored
Add pr review GH workflow (microsoft#5418)
* Add workflow PR review * Allow reviews on draft PRs * Update .github/workflows/devflow-pr-review.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update .github/workflows/devflow-pr-review.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Bump actions/checkout to v6 and uv to 0.11.x --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent bca40a7 commit 9e915b3

1 file changed

Lines changed: 161 additions & 0 deletions

File tree

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
name: DevFlow PR Review
2+
3+
on:
4+
pull_request_target:
5+
types:
6+
- opened
7+
- reopened
8+
- ready_for_review
9+
workflow_dispatch:
10+
inputs:
11+
pr_number:
12+
description: Pull request number to review
13+
required: true
14+
type: string
15+
16+
permissions:
17+
contents: read
18+
issues: write
19+
pull-requests: write
20+
21+
concurrency:
22+
group: devflow-pr-review-${{ github.repository }}-${{ github.event.pull_request.number || inputs.pr_number || github.run_id }}
23+
cancel-in-progress: true
24+
25+
env:
26+
DEVFLOW_REPOSITORY: ${{ vars.DF_REPO }}
27+
DEVFLOW_REF: main
28+
TARGET_REPO_PATH: ${{ github.workspace }}/target-repo
29+
DEVFLOW_PATH: ${{ github.workspace }}/devflow
30+
31+
jobs:
32+
team_check:
33+
runs-on: ubuntu-latest
34+
outputs:
35+
is_team_member: ${{ steps.check.outputs.is_team_member }}
36+
pr_number: ${{ steps.pr.outputs.pr_number }}
37+
pr_url: ${{ steps.pr.outputs.pr_url }}
38+
repo: ${{ steps.pr.outputs.repo }}
39+
steps:
40+
- name: Resolve PR metadata
41+
id: pr
42+
shell: bash
43+
env:
44+
PR_HTML_URL: ${{ github.event.pull_request.html_url }}
45+
PR_NUMBER_EVENT: ${{ github.event.pull_request.number }}
46+
PR_NUMBER_INPUT: ${{ inputs.pr_number }}
47+
run: |
48+
set -euo pipefail
49+
50+
if [[ "${GITHUB_EVENT_NAME}" == "pull_request_target" ]]; then
51+
pr_number="${PR_NUMBER_EVENT}"
52+
pr_url="${PR_HTML_URL}"
53+
else
54+
pr_number="${PR_NUMBER_INPUT}"
55+
pr_url="https://github.com/${GITHUB_REPOSITORY}/pull/${pr_number}"
56+
fi
57+
58+
if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]]; then
59+
echo "Could not determine PR number; for workflow_dispatch runs, the 'pr_number' input is required when not running on pull_request_target." >&2
60+
exit 1
61+
fi
62+
63+
echo "pr_url=${pr_url}" >> "$GITHUB_OUTPUT"
64+
echo "pr_number=${pr_number}" >> "$GITHUB_OUTPUT"
65+
echo "repo=${GITHUB_REPOSITORY}" >> "$GITHUB_OUTPUT"
66+
67+
- name: Check PR author team membership
68+
id: check
69+
uses: actions/github-script@v8
70+
env:
71+
TEAM_NAME: ${{ secrets.DEVELOPER_TEAM }}
72+
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
73+
with:
74+
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
75+
script: |
76+
let author = context.payload.pull_request?.user?.login;
77+
if (!author) {
78+
const { data: pr } = await github.rest.pulls.get({
79+
owner: context.repo.owner,
80+
repo: context.repo.repo,
81+
pull_number: Number(process.env.PR_NUMBER),
82+
});
83+
author = pr.user.login;
84+
}
85+
86+
let isTeamMember = false;
87+
try {
88+
const teamMembership = await github.rest.teams.getMembershipForUserInOrg({
89+
org: context.repo.owner,
90+
team_slug: process.env.TEAM_NAME,
91+
username: author,
92+
});
93+
isTeamMember = teamMembership.data.state === 'active';
94+
} catch (error) {
95+
console.log(`Team membership lookup failed for ${author}: ${error.message}`);
96+
isTeamMember = false;
97+
}
98+
99+
core.setOutput('is_team_member', isTeamMember ? 'true' : 'false');
100+
if (isTeamMember) {
101+
core.info(`Author ${author} is a team member; proceeding with review.`);
102+
} else {
103+
core.info(`Author ${author} is not a member of ${process.env.TEAM_NAME}; skipping review.`);
104+
}
105+
106+
review:
107+
runs-on: ubuntu-latest
108+
needs: team_check
109+
if: ${{ needs.team_check.outputs.is_team_member == 'true' }}
110+
timeout-minutes: 60
111+
112+
steps:
113+
# Safe checkout: base repo only, not the untrusted PR head.
114+
- name: Checkout target repo base
115+
uses: actions/checkout@v6
116+
with:
117+
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.sha }}
118+
fetch-depth: 0
119+
persist-credentials: false
120+
path: target-repo
121+
122+
# Private DevFlow checkout: the PAT/token grants access to this repo's code.
123+
- name: Checkout DevFlow
124+
uses: actions/checkout@v6
125+
with:
126+
repository: ${{ env.DEVFLOW_REPOSITORY }}
127+
ref: ${{ env.DEVFLOW_REF }}
128+
token: ${{ secrets.DEVFLOW_TOKEN }}
129+
fetch-depth: 1
130+
persist-credentials: false
131+
path: devflow
132+
133+
- name: Set up Python
134+
uses: actions/setup-python@v5
135+
with:
136+
python-version: "3.13"
137+
138+
- name: Set up uv
139+
uses: astral-sh/setup-uv@v7
140+
with:
141+
version: "0.11.x"
142+
enable-cache: true
143+
144+
- name: Install DevFlow dependencies
145+
working-directory: ${{ env.DEVFLOW_PATH }}
146+
run: uv sync --frozen
147+
148+
- name: Run PR review
149+
id: review
150+
working-directory: ${{ env.DEVFLOW_PATH }}
151+
env:
152+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
153+
GH_COPILOT_TOKEN: ${{ secrets.GH_COPILOT_TOKEN }}
154+
SK_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
155+
AGENT_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
156+
PR_URL: ${{ needs.team_check.outputs.pr_url }}
157+
run: |
158+
uv run python scripts/trigger_pr_review.py \
159+
--pr-url "$PR_URL" \
160+
--github-username "$GITHUB_ACTOR" \
161+
--no-require-comment-selection

0 commit comments

Comments
 (0)