Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions .github/workflows/devflow-pr-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: DevFlow PR Review

on:
pull_request_target:
types:
- opened
- reopened
- ready_for_review
workflow_dispatch:
inputs:
pr_url:
description: Pull request URL to review
required: true
type: string

permissions:
contents: read
issues: write
pull-requests: write

concurrency:
group: devflow-pr-review-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

env:
DEVFLOW_REPOSITORY: ${{ vars.DF_REPO }}
DEVFLOW_REF: main
TARGET_REPO_PATH: ${{ github.workspace }}/target-repo
DEVFLOW_PATH: ${{ github.workspace }}/devflow

jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 60
if: ${{ github.event_name != 'pull_request_target' || !github.event.pull_request.draft }}

steps:
- name: Resolve PR metadata
id: pr
shell: bash
env:
PR_HTML_URL: ${{ github.event.pull_request.html_url }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_URL_INPUT: ${{ inputs.pr_url }}
run: |
set -euo pipefail

if [[ "${GITHUB_EVENT_NAME}" == "pull_request_target" ]]; then
echo "pr_url=${PR_HTML_URL}" >> "$GITHUB_OUTPUT"
echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
echo "repo=${GITHUB_REPOSITORY}" >> "$GITHUB_OUTPUT"
exit 0
fi

if [[ -z "$PR_URL_INPUT" ]]; then
echo "workflow_dispatch requires pr_url" >&2
exit 1
fi

if [[ ! "$PR_URL_INPUT" =~ ^https://github\.com/([^/]+/[^/]+)/pull/([0-9]+)([/?].*)?$ ]]; then
echo "Could not parse pull request URL (expected https://github.com/<owner>/<repo>/pull/<number>): $PR_URL_INPUT" >&2
exit 1
fi

pr_repo="${BASH_REMATCH[1]}"
pr_number="${BASH_REMATCH[2]}"

if [[ "$pr_repo" != "$GITHUB_REPOSITORY" ]]; then
echo "PR URL repository ($pr_repo) does not match current repository ($GITHUB_REPOSITORY)" >&2
exit 1
fi

echo "pr_url=${PR_URL_INPUT}" >> "$GITHUB_OUTPUT"
echo "pr_number=${pr_number}" >> "$GITHUB_OUTPUT"
echo "repo=${pr_repo}" >> "$GITHUB_OUTPUT"

# Safe checkout: base repo only, not the untrusted PR head.
- name: Checkout target repo base
uses: actions/checkout@v5
with:
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.sha }}
fetch-depth: 0
persist-credentials: false
path: target-repo

# Private DevFlow checkout: the PAT/token grants access to this repo's code.
- name: Checkout DevFlow
uses: actions/checkout@v5
with:
repository: ${{ env.DEVFLOW_REPOSITORY }}
ref: ${{ env.DEVFLOW_REF }}
token: ${{ secrets.DEVFLOW_TOKEN }}
fetch-depth: 1
persist-credentials: false
path: devflow

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true

- name: Install DevFlow dependencies
working-directory: ${{ env.DEVFLOW_PATH }}
run: uv sync --frozen

- name: Classify PR relevance
id: spam
working-directory: ${{ env.DEVFLOW_PATH }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_COPILOT_TOKEN: ${{ secrets.GH_COPILOT_TOKEN }}
SK_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
AGENT_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
PR_REPO: ${{ steps.pr.outputs.repo }}
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
run: |
uv run python scripts/classify_pr_spam.py \
--repo "$PR_REPO" \
--pr-number "$PR_NUMBER" \
--repo-path "${TARGET_REPO_PATH}" \
--apply-labels

- name: Stop after spam gate
if: ${{ steps.spam.outputs.decision != 'allow' }}
shell: bash
env:
SPAM_DECISION: ${{ steps.spam.outputs.decision }}
run: |
echo "Skipping review because spam gate decided: ${SPAM_DECISION}"

- name: Run PR review
if: ${{ steps.spam.outputs.decision == 'allow' }}
id: review
working-directory: ${{ env.DEVFLOW_PATH }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_COPILOT_TOKEN: ${{ secrets.GH_COPILOT_TOKEN }}
SK_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
AGENT_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
PR_URL: ${{ steps.pr.outputs.pr_url }}
run: |
uv run python scripts/trigger_pr_review.py \
--pr-url "$PR_URL" \
--github-username "$GITHUB_ACTOR" \
--no-require-comment-selection
Loading