Skip to content

Commit e7cd8f3

Browse files
durandomclaude
andcommitted
feat: initial repo — custom fullsend sandbox image with yarn/corepack
Extends ghcr.io/fullsend-ai/fullsend-code:latest with corepack and yarn pre-activated for the rhdh-plugins monorepo. Includes multi-arch GHA build workflow mirroring upstream fullsend image CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
0 parents  commit e7cd8f3

4 files changed

Lines changed: 192 additions & 0 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Sandbox Images
2+
3+
on:
4+
push:
5+
tags:
6+
- "v[0-9]+.[0-9]+*"
7+
branches:
8+
- main
9+
paths:
10+
- "images/code/**"
11+
pull_request:
12+
paths:
13+
- "images/code/**"
14+
workflow_dispatch:
15+
16+
# Cancel in-progress runs for the same branch/PR.
17+
concurrency:
18+
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
19+
cancel-in-progress: true
20+
21+
jobs:
22+
build-code:
23+
runs-on: ubuntu-latest
24+
permissions:
25+
contents: read
26+
packages: write
27+
env:
28+
IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/rhdh-fullsend-code
29+
steps:
30+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
31+
32+
- name: Log in to GitHub Container Registry
33+
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
34+
with:
35+
registry: ghcr.io
36+
username: ${{ github.actor }}
37+
password: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: Set up QEMU
40+
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
41+
42+
- name: Set up Docker Buildx
43+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
44+
45+
- name: Extract metadata
46+
id: meta
47+
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
48+
with:
49+
images: ${{ env.IMAGE_NAME }}
50+
tags: |
51+
type=semver,pattern={{version}}
52+
type=semver,pattern={{major}}.{{minor}}
53+
type=sha,prefix=
54+
type=raw,value=latest,enable={{is_default_branch}}
55+
type=raw,value=dev,enable=${{ github.event_name != 'pull_request' }}
56+
57+
- name: Build and push
58+
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
59+
with:
60+
context: images/code
61+
file: images/code/Containerfile
62+
platforms: linux/amd64,linux/arm64
63+
push: ${{ github.event_name != 'pull_request' }}
64+
tags: ${{ steps.meta.outputs.tags }}
65+
labels: ${{ steps.meta.outputs.labels }}
66+
cache-from: type=gha,scope=code
67+
cache-to: type=gha,mode=max,scope=code

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @durandom @kadel

