Claude
Runs Claude Code headless in CI jobs, with budget, turn, and permission guards.
Usage
include:
- project: hosstio/gitlab-pipelines
file: helpers/claude.yamlVariables
| Variable | Default | Description |
|---|---|---|
AGENT_CLAUDE_MODEL | "" | Model to use, as an alias (opus, sonnet, haiku) or a full model name; empty uses the CLI's default |
AGENT_CLAUDE_EFFORT | "" | Effort level: low, medium, high, xhigh or max; empty uses the model's default |
AGENT_CLAUDE_MAX_BUDGET_USD | 1.00 | Dollar ceiling per invocation |
AGENT_CLAUDE_MAX_TURNS | 30 | Turn ceiling per invocation |
AGENT_CLAUDE_PERMISSION_MODE | dontAsk | Denies anything outside AGENT_CLAUDE_ALLOWED_TOOLS and the read-only set |
AGENT_CLAUDE_ALLOWED_TOOLS | Read,Grep,Glob,Bash(git diff *),Bash(git log *) | Tools the model may use |
AGENT_CLAUDE_OUTPUT_FILE | /tmp/claude-output.json | Where the JSON run output is written |
AGENT_CLAUDE_RUNS_FILE | /tmp/claude-runs.jsonl | One line of usage per run, which the report reads |
AGENT_CLAUDE_TRANSCRIPT_FILE | /tmp/claude-transcript.jsonl | Every event of every run, which the report copies to the artifacts |
CLAUDE_CODE_OAUTH_TOKEN | Subscription auth token from claude setup-token; set as a masked CI/CD variable | |
ANTHROPIC_API_KEY | Anthropic API key; takes precedence when both are set |
Base (.claude)
Base job using the agents:claude image (registry.gitlab.com/hosstio/docker-images/agents/main:claude), which has a pinned Claude Code with auto-updates off, plus glab, Git, curl, jq and ripgrep. Its before_script prints the Claude Code version. Extend this for custom Claude integrations.
The image's entrypoint is off (entrypoint: [""]), because GitLab would run it before the job has a token. A job that wants the image's startup scripts, which clone GIT_REPOSITORIES and link the skills of AGENT_SKILLS, runs docker-entrypoint.sh once it has exported GITLAB_TOKEN; see the agents image README.
Variables
| Variable | Default | Description |
|---|---|---|
AGENT_ASSISTED_BY | Claude Code | Tool name in the Assisted-by trailer that the agent jobs add to every commit |
Examples
Run (.claude_run)
Defines claude_run "<prompt>" [extra args…]. It skips with a warning when neither CLAUDE_CODE_OAUTH_TOKEN nor ANTHROPIC_API_KEY is set, then runs claude --print with --output-format stream-json and the budget, turn, permission, and allowed-tools caps. While Claude works, the job log shows one line per tool call, such as Claude > Bash: glab mr view 70 --comments or Claude > Edit: charts/yopass/Chart.yaml, without the contents the call returned. The stream goes to AGENT_CLAUDE_TRANSCRIPT_FILE, and its final result line, the same JSON that --output-format json prints, goes to AGENT_CLAUDE_OUTPUT_FILE. Any extra arguments are passed through to the CLI. Output is written to AGENT_CLAUDE_OUTPUT_FILE.
After each run it appends one line to AGENT_CLAUDE_RUNS_FILE with the model that ran, the effort, turns, duration, cost, and input, output and cache tokens. It then calls claude_result, which validates the output (rejecting unparseable JSON, an .is_error result, or a missing result) and exports AGENT_CLAUDE_RESULT. When a --json-schema run returns no plain .result, claude_result falls back to the compact .structured_output. The run's cost is logged.
The CLI exits non-zero when it hits the budget or turn ceiling, but the output may still hold a usable result, so claude_run warns rather than aborting and lets claude_result decide.
Variables
Examples
A job that extends .claude, pulls in the log and run helpers via !reference, then invokes it:
summarise:
extends: .claude
script:
- !reference [.log_helpers, script]
- !reference [.claude_run, script]
- claude_run "Summarize the last 10 commits"Report (.claude_report)
.claude runs claude_report in its after_script, so it also runs when the job fails, and after Claude has finished, so Claude can't commit the files. It turns AGENT_CLAUDE_RUNS_FILE into a table with one row per run and a total row when there were several:
| Run | Model | Effort | Turns | Duration (s) | Cost (USD) | Input tokens | Output tokens | Cache read tokens | Cache write tokens |
|---|---|---|---|---|---|---|---|---|---|
| 1 | claude-opus-5-5 | high | 12 | 48.3 | 0.4124 | 30 | 2100 | 180000 | 25000 |
The table goes to three places:
- The job log.
claude-report.md, a job artifact that a merge request links as "Claude report" (expose_as), kept for 30 days, next toclaude-transcript.jsonl, the full stream of every run. The transcript holds repository content and API responses, so the artifacts are limited to members with at least the Developer role (access: developer).claude-metrics.txt, a metrics report with the totals, such asreview_claude_cost_usd, which GitLab shows in the merge request's metrics widget on Premium and Ultimate.
The cost is Claude Code's estimate at list price. On a CLAUDE_CODE_OAUTH_TOKEN subscription, a run uses plan limits rather than money.