diff --git a/.circleci/claude-code-ci.yml b/.circleci/claude-code-ci.yml new file mode 100644 index 0000000..32c4034 --- /dev/null +++ b/.circleci/claude-code-ci.yml @@ -0,0 +1,20 @@ +version: 2.1 +orbs: + architect: giantswarm/architect@6.12.0 + +workflows: + build-claude-code-ci-default: + jobs: + - architect/push-to-registries: + context: architect + name: push-to-registries + git-tag-prefix: claude-code-ci + tag-suffix: "" + image: giantswarm/claude-code-ci + dockerfile: ./claude-code-ci/default.dockerfile + build-context: claude-code-ci + filters: + tags: + only: "/^claude-code-ci.*/" + branches: + ignore: main diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c6d6144 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,53 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +We use dates instead of semantic versions. + +## 2026-02-07 + +### Added + +- `claude-code-ci` image based on `node:24-alpine` with Claude Code, dev tools (git, gh, jq, ripgrep, fd, bat, etc.), and Python tools (uv, tldr) +- `claude-code-ci` custom entrypoint with CI defaults: `--print`, `--allow-dangerously-skip-permissions`, `--model claude-haiku-4-5-20251001`, `--max-turns 10`, `--max-budget-usd 0.20`, `--verbose`; all overridable via env vars + +## 2025-12-08 + +### Changed + +- `apache2-utils`: added arm64 architecture for use in mc-bootstrap on ARM + +## 2025-10-07 + +### Added + +- `yamllint` image based on Python 3.13 Alpine +- `alpine-bats` image for giantswarm/helmclient integration tests + +## 2025-08-28 + +### Changed + +- Switched base image registry from quay.io to gsoci.azurecr.io + +## 2025-03-04 + +### Added + +- `apache2-utils` image + +## 2024-10-29 + +### Changed + +- Updated base image for `calico-crd-installer` +- Corrected pipelines for `awscli-tar` and `calico-crd-installer` +- Updated images to the latest versions done by retagger +- Bumped `dynamic-continuation` orb to v3.9.1 + +## 2024-10-04 + +### Changed + +- Updated `alpine/envsubst` Dockerfile diff --git a/CODEOWNERS b/CODEOWNERS index 237c837..3748acc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -5,6 +5,9 @@ alpine/bats.dockerfile @giantswarm/team-honeybadger ./circleci/apache2-utils.yml @giantswarm/team-honeybadger apache2-utils @giantswarm/team-honeybadger +.circleci/claude-code-ci.yml @giantswarm/team-honeybadger +claude-code-ci/default.dockerfile @giantswarm/team-honeybadger + .gitignore @giantswarm/team-honeybadger README.md @giantswarm/team-honeybadger add-new-image.sh @giantswarm/team-honeybadger diff --git a/claude-code-ci/README.md b/claude-code-ci/README.md new file mode 100644 index 0000000..0f30532 --- /dev/null +++ b/claude-code-ci/README.md @@ -0,0 +1,127 @@ +# claude-code-ci + +Claude Code for use in CI/CD pipelines. + +## TODO + +- Add GitHub PR details + +## Defaults + +The entrypoint always applies `--print` and `--allow-dangerously-skip-permissions`. All other defaults are configurable via environment variables. + +## Environment variables + +Use these environment variables to configure the execution of `claude`. + +| Variable | Default | CLI flag | Description | +|----------|---------|----------|-------------| +| `CLAUDE_PROMPT` | **(required)** | positional | The prompt to send to Claude | +| `CLAUDE_ALLOWED_TOOLS` | *(empty)* | `--allowedTools` | Comma-separated list of allowed tools | +| `CLAUDE_MAX_BUDGET` | `0.20` | `--max-budget-usd` | Maximum spend in USD | +| `CLAUDE_MAX_TURNS` | `10` | `--max-turns` | Maximum agentic turns | +| `CLAUDE_MODEL` | `claude-haiku-4-5-20251001` | `--model` | Model to use | +| `CLAUDE_OUTPUT_FORMAT` | `stream-json` | `--output-format` | Output format (`text`, `json`, `stream-json`) | +| `CLAUDE_TOOLS` | `default` | `--tools` | Available tools (`default`, `""`, or tool names) | +| `CLAUDE_VERBOSE` | `1` | `--verbose` | Verbose logging (`1` = on, `0` = off) | + +Any additional arguments passed to the container are forwarded directly to `claude`. + +Depending on the use, you might have to permit specific tool use via the `--allowedTools` flag. + +## Testing + +`$TAG` must be an existing tag. + +```bash +# With defaults +docker run \ + gsoci.azurecr.io/giantswarm/claude-code-ci:$TAG \ + "explain this code" + +# With overrides +docker run \ + -e CLAUDE_MODEL=sonnet \ + -e CLAUDE_MAX_BUDGET=1.00 \ + gsoci.azurecr.io/giantswarm/claude-code-ci:$TAG \ + "review this PR" + +# Pass extra flags +docker run \ + gsoci.azurecr.io/giantswarm/claude-code-ci:$TAG \ + --output-format json \ + "summarize this repo" +``` + +## Usage in a GitHub workflow + +Here is an example. + +```yaml +name: Auto-update changelog + +on: + pull_request: {} + +permissions: {} + +jobs: + report: + runs-on: ubuntu-latest + permissions: + contents: write # To push commits + pull-requests: write # To comment in PR + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 # We need the full history to compare changes + persist-credentials: true # For the next step + + - name: Update changelog + uses: docker://gsoci.azurecr.io/giantswarm/claude-code:v0.0.1 + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + args: | + --allowedTools "Edit(/github/workspace/CHANGELOG.md)" \ + --allowedTools "Bash(git *)" \ + "You are executed in a Github action runner, in the context of a pull request. + Environment variables give you information about the repository etc. + You have the gh CLI available. + + Your task: If a CHANGELOG.md file exists in the repository root, check if the current pull + request updates it. + + If CHANGELOG.md exists, but is not updated in this PR, update it with information + about the changes in this branch compared to the default branch. Push a simple commit + to the PR's branch. Do not rebase. + + Normally, dependency updates fall under the '### Changed' category. In some cases, + if they are security-related, they might fall under the '### Fixed' category. + + Use the pull request title and description for hints. Use `git diff` to find out details about file changes. + + Do nothing else. Do not recommend next actions. Finish the given task in one step. + + --------------------------------- + Pull request details: + + - **Title:** '${{ github.event.pull_request.title }}' + - **Author:** '${{ github.event.pull_request.user.login }}' (${{ github.event.pull_request.user.name }}) + - **Branch:** '${{ github.head_ref }}' + - **Base branch:** ${{ github.event.pull_request.base.ref }} + - **Repository:** ${{ github.repository }} + - **PR number:** ${{ github.event.pull_request.number }} + + **Description:** + + ${{ github.event.pull_request.body }} + + **Changed files:** + + ${{ join(github.event.pull_request.changed_files, ', ') }} + + ---------------------------------" +``` \ No newline at end of file diff --git a/claude-code-ci/default.dockerfile b/claude-code-ci/default.dockerfile new file mode 100644 index 0000000..94f1a6f --- /dev/null +++ b/claude-code-ci/default.dockerfile @@ -0,0 +1,45 @@ +FROM --platform=linux/amd64 node:24.13.0-alpine3.23 + +RUN apk add --no-cache \ + bash \ + git \ + github-cli \ + curl \ + wget \ + python3 \ + py3-pip \ + build-base \ + jq \ + ripgrep \ + fd \ + bat \ + tree \ + httpie \ + rsync \ + shellcheck + +# Install Claude Code globally +RUN npm install -g @anthropic-ai/claude-code@2.1.36 + +# Install Python tools +RUN pip3 install \ + --break-system-packages \ + uv==0.10.0 \ + tldr==3.4.4 + +# Create a non-root user +RUN adduser -s bash -D user + +# Create necessary directories +RUN mkdir -p /home/user/.config/claude-code \ + && mkdir -p /home/user/.local/bin \ + && chown -R user:user /home/user + +# Copy entrypoint script +COPY --chmod=755 entrypoint.sh /usr/local/bin/entrypoint.sh + +# Switch to user +USER user +WORKDIR /home/user + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/claude-code-ci/entrypoint.sh b/claude-code-ci/entrypoint.sh new file mode 100755 index 0000000..ca75b9c --- /dev/null +++ b/claude-code-ci/entrypoint.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +# CI defaults — override via environment variables +CLAUDE_MAX_BUDGET="${CLAUDE_MAX_BUDGET:-0.20}" +CLAUDE_MAX_TURNS="${CLAUDE_MAX_TURNS:-10}" +CLAUDE_MODEL="${CLAUDE_MODEL:-claude-haiku-4-5-20251001}" +CLAUDE_OUTPUT_FORMAT="${CLAUDE_OUTPUT_FORMAT:-stream-json}" +CLAUDE_TOOLS="${CLAUDE_TOOLS:-default}" +CLAUDE_ALLOWED_TOOLS="${CLAUDE_ALLOWED_TOOLS:-}" +CLAUDE_VERBOSE="${CLAUDE_VERBOSE:-1}" + +# test if CLAUDE_PROMPT is set, otherwise exit with an error +if [ -z "${CLAUDE_PROMPT:-}" ]; then + echo "Error: CLAUDE_PROMPT environment variable is not set" >&2 + exit 1 +fi + +args=( + --max-budget-usd "$CLAUDE_MAX_BUDGET" + --max-turns "$CLAUDE_MAX_TURNS" + --model "$CLAUDE_MODEL" + --output-format "$CLAUDE_OUTPUT_FORMAT" + --permission-mode "dontAsk" + --print + --tools "$CLAUDE_TOOLS" +) + +if [ -n "$CLAUDE_ALLOWED_TOOLS" ]; then + IFS=',' read -ra ALLOWED <<< "$CLAUDE_ALLOWED_TOOLS" + for tool in "${ALLOWED[@]}"; do + tool="$(echo "$tool" | xargs)" # trim whitespace + args+=(--allowedTools "$tool") + done +fi + +if [ "$CLAUDE_VERBOSE" = "1" ]; then + args+=(--verbose) +fi + +exec claude "${args[@]}" "$CLAUDE_PROMPT"