From 9d5bcf0c580bf1111103b29a04a5685098521d5e Mon Sep 17 00:00:00 2001 From: CaliBrain Date: Thu, 22 Oct 2026 02:40:50 -0400 Subject: [PATCH] ci(jules): review fork PRs through a gated pull_request_target workflow (#1528) Fork PRs get no secrets under pull_request and Dependabot runs get only Dependabot secrets, so JULES_API_KEY was empty and every such run failed at config. The same-repo workflow now skips them, and a new pull_request_target workflow reviews fork PRs behind maintainer approval on the jules-forks environment, without checking out PR code. Both use calibrain's fork of jules-pr-reviewer, since upstream refuses pull_request_target. --- .github/workflows/jules-pr-reviewer-forks.yml | 37 +++++++++++++++++++ .github/workflows/jules-pr-reviewer.yml | 9 ++++- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/jules-pr-reviewer-forks.yml diff --git a/.github/workflows/jules-pr-reviewer-forks.yml b/.github/workflows/jules-pr-reviewer-forks.yml new file mode 100644 index 00000000..b4f0b127 --- /dev/null +++ b/.github/workflows/jules-pr-reviewer-forks.yml @@ -0,0 +1,37 @@ +name: Jules PR Review (forks) +# Fork PRs get no secrets under pull_request, so they are reviewed here under +# pull_request_target, which runs this file from main with the repo's secrets. +# That is safe only while nothing here checks out or runs the PR's code: the +# action reads the diff through the API. Upstream jules-pr-reviewer refuses +# this event, hence the calibrain fork. +# Each run waits for a maintainer to approve it on the jules-forks environment +# (Settings → Environments → required reviewers), so every fork push is looked +# at before Jules reads it, and strangers can't spend the Jules session quota. +on: + pull_request_target: + types: [opened, synchronize, reopened, ready_for_review] +permissions: {} + +concurrency: + # Not jules-review-: the same-repo workflow's skipped runs would cancel this one + group: jules-review-fork-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + review: + # Drafts are skipped here rather than by the action, so they don't ask for approval + if: github.event.pull_request.head.repo.full_name != github.repository && !github.event.pull_request.draft + runs-on: ubuntu-latest + environment: + name: jules-forks + deployment: false + permissions: + pull-requests: write + contents: read + checks: write + steps: + - uses: calibrain/jules-pr-reviewer@9061503ac181f0da714bcfa9f16939dd6148f688 # v0.0.2 + with: + jules_api_key: ${{ secrets.JULES_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + skip_forks: false diff --git a/.github/workflows/jules-pr-reviewer.yml b/.github/workflows/jules-pr-reviewer.yml index 1305f411..c11be5e1 100644 --- a/.github/workflows/jules-pr-reviewer.yml +++ b/.github/workflows/jules-pr-reviewer.yml @@ -1,7 +1,12 @@ name: Jules PR Review +# Reviews PRs whose branch lives in this repo. Fork PRs get no secrets under +# pull_request and Dependabot runs get only Dependabot secrets, so +# JULES_API_KEY would be empty for both; jules-pr-reviewer-forks.yml reviews +# fork PRs instead. on: pull_request: types: [opened, synchronize, reopened, ready_for_review] +permissions: {} concurrency: group: jules-review-${{ github.event.pull_request.number }} @@ -9,14 +14,14 @@ concurrency: jobs: review: + if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' runs-on: ubuntu-latest permissions: pull-requests: write contents: read checks: write steps: - - uses: thalesraymond/jules-pr-reviewer@v1 + - uses: calibrain/jules-pr-reviewer@9061503ac181f0da714bcfa9f16939dd6148f688 # v0.0.2 with: jules_api_key: ${{ secrets.JULES_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} - skip_forks: false