Rubric
Contents — domains, guide and mocks

Claude Code in CI/CD pipelines

CCAR-F 3.614 min read · checked 21 September 2026

Task statementIntegrate Claude Code into CI/CD pipelines

A pull-request review job, message by message

Developer
CI runner
Claude Code
Claude API
Step 1: Developer to CI runner: Opens or updates a PR
Step 2: CI runner to Claude Code: claude -p + diff + flags
Step 3: Claude Code : Loads CLAUDE.md, reads code
Step 4: Claude Code to Claude API: Agent loop: model requests
Step 5: Claude API to Claude Code: Findings
Step 6: Claude Code to CI runner: JSON result, exit code
Step 7: CI runner to Developer: Inline comments on the PR
Nobody is at the keyboard. The pipeline supplies the prompt, the permissions and the parsing; Claude supplies the judgement.

Running without a person: -p

Plain claude starts an interactive session that waits for someone to type. In a pipeline there is no one, so every CI invocation uses -p (long form --print): Claude Code takes the prompt, runs the agent loop to completion, prints the result and exits. It reads stdin, so you can pipe a diff or a log straight in. It exits with code 0 on success and non-zero when the run fails, so the job can branch on the exit status.

A review step a pipeline can parsebash
# Pipe the PR diff in; get JSON that matches a schema back
gh pr diff "$PR" | claude -p \
  "Review this diff for correctness and security bugs only.
   Skip style. Report each issue with file, line, severity." \
  --output-format json \
  --json-schema "$(cat .ci/review-schema.json)" \
  --permission-mode dontAsk \
  --allowedTools "Read,Grep,Glob" \
  --max-turns 15 \
  > review.json

# Exit code tells the job whether the run itself succeeded
jq '.structured_output.issues' review.json
FlagWhat it does in CI
-p / --printNon-interactive: run the prompt to completion and exit
--output-format jsonOne JSON object with the text in result, plus session_id, usage and a cost estimate
--json-schema '<schema>'With json, returns output matching your schema in structured_output
--output-format stream-jsonNewline-delimited JSON events as the run progresses (pair with --verbose)
--allowedTools "…"Pre-approves specific tools or commands, e.g. Bash(npm test *)
--permission-mode dontAskDenies anything that would otherwise prompt — for locked-down jobs
--append-system-promptAdds instructions while keeping Claude Code’s default behaviour
--max-turnsCaps how many turns the run may take — a cost backstop
--resume <session_id>Continues a specific earlier run, e.g. a follow-up pass

Permissions for an unattended run

In a -p run, the documentation says the built-in starting permission mode is Manual on every plan — but there is nobody to answer a prompt. Decide up front what the job may do. A review job needs to read; a fix-the-tests job needs to edit and run the test command; almost no job needs arbitrary shell access or network calls.

Choosing a permission baseline for a CI job

What must the job be able to do?
  • Read and report only
    dontAsk + read toolsanything else is denied
  • Edit files, run tests
    acceptEditsplus Bash(npm test *)
  • Many varied actions
    autoa classifier reviews each
  • Skip every check
    Avoidisolated sandboxes only
Whatever the baseline, list the exact tools the job needs with --allowedTools, and keep deny rules for anything it must never touch.

dontAsk still lets through what needs no approval in Manual mode — file reads in the working directory and the built-in read-only commands — plus anything your --allowedTools entries or allow rules cover. Everything else is denied rather than left waiting. The --allowedTools syntax matches the permission rules: Bash(git diff *) allows any command starting with git diff, and the space before * matters.

Project context: CLAUDE.md in the pipeline

By default, claude -p loads the same context an interactive session would, including the project’s CLAUDE.md. That is where review criteria, test conventions and “what not to flag” belong: the GitHub Actions and GitLab guides both recommend a root CLAUDE.md for coding standards and review criteria, kept concise because Claude reads it on every run. Because the CI runner checks out the repository, only the committed project file is there — a rule in someone’s ~/.claude/CLAUDE.md never reaches the pipeline (see 3.1).

