CI/CD & Automation
Claude Code can run non-interactively in CI/CD pipelines. This enables automated code review, PR descriptions, migrations, and more.
Headless Mode Basics
Section titled “Headless Mode Basics”Two flags make Claude non-interactive:
# -p: Single prompt, exits after responseclaude -p "explain this codebase"
# --print: Output only (no interactive UI)claude --print -p "what does this function do"Combine them for CI:
claude --print -p "review the changes in this PR for security issues"Environment Setup
Section titled “Environment Setup”Claude needs an API key in CI:
# GitHub Actions exampleenv: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}For headless operation, also set:
# Accept all permissions (use carefully)claude --dangerously-skip-permissions -p "..."
# Or pre-approve specific tools in settingsGitHub Actions Examples
Section titled “GitHub Actions Examples”Automated PR Review
Section titled “Automated PR Review”name: Claude PR Review
on: pull_request: types: [opened, synchronize]
jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0
- name: Install Claude Code run: npm install -g @anthropic-ai/claude-code
- name: Review PR env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} run: | claude --print -p " Review the changes in this PR. Focus on: bugs, security issues, performance. Be concise. Output as markdown.
$(git diff origin/main...HEAD) " > review.md
- name: Post Review Comment uses: actions/github-script@v7 with: script: | const fs = require('fs'); const review = fs.readFileSync('review.md', 'utf8'); github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: review });Generate PR Description
Section titled “Generate PR Description”name: Generate PR Description
on: pull_request: types: [opened]
jobs: describe: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0
- name: Install Claude Code run: npm install -g @anthropic-ai/claude-code
- name: Generate Description env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} run: | claude --print -p " Generate a PR description for these changes. Include: summary, key changes, testing notes.
$(git log origin/main...HEAD --oneline) $(git diff origin/main...HEAD --stat) " > description.md
- name: Update PR uses: actions/github-script@v7 with: script: | const fs = require('fs'); const body = fs.readFileSync('description.md', 'utf8'); github.rest.pulls.update({ owner: context.repo.owner, repo: context.repo.repo, pull_number: context.issue.number, body: body });Automated Fixes on Push
Section titled “Automated Fixes on Push”name: Auto-fix Issues
on: push: branches: [main]
jobs: fix: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Install Claude Code run: npm install -g @anthropic-ai/claude-code
- name: Fix Linting Issues env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} run: | # Run linter, capture issues npm run lint 2>&1 | head -100 > lint-issues.txt || true
# Only run if there are issues if [ -s lint-issues.txt ]; then claude --dangerously-skip-permissions --print -p " Fix these linting issues: $(cat lint-issues.txt) " fi
- name: Commit Fixes run: | git config user.name "Claude Bot" git config user.email "claude@example.com" git add -A git diff --staged --quiet || git commit -m "fix: auto-fix linting issues" git pushWhat Works Well in CI
Section titled “What Works Well in CI”| Use Case | Why It Works |
|---|---|
| PR review | Stateless, single pass |
| PR descriptions | Summarization task |
| Commit message generation | Small, focused input |
| Documentation updates | Isolated changes |
| Simple migrations | Pattern-based transforms |
What Doesn’t Work Well
Section titled “What Doesn’t Work Well”| Use Case | Why It Fails |
|---|---|
| Complex debugging | Needs iteration |
| Large refactors | Requires human judgment |
| Architectural decisions | Needs context/discussion |
Anything requiring /compact | Long context management |
Cost Control in CI
Section titled “Cost Control in CI”Automated runs can burn through tokens fast:
# Use Haiku for simple tasks- run: | claude --print --model haiku -p "..."
# Limit agentic turns per run- run: | claude --print --max-turns 5 -p "..."
# Cache results where possible- uses: actions/cache@v4 with: path: .claude-cache key: claude-${{ hashFiles('**/*.py') }}Outputting Structured Data
Section titled “Outputting Structured Data”For programmatic consumption:
# Request JSON outputclaude --print -p " Analyze this code and output JSON: { \"issues\": [{\"file\": string, \"line\": number, \"severity\": string, \"message\": string}], \"summary\": string }
$(cat src/main.py)"
# Parse in CIclaude --print -p "..." | jq '.issues[] | select(.severity == "high")'Debugging CI Runs
Section titled “Debugging CI Runs”When Claude fails in CI:
# Capture full output- run: | claude --print -p "..." 2>&1 | tee claude-output.txt
# Upload as artifact- uses: actions/upload-artifact@v4 if: failure() with: name: claude-debug path: claude-output.txt