Private AI Code Review in GitHub Actions

The important design choice is not the model. It is the boundary between untrusted pull-request code and your API secrets.

Start with the threat model

A reviewer needs the pull-request diff and a model API key. A fork author controls the proposed files and may try to alter a workflow, build script, or dependency hook. If a secret-bearing job checks out and executes that code, the key can be exposed. Skipping forks avoids the immediate problem but also prevents review where it is often most useful.

Keep the review job API-only

  1. Trigger a protected base-branch workflow with pull_request_target.
  2. Grant only contents: read and pull-requests: write.
  3. Fetch the diff through the GitHub API.
  4. Never add checkout, package installation from the PR, build, test, or script execution to this secret-bearing job.
  5. Pin third-party Actions to a reviewed commit and update the pin deliberately.
name: Private AI PR Review
on:
  pull_request_target:
    types: [opened, synchronize, reopened]

permissions: {}

concurrency:
  group: private-ai-review-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  review:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      contents: read
      pull-requests: write
    steps:
      # Intentionally no checkout: do not execute pull-request code here.
      - name: Review pull request
        uses: codylabs/cody-code-reviewer@5ec18da9b75541f5ce2b33edcfb8a7c666551b31 # v1.7.0
        with:
          openai_api_key: ${{ secrets.OPENAI_API_KEY }}
          model: gpt-5.6-luna

Fork pull requests are intentionally included: the workflow definition and Action pin come from the protected base branch, while the Action reads the proposed diff through the API. Do not add a fork skip as a substitute for keeping the job API-only.

v1.7.0 posts and then updates one pull-request timeline comment through GitHub's issue-comments endpoint, rather than adding a new comment on every run. GitHub's permission documentation says this endpoint accepts either Pull requests: write or Issues: write; the example deliberately grants only the former.

The pinned SHA is the commit referenced by the published v1.7.0 release. Inspect its exact Action manifest: it installs hash-locked dependencies from the Action directory and invokes its own Python entry points, with no checkout step or local uses: ./ execution.

A pinned Action is third-party code with access to the provider secret and a write-capable pull-request token. Review the exact pinned source before adoption and before changing the pin. Sensitive organizations should also require approved Actions or maintain an audited internal fork.

Protect who can change the base workflow. Enable default-branch protection and require owner or CODEOWNERS review for the workflow file and Action/dependency pins. A collaborator who can modify the base-branch workflow can change what executes with its secrets.

Run tests in a separate pull_request workflow without provider secrets. The review workflow can inspect code; it should not execute it. Prefer GitHub-hosted runners; a self-hosted runner for this job must be ephemeral, single-purpose, isolated from sensitive workloads, and cleaned after every job.

Use Anthropic instead

The live v1.7.0 Action supports both providers. Replace the OpenAI inputs in the example with the following:

        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          model: claude-opus-4-8

Model IDs beginning with claude route to Anthropic; other model IDs route to OpenAI. The examples were checked on 3 August 2026 against the official model pages for gpt-5.6-luna and claude-opus-4-8. Model availability is controlled by your provider account and can change. A missing matching key, authentication failure, unsupported model ID, rate limit, or provider error fails the workflow with a non-secret error message; check the provider's current model list before copying an example.

Control noise and spend

The Action caps pull-request diff data at 200,000 characters and marks truncated input in the prompt. The concurrency block above cancels an older in-flight run when another commit arrives. The live v1.7.0 release updates its existing pull-request comment by default; set the update_existing_comment input to false to post a new comment on every run instead. Choose a model whose price and depth fit the repository.

Where the code actually goes

This setup keeps the review job API-only. For the full data-flow answer, see does AI code review send your code to a third party.

Use the reviewed workflow pattern

Private AI PR Reviewer v1.7.0 implements this API-only flow for Anthropic and OpenAI.

See the free GitHub Action