# Assign Reviewers — Smart team assignment based on diff weight # # Triggers on PR open and ready_for_review events. Checks out the coordinator # repo (zed-industries/codeowner-coordinator) to access the assignment script and rules, # then assigns the 1-2 most relevant teams as reviewers. # # NOTE: This file is stored in the codeowner-coordinator repo but must be deployed to # the zed repo at .github/workflows/assign-reviewers.yml. See INSTALL.md. # # AUTH NOTE: Uses a GitHub App (COORDINATOR_APP_ID + COORDINATOR_APP_PRIVATE_KEY) # for all API operations: cloning the private coordinator repo, requesting team # reviewers, and setting PR assignees. GITHUB_TOKEN is not used. # # SECURITY INVARIANTS (pull_request_target): # This workflow runs with access to secrets for ALL PRs including forks. # It is safe ONLY because: # 1. The checkout is the coordinator repo at ref: main — NEVER the PR head/branch # 2. No ${{ }} interpolation of event fields in run: blocks — all routed via env: # 3. The script never executes, sources, or reads files from the PR branch # Violating any of these enables remote code execution with secret access. name: Assign Reviewers on: # zizmor: ignore[dangerous-triggers] reviewed — no PR code checkout, only coordinator repo at ref: main pull_request_target: types: [opened, ready_for_review] # GITHUB_TOKEN is not used — all operations use the GitHub App token. # Declare minimal permissions so the default token has no write access. permissions: {} # Prevent duplicate runs for the same PR (e.g., rapid push + ready_for_review). concurrency: group: assign-reviewers-${{ github.event.pull_request.number }} cancel-in-progress: true # NOTE: For ready_for_review events, the webhook payload may still carry # draft: true due to a GitHub race condition (payload serialized before DB # update). We trust the event type instead — the script rechecks draft status # via a live API call as defense-in-depth. # # No author_association filter — external and fork PRs also get reviewer # assignments. Assigned reviewers are inherently scoped to org team members # by the GitHub Teams API. jobs: assign-reviewers: if: >- github.event.action == 'ready_for_review' || github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - name: Generate app token id: app-token uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 with: app-id: ${{ vars.COORDINATOR_APP_ID }} private-key: ${{ secrets.COORDINATOR_APP_PRIVATE_KEY }} repositories: codeowner-coordinator,zed # SECURITY: checks out the coordinator repo at ref: main, NOT the PR branch. # persist-credentials: false prevents the token from leaking into .git/config. - name: Checkout coordinator repo uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: repository: zed-industries/codeowner-coordinator ref: main path: codeowner-coordinator token: ${{ steps.app-token.outputs.token }} persist-credentials: false - name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.11" - name: Install dependencies run: | pip install --no-deps -q --only-binary ':all:' \ -r /dev/stdin <<< "pyyaml==6.0.3 --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d" - name: Assign reviewers env: GH_TOKEN: ${{ steps.app-token.outputs.token }} PR_URL: ${{ github.event.pull_request.html_url }} TARGET_REPO: ${{ github.repository }} ASSIGN_INTERNAL: ${{ vars.ASSIGN_INTERNAL || 'false' }} ASSIGN_EXTERNAL: ${{ vars.ASSIGN_EXTERNAL || 'true' }} run: | cd codeowner-coordinator python .github/scripts/assign-reviewers.py \ --pr "$PR_URL" \ --apply \ --rules-file team-membership-rules.yml \ --repo "$TARGET_REPO" \ --org zed-industries \ 2>&1 | tee /tmp/assign-reviewers-output.txt - name: Upload output if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: assign-reviewers-output path: /tmp/assign-reviewers-output.txt retention-days: 30