Independent review: don’t let the author grade its own work

If one job generates code or tests, reviewing them in the same session is weak: the reviewer carries the reasoning that produced the change and tends to agree with it. The best-practices guide notes that a fresh context improves code review because Claude won’t be biased toward code it just wrote. In a pipeline, that means a separate claude -p invocation (or a separate job) for review, fed the diff and the criteria — not a follow-up question in the generating session.

Same-session review versus independent review

Review in the generating session

  • Reviewer holds the author’s reasoning
  • Tends to confirm its own choices
  • Context already full of the build-up

Separate review invocation

  • Sees only the diff and the criteria
  • Judges the result on its own terms
  • Can run as its own job with read-only tools

When a review job runs again after new commits, give it what it needs to avoid noise: the current diff plus the earlier findings, with an instruction to report only new or still-unresolved issues. Otherwise every push re-posts the same comments and developers learn to ignore them.

GitHub Actions and GitLab CI/CD

You can call claude -p from any pipeline, but there are packaged integrations. On GitHub, anthropics/claude-code-action@v1 runs Claude Code in a workflow. Set up the Claude GitHub App and secret with /install-github-app, or do it by hand. The action has two modes, and it picks one from your configuration.

Interactive modeAutomation mode
Triggered byAn @claude mention in an issue or PR commentAny workflow event, including pull_request and schedule
Configured byNo prompt inputA prompt input (plain text or a /skill)
Results go toA comment on the issue or PRThe workflow log, unless the prompt and tools post elsewhere
.github/workflows/claude-review.yml (sketch)yaml
on:
  pull_request:
    types: [opened, synchronize]
jobs:
  review:
    runs-on: ubuntu-latest
    timeout-minutes: 15            # stop runaway jobs
    permissions:
      contents: read
      pull-requests: write
      id-token: write
    steps:
      - uses: actions/checkout@v6  # CLAUDE.md and skills come from the repo
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: "/review-pr"     # a skill committed in .claude/skills/
          claude_args: "--max-turns 10 --allowedTools Read,Grep,Glob"
  • The claude_args input passes any Claude Code CLI flag — --max-turns, --model, --allowedTools, --mcp-config.
  • Secrets go in repository or organisation secrets (ANTHROPIC_API_KEY, or CLAUDE_CODE_OAUTH_TOKEN for a subscription), never in the workflow file.
  • Who can trigger — by default the triggering user needs write access, and bot actors are rejected unless listed, which stops bots triggering Claude in a loop.
  • Costs — each run uses runner minutes and tokens; the docs suggest a concise CLAUDE.md, --max-turns, workflow timeouts and concurrency limits.
  • GitLab — Claude Code for GitLab CI/CD is in beta and maintained by GitLab: you add a job to .gitlab-ci.yml that installs Claude Code and runs claude -p, with ANTHROPIC_API_KEY as a masked CI/CD variable and GitLab’s timeout keyword to bound the job.

Traps the wrong answers are built from

Tempting but wrongDo this instead
Running claude without -p in a pipelineUse -p so it runs the prompt to completion and exits.
Parsing free-text review output with regexesUse --output-format json with --json-schema and read structured_output.
Granting broad tool access or bypassing permissions in CIUse dontAsk or acceptEdits with a minimal --allowedTools list.
Reviewing generated code in the session that generated itRun review as a separate invocation with fresh context.
Committing an API key into the workflow fileStore it as a CI secret or masked variable, or use OIDC federation.
Using --bare and expecting CLAUDE.md to applyBare mode skips CLAUDE.md; pass standards with --append-system-prompt-file.

You should now be able to

  • Run Claude Code non-interactively with -p and branch on its exit code.
  • Produce machine-readable results with --output-format json and --json-schema.
  • Choose a permission baseline and --allowedTools list for an unattended job.
  • Supply project standards to CI through the committed CLAUDE.md, or explicitly in bare mode.
  • Design an independent review step and reduce repeat or low-value findings.
  • Configure the GitHub action (prompt, claude_args, secrets) or a GitLab job.

