1# Assign Reviewers — Smart team assignment based on diff weight
2#
3# Triggers on PR open and ready_for_review events. Checks out the coordinator
4# repo (zed-industries/codeowner-coordinator) to access the assignment script and rules,
5# then assigns the 1-2 most relevant teams as reviewers.
6#
7# NOTE: This file is stored in the codeowner-coordinator repo but must be deployed to
8# the zed repo at .github/workflows/assign-reviewers.yml. See INSTALL.md.
9#
10# AUTH NOTE: Uses a GitHub App (COORDINATOR_APP_ID + COORDINATOR_APP_PRIVATE_KEY)
11# for all API operations: cloning the private coordinator repo, requesting team
12# reviewers, and setting PR assignees. GITHUB_TOKEN is not used.
13#
14# SECURITY INVARIANTS (pull_request_target):
15# This workflow runs with access to secrets for ALL PRs including forks.
16# It is safe ONLY because:
17# 1. The checkout is the coordinator repo at ref: main — NEVER the PR head/branch
18# 2. No ${{ }} interpolation of event fields in run: blocks — all routed via env:
19# 3. The script never executes, sources, or reads files from the PR branch
20# Violating any of these enables remote code execution with secret access.
21
22name: Assign Reviewers
23
24on:
25 # zizmor: ignore[dangerous-triggers] reviewed — no PR code checkout, only coordinator repo at ref: main
26 pull_request_target:
27 types: [opened, ready_for_review]
28
29# GITHUB_TOKEN is not used — all operations use the GitHub App token.
30# Declare minimal permissions so the default token has no write access.
31permissions: {}
32
33# Prevent duplicate runs for the same PR (e.g., rapid push + ready_for_review).
34concurrency:
35 group: assign-reviewers-${{ github.event.pull_request.number }}
36 cancel-in-progress: true
37
38# NOTE: For ready_for_review events, the webhook payload may still carry
39# draft: true due to a GitHub race condition (payload serialized before DB
40# update). We trust the event type instead — the script rechecks draft status
41# via a live API call as defense-in-depth.
42#
43# No author_association filter — external and fork PRs also get reviewer
44# assignments. Assigned reviewers are inherently scoped to org team members
45# by the GitHub Teams API.
46jobs:
47 assign-reviewers:
48 if: >-
49 github.event.action == 'ready_for_review' || github.event.pull_request.draft == false
50 runs-on: ubuntu-latest
51 steps:
52 - name: Generate app token
53 id: app-token
54 uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
55 with:
56 app-id: ${{ vars.COORDINATOR_APP_ID }}
57 private-key: ${{ secrets.COORDINATOR_APP_PRIVATE_KEY }}
58 repositories: codeowner-coordinator,zed
59
60 # SECURITY: checks out the coordinator repo at ref: main, NOT the PR branch.
61 # persist-credentials: false prevents the token from leaking into .git/config.
62 - name: Checkout coordinator repo
63 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
64 with:
65 repository: zed-industries/codeowner-coordinator
66 ref: main
67 path: codeowner-coordinator
68 token: ${{ steps.app-token.outputs.token }}
69 persist-credentials: false
70
71 - name: Setup Python
72 uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
73 with:
74 python-version: "3.11"
75
76 - name: Install dependencies
77 run: |
78 pip install --no-deps -q --only-binary ':all:' \
79 -r /dev/stdin <<< "pyyaml==6.0.3 --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"
80
81 - name: Assign reviewers
82 env:
83 GH_TOKEN: ${{ steps.app-token.outputs.token }}
84 PR_URL: ${{ github.event.pull_request.html_url }}
85 TARGET_REPO: ${{ github.repository }}
86 ASSIGN_INTERNAL: ${{ vars.ASSIGN_INTERNAL || 'false' }}
87 ASSIGN_EXTERNAL: ${{ vars.ASSIGN_EXTERNAL || 'true' }}
88 run: |
89 cd codeowner-coordinator
90 python .github/scripts/assign-reviewers.py \
91 --pr "$PR_URL" \
92 --apply \
93 --rules-file team-membership-rules.yml \
94 --repo "$TARGET_REPO" \
95 --org zed-industries \
96 2>&1 | tee /tmp/assign-reviewers-output.txt
97
98 - name: Upload output
99 if: always()
100 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
101 with:
102 name: assign-reviewers-output
103 path: /tmp/assign-reviewers-output.txt
104 retention-days: 30