Skip to content

Commit 70b8c20

Browse files
committed
add ccwrapper
1 parent b042d64 commit 70b8c20

8 files changed

Lines changed: 239 additions & 0 deletions

File tree

.github/workflows/docker.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ jobs:
4242
- audiosprite
4343
- bind
4444
- bpython
45+
- ccwrapper
4546
- chardet
4647
- checkmake
4748
- chromium

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Name | Description | Dockerfile Link | Image Link
2222
[audiosprite](audiosprite) | [`alpine:edge`](https://hub.docker.com/_/alpine/)-based dockerization of [audiosprite](https://github.com/tonistiigi/audiosprite), the "ffmpeg wrapper that will take in multiple audio files and combines them into a single file" | [Dockerfile](audiosprite/Dockerfile) | [backplane/audiosprite](https://hub.docker.com/r/backplane/audiosprite)
2323
[bind](bind) | [`alpine:edge`](https://hub.docker.com/_/alpine/)-based dockerization of [bind](https://www.isc.org/bind/), the DNS server software | [Dockerfile](bind/Dockerfile) | [backplane/bind](https://hub.docker.com/r/backplane/bind)
2424
[bpython](bpython) | [`python:3-alpine`](https://hub.docker.com/_/python/)-based dockerization of [the bpython interpreter](https://bpython-interpreter.org/) | [Dockerfile](bpython/Dockerfile) | [backplane/bpython](https://hub.docker.com/r/backplane/bpython)
25+
[ccwrapper](ccwrapper) | [`alpine:3.23`](https://hub.docker.com/_/alpine/)-based dockerization of [Claude Code](https://code.claude.com/docs/en/overview), the agentic coding tool | [Dockerfile](ccwrapper/Dockerfile) | [backplane/ccwrapper](https://hub.docker.com/r/backplane/ccwrapper)
2526
[chardet](chardet) | [`python:3-alpine`](https://hub.docker.com/_/python/)-based dockerization of [chardet](https://github.com/chardet/chardet), The Universal Character Encoding Detector | [Dockerfile](chardet/Dockerfile) | [backplane/chardet](https://hub.docker.com/r/backplane/chardet)
2627
[checkmake](checkmake) | [`scratch`](https://hub.docker.com/_/scratch/)-based single-binary dockerization of [checkmake](https://github.com/mrtazz/checkmake/), the `Makefile` linter | [Dockerfile](checkmake/Dockerfile) | [backplane/checkmake](https://hub.docker.com/r/backplane/checkmake)
2728
[chromium](chromium) | [`debian:unstable-slim`](https://hub.docker.com/_/debian/)-based dockerization of the Google Chromium web browser | [Dockerfile](chromium/Dockerfile) | [backplane/chromium](https://hub.docker.com/r/backplane/chromium)

ccwrapper/.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CLAUDE.md
2+
README.md
3+
docs.md

ccwrapper/Dockerfile

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
FROM alpine:3.23
2+
LABEL maintainer="Backplane BV <backplane@users.noreply.github.com>"
3+
4+
RUN apk add --no-cache \
5+
bash \
6+
ca-certificates \
7+
cargo \
8+
curl \
9+
deno \
10+
docker-cli \
11+
docker-cli-compose \
12+
gcc \
13+
git \
14+
go \
15+
gpg \
16+
jq \
17+
libgcc \
18+
libstdc++ \
19+
libxml2-utils \
20+
musl \
21+
openssh-client \
22+
patch \
23+
perl \
24+
pnpm \
25+
rage \
26+
ripgrep \
27+
rsync \
28+
shellcheck \
29+
uv \
30+
yq-go \
31+
zstd \
32+
;
33+
34+
# get the first stage installer script, make some light edits
35+
ARG INSTALLER=/var/claude-install.sh
36+
RUN set -eux; \
37+
curl -fsSL -o "${INSTALLER}" https://claude.ai/install.sh; \
38+
chmod a=r "${INSTALLER}"; \
39+
perl -i \
40+
-e 's/^set -e$/set -ex/;' \
41+
-e 's/^(\s*rm -f) /\1 -v /;' \
42+
"${INSTALLER}";
43+
44+
# create a non-root user
45+
ARG NONROOT_GID=65532
46+
ARG NONROOT_UID=65532
47+
ARG NONROOT_HOME="/home/nonroot"
48+
ARG NONROOT_SKEL="/.nonroot_skeleton"
49+
50+
RUN set -eux; \
51+
addgroup -g "${NONROOT_GID}" nonroot; \
52+
adduser -u "${NONROOT_UID}" -G nonroot \
53+
-s "/bin/bash" \
54+
-h "${NONROOT_HOME}" \
55+
-D \
56+
nonroot; \
57+
for d in \
58+
"/work" \
59+
"${NONROOT_HOME}/.claude" \
60+
"${NONROOT_SKEL}" \
61+
; do \
62+
mkdir -m "0770" -p "$d"; \
63+
chown "nonroot:nonroot" "$d"; \
64+
done;
65+
USER nonroot
66+
WORKDIR /work
67+
68+
# add ~nonroot/.local/bin to the search path
69+
ENV PATH="${NONROOT_HOME}/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
70+
71+
# install claude (as non-root)
72+
ARG INSTALL_LOG="${NONROOT_HOME}/.claude-install-log.txt"
73+
RUN set -eux; \
74+
bash "$INSTALLER" 2>&1 | tee "$INSTALL_LOG"; \
75+
gzip -9 "$INSTALL_LOG";
76+
77+
78+
# see https://code.claude.com/docs/en/memory#claude-md-files
79+
# this one advises about the tools that are locally installed
80+
COPY --chown=nonroot:nonroot skel-CLAUDE.md "${NONROOT_HOME}/.claude/CLAUDE.md"
81+
82+
# copy (via hard link) the nonroot homedir to a skeleton dir, so that if a volume
83+
# is placed over the nonroot homedir, that volume gets a copy of the contents of
84+
# the skeleton
85+
RUN cp -al "${NONROOT_HOME}/." "${NONROOT_SKEL}/";
86+
87+
COPY --chown=root:root entrypoint.sh /
88+
ENTRYPOINT [ "/entrypoint.sh" ]

ccwrapper/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# ccwrapper
2+
3+
[`alpine:3.23`](https://hub.docker.com/_/alpine/)-based dockerization of [Claude Code](https://code.claude.com/docs/en/overview), the agentic coding tool
4+
5+
As the site says:
6+
7+
> Claude Code is an agentic coding tool that reads your codebase, edits files, runs commands, and integrates with your development tools. Available in your terminal, IDE, desktop app, and browser. ... [It] helps you build features, fix bugs, and automate development tasks.
8+
9+
## Repositories
10+
11+
| Repo | URL |
12+
| ------------ | -------------------------------------------------------- |
13+
| Docker File | <https://github.com/backplane/conex/tree/main/ccwrapper> |
14+
| Docker Image | <https://hub.docker.com/r/backplane/ccwrapper> |
15+
16+
## Included Utils
17+
18+
In addition to claude-code the following utils are included
19+
20+
| Package | Commands | Description |
21+
| ------------------- | --------------------- | -------------------------------------------------------------------------------- |
22+
| cargo 1.94 | `cargo` | The Rust package manager |
23+
| curl 8.19 | `curl`; `wcurl` | URL retrival utility and library |
24+
| deno 2.7 | `deno` | A modern runtime for JavaScript and TypeScript |
25+
| docker-cli 29.3 | `docker` | Docker CLI (the plugin for `docker compose` is also included) |
26+
| gcc 15.2 | `gcc` | The GNU Compiler Collection |
27+
| git 2.53 | `git` | Distributed version control system |
28+
| go 1.26 | `go`; `gofmt` | Go programming language compiler |
29+
| gpg 2.4 | `gpg` | GNU Privacy Guard 2 - public key operations only |
30+
| jq 1.8 | `jq` | A lightweight and flexible command-line JSON processor |
31+
| nodejs 24.14 | `node` | JavaScript runtime built on V8 engine - LTS version |
32+
| openssh-client 10.2 | `scp`; `sftp`; `ssh` | OpenBSD's SSH client common files |
33+
| patch 2.8 | `patch` | Utility to apply diffs to files |
34+
| perl 5.42 | `perl` | Larry Wall's Practical Extraction and Report Language |
35+
| pnpm 10.32 | `pnpm`; `pnpx` | Fast, disk space efficient package manager |
36+
| rage 0.11 | `rage`; `rage-keygen` | Simple, modern and secure encryption tool |
37+
| ripgrep 15.1 | `rg` | ripgrep combines the usability of The Silver Searcher with the raw speed of grep |
38+
| rsync 3.4 | `rsync`; `rsync-ssl` | A file transfer program to keep remote files in sync |
39+
| rust 1.94 | `rustc`; `rustdoc` | Rust Programming Language toolchain |
40+
| shellcheck 0.11 | `shellcheck` | a static analysis tool for shell scripts |
41+
| ssl_client 1.37 | `ssl_client` | External ssl_client for busybox wget |
42+
| uv 0.10 | `uv`; `uvx` | Extremely fast Python package installer and resolver, written in Rust |
43+
| yq-go 4.52 | `yq` | Portable command-line YAML processor written in Go |
44+
| zstd 1.5 | `unzstd`; `zstd` | Zstandard - Fast real-time compression algorithm |
45+
46+
## Usage
47+
48+
This image runs as the user nonroot (`65532:65532`), in the directory `/work`, with the entrypoint `claude`.
49+
50+
Mount points:
51+
52+
- `/work` - the current working directory, where the codebase should be mounted
53+
- `/home/nonroot` - the directory where claude and related state lives
54+
55+
Example:
56+
57+
```sh
58+
docker run -it --init --rm \
59+
--volume "$(pwd):/work:rw" \
60+
--volume "${HOME}/.ccwrapper/$(pwd | tr -C 'A-Za-z0-9\n' '_'):/home/nonroot:rw" \
61+
backplane/ccwrapper
62+
```

ccwrapper/docs.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# package index
2+
3+
This command line (or something like it) can be used to build up some of the info in the README.md
4+
5+
```sh
6+
apk list -I -q \
7+
| xargs apk info --all --format json \
8+
| jq -r --arg path "$PATH" '
9+
def on_path($dirs):
10+
. as $f
11+
| ($f | split("/")[:-1] | join("/")) as $dir
12+
| ($dirs | index($dir)) != null;
13+
14+
($path | split(":")) as $pathdirs
15+
| .[]
16+
| {
17+
package: .name,
18+
version: .version,
19+
executables: (
20+
.contents
21+
| map("/" + .)
22+
| map(select(on_path($pathdirs)))
23+
),
24+
description: .description
25+
}
26+
| select(.executables | length > 0)
27+
'
28+
```

ccwrapper/entrypoint.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/sh
2+
# entrypoint for claude code wrapper, which allows the nonroot homedir to be a volume
3+
set -eu
4+
SELF=$(basename "$0" '.sh')
5+
6+
NR_HOME="${NR_HOME:-/home/nonroot}"
7+
NR_SKEL="${NR_SKEL:-/.nonroot_skeleton}"
8+
9+
log() {
10+
printf '%s %s %s\n' "$(date '+%FT%T%z')" "$SELF" "$*" >&2
11+
}
12+
13+
die() {
14+
log "FATAL:" "$@"
15+
exit 1
16+
}
17+
18+
main() {
19+
if find "$NR_HOME" -maxdepth 0 -empty | grep -q .; then
20+
log "populating ${NR_HOME} from ${NR_SKEL}"
21+
rsync -avHP8 "${NR_SKEL}/." "${NR_HOME}/." || die "failed to rsync home directory from skeleton"
22+
fi
23+
24+
exec "${NR_HOME}/.local/bin/claude" "$@"
25+
}
26+
27+
main "$@"
28+
# shellcheck disable=SC2317
29+
exit

ccwrapper/skel-CLAUDE.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Environment
2+
3+
Alpine Linux container. Running as `nonroot` (65532:65532) in `/work`.
4+
5+
# Available Tools
6+
7+
- **cargo**, **rustc**, **rustdoc** — Rust toolchain
8+
- **curl**, **wcurl** — HTTP/URL retrieval
9+
- **deno** — JavaScript/TypeScript runtime
10+
- **docker**, **docker compose** — Docker CLI
11+
- **gcc** — GNU C compiler
12+
- **git** — version control
13+
- **go**, **gofmt** — Go toolchain
14+
- **gpg** — public key operations
15+
- **jq** — JSON processor
16+
- **node**, **pnpm**, **pnpx** — Node.js + package manager
17+
- **patch** — apply diffs
18+
- **perl** — Perl interpreter
19+
- **rage**, **rage-keygen** — modern file encryption
20+
- **rg** (ripgrep) — fast recursive search
21+
- **rsync** — file sync/transfer
22+
- **scp**, **sftp**, **ssh** — OpenSSH client
23+
- **shellcheck** — shell script linter
24+
- **uv**, **uvx** — fast Python package manager
25+
- **xmllint** — XML processor (libxml2-utils)
26+
- **yq** — YAML processor
27+
- **zstd**, **unzstd** — Zstandard compression

0 commit comments

Comments
 (0)