Lesson 29 of 32
Claude Code in CI
Running Claude inside GitHub Actions — responding to mentions, turning an issue into a pull request, automated review, and being sensible about secrets and permissions.
The last two lessons pushed Claude further from your keyboard: first into a script, then into a second terminal. This one pushes it off your machine entirely.
Claude Code can run inside GitHub Actions, which means it can respond to a comment on an issue, open a pull request while you are asleep, or review every change that lands. The mechanics are a workflow file and a secret. The judgement is about what a thing triggered by a public comment box should be allowed to do.
What this actually is
The integration is a GitHub Action, anthropics/claude-code-action, that runs
Claude Code on a GitHub runner inside your repository. Underneath it is the
same agent you have been using; the difference is where it runs and what starts
it.
Several products share the Claude Code name and it is worth separating them now. This lesson is about the workflow integration you configure yourself. There is also a managed Code Review service that reviews pull requests without you writing any workflow at all, which we come to later, and Claude Code on the web, which is a session you drive from a browser. Different things, similar names.
Setting it up
There are two paths, and the difference is only how much you type.
The quick path is /install-github-app, run from a Claude Code session in the
repository. It installs the Claude GitHub App, sets up the authentication
secret, and pushes a branch with the workflow files ready for you to open as a
pull request. It expects the GitHub CLI, gh, to be installed and signed in.
The manual path is the same three steps done by hand: install the app, add a secret, add a workflow file. Do it this way if you want to read every line before it lands, which for something that will run with write access to your repository is a defensible instinct.
- Step 1
Install the app
The Claude GitHub App needs read and write on contents, issues and pull requests to do this job. You need admin access on the repository to install it.
- Step 2
Add the secret
Either an API key from the console, or a long-lived token generated with claude setup-token if you would rather the runs draw on your subscription.
- Step 3
Add the workflow
A file in .github/workflows that runs the action on the events you choose and passes it the secret.
The secret is named ANTHROPIC_API_KEY for an API key or
CLAUDE_CODE_OAUTH_TOKEN for a subscription token, and the workflow passes it
to the matching input — anthropic_api_key or claude_code_oauth_token.
For an organisation, install the app once at the organisation level and store
the secret as an organisation secret rather than copying it into every
repository. Prefer an API key over a subscription token there, since a
subscription token belongs to whichever person ran claude setup-token and
leaves with them.
Responding to mentions
Here is a workflow that does the most common thing: watch for someone saying
@claude in an issue or pull request comment, and respond.
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}Most of that is boilerplate. Four lines are not.
The if condition stops a runner starting for every comment on the
repository, which is a cost saving rather than a security control — the action
checks the trigger phrase itself before doing anything. actions: read is what
lets Claude look at CI results on a pull request, which is most of what makes
it useful there. id-token: write is needed for the action's default
authentication. And actions/checkout is what puts a copy of the repository on
the runner for Claude to work in; without it, there is nothing to read.
The action decides how to behave from your configuration. Give it no prompt
input, as above, and it waits for the trigger phrase — this is called
interactive mode, though nobody is sitting there. Give it a prompt and it
runs on whatever event fired the workflow, mention or not, which is automation
mode. In automation mode the results go to the workflow run log rather than to
a comment.
From an issue to a pull request
WhyThe issue itself was written by a colleague and says what is wrong but not where. Everything below happens without anyone opening an editor.
You typed: @claude the CSV export drops the last row when the file ends without a trailing newline. Find it, fix it, add a regression test, and open a PR.
Claude replied: Working on it — I'll update this comment as I go.
Claude ran: Search: csv export
Claude ran: Read src/export/csv.ts
Claude ran: Edit src/export/csv.test.ts
Claude ran: Run: npm test csv
Output: FAIL csv.test.ts expected 4 rows, received 3WhyIt wrote the failing test before the fix. Nobody asked it to do them in that order, but it is the order that produces evidence: the test is now known to catch the bug, rather than merely known to pass.
Claude ran: Edit src/export/csv.ts
Claude ran: Run: npm test csv
Output: PASS csv.test.ts (7 tests)Claude replied: Opened #483. The reader stopped at the final newline rather than at end of input, so a file without one lost its last record. Test added covering both endings.
WhyWhat arrives is a pull request, not a merge. That is the whole safety model here — the same review you would give a colleague's branch is still the gate, and it is the reason giving the app write access to contents is reasonable.
Notice what you did not have to do: find the file, reproduce the bug locally, or write the branch. Notice also what you still have to do, which is read the diff before merging it.
Automated review
There are three ways to have changes reviewed, in increasing order of how much you set up yourself.
Locally, before you push
The /code-review command reviews your branch's changes against its upstream, plus anything uncommitted, in a fresh subagent context. Nothing to install and no GitHub App involved. This is the one to reach for first.
The managed Code Review service
Once an owner enables it, pull requests are reviewed on Anthropic infrastructure and findings arrive as inline comments tagged Important, Nit or pre-existing. You choose per repository whether it runs once per pull request, on every push, or only when someone comments. A research preview, on Team and Enterprise plans.
Your own review workflow
A workflow you write that runs the action with a review prompt on each pull request. More work, and the findings land in the run log rather than on the diff, but you control the prompt, the model and the triggers entirely.
Two details about the managed service are worth carrying even if you never use it, because they are good design and worth copying.
The first is that its check run always completes neutrally, so a review never blocks a merge through branch protection. An automated reviewer that can hold your pull requests hostage becomes something people route around within a fortnight.
The second is REVIEW.md, a file at your repository root holding review-only
instructions. It is where you say what "important" means in this repository,
cap how many minor comments a single review may post, and list the paths where
Claude should say nothing at all — generated code, lockfiles, anything your
linter already enforces. Reviews that comment on everything get ignored
entirely, which is worse than no review. CLAUDE.md still applies as general
project context; REVIEW.md is for the rules that only make sense during a
review.
Being sensible about secrets and permissions
Four things, none of them exotic.
Never commit a key. Store it as a secret and reference it. A key committed to a repository is readable by everyone who can read the repository, including everyone who forks it, and deleting the file afterwards does not remove it from history.
Grant the workflow the minimum it needs. The permissions block in the workflow is per-job. A review job that only reads does not need write access to repository contents.
Understand the trigger checks. On issue and pull request events, the person who triggered the run must have write access to the repository, and bot actors are rejected unless you list them. This is what stops a comment box that anyone on the internet can type into from being a way to run code in your repository. There are inputs to widen both checks; think carefully before you use them.
Expect fork pull requests not to work on public repositories. GitHub withholds secrets from runs triggered by a pull request from a fork, which is a protection rather than a bug. Without the secret the run cannot authenticate.
Two smaller ones. Cap the work each run may do — --max-turns in the action's
claude_args bounds the number of agentic turns, and a workflow-level timeout
bounds the wall clock — because a task that turns out to be much harder than
expected will otherwise spend both tokens and runner minutes discovering that.
And if your own CI does not run on the commits Claude pushes, check whether you
passed it the default GITHUB_TOKEN: GitHub deliberately does not trigger
workflows on commits made with that token, to prevent loops.
What to take away
The GitHub integration runs the same agent on a runner instead of your machine, started either by a mention or by an event. Setting it up is an app, a secret and a workflow file, and the quick path does all three for you. With no prompt input it waits to be mentioned; with a prompt input it runs on the event directly. What comes back from a mention is a pull request, which is exactly the right output, because your existing review remains the gate. For reviewing changes, start with the local command before reaching for anything hosted, and if you do automate review, make it non-blocking and tell it what not to comment on. The security posture is unremarkable and therefore easy: secrets stay secrets, jobs get the permissions they need and no more, and the write-access and bot checks on triggers are load-bearing rather than decorative.
Next: what all of this costs, and the handful of habits that reduce it — almost all of which you already know for other reasons.
Check yourself
5 questions · pass 4/5 to unlock Managing Cost
1.In a workflow that provides no prompt input, when does the Claude Code GitHub Action do anything?
2.Someone with no write access to the repository comments
@claude fix this. What happens by default?3.Which is the correct way to give the workflow its credential?
4.Why does a pull request from a fork often fail to get a Claude run on a public repository?
5.What is the practical effect of setting
--max-turnsin the action's arguments?
5 left to answer