ci(pr-review): label-only manual trigger, thin dispatcher (#23449)
Addresses the review feedback on #23418 (Paul + Copilot + cubic) and switches the manual trigger from comments to **labels**, consistent with the e2e labels. ## What changed - **Manual reviews are label-driven** — add `pr-review-security` / `pr-review-triage` / `pr-review-standard`. Labeling requires write access (team-only). The **`/pr-review` comment trigger is removed.** - Like the e2e labels, a check runs on every push **while its label is present** (the orchestrator reads the PR's current labels each run) — so `pr-review-standard` keeps the deep review current until removed. - **Dispatcher is now dumb** — it forwards only `pr_number`. All resolution + validation lives in the privileged orchestrator (Paul's suggestion: it fetches PR metadata, incl. labels, there anyway). This fixes the bot findings (regex allowlist bypass, `/pr-review`→standard default, delimiter edge cases) at the source. - `cancel-in-progress: true` (latest-push-wins, matching the previous dispatcher). Fires on non-draft PR events (the auto `security,triage` gate) and on `pr-review-*` label adds. ## Depends on A companion change to the privileged CI (reads labels + resolves/validates checks) — merge that first; it's backward-compatible, so nothing breaks in between.
This commit is contained in:
@@ -1,67 +1,25 @@
|
|||||||
name: PR Review Dispatch
|
name: PR Review Dispatch
|
||||||
run-name: "PR Review Dispatch (${{ github.event_name }})"
|
run-name: "PR Review Dispatch #${{ github.event.pull_request.number }}"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [ready_for_review, synchronize, labeled]
|
types: [ready_for_review, synchronize, labeled]
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
permissions: {}
|
permissions: {}
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: pr-review-dispatch-${{ github.event.pull_request.number || github.event.issue.number }}
|
group: pr-review-dispatch-${{ github.event.pull_request.number }}
|
||||||
cancel-in-progress: false
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
dispatch:
|
dispatch:
|
||||||
if: >
|
if: >
|
||||||
(github.event_name == 'pull_request_target' && github.event.action != 'labeled' && github.event.pull_request.draft == false) ||
|
github.event.pull_request.draft == false &&
|
||||||
(github.event_name == 'pull_request_target' && github.event.action == 'labeled' && startsWith(github.event.label.name, 'pr-review-')) ||
|
(github.event.action != 'labeled' || startsWith(github.event.label.name, 'pr-review-'))
|
||||||
(github.event_name == 'issue_comment' && github.event.issue.pull_request != null && startsWith(github.event.comment.body, '/pr-review') && contains(fromJSON('["MEMBER", "OWNER", "COLLABORATOR"]'), github.event.comment.author_association))
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
steps:
|
steps:
|
||||||
- name: Resolve target + checks
|
|
||||||
id: resolve
|
|
||||||
env:
|
|
||||||
EVENT_NAME: ${{ github.event_name }}
|
|
||||||
ACTION: ${{ github.event.action }}
|
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
||||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
||||||
LABEL_NAME: ${{ github.event.label.name }}
|
|
||||||
COMMENT_BODY: ${{ github.event.comment.body }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
allowed="security triage standard"
|
|
||||||
is_valid() { printf '%s\n' $allowed | grep -qx "$1"; }
|
|
||||||
|
|
||||||
if [ "$EVENT_NAME" = "issue_comment" ]; then
|
|
||||||
NUM="$ISSUE_NUMBER"
|
|
||||||
CHECK=$(printf '%s' "$COMMENT_BODY" | sed -nE 's#^/pr-review[[:space:]]+([a-z]+).*#\1#p' | head -1)
|
|
||||||
CHECKS="${CHECK:-standard}"
|
|
||||||
elif [ "$ACTION" = "labeled" ]; then
|
|
||||||
NUM="$PR_NUMBER"
|
|
||||||
CHECKS="${LABEL_NAME#pr-review-}"
|
|
||||||
else
|
|
||||||
NUM="$PR_NUMBER"
|
|
||||||
CHECKS="security,triage"
|
|
||||||
fi
|
|
||||||
|
|
||||||
ok=true
|
|
||||||
IFS=',' read -ra parts <<< "$CHECKS"
|
|
||||||
for p in "${parts[@]}"; do is_valid "$p" || ok=false; done
|
|
||||||
|
|
||||||
if [ "$ok" = true ] && [ -n "$NUM" ]; then
|
|
||||||
echo "pr_number=$NUM" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "checks=$CHECKS" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "dispatch=true" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "dispatch=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Mint ci-privileged dispatch token
|
- name: Mint ci-privileged dispatch token
|
||||||
if: steps.resolve.outputs.dispatch == 'true'
|
|
||||||
id: app-token
|
id: app-token
|
||||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -71,12 +29,10 @@ jobs:
|
|||||||
repositories: ci-privileged
|
repositories: ci-privileged
|
||||||
permission-actions: write
|
permission-actions: write
|
||||||
|
|
||||||
- name: Dispatch to ci-privileged
|
- name: Forward to PR review
|
||||||
if: steps.resolve.outputs.dispatch == 'true'
|
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
PR_NUMBER: ${{ steps.resolve.outputs.pr_number }}
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
CHECKS: ${{ steps.resolve.outputs.checks }}
|
|
||||||
run: |
|
run: |
|
||||||
gh workflow run pr-review.yaml --repo twentyhq/ci-privileged --ref main \
|
gh workflow run pr-review.yaml --repo twentyhq/ci-privileged --ref main \
|
||||||
-f pr_number="$PR_NUMBER" -f checks="$CHECKS"
|
-f pr_number="$PR_NUMBER"
|
||||||
|
|||||||
Reference in New Issue
Block a user