Skip to content

CICD/claude-action-review#373

Closed
zander-raycraft wants to merge 15 commits into
abhigyanpatwari:mainfrom
zander-raycraft:CICD/claude-actions-review
Closed

CICD/claude-action-review#373
zander-raycraft wants to merge 15 commits into
abhigyanpatwari:mainfrom
zander-raycraft:CICD/claude-actions-review

Conversation

@zander-raycraft

Copy link
Copy Markdown
Collaborator

Fork PR Claude Workflow Fix

The Issue

When a contributor opens a PR from a fork (e.g. zander-raycraft/GitNexus -> abhigyanpatwari/GitNexus) and someone comments @claude or a review is triggered, the workflow fails intermittently with HTTP 403 errors.

The root cause: anthropics/claude-code-action@v1 internally runs git fetch origin <branch-name> to check out the PR code. But fork branches don't exist on the base repo's origin, they only exist on the fork. So when the branch name doesn't happen to match an existing branch on origin, the fetch fails.

This made it look "unreliable", same-repo PRs always worked, fork PRs only worked when branch names coincidentally matched.

How It Was Found

  • Fork PRs from outside places likerandom-guy/GitNexus to abhigyanpatwari/GitNexus would sometimes trigger Claude successfully and sometimes 403
  • Traced it to the git fetch origin <branch> call inside the official action, fork branch names don't exist on origin
  • Similar issue found in Anthropic's repo, confirmed by the open issue: fix: handle fork PRs by fetching via refs/pull/N/head fix: handle fork PRs by fetching via refs/pull/N/head anthropics/claude-code-action#963

How To Verify

  1. Push these workflow changes to main on abhigyanpatwari/GitNexus (workflow changes only take effect from the default branch for issue_comment events)
  2. Open a PR from any fork to abhigyanpatwari/GitNexus
  3. Comment @claude on the PR, should respond every time, not just sometimes
  4. Open a fork PR and confirm the automated code review triggers

The Fix: What Repo Was Used

Used luccabb/claude-code-action, a fork of the official anthropics/claude-code-action that adds fork PR support.

The fork detects fork PRs via the GitHub GraphQL field isCrossRepository and fetches pull/{N}/head instead of the branch name. That ref always exists for any PR regardless of where it came from.

Files Modified

.github/workflows/claude.yml

  • Swapped uses: anthropics/claude-code-action@v1 to uses: luccabb/claude-code-action@7f39722b8a782471258f32e1d5a9a531b2b68056
  • Bumped pull-requests: read to pull-requests: write (so Claude can post comments)
  • Bumped issues: read to issues: write (same reason)

.github/workflows/claude-code-review.yml

  • Swapped uses: anthropics/claude-code-action@v1 to uses: luccabb/claude-code-action@7f39722b8a782471258f32e1d5a9a531b2b68056
  • Replaced pull_request trigger with pull_request_target, this runs in the base repo context so secrets are available for fork PRs. Using only pull_request_target (not both) avoids double-firing on same-repo PRs.
  • Added security guard if: condition, only runs for OWNER, MEMBER, or COLLABORATOR author associations, blocks untrusted fork authors from running with repo secrets
  • Bumped pull-requests and issues permissions to write

gitnexus/test/unit/workflows.test.ts (new file)

37 regression tests covering:

  • YAML structure validity
  • SHA pinning (no @v1 or @main refs)
  • Correct permissions (write where needed, read where not)
  • Trigger events are correct
  • Security guard exists and checks author_association
  • Step structure (2 steps, no legacy git/refs API calls)

Official Documentation Backing This Approach

GitHub Docs: pull_request_target

"This event runs in the context of the base of the pull request, rather than in the context of the merge commit as the pull_request event does. This prevents execution of unsafe code from the head of the pull request that could alter your repository or steal any secrets you use in your workflow."
https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target

GitHub Docs: Fork PR permissions

"With the exception of GITHUB_TOKEN, secrets are not passed to the runner when a workflow is triggered from a forked repository."
https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#using-secrets-in-a-workflow

GitHub Docs: GITHUB_TOKEN permissions for forks

"The token for a workflow that is triggered by a pull_request event from a forked repository is a read-only token. The permissions can't be changed."
https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token

GitHub Docs: Pull request refs

"Each pull request has a ref at refs/pull/{N}/head, which points to the last commit for that pull request."
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