Practice questions

Original questions written for this lesson, in the exam’s style. Answer first, then open the reasoning — every option is explained, including why the wrong ones are tempting.

  1. Question 1

    A team adds claude "Review the changes in this branch" as a step in their CI pipeline. The job never finishes and is killed by the runner’s timeout.

    What is the most likely fix?

    1. AAdd --max-turns 5 so the review ends sooner.
    2. BAdd the -p flag so Claude Code runs non-interactively and exits.
    3. CRaise the runner’s timeout to give the review more time.
    4. DMove the prompt into CLAUDE.md so it runs automatically.
    Show answer and reasoning
    1. AIncorrect. A turn cap limits a running agent loop; it doesn’t stop an interactive session waiting for input.
    2. BCorrect. Without -p, claude starts an interactive session that waits for a person; -p runs the prompt to completion and exits.
    3. CIncorrect. The job isn’t slow; it’s waiting for input that will never come.
    4. DIncorrect. CLAUDE.md supplies context; it doesn’t make a session non-interactive.
  2. Question 2

    A platform team wants review findings posted as inline comments on the exact lines of a pull request, and wants the build to fail on any high-severity finding.

    Which approach best supports this?

    1. AAsk Claude to write findings as a Markdown list and parse it with regexes.
    2. BUse --output-format stream-json and read the text deltas.
    3. CTell Claude in the prompt to “always use a consistent format”.
    4. DUse --output-format json with a --json-schema for findings, and read structured_output.
    Show answer and reasoning
    1. AIncorrect. Prose formats drift between runs; regex parsing breaks on small wording changes.
    2. BIncorrect. Streaming shows progress; it doesn’t give you findings in a defined shape.
    3. CIncorrect. An instruction without a schema is still free text the pipeline has to guess at.
    4. DCorrect. The schema gives each finding file, line and severity fields the job can post as comments and use to fail the build.
  3. Question 3

    A CI job asks Claude Code to generate unit tests for new code, then, in the same session, asks it to review those tests for gaps. Reviewers keep finding weak assertions the review step approved.

    What is the best improvement?

    1. ARun the review as a separate claude -p invocation that sees only the tests and the criteria.
    2. BTell the generating session to “review critically” before finishing.
    3. CIncrease --max-turns so the session has more time to review.
    4. DResume the generating session with --resume for the review step.
    Show answer and reasoning
    1. ACorrect. A fresh context isn’t biased toward work it just produced, so it judges the tests on their own terms.
    2. BIncorrect. The same session still carries the reasoning that produced the tests.
    3. CIncorrect. More turns in the same context don’t remove the self-review bias.
    4. DIncorrect. Resuming restores the same context, which is the problem.
  4. Question 4

    A security-conscious fintech wants a nightly job that reads the codebase and reports dependency risks. It must never edit files or run arbitrary shell commands, and nobody will be watching.

    Which configuration fits best?

    1. A--permission-mode bypassPermissions so the run never stalls on a prompt.
    2. B--permission-mode acceptEdits with --allowedTools "Bash".
    3. C--permission-mode dontAsk with only read and search tools allowed.
    4. DThe default Manual mode, relying on the prompt to say “don’t edit”.
    Show answer and reasoning
    1. AIncorrect. That removes every check — the opposite of what the job needs.
    2. BIncorrect. This allows edits and any shell command, both of which the job must not do.
    3. CCorrect. dontAsk denies anything that would prompt, so the job can read and report but can’t edit or run unapproved commands.
    4. DIncorrect. A prompt instruction isn’t enforcement, and Manual mode has no one to answer its prompts.

Sources

Drafted with AI assistance and checked against the sources above; expert review is in progress. Spotted an error? Tell us and it gets fixed, dated and listed on how this is written.