Headless mode and CI/CD integration
Use Claude Code in headless mode for CI/CD pipelines: GitHub Actions, GitLab CI, automated code review, and batch processing.
Headless mode: Claude Code without an interface
Headless mode lets you use Claude Code as a standard command-line tool: it receives an instruction, executes it, and returns a result, with no interactive interface, no confirmation prompts, no waiting for input.
It's the gateway to integration with CI/CD pipelines, automation scripts, and DevOps tools.
The --print (-p) flag: basic usage
The --print (or -p) flag launches Claude Code in non-interactive mode and outputs the result to stdout:
# Basic usageclaude --print "Explain what this function does"# With a file as context (piping)cat src/utils/auth.ts | claude --print "Identify security risks in this code"# Piping to other commandsgit diff HEAD~1 | claude --print "Write a CHANGELOG summary for these changes"# Shorthand -pclaude -p "Generate unit tests for the file src/lib/parser.ts"
Piping and scripts
The --print mode integrates naturally into Unix pipes:
#!/bin/bash# Pre-commit code analysis scriptCHANGED_FILES=$(git diff --name-only HEAD)for file in $CHANGED_FILES; doif [[ "$file" == *.ts ]] || [[ "$file" == *.tsx ]]; thenecho "Analyzing $file..."REVIEW=$(cat "$file" | claude --print \"Identify critical issues in this TypeScript code.Respond in JSON: {issues: array, severity: critical|high|medium|low}")echo "$REVIEW" | python3 -c "import sys, jsondata = json.load(sys.stdin)critical = [i for i in data.get('issues', []) if i.get('severity') == 'critical']if critical:print('BLOCKING:', critical)exit(1)print('OK: no critical issues')"fidone
--output-format json: automated parsing
To integrate Claude's output into scripts, use the JSON format:
# Structured JSON formatclaude --print --output-format json "Analyze this code and return a JSON object"# Stream JSON (for long outputs)claude --print --output-format stream-json "Generate the documentation for this API"
The json format returns an object with the following structure:
{"type": "result","subtype": "success","result": "The content of Claude's response...","session_id": "sess_abc123","total_cost_usd": 0.0023,"num_turns": 1}
Extract only the response with jq:
RESULT=$(claude --print --output-format json "Summarize in one sentence: $(cat README.md)" | jq -r '.result')echo "Summary: $RESULT"
--max-turns: limiting iterations
In headless mode, Claude can perform multiple "turns" (read files, write code, verify...). --max-turns limits this number to prevent infinite loops:
# Limit to 5 execution turnsclaude --print --max-turns 5 "Fix the TypeScript errors in src/"# For simple tasks, 1 turn is enoughclaude --print --max-turns 1 "Generate a README.md file for this project"
--dangerously-skip-permissions: full automatic mode
High security risk
This flag suppresses all Claude Code confirmation prompts. It can read, write, and execute any file or command without human validation. Use only in isolated environments (CI/CD containers, sandboxes) with system permissions reduced to the minimum.
Never use it on your personal development machine for unaudited tasks.
# In CI/CD only, in an isolated containerclaude --print --dangerously-skip-permissions \"Generate the missing tests for src/services/ and run them"
In production CI/CD, the recommended combination is:
claude \--print \--dangerously-skip-permissions \--max-turns 10 \--output-format json \"Your task here"
GitHub Actions integration
Full workflow: automated PR review
This workflow triggers a Claude Code review on every Pull Request:
name: Claude Code PR Reviewon:pull_request:types: [opened, synchronize]jobs:claude-review:runs-on: ubuntu-latestpermissions:contents: readpull-requests: writesteps:- name: Checkoutuses: actions/checkout@v4with:fetch-depth: 0- name: Setup Node.jsuses: actions/setup-node@v4with:node-version: '20'- name: Install Claude Coderun: npm install -g @anthropic-ai/claude-code- name: Run Claude Code reviewid: reviewenv:ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}run: |DIFF=$(git diff origin/${{ github.base_ref }}...HEAD -- '*.ts' '*.tsx' '*.js' | head -c 10000)REVIEW=$(echo "$DIFF" | claude \--print \--max-turns 3 \--output-format json \"You are a code review expert. Analyze this Git diff and provide:1. A summary of the changes2. Potential issues (security, performance)3. Concrete improvement suggestionsFormat: Structured and concise Markdown." | jq -r '.result')echo "$REVIEW" > /tmp/review.mdecho "review_done=true" >> $GITHUB_OUTPUT- name: Post review commentif: steps.review.outputs.review_done == 'true'uses: actions/github-script@v7with:script: |const fs = require('fs');const review = fs.readFileSync('/tmp/review.md', 'utf8');github.rest.issues.createComment({issue_number: context.issue.number,owner: context.repo.owner,repo: context.repo.repo,body: `## Automated review: Claude Code\n\n${review}\n\n---\n*Automatically generated by Claude Code*`});
Workflow: automatic test generation
name: Generate missing testson:workflow_dispatch:inputs:target_path:description: 'Path to analyze (e.g. src/services/)'required: truedefault: 'src/'jobs:generate-tests:runs-on: ubuntu-latestpermissions:contents: writepull-requests: writesteps:- uses: actions/checkout@v4- uses: actions/setup-node@v4with:node-version: '20'- name: Install dependenciesrun: npm ci- name: Install Claude Coderun: npm install -g @anthropic-ai/claude-code- name: Generate missing testsenv:ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}run: |claude \--print \--dangerously-skip-permissions \--max-turns 15 \"Analyze the files in ${{ github.event.inputs.target_path }}.For each file without tests: create the Jest + TypeScript test filecovering nominal and error cases.Then run npm test to verify."- name: Create Pull Requestuses: peter-evans/create-pull-request@v5with:commit-message: "test: automatically generate missing tests"title: "Tests automatically generated by Claude Code"body: |Tests automatically generated for files without coverage.**Verify before merging:**- [ ] Tests cover nominal cases- [ ] Tests cover error cases- [ ] Coverage is > 80%branch: "auto/generated-tests"
Workflow: weekly security audit
name: Claude Code Security Auditon:schedule:- cron: '0 6 * * 1' # Every Monday at 6amworkflow_dispatch:jobs:security-audit:runs-on: ubuntu-latestpermissions:contents: readissues: writesteps:- uses: actions/checkout@v4- uses: actions/setup-node@v4with:node-version: '20'- name: Install Claude Coderun: npm install -g @anthropic-ai/claude-code- name: Run security auditid: auditenv:ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}run: |AUDIT=$(claude \--print \--max-turns 8 \--output-format json \"Full security audit:1. Search for hardcoded secrets2. Vulnerable dependencies (package.json)3. Dangerous patterns (eval, innerHTML)4. Security configurations (CORS, CSP)Return JSON with: critical (array), warnings (array), score (/10)" \| jq -r '.result')echo "$AUDIT" > /tmp/audit.jsonCRITICAL_COUNT=$(echo "$AUDIT" | python3 -c "import sys, jsontry:data = json.loads(sys.stdin.read())print(len(data.get('critical', [])))except:print(0)")echo "critical_count=$CRITICAL_COUNT" >> $GITHUB_OUTPUT- name: Create issue if critical findingsif: steps.audit.outputs.critical_count > 0uses: actions/github-script@v7with:script: |const fs = require('fs');const audit = fs.readFileSync('/tmp/audit.json', 'utf8');github.rest.issues.create({owner: context.repo.owner,repo: context.repo.repo,title: '[SECURITY] Critical issues detected by Claude Code',body: `## Security audit report\n\n\`\`\`json\n${audit}\n\`\`\``,labels: ['security', 'critical']});
Secrets and configuration variables
Managing secrets in GitHub Actions
Never put ANTHROPIC_API_KEY in plaintext in your workflows. Always use GitHub secrets:
- Go to Settings > Secrets and variables > Actions
- Click "New repository secret"
- Name:
ANTHROPIC_API_KEY, Value: your Anthropic API key
In the workflow, reference via ${{ secrets.ANTHROPIC_API_KEY }}.
GitLab CI integration
Basic example of integration in a GitLab pipeline:
# .gitlab-ci.ymlstages:- review- testclaude-review:stage: reviewimage: node:20-alpineonly:- merge_requestsscript:- npm install -g @anthropic-ai/claude-code- |DIFF=$(git diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD | head -c 8000)REVIEW=$(echo "$DIFF" | claude \--print \--max-turns 3 \"Review this diff and identify potential issues.")echo "$REVIEW"variables:ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
Pre-commit hooks with Claude Code
Integrate Claude Code into your git pre-commit hooks:
#!/bin/bash# .git/hooks/pre-commit# Make executable: chmod +x .git/hooks/pre-commitset -eSTAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(ts|tsx|js|jsx)$' || true)if [ -z "$STAGED_FILES" ]; thenexit 0fiecho "Claude Code analysis of staged files..."for file in $STAGED_FILES; doISSUES=$(cat "$file" | claude \--print \--max-turns 1 \--output-format json \"Identify CRITICAL issues (obvious bugs, security flaws).Respond with a JSON {issues: string[], has_critical: boolean}" \| jq -r '.result' 2>/dev/null || echo '{"has_critical": false}')HAS_CRITICAL=$(echo "$ISSUES" | python3 -c "import sys, jsontry:d = json.loads(sys.stdin.read())print('true' if d.get('has_critical') else 'false')except:print('false')")if [ "$HAS_CRITICAL" = "true" ]; thenecho "BLOCKED: critical issue in $file"exit 1fidoneecho "OK: no critical issues detected"exit 0
Security best practices in headless mode
Mandatory security checklist
Before deploying Claude Code in headless mode in a production pipeline:
- Use encrypted secrets (GitHub Secrets, GitLab Variables, AWS Secrets Manager)
- Reduce runner permissions to the minimum necessary
- Limit
--max-turnsto avoid expensive loops - Log token costs to detect abuse
- Isolate in containers without unnecessary network access
- Never commit files containing
ANTHROPIC_API_KEY - Use
--output-format jsonto parse results safely
Controlling costs in CI/CD
# Bad: sends the entire codebaseclaude --print "Analyze the entire project"# Good: target modified filesgit diff --name-only HEAD~1 | xargs -I{} sh -c \'cat {} | claude --print --max-turns 1 "Analyze this file"'# Good: limit diff sizegit diff | head -c 5000 | claude --print --max-turns 2 "Review this diff"
Next steps
Combine headless mode with enterprise providers (Bedrock, Vertex) for CI/CD pipelines that comply with your organization's data constraints.