Flow Diagram

BEFORE (broken)
═══════════════

 ┌──────────────────────┐     ┌──────────────────────────┐
 │  random-guy/          │     │  abhigyanpatwari/         │
 │  GitNexus             │     │  GitNexus (origin)        │
 │                       │     │                           │
 │  branch: my-feature ──┼──PR─▶  receives PR #42          │
 │                       │     │  origin has NO branch      │
 └──────────────────────┘     │  called "my-feature"       │
                               └─────────┬─────────────────┘
                                         │
                          ┌──────────────▼──────────────┐
                          │  GitHub Actions Runner       │
                          │                              │
                          │  1. actions/checkout          │
                          │     (clones base repo)        │
                          │                              │
                          │  2. anthropics/claude-code-   │
                          │     action@v1                 │
                          │                              │
                          │  3. git fetch origin          │
                          │     "my-feature"              │
                          │         │                     │
                          │         ▼                     │
                          │  ┌─────────────────┐         │
                          │  │ BRANCH NOT FOUND │         │
                          │  │   on origin!     │         │
                          │  │                  │         │
                          │  │  ══> HTTP 403    │         │
                          │  └─────────────────┘         │
                          └──────────────────────────────┘


AFTER (fixed)
═════════════

 ┌──────────────────────┐     ┌──────────────────────────┐
 │  random-guy/          │     │  abhigyanpatwari/         │
 │  GitNexus             │     │  GitNexus (origin)        │
 │                       │     │                           │
 │  branch: my-feature ──┼──PR─▶  receives PR #42          │
 │                       │     │                           │
 └──────────────────────┘     │  refs/pull/42/head ◄──────┤
                               │  (always exists for       │
                               │   every PR, fork or not)  │
                               └─────────┬─────────────────┘
                                         │
                          pull_request_target fires
                          (base repo context, secrets available)
                                         │
                          ┌──────────────▼──────────────┐
                          │        SECURITY GUARD        │
                          │                              │
                          │  author_association check:    │
                          │  ┌────────┐  ┌───────────┐  │
                          │  │ OWNER  │  │  MEMBER   │  │
                          │  └───┬────┘  └─────┬─────┘  │
                          │      │      ┌──────┘        │
                          │      ▼      ▼               │
                          │  ┌──────────────┐           │
                          │  │ COLLABORATOR │           │
                          │  └──────┬───────┘           │
                          │         │                    │
                          │    trusted? ─── NO ──▶ SKIP │
                          │         │                    │
                          │        YES                   │
                          └─────────┬────────────────────┘
                                    │
                     ┌──────────────▼──────────────────┐
                     │  GitHub Actions Runner           │
                     │                                  │
                     │  1. actions/checkout              │
                     │     (shallow clone base repo)     │
                     │                                  │
                     │  2. luccabb/claude-code-action    │
                     │     @7f39722... (SHA-pinned)      │
                     │                                  │
                     │  3. GraphQL: isCrossRepository?   │
                     │              │                    │
                     │       ┌──────┴──────┐            │
                     │       │             │            │
                     │      YES           NO            │
                     │   (fork PR)    (same repo)       │
                     │       │             │            │
                     │       ▼             ▼            │
                     │  ┌──────────┐ ┌────────────┐    │
                     │  │ fetch    │ │ fetch      │    │
                     │  │ pull/42/ │ │ origin/    │    │
                     │  │ head     │ │ my-feature │    │
                     │  └────┬─────┘ └─────┬──────┘    │
                     │       │             │            │
                     │       └──────┬──────┘            │
                     │              ▼                    │
                     │  ┌────────────────────┐          │
                     │  │ git checkout       │          │
                     │  │ <branch>           │          │
                     │  └────────┬───────────┘          │
                     │           ▼                      │
                     │  ┌────────────────────┐          │
                     │  │ Claude runs        │          │
                     │  │ against PR code    │          │
                     │  └────────┬───────────┘          │
                     │           ▼                      │
                     │  ┌────────────────────┐          │
                     │  │ Posts review on PR  │          │
                     │  └────────────────────┘          │
                     └──────────────────────────────────┘

@vercel

vercel Bot commented Mar 18, 2026

Copy link
Copy Markdown

@zander-raycraft is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@magyargergo

Copy link
Copy Markdown
Collaborator

@claude let's see if this works now

