Skip to content

feat: initial Claude Code DevContainer Feature implementation#1

Merged
PKramek merged 55 commits intodevelopfrom
feat/initial-implementation
Apr 2, 2026
Merged

feat: initial Claude Code DevContainer Feature implementation#1
PKramek merged 55 commits intodevelopfrom
feat/initial-implementation

Conversation

@PKramek
Copy link
Copy Markdown
Owner

@PKramek PKramek commented Apr 1, 2026

Summary

  • Universal DevContainer Feature that installs Claude Code CLI into any container
  • Supports Debian, Ubuntu, Alpine, Arch, Fedora, RHEL, Rocky, Alma, Amazon Linux on amd64 + arm64
  • SHA256-verified Node.js binary install; distro packages for Alpine/Arch
  • Shell completions (bash/zsh/fish), MCP config, mount docs, per-distro cache cleanup
  • 10 test scenarios + 27-image amd64 CI matrix + 4-image arm64 matrix
  • Pre-commit hooks: ShellCheck, shfmt, Prettier, markdownlint

Test plan

  • CI lint job passes on this PR
  • src/claude-code/devcontainer-feature.json references claude-devcontainer (not claude-code-devcontainer)
  • README.md badge and GHCR refs use claude-devcontainer

PKramek added 30 commits April 1, 2026 19:45
…ainer Feature

Comprehensive DevContainer Feature for installing Claude Code into any
devcontainer. Includes design spec (3 rounds of review) and implementation
plan (4 rounds of expert review covering shell, security, CI/CD, and
DevContainer ecosystem compliance).
PKramek added 25 commits April 1, 2026 21:15
…r dep, fix completions hang

- Change shebang to #!/bin/sh so Alpine's ash can execute the bootstrap preamble;
  the existing re-exec-with-bash preamble was already correct but unreachable with
  #!/usr/bin/env bash (Alpine has no bash pre-installed)
- Add -ln bash flag to shfmt in CI and pre-commit so bash constructs (local, arrays)
  are not flagged as POSIX violations despite the /bin/sh shebang
- pacman -Sy: sync package database before install (archlinux:latest has no DB)
- Add tar to rhel dependency check (amazonlinux:2023 ships without tar)
- Add timeout 30 and </dev/null to claude completions calls to prevent hang on
  first-run interactive prompts in fresh containers
((var++)) returns exit code 1 when var is 0, killing test.sh under
set -Eeuo pipefail after the first assertion's echo. Replace with
$((var + 1)) assignment which is always safe under strict mode.

Also extend CI grep pattern to catch "Failed:" so the devcontainer
CLI's own test report line (❌ Failed: 'claude-code') is detected and
causes the job to exit 1 rather than silently passing.
npm global install creates the bin wrapper with 777 permissions.
Explicitly chmod 755 after install so the binary is not world-writable.
- check_permissions: stat -Lc '%a' follows symlinks to the real file
  (Linux symlinks always show 0777 via lstat; chmod modifies the target)
- check_file_owner: stat -Lc '%U' for the same reason
- CI grep: add '  FAIL:' pattern so test assertion failures are caught
  even if the devcontainer CLI swallows the test script exit code
- duplicate.sh: satisfies devcontainer framework's idempotency test
  (sources test.sh and runs core_assertions after a second feature install)
- test-arm64: switch from ubuntu-24.04-arm64 (native, never picked up)
  to ubuntu-latest + docker/setup-qemu-action v3.7.0 with
  DOCKER_DEFAULT_PLATFORM=linux/arm64; increase timeout to 60m for QEMU
When install.sh is re-run from its persisted path
(/usr/local/share/devcontainer-features/claude-code/install.sh),
the `cp "$0" "${PERSIST_DIR}/install.sh"` was a same-file copy
that exits non-zero under `set -e`, failing the second run ~5s in.

Resolve canonical paths via readlink -f and skip the copy when
source and destination are the same file.
devcontainer CLI v0.85.0 hardcodes --platform linux/amd64 in its
internal updateUID.Dockerfile step, which remaps the non-root vscode
user UID. On arm64 this step fails with "Failed to launch container".

Raw OS images (ubuntu, alpine) have no pre-existing non-root user so
the UID fixup is skipped and arm64 works. Remove the two devcontainer
base images that trigger the remapping until the CLI bug is fixed.
Add 'Annotate install warnings' step (if: always()) to all three test
jobs. Greps the log for '[claude-code feature] WARNING:' lines and emits
::warning:: annotations — visible as yellow in the PR checks UI without
blocking the build.
- install.sh: gate zsh completions behind 'command -v zsh' so the attempt
  is skipped entirely on images without zsh (mkdir -p was silently creating
  the dir and causing a spurious WARNING on every raw OS image)
- test.yml: add continue-on-error: true + exit 1 when warnings found so
  the Annotate step shows orange/yellow while the overall job stays green
- setup_mount_docs: replace log_info 'WARNING: ...' with log_warn so the
  API keys notice is emitted via the same code path as all other warnings
- CI annotation steps: drop continue-on-error + exit 1 (misleading orange
  step); annotations are the signal, job stays cleanly green or red
- Add sort -u to deduplicate repeated warnings within a single job run
Warnings now appear as a formatted list in the job Summary tab
(GITHUB_STEP_SUMMARY) in addition to the ::warning:: annotations,
so they are visible without expanding the Annotations section.
Docker log lines contain \r (CRLF), causing sort -u to treat identical
warning messages as distinct lines. Pipe through tr -d '\r' before
sort -u so duplicate warnings collapse to a single annotation entry.
The API keys message is documentation printed when mountHostConfig=true
is explicitly set by the user. It is expected output, not an install
failure. Emitting it as WARNING caused CI annotation noise whenever the
mount_host_config scenario ran.
…laims

Add "Why Not Just Use the Official Image?" section explaining the golden
image, size overhead (~120 MB vs 1.5 GB replacement), and composability
arguments. Document known limitations: nodeVersion ignored on Alpine/Arch,
arm64 via QEMU only, readonly mount prevents write-back. Fix arm64 count
(2 images, not 4). Correct the CI badge alt text to match workflow name.
@PKramek PKramek merged commit 3b6ee16 into develop Apr 2, 2026
31 checks passed
@PKramek PKramek deleted the feat/initial-implementation branch April 2, 2026 19:23
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