CI/CD Integration: Claude Code in Your Pipeline
Integrate Claude Code into GitHub Actions and GitLab CI/CD. Automate PR reviews, issue implementation, security audits, and documentation updates in your development pipeline.
Updated October 21, 2026: This guide previously used invented CLI commands such as
claude code reviewand/github-action-setup. Every workflow here is now built on the real integrations: the officialanthropics/claude-code-action@v1GitHub Action, the GitLab CI/CD job from Anthropic’s docs, and headlessclaude -pruns. It also covers the v2.1.302 change that starts-pruns in Manual mode,--permission-prompts none, and the current models.
Claude Code isn’t just a local development tool—it can be integrated into your CI/CD pipeline for automated PR reviews, issue implementation, security audits, and more.
This guide covers integration patterns for GitHub Actions and GitLab CI/CD.
Why CI/CD Integration?
Traditional CI/CD:
└── Run tests
└── Run linting
└── Deploy (if passing)
With Claude Code:
└── Run tests
└── Run linting
└── AI-powered code review
└── Automated security audit
└── Documentation sync check
└── Issue auto-triage
└── Implement features from issues
└── Deploy (if all pass)
Which integration to use
| You want | Use |
|---|---|
@claude mentions and issue-to-PR work | Claude Code GitHub Action (anthropics/claude-code-action@v1) |
| Automatic review on every PR, no workflow file | Code Review, GA since October 15, 2026 for Team and Enterprise |
| Your own scripted step in any pipeline | Headless claude -p |
| GitLab pipelines | GitLab CI/CD integration (beta, maintained by GitLab) |
GitHub Actions Integration
Quick Setup
Run this inside Claude Code, in the repository you want to connect:
/install-github-app
Claude Code installs the Claude GitHub App, saves your credential as a repository secret (ANTHROPIC_API_KEY for an API key, or CLAUDE_CODE_OAUTH_TOKEN for a subscription token from claude setup-token), pushes a branch with the workflow files you select, and opens a pull request for you to merge. It works only with github.com repositories and needs admin access plus the GitHub CLI authenticated with gh auth login.
Prefer to do it by hand? Install the Claude GitHub App, add the secret yourself, and copy examples/claude.yml into .github/workflows/.
PR Auto-Review Workflow
The action runs the bundled code-review plugin and posts findings as inline comments:
# .github/workflows/claude-code-review.yml
name: Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment"'
Two lines decide where the review goes:
--commentposts the review on the pull request: an inline comment per issue found, or one summary comment when there are none. Without it, findings stay in the workflow run log.claude_argsmust name the inline-comment tool, because the action only starts that MCP server when--allowedToolslists it.
Claude skips draft and closed pull requests, ones it judges too trivial to review, and ones it has already commented on. On public repositories, GitHub withholds secrets from fork pull requests, so reviews run only on branches in the same repository.
For a deeper, multi-agent review, run claude ultrareview <pr> in a job of its own, or use the hosted Code Review product, which needs no workflow file at all.
Issue Auto-Triage Workflow
Supplying a prompt puts the action in automation mode, where it runs without waiting for an @claude mention:
# .github/workflows/claude-issue-triage.yml
name: Claude Issue Triage
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
id-token: write
steps:
- uses: actions/checkout@v6
- uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Read issue #${{ github.event.issue.number }} in ${{ github.repository }}.
Decide which existing labels apply, estimate complexity as low, medium, or high,
and add the labels with a short comment explaining the triage.
claude_args: |
--model claude-sonnet-5-5
--max-turns 10
--allowedTools "Bash(gh issue view *),Bash(gh issue edit *),Bash(gh issue comment *),Bash(gh label list *)"
In automation mode, Claude has no shell or GitHub API access until you grant it: list the tools in --allowedTools, or set permissions.allow rules through the settings input. Here the allowlist is scoped gh commands, which GitHub-hosted runners provide; the action’s own documented GitHub MCP tools, such as mcp__github__list_issues and mcp__github__list_commits, work the same way when you prefer them for read-only steps.
Issue Auto-Implementation Workflow
# .github/workflows/claude-implement-issue.yml
name: Claude Implement Issue
on:
issues:
types: [labeled]
jobs:
implement:
if: github.event.label.name == 'claude-implement'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- run: npm ci
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Implement issue #${{ github.event.issue.number }} on a new branch,
run the test suite, and open a pull request that closes the issue.
claude_args: |
--model claude-opus-5-5
--max-turns 40
--allowedTools "Bash,Read,Edit"
The action authenticates as the Claude GitHub App by default, so the commits it pushes do trigger your other workflows. If you pass github_token: ${{ secrets.GITHUB_TOKEN }}, GitHub suppresses those triggers—remove it or use a custom app token.
Documentation Sync Check
# .github/workflows/claude-doc-check.yml
name: Documentation Sync Check
on:
pull_request:
paths:
- 'src/api/**'
- 'src/lib/**'
jobs:
doc-check:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Compare the API changes in this pull request with docs/ and README.md.
If documentation is missing or now wrong, post one PR comment listing
exactly what to update. If the docs are in sync, say so and stop.
claude_args: |
--max-turns 15
--allowedTools "Bash(git diff *),Read,Glob,Grep,Bash(gh pr comment *)"
GitLab CI/CD Integration
Claude Code for GitLab CI/CD is in beta and maintained by GitLab. There is no slash command for setup: you add a masked CI/CD variable and one job.
Quick Setup
- Add a masked CI/CD variable under Settings → CI/CD → Variables:
ANTHROPIC_API_KEY. - Add a Claude job to
.gitlab-ci.yml.
GitLab CI Configuration
# .gitlab-ci.yml
stages:
- ai
claude:
stage: ai
image: node:24-alpine3.21
rules:
- if: '$CI_PIPELINE_SOURCE == "web"'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
variables:
GIT_STRATEGY: fetch
before_script:
- apk update
- apk add --no-cache git curl bash
- curl -fsSL https://claude.ai/install.sh | bash
# The installer places claude in ~/.local/bin, which isn't on PATH in this image
- export PATH="$HOME/.local/bin:$PATH"
script:
# Optional: start a GitLab MCP server if your setup provides one
- /bin/gitlab-mcp-server || true
# AI_FLOW_* variables arrive from web/API triggers that carry context payloads
- echo "$AI_FLOW_INPUT for $AI_FLOW_CONTEXT on $AI_FLOW_EVENT"
- >
claude
-p "${AI_FLOW_INPUT:-'Review this MR and implement the requested changes'}"
--permission-mode acceptEdits
--allowedTools "Bash Read Edit Write mcp__gitlab"
--debug
A second job can run a review that posts nothing but fails the pipeline on findings:
claude-review:
stage: ai
image: node:24-alpine3.21
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
before_script:
- apk add --no-cache git curl bash jq
- curl -fsSL https://claude.ai/install.sh | bash
- export PATH="$HOME/.local/bin:$PATH"
script:
- >
claude -p "/code-review high"
--permission-mode dontAsk
--output-format json > review.json
- jq -r '.result' review.json
artifacts:
when: always
paths:
- review.json
GitLab Mention Handling
GitLab has no built-in @claude trigger. To answer mentions, add a project webhook for Comments (notes) to a listener you run, and have that listener call GitLab’s pipeline trigger API with AI_FLOW_INPUT and AI_FLOW_CONTEXT when a comment contains @claude. The job above already reads those variables.
API Provider Options
Anthropic API (Direct)
# GitHub Action input
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# Or a Claude subscription token from `claude setup-token`
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
To avoid storing a long-lived secret, authenticate through workload identity federation with the anthropic_federation_rule_id and anthropic_organization_id inputs, and grant the job id-token: write.
Amazon Bedrock
# GitHub Action: route inference through your AWS account
with:
use_bedrock: "true"
# Headless runs set the environment instead
env:
CLAUDE_CODE_USE_BEDROCK: "1"
AWS_REGION: us-east-1
Google Cloud’s Agent Platform
# GitHub Action
with:
use_vertex: "true"
# Headless runs
env:
CLAUDE_CODE_USE_VERTEX: "1"
CLOUD_ML_REGION: global
ANTHROPIC_VERTEX_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
Microsoft Foundry uses use_foundry: "true" and CLAUDE_CODE_USE_FOUNDRY=1. With all three providers you authenticate through OIDC identity federation, so no static cloud credentials live in your repository. See cloud providers for the setup.
Workflow Patterns
Pattern 1: Review Depth by Label
/code-review takes an effort level, so one workflow can review harder when a PR is labeled:
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
id-token: write
steps:
- uses: actions/checkout@v6
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: >
/code-review:code-review
${{ contains(github.event.pull_request.labels.*.name, 'security') && 'xhigh' || 'medium' }}
--comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment"'
Pattern 2: Security Review on Sensitive Paths
jobs:
security:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check for sensitive paths
id: paths
run: |
if git diff --name-only origin/${{ github.base_ref }}... | grep -E 'auth/|payment/|admin/'; then
echo "sensitive=true" >> $GITHUB_OUTPUT
fi
- if: steps.paths.outputs.sensitive == 'true'
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: "/security-review"
claude_args: '--model claude-opus-5-5 --effort high'
Pattern 3: Issue to PR Pipeline
name: Issue Implementation Pipeline
on:
issues:
types: [labeled]
jobs:
triage:
if: github.event.label.name == 'auto-implement'
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
id-token: write
steps:
- uses: actions/checkout@v6
- uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Read issue #${{ github.event.issue.number }} and label it
complexity:low, complexity:medium, or complexity:high.
claude_args: '--allowedTools "Bash(gh issue view *),Bash(gh issue edit *)"'
implement:
needs: triage
if: "!contains(github.event.issue.labels.*.name, 'complexity:high')"
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v6
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: "Implement issue #${{ github.event.issue.number }} and open a pull request."
claude_args: '--max-turns 40 --allowedTools "Bash,Read,Edit"'
High-complexity issues stay with a human. Keep the model choice in claude_args rather than guessing it per job.
Security Considerations
Secret Management
# Never commit keys; always reference repository or organization secrets
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# Configure in: Settings > Secrets and variables > Actions
An OAuth token from claude setup-token is tied to one person’s subscription, so for shared organization secrets use a Claude Console API key, or workload identity federation with no stored secret at all.
Permission Scoping
# Minimum required permissions; id-token: write is needed for the action's
# default GitHub App authentication
permissions:
contents: read
pull-requests: write
issues: write
id-token: write
# Don't use: permissions: write-all
The action also checks the triggering actor: on issue and PR events the user must have write access (or be listed in allowed_non_write_users alongside your own github_token), and bot actors are rejected unless listed in allowed_bots. That check also covers scheduled runs, which GitHub attributes to a repository user.
Keep Headless Runs Locked Down
Since v2.1.302, claude -p and Agent SDK runs with no permission mode set start in Manual mode, so anything needing approval is denied rather than silently allowed. Make that explicit in CI:
# Exact allowlist, nothing else can run
claude -p "Run the test suite and summarize failures" \
--permission-mode dontAsk \
--allowedTools "Bash(npm test *),Read"
# Classifier-reviewed autonomy with nobody to answer prompts
claude -p "Update the dependency pins and run the tests" \
--permission-mode auto \
--permission-prompts none
--permission-prompts none (v2.1.259+) denies anything that would prompt instead of waiting for a host that isn’t there. Add --bare to skip discovery of hooks, skills, plugins, MCP servers, and CLAUDE.md so a teammate’s local configuration can’t change what runs in CI. For evaluation harnesses on shared machines, --restricted removes code-running tools and WebFetch and ignores user, project, and local settings.
Audit Logging
steps:
- name: Review with a saved transcript
run: |
claude -p "/code-review high" \
--permission-mode dontAsk \
--output-format json > claude-review.json
- name: Upload the run record
if: always()
uses: actions/upload-artifact@v4
with:
name: claude-review
path: claude-review.json
For organization-wide visibility, export metrics and events with OpenTelemetry (CLAUDE_CODE_ENABLE_TELEMETRY=1) instead of parsing logs per job.
Cost Management
Model Selection by Task
# Match model to task, in claude_args
claude_args: >
--model ${{ contains(github.event.pull_request.labels.*.name, 'security')
&& 'claude-opus-5-5' || 'claude-sonnet-5-5' }}
Opus 5.5 ($4/$20 per million input/output tokens) is the default model; Sonnet 5.5 ($2/$10) suits routine review, and Haiku 5.5 ($0.10/$0.50 for prompts up to 100K tokens) suits mechanical checks. Opus 5.5, Sonnet 5.5, and Haiku 5.5 run at medium effort by default—change it with --effort rather than reaching for a bigger model first.
Budgets and Turn Limits
steps:
- name: Review with a spend cap
run: |
claude -p "/code-review" \
--permission-mode dontAsk \
--max-turns 20 \
--max-budget-usd 2.00
--max-budget-usd stops a print-mode run once its estimated spend reaches the cap, counting subagent spend too; leave headroom, because spend can pass the cap slightly. --max-turns caps agentic turns. Both are print-mode only, and in the GitHub Action they go in claude_args.
Prompt Caching
Claude Code caches prompts automatically, and cache reads are far cheaper than fresh input (for example $0.20 per million tokens on Opus 5.5). You get more out of it by keeping CLAUDE.md concise, writing specific prompts so runs need fewer turns, and using GitHub’s concurrency controls to avoid duplicate runs on the same branch.
Output Formats
Inline PR Comments
prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment"'
Output: inline comments on the lines concerned, or one summary comment when Claude finds nothing.
JSON
claude -p "/code-review" --permission-mode dontAsk --output-format json > review.json
The payload includes the final result text, total_cost_usd, a per-model cost breakdown, and permission_denials. Branch on it in later steps:
COST=$(jq -r '.total_cost_usd' review.json)
echo "Review cost: $COST USD"
Structured Output with a Schema
When a later step needs machine-readable fields, validate the run’s output against a JSON Schema:
claude -p "List the blocking review findings" \
--permission-mode dontAsk \
--json-schema '{"type":"object","properties":{"blocking":{"type":"integer"}},"required":["blocking"]}'
Streaming
claude -p "Fix the failing tests" --output-format stream-json --include-partial-messages
Useful for long jobs where you want progress in the log rather than one block at the end.
Integration Examples
Example 1: Complete Review Pipeline
name: Complete PR Pipeline
on:
pull_request:
types: [opened, synchronize]
jobs:
# Stage 1: Quick checks
quick-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: npm ci
- run: npm run lint
- run: npm run typecheck
# Stage 2: Claude review (only if quick checks pass)
claude-review:
needs: quick-checks
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment"'
# Stage 3: Security review for labeled PRs
security-review:
needs: quick-checks
if: contains(github.event.pull_request.labels.*.name, 'needs-security-review')
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: "/security-review"
claude_args: '--model claude-opus-5-5 --effort high'
# Stage 4: Tests (parallel with Claude review)
tests:
needs: quick-checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: npm ci
- run: npm test -- --coverage
Example 2: Nightly Security Scan
name: Nightly Security Scan
on:
schedule:
- cron: '0 2 * * *' # 2 AM daily
jobs:
security-scan:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Audit this repository for security vulnerabilities introduced in the
last 24 hours of commits. If you find critical or high severity issues,
open one GitHub issue titled "Security scan findings" labeled
security,urgent, listing each finding with its severity and file.
If you find none, do nothing.
claude_args: |
--model claude-opus-5-5
--effort high
--max-turns 30
--allowedTools "Bash(git log *),Bash(git diff *),Read,Glob,Grep,Bash(gh issue create *)"
GitHub runs scheduled workflows only from the default branch, disables them in public repositories after 60 days without activity, and attributes the run to a repository user—so if that user is a bot, add it to allowed_bots.
Troubleshooting
Claude Doesn’t Respond to @claude
- Verify the Claude GitHub App is installed on the repository and workflows are enabled
- Confirm the secret exists and matches the input you pass (
anthropic_api_keyorclaude_code_oauth_token) - Confirm the comment contains
@claudeas a complete word, not/claudeor@claude-bot - Confirm the commenting user has write access to the repository
CI Doesn’t Run on Claude’s Commits
GitHub doesn’t trigger workflows for commits made with the default GITHUB_TOKEN. Remove github_token: ${{ secrets.GITHUB_TOKEN }} so the action authenticates as the Claude GitHub App, or pass a custom app token.
Authentication Errors
# Test the credential locally before debugging the workflow
claude auth status --text
claude -p "say ok"
For Bedrock, Agent Platform, and Foundry, check the cloud providers troubleshooting section.
Tool Calls Denied in a Headless Run
A -p run with no permission mode starts in Manual mode, so unapproved calls are denied. Add the tools to --allowedTools, or pass --permission-mode auto (classifier-reviewed) or dontAsk (allowlist only). With --output-format stream-json, the denials appear as permission_denied messages and in permission_denials on the final result.
Large PRs
- name: Review large PRs with a turn cap
run: |
claude -p "/code-review --max-findings 20" \
--permission-mode dontAsk \
--max-turns 30 \
--max-budget-usd 5.00
Best Practices
1. Start Small
# Begin with review only
prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
# Add more later: security review, issue triage, implementation
2. Define Standards in CLAUDE.md
Claude reads your repository’s CLAUDE.md (or AGENTS.md when there is no CLAUDE.md) on every run, so put review criteria, code style, and project conventions there instead of repeating them in each workflow prompt. Keep it concise—it costs tokens on every run.
3. Set Expectations
<!-- Add to PR template -->
## AI Review Note
This PR is reviewed by Claude Code.
Findings are suggestions—use your judgment.
Security findings marked critical or high must be resolved before merge.
4. Monitor Usage
Track cost per run from total_cost_usd in JSON output, and roll organization-wide usage up through OpenTelemetry metrics and the analytics dashboard rather than ad-hoc log files.
Getting Started
Today:
- Run
/install-github-app(GitHub) or add the Claude job to.gitlab-ci.yml(GitLab) - Store
ANTHROPIC_API_KEYorCLAUDE_CODE_OAUTH_TOKENas a secret - Test on one pull request
This week:
- Add the review workflow and tune its effort level
- Add a security review for sensitive paths
- Set
--max-turnsand--max-budget-usdon every job
This month:
- Add issue triage and implementation
- Decide between the action and the hosted Code Review product for your team
- Export usage metrics and review cost
CI/CD integration brings Claude Code into your automated workflow. Every PR gets consistent, thorough review. Every issue can be auto-triaged. Your team focuses on what matters.
References:
- Claude Code GitHub Actions - the official action and its inputs
- Claude Code GitLab CI/CD - the GitLab-maintained integration (beta)
- Run Claude Code programmatically - headless
claude -pruns - Code Review - hosted PR review for Team and Enterprise
- Permission modes - what each mode allows in CI