README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# rhdh-fullsend
2+
3+
Custom fullsend sandbox images for the RHDH team's agent infrastructure.
4+
5+
## Why this repo exists
6+
7+
The upstream [fullsend-code](https://github.com/fullsend-ai/fullsend) sandbox
8+
image ships with Go, Python, and shell tooling but no JavaScript package
9+
manager. The rhdh-plugins monorepo (23 workspaces, yarn) requires yarn to run
10+
tests, linting, and OpenSpec validation. Without it baked into the image, agents
11+
spend 10-15 minutes bootstrapping corepack/yarn on every run — and the
12+
workaround (a `host_files`-mounted shell script) is fragile.
13+
14+
This repo builds a single image that extends `fullsend-code:latest` with
15+
corepack and yarn pre-activated.
16+
17+
## Image
18+
19+
```
20+
ghcr.io/fullsend-ai/fullsend-code:latest (upstream)
21+
└── ghcr.io/redhat-developer/rhdh-fullsend-code:latest (this repo)
22+
```
23+
24+
| What's added | Why |
25+
|-------------|-----|
26+
| `corepack enable` | `/usr` is read-only in the sandbox — can't enable at runtime |
27+
| `corepack prepare yarn@stable` | Pre-downloads yarn binary, zero cold-start |
28+
| `/usr/local/bin/yarn` wrapper | Git hooks (husky) run in subprocesses without the agent's PATH |
29+
30+
## Tags
31+
32+
| Tag | When | Use |
33+
|-----|------|-----|
34+
| `latest` | Push to `main` | Production — harness configs reference this |
35+
| `dev` | Any non-PR build | Testing and CI |
36+
| `X.Y.Z` | Tag push `v*` | Immutable release pin |
37+
| `X.Y` | Tag push `v*` | Floating minor for auto-patch |
38+
| `<sha>` | Every non-PR build | Debugging and rollback |
39+
40+
PRs build but don't push (validation only).
41+
42+
## Usage
43+
44+
Reference in your fullsend harness config:
45+
46+
```yaml
47+
# .fullsend/customized/harness/code.yaml
48+
image: ghcr.io/redhat-developer/rhdh-fullsend-code:latest
49+
```
50+
51+
This replaces the `sandbox-yarn-setup.sh` + `host_files` workaround.
52+
53+
## Local build
54+
55+
```bash
56+
podman build -t rhdh-fullsend-code:local \
57+
-f images/code/Containerfile images/code/
58+
```
59+
60+
## Adding more tools
61+
62+
Edit `images/code/Containerfile`. Follow the upstream pattern:
63+
- Pin versions via `ARG`
64+
- Verify checksums with `sha256sum -c` for binary downloads
65+
- Keep `USER sandbox` as the last line
66+
67+
## Upstream tracking
68+
69+
This image inherits everything from `fullsend-code:latest`. When upstream
70+
updates their base image (Claude Code, gitleaks, Go, etc.), our image picks
71+
it up automatically on the next rebuild. Pin `BASE_IMAGE` to a specific
72+
digest if you need reproducibility:
73+
74+
```dockerfile
75+
ARG BASE_IMAGE=ghcr.io/fullsend-ai/fullsend-code@sha256:abc123...
76+
```

images/code/Containerfile

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Containerfile — RHDH custom code agent sandbox image.
2+
#
3+
# Extends the upstream fullsend-code image with tools needed by the
4+
# rhdh-plugins monorepo:
5+
# - corepack enabled with yarn pre-activated (avoids 10-15 min
6+
# bootstrap inside read-only /usr sandbox)
7+
# - Node.js is already in the base image (installed by Claude Code)
8+
#
9+
# This image eliminates the need for the sandbox-yarn-setup.sh
10+
# host_files workaround in harness configs.
11+
#
12+
# Build (native arch):
13+
# podman build -t rhdh-fullsend-code:local \
14+
# -f images/code/Containerfile images/code/
15+
#
16+
# Build (cross-platform):
17+
# podman build --platform linux/arm64 -t rhdh-fullsend-code:arm64 \
18+
# -f images/code/Containerfile images/code/
19+
20+
ARG BASE_IMAGE=ghcr.io/fullsend-ai/fullsend-code:latest
21+
FROM ${BASE_IMAGE}
22+
23+
USER root
24+
25+
# ---------------------------------------------------------------------------
26+
# corepack + yarn — the sandbox filesystem policy makes /usr read-only,
27+
# so `corepack enable` fails at runtime. Pre-enable it here and
28+
# pre-download yarn so agents get yarn on PATH with zero startup cost.
29+
#
30+
# COREPACK_HOME is set to a writable location that persists across
31+
# the sandbox session. The shim symlinks land in /usr/local/bin
32+
# (writable during build, read-only at runtime — which is fine,
33+
# they're already there).
34+
ENV COREPACK_HOME=/usr/local/share/corepack
35+
RUN mkdir -p "$COREPACK_HOME" \
36+
&& corepack enable \
37+
&& corepack prepare yarn@stable --activate \
38+
&& yarn --version
39+
40+
# ---------------------------------------------------------------------------
41+
# Wrapper for git hooks (husky) — hooks run in subprocesses without
42+
# the agent's PATH modifications. This wrapper in /usr/local/bin
43+
# (on default PATH) ensures `yarn lint-staged` etc. work.
44+
RUN printf '#!/bin/bash\nexport COREPACK_HOME=/usr/local/share/corepack\nexec /usr/bin/corepack yarn "$@"\n' \
45+
> /usr/local/bin/yarn \
46+
&& chmod +x /usr/local/bin/yarn
47+
48+
USER sandbox

0 commit comments

Comments
 (0)