@zander-raycraft zander-raycraft marked this pull request as ready for review March 18, 2026 21:32
@github-actions

github-actions Bot commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Unit Tests success 3 platforms
✅ Integration success 3 OS x 4 groups = 12 jobs

Test Results

Suite Tests Passed Failed Skipped Duration
Unit 1629 1628 0 1 8s
Integration 1323 1304 0 19 54s
Total 2952 2932 0 20 62s

✅ All 2932 tests passed

20 test(s) skipped — expand for details

Integration:

  • Python match/case as-pattern type binding > resolves u.save() to User#save via match/case as-pattern binding
  • Python match/case as-pattern type binding > does NOT resolve u.save() to Repo#save (negative disambiguation)
  • Swift constructor-inferred type resolution > detects User and Repo classes, both with save methods
  • Swift constructor-inferred type resolution > resolves user.save() to Models/User.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > resolves repo.save() to Models/Repo.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > emits exactly 2 save() CALLS edges (one per receiver type)
  • Swift self resolution > detects User and Repo classes, each with a save function
  • Swift self resolution > resolves self.save() inside User.process to User.save, not Repo.save
  • Swift parent resolution > detects BaseModel and User classes plus Serializable protocol
  • Swift parent resolution > emits EXTENDS edge: User → BaseModel
  • Swift parent resolution > emits IMPLEMENTS edge: User → Serializable (protocol conformance)
  • Swift cross-file User.init() inference > resolves user.save() via User.init(name:) inference
  • Swift cross-file User.init() inference > resolves user.greet() via User.init(name:) inference
  • Swift return type inference > detects User class and getUser function
  • Swift return type inference > detects save function on User (Swift class methods are Function nodes)
  • Swift return type inference > resolves user.save() to User#save via return type of getUser() -> User
  • Swift return-type inference via function return type > resolves user.save() to User#save via return type of getUser()
  • Swift return-type inference via function return type > user.save() does NOT resolve to Repo#save
  • Swift return-type inference via function return type > resolves repo.save() to Repo#save via return type of getRepo()

Unit:

  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature

Code Coverage

Combined (Unit + Integration)

Metric Coverage Covered Base Delta Status
Statements 61.6% 7142/11594 46.69% 📈 +14.9 🟢 ████████████░░░░░░░░
Branches 55.45% 4942/8912 41.23% 📈 +14.2 🟢 ███████████░░░░░░░░░
Functions 60.78% 606/997 46.33% 📈 +14.5 🟢 ████████████░░░░░░░░
Lines 63.58% 6383/10038 48.32% 📈 +15.3 🟢 ████████████░░░░░░░░
Coverage breakdown by test suite

Unit Tests

Metric Coverage Covered Base Delta Status
Statements 46.69% 5414/11594 46.69% = 0.0 🟢 █████████░░░░░░░░░░░
Branches 41.23% 3675/8912 41.23% = 0.0 🟢 ████████░░░░░░░░░░░░
Functions 46.33% 462/997 46.33% = 0.0 🟢 █████████░░░░░░░░░░░
Lines 48.32% 4851/10038 48.32% = 0.0 🟢 █████████░░░░░░░░░░░

Integration Tests

Metric Coverage Covered Base Delta Status
Statements 47.1% 5461/11594 46.69% 📈 +0.4 🟢 █████████░░░░░░░░░░░
Branches 44.19% 3939/8912 41.23% 📈 +3.0 🟢 ████████░░░░░░░░░░░░
Functions 44.43% 443/997 46.33% 📉 -1.9 🔴 ████████░░░░░░░░░░░░
Lines 48.26% 4845/10038 48.32% 📉 -0.1 🔴 █████████░░░░░░░░░░░

📋 View full run · Generated by CI

@magyargergo

Copy link
Copy Markdown
Collaborator

@zander-raycraft

Copy link
Copy Markdown
Collaborator Author

@claude can you look at this

@zander-raycraft

Copy link
Copy Markdown
Collaborator Author

Thanks @magyargergo looking at this now, needed actual PR requests to test

@zander-raycraft

Copy link
Copy Markdown
Collaborator Author

closing for a second while testing

@zander-raycraft

Copy link
Copy Markdown
Collaborator Author

@claude look at this

@zander-raycraft zander-raycraft deleted the CICD/claude-actions-review branch March 18, 2026 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants