Skip to content

feat(ralphex-fe): rebuild from Alpine to Debian - #45

Merged
gatezh merged 20 commits into
masterfrom
ralphex-update
Mar 24, 2026
Merged

gatezh merged 20 commits into
masterfrom
ralphex-update

Conversation

@gatezh

@gatezh gatezh commented Mar 24, 2026 •

Copy link
Copy Markdown
Owner

What

Rebuild the ralphex-fe standalone Docker image from node:24-trixie-slim (Debian) instead of the Alpine-based ghcr.io/umputun/ralphex base image, eliminating all Alpine/musl compatibility hacks while maintaining full compatibility with the ralphex-dk.sh docker-wrapper script.

Why

The previous Alpine-based image required painful workarounds:

  • gcompat shim for Hugo Extended (glibc binary on musl)
  • PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD + manual system Chromium + executablePath wiring (Playwright's bundled browser doesn't work on Alpine/musl)
  • Limited apk package ecosystem

On Debian, all tools work natively with zero compatibility hacks.

Changes

Core: ralphex-fe Dockerfile rewrite

  • New base: node:24-trixie-slim (Debian) instead of ghcr.io/umputun/ralphex (Alpine)
  • New entrypoint: files/init.sh — adapted from umputun/baseimage for Debian (gosu instead of su-exec, groupadd/groupdel instead of addgroup/delgroup)
  • New credential copier: files/init-docker.sh — from umputun/ralphex (handles ~/.claude and ~/.codex mounts)
  • App user: app with UID 1001, matching ralphex wrapper expectations (remappable via APP_UID env var)
  • Tools added: Python 3, Go, Docker CLI, fzf, Playwright (native Debian two-layer install)
  • Tools removed: gcompat, system Chromium, ttf-freefont (no longer needed)
  • Env vars removed: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD, PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH (Alpine-only hacks)

Build optimizations

  • Go: direct binary from go.dev/dl/ instead of apt golang-go (matches ralphex-go pattern)
  • Docker CLI: static binary from download.docker.com instead of docker.io package (avoids containerd, runc, iptables — matches ralphex base's docker-cli)
  • Docker best practices: SHELL pipefail for curl|bash, useradd --no-log-init, download-then-extract (no curl|tar pipes), consolidated LABEL

Build workflow: migrated to docker/github-builder

  • Builds amd64 and arm64 in parallel on native runners (no QEMU emulation)
  • Verifies on native runners (ubuntu-24.04 + ubuntu-24.04-arm)
  • Drops the old reusable-docker-build.yml dependency

CI improvements

  • Upgraded all actions to latest stable: actions/checkout@v6, docker/*@v4/v7, dorny/paths-filter@v4
  • Added DL3002 and DL3016 to hadolint ignores (intentional: entrypoint handles privilege drop; Claude Code intentionally unpinned)
  • Added pre-commit CI validation rule to .claude/CLAUDE.md

Documentation

  • Rewritten README with ralphex wrapper usage, runtime env vars, provenance table
  • Implementation plan at docs/superpowers/plans/2026-03-23-ralphex-fe-debian-rebuild.md

Housekeeping (earlier commits on this branch)

  • Restructured .claude/CLAUDE.md into path-scoped rules under .claude/rules/
  • Added quality-of-life settings to devcontainer configs (formatter, theme, NODE_OPTIONS)
  • Fixed missing bun install in sandbox postCreateCommand

Verified

  • Local build succeeds on arm64
  • All tools verified: Node 24.14, Bun 1.3.9, Hugo 0.156.0, Go 1.24.4, Python 3.13.5, Docker CLI 29.3.0, Ralphex 0.25.0, Claude Code 2.1.81
  • APP_UID remapping works (tested with host UID 502)
  • init.sh → /srv/init.sh → gosu privilege drop chain works
  • Hadolint + actionlint pass locally
  • 3 rounds of code review passed (ralphex compatibility, Dockerfile best practices, GitHub Actions)

🤖 Generated with Claude Code

gatezh and others added 20 commits March 23, 2026 11:20
- Without bun install, npx playwright fails due to npm 11 cache key
  mismatch between versioned build-time and unversioned runtime lookups

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- updateRemoteUserUID, otherPortsAttributes (silent auto-forward)
- editor.formatOnSave + OXC default formatter with customization guide
- window.title for multi-window distinction, NODE_OPTIONS 4GB heap
- Sandbox: Claude Dark theme, coral status bar, skip permissions
- Plugins: frontend-design, typescript-lsp, playwright, posthog
- Firewall: allow OpenAI domains (auth, api, chatgpt)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Slim CLAUDE.md from 290 to 39 lines (core rules only)
- Add .claude/rules/ with path-scoped files: dockerfile, devcontainer, workflows, new-image
- Fix stale references: Biome→OXC, zsh→fish, remove AGENTS.md symlink mentions
- Remove derivable content (directory tree, README patterns)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
init.sh adapted from umputun/baseimage for Debian (gosu, groupadd/groupdel).
init-docker.sh copied from umputun/ralphex (credential copying from mounts).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rixie-slim)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Drop RALPHEX_VERSION extraction (binary fetched as latest at build time)
- Add ralphex-fe/files/** to trigger paths
- Update verify commands for Debian (app user, Playwright cache, init scripts)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add docker.io package (needed for wrapper's --docker flag)
- Add fzf (fuzzy finder, matches official ralphex image)
- Add SHELL pipefail for Bun curl|bash install (Docker best practice)
- Upgrade actions/checkout to v6

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- useradd --no-log-init prevents large sparse log files (Docker best practice)
- Ralphex download separated from tar extraction to avoid masked curl failures
  in pipeless /bin/sh context

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DL3002 (last USER not root): ralphex-fe entrypoint runs as root for
  APP_UID remapping, then drops to app user via gosu
- DL3016 (pin npm versions): Claude Code intentionally unpinned —
  images rebuild daily to always get latest

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
docker.io package creates a docker group during installation.
Use groupadd -f (idempotent) + groupmod to ensure GID 999.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- actions/checkout: v4 → v6
- docker/setup-qemu-action: v3 → v4
- docker/setup-buildx-action: v3 → v4
- docker/build-push-action: v5 → v7

Resolves Node.js 20 deprecation warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… platforms

Replace sequential QEMU-based build with docker/github-builder which
builds amd64 and arm64 in parallel on native runners. Verification
runs on native runners (ubuntu-24.04 + ubuntu-24.04-arm).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolves Node.js 20 deprecation warning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…downloads

- Docker CLI: static binary from download.docker.com instead of docker.io
  package (avoids containerd, runc, iptables — ~55MB of unneeded deps)
- Go: direct binary from go.dev/dl/ instead of apt golang-go (avoids
  ~200MB of Go source + build deps via apt)

Both match the patterns used in the official ralphex images.
End result is identical: same Go version, same Docker CLI functionality.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dockerfile: restructured as multi-stage build — Go, Docker CLI, Hugo,
and Ralphex binaries download in parallel stages (BuildKit runs
independent stages concurrently). Final stage COPY --from each.

CI: dropped arm64 QEMU build and QEMU setup step. arm64 is tested
on native runners at merge time via docker/github-builder — no need
to emulate it in pre-merge CI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…p wget

- Playwright: install only Chromium headless shell (was installing Firefox
  + WebKit too). This image is for headless Claude Code testing only.
- apt-get: use --mount=type=cache for /var/cache/apt and /var/lib/apt/lists
  (Docker best practice — avoids re-downloading on layer rebuild)
- npm: use --mount=type=cache for /root/.npm
- Remove wget from final stage (Hugo downloads in parallel stage now)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@gatezh
gatezh merged commit db9312a into master Mar 24, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant