From 780dfa672810e63970b8dcfc70c565f8cfd1b6e4 Mon Sep 17 00:00:00 2001 From: bilby91 Date: Fri, 12 Jun 2026 13:45:35 -0300 Subject: [PATCH 1/2] devcontainer: add prebuild-based dev environment + CI Mirrors the crunchloop/dap devcontainer strategy: a two-file build split where CI prebuilds a multi-arch toolchain image and the runtime config just pulls it. - devcontainer-build.json: base:debian + Go 1.26, Node 22, github-cli, docker-in-docker, and local golangci-lint (v2.5.0, pinned to Makefile) and claude-code features. - devcontainer.json + docker-compose.yml: pull the prebuilt image; dind (privileged) so the integration suite can drive docker compose. - post-create.sh: persist Claude config across rebuilds + go mod download. - devcontainer-cache.yml: multi-arch (amd64/arm64) prebuild on main, merged into a :latest manifest on GHCR. - devcontainer-release.yml: publish local features. The Apple container backend stays darwin/arm64-only and is not built in this Linux container, matching the Linux CI jobs. Co-Authored-By: Claude Opus 4.8 --- .devcontainer/README.md | 63 ++++++++++++++ .devcontainer/devcontainer-build.json | 45 ++++++++++ .devcontainer/devcontainer.json | 35 ++++++++ .devcontainer/docker-compose.yml | 16 ++++ .../claude-code/devcontainer-feature.json | 17 ++++ .devcontainer/features/claude-code/install.sh | 9 ++ .../golangci-lint/devcontainer-feature.json | 17 ++++ .../features/golangci-lint/install.sh | 48 ++++++++++ .devcontainer/post-create.sh | 40 +++++++++ .github/workflows/devcontainer-cache.yml | 87 +++++++++++++++++++ .github/workflows/devcontainer-release.yml | 48 ++++++++++ 11 files changed, 425 insertions(+) create mode 100644 .devcontainer/README.md create mode 100644 .devcontainer/devcontainer-build.json create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml create mode 100644 .devcontainer/features/claude-code/devcontainer-feature.json create mode 100755 .devcontainer/features/claude-code/install.sh create mode 100644 .devcontainer/features/golangci-lint/devcontainer-feature.json create mode 100755 .devcontainer/features/golangci-lint/install.sh create mode 100755 .devcontainer/post-create.sh create mode 100644 .github/workflows/devcontainer-cache.yml create mode 100644 .github/workflows/devcontainer-release.yml diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 0000000..28c89bb --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,63 @@ +# Development Container Configuration + +This directory contains the devcontainer configuration for developing the +`crunchloop/devcontainer` CLI. + +## Key Concepts + +The devcontainer uses a **prebuild strategy** (the same one as +`crunchloop/dap`): + +1. CI builds a complete development environment image using + `devcontainer-build.json` (base image + features). +2. The image is published multi-arch (amd64 + arm64) to the GitHub Container + Registry as `ghcr.io/crunchloop/devcontainer/devcontainer:latest`. +3. Developers pull that prebuild image via `devcontainer.json` → + `docker-compose.yml` instead of building the toolchain locally. +4. `post-create.sh` runs lightweight, per-checkout setup (Go module download, + Claude config persistence). + +This keeps container startup fast while the toolchain stays reproducible. + +## Contents + +- `devcontainer.json` — local development configuration (used by developers). +- `devcontainer-build.json` — prebuild image configuration (used by CI). +- `docker-compose.yml` — runs the prebuilt `app` service. +- `post-create.sh` — per-checkout setup hook. +- `features/` — local devcontainer features: + - `golangci-lint` — installs the linter pinned to the Makefile / `ci.yml` + version (`v2.5.0`). + - `claude-code` — installs the Claude Code CLI. + +## Toolchain + +The prebuild image provides everything the Linux CI jobs need: + +- **Go** 1.26 (CI also exercises 1.25; `go.mod` declares 1.25.0). +- **golangci-lint** `v2.5.0` (keep in sync with `Makefile`'s + `GOLANGCI_LINT_VERSION` and the `ci.yml` lint job). +- **docker-in-docker** so the integration suite + (`go test -tags=integration ./test/integration/...`) can drive + `docker` / `docker compose` from inside the container. +- **GitHub CLI**, **Node.js 22** (for Claude Code), and `make`. + +> The Apple `container` backend (`runtime/applecontainer`) is darwin/arm64-only +> and cannot be built inside this Linux container — exactly as on the Linux CI +> jobs, where `make bridge` is a no-op. Use a native macOS checkout for that +> backend. + +## Common tasks + +```bash +make lint # golangci-lint run ./... +make test # go test -race ./... (bridge is a no-op on Linux) +make test-integration # docker-backed integration suite +``` + +## CI + +- `.github/workflows/devcontainer-cache.yml` — rebuilds and republishes the + prebuild image on pushes to `main` that touch `.devcontainer/**`. +- `.github/workflows/devcontainer-release.yml` — publishes the local + `features/` to GHCR (manual dispatch). diff --git a/.devcontainer/devcontainer-build.json b/.devcontainer/devcontainer-build.json new file mode 100644 index 0000000..95f288c --- /dev/null +++ b/.devcontainer/devcontainer-build.json @@ -0,0 +1,45 @@ +{ + "name": "devcontainer-build", + "image": "mcr.microsoft.com/devcontainers/base:debian", + + // Features to add to the dev container. More info: https://containers.dev/features. + "features": { + // Registry features + "ghcr.io/devcontainers/features/go:1": { + // Primary dev toolchain. CI also exercises 1.25 (see + // .github/workflows/ci.yml matrix); go.mod declares go 1.25.0. + "version": "1.26" + }, + "ghcr.io/devcontainers/features/node:1": { + // Required by the local claude-code feature (installs via npm). + "version": "22" + }, + "ghcr.io/devcontainers/features/github-cli:1": {}, + "ghcr.io/devcontainers/features/docker-in-docker:1": {}, + "ghcr.io/devcontainers-extra/features/vscode-server:1": {}, + "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { + "packages": "make" + }, + + // Local features + "./features/golangci-lint": { "version": "2.5.0" }, + "./features/claude-code": { "version": "2.1.170" } + }, + + // Customize the vscode extensions that are installed. + "customizations": { + "vscode": { + "extensions": [ + "golang.go", + "streetsidesoftware.code-spell-checker", + "mhutchie.git-graph", + "eamodio.gitlens", + "GitHub.copilot", + "GitHub.copilot-chat", + "GitHub.vscode-pull-request-github", + "github.vscode-github-actions" + ], + "settings": {} + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..76dbce5 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,35 @@ +{ + "name": "devcontainer", + + // Local development configuration. The heavy toolchain image is built in + // CI from devcontainer-build.json and published to GHCR; here we just pull + // it via docker-compose. See README.md for the prebuild strategy. + "dockerComposeFile": [ + "docker-compose.yml" + ], + + "service": "app", + + "workspaceFolder": "/workspaces/devcontainer", + + // Keep containers running after VS Code shuts down. + "shutdownAction": "stopCompose", + + "postCreateCommand": ".devcontainer/post-create.sh", + + "customizations": { + "vscode": { + "extensions": [ + "golang.go", + "streetsidesoftware.code-spell-checker", + "mhutchie.git-graph", + "eamodio.gitlens", + "GitHub.copilot", + "GitHub.copilot-chat", + "GitHub.vscode-pull-request-github", + "github.vscode-github-actions" + ], + "settings": {} + } + } +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..148d259 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,16 @@ +volumes: + claude-config: + +services: + app: + image: ghcr.io/crunchloop/devcontainer/devcontainer:latest + # privileged is required by the docker-in-docker feature so the + # in-container dockerd can start. The integration suite shells out to + # `docker` / `docker compose` (see test/integration and ci.yml), so the + # daemon must be available inside the workspace. + privileged: true + command: sleep infinity + volumes: + - ..:/workspaces/devcontainer + # Persist Claude Code config/credentials across container rebuilds. + - claude-config:/home/vscode/.claude diff --git a/.devcontainer/features/claude-code/devcontainer-feature.json b/.devcontainer/features/claude-code/devcontainer-feature.json new file mode 100644 index 0000000..ab3c73b --- /dev/null +++ b/.devcontainer/features/claude-code/devcontainer-feature.json @@ -0,0 +1,17 @@ +{ + "id": "claude-code", + "version": "0.0.1", + "name": "Claude Code", + "description": "Installs the Claude Code CLI via npm. Depends on the Node.js feature.", + "documentationURL": "https://docs.anthropic.com/en/docs/claude-code", + "installsAfter": [ + "ghcr.io/devcontainers/features/node" + ], + "options": { + "version": { + "type": "string", + "default": "2.1.170", + "description": "Version of Claude Code to install (e.g., '2.1.170' or 'latest')." + } + } +} diff --git a/.devcontainer/features/claude-code/install.sh b/.devcontainer/features/claude-code/install.sh new file mode 100755 index 0000000..8479b33 --- /dev/null +++ b/.devcontainer/features/claude-code/install.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -e + +VERSION=${VERSION:-2.1.170} + +echo "Installing Claude Code (version: $VERSION)..." +npm install -g "@anthropic-ai/claude-code@$VERSION" + +echo "Claude Code installed successfully!" diff --git a/.devcontainer/features/golangci-lint/devcontainer-feature.json b/.devcontainer/features/golangci-lint/devcontainer-feature.json new file mode 100644 index 0000000..a6c8acd --- /dev/null +++ b/.devcontainer/features/golangci-lint/devcontainer-feature.json @@ -0,0 +1,17 @@ +{ + "id": "golangci-lint", + "version": "0.0.1", + "name": "golangci-lint", + "description": "Installs golangci-lint, the Go linters aggregator. Keep the default version in sync with the Makefile GOLANGCI_LINT_VERSION and the ci.yml lint job.", + "documentationURL": "https://golangci-lint.run", + "installsAfter": [ + "ghcr.io/devcontainers/features/go" + ], + "options": { + "version": { + "type": "string", + "default": "2.5.0", + "description": "Version of golangci-lint to install, without the leading 'v' (e.g. '2.5.0')." + } + } +} diff --git a/.devcontainer/features/golangci-lint/install.sh b/.devcontainer/features/golangci-lint/install.sh new file mode 100755 index 0000000..e0a3013 --- /dev/null +++ b/.devcontainer/features/golangci-lint/install.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -e + +VERSION=${VERSION:-2.5.0} + +echo "Installing golangci-lint (version: $VERSION)..." + +# Detect architecture +ARCH=$(uname -m) +case $ARCH in + x86_64) + ARCH="amd64" + ;; + aarch64|arm64) + ARCH="arm64" + ;; + *) + echo "Unsupported architecture: $ARCH" + exit 1 + ;; +esac + +# Detect OS +OS=$(uname -s | tr '[:upper:]' '[:lower:]') + +ASSET="golangci-lint-${VERSION}-${OS}-${ARCH}" +DOWNLOAD_URL="https://github.com/golangci/golangci-lint/releases/download/v${VERSION}/${ASSET}.tar.gz" + +echo "Downloading from: $DOWNLOAD_URL" + +TEMP_DIR=$(mktemp -d) +cd "$TEMP_DIR" + +curl -sL "$DOWNLOAD_URL" -o golangci-lint.tar.gz +tar -xzf golangci-lint.tar.gz + +# The archive extracts into a directory named after the asset. +cp "${ASSET}/golangci-lint" /usr/local/bin/golangci-lint +chmod +x /usr/local/bin/golangci-lint + +# Cleanup +cd / +rm -rf "$TEMP_DIR" + +# Verify installation +golangci-lint --version + +echo "golangci-lint feature installed successfully!" diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100755 index 0000000..ee03442 --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Post-create script for the devcontainer dev environment. +# Runs once after the container is created. + +set -e + +cd /workspaces/devcontainer + +# Fix ownership of the claude-config volume (Docker named volumes default to root) +sudo chown -R vscode:vscode /home/vscode/.claude + +# Persist .claude.json across rebuilds by symlinking into the volume. +# The file lives at ~/.claude.json but the volume only mounts ~/.claude/, +# so without this symlink it's lost on every container rebuild. +if [ ! -L /home/vscode/.claude.json ]; then + # Migrate existing .claude.json into the volume if present + if [ -f /home/vscode/.claude.json ]; then + mv /home/vscode/.claude.json /home/vscode/.claude/.claude.json + fi + + # Restore from backup if no .claude.json exists in the volume yet + if [ ! -f /home/vscode/.claude/.claude.json ]; then + BACKUP=$(ls -t /home/vscode/.claude/backups/.claude.json.backup.* 2>/dev/null | head -1) + if [ -n "$BACKUP" ]; then + cp "$BACKUP" /home/vscode/.claude/.claude.json + echo "Restored .claude.json from backup: $BACKUP" + fi + fi + + ln -sf /home/vscode/.claude/.claude.json /home/vscode/.claude.json + echo "Symlinked ~/.claude.json -> ~/.claude/.claude.json" +fi + +# Warm the Go module + build caches so the first `make test` / `make lint` +# is fast. golangci-lint is baked into the image (local feature), so we only +# need to fetch dependencies here. +echo "Downloading Go module dependencies..." +go mod download + +echo "Post-create setup complete." diff --git a/.github/workflows/devcontainer-cache.yml b/.github/workflows/devcontainer-cache.yml new file mode 100644 index 0000000..7020ad5 --- /dev/null +++ b/.github/workflows/devcontainer-cache.yml @@ -0,0 +1,87 @@ +name: DevContainer Prebuild + +on: + push: + branches: + - main + paths: + - '.devcontainer/**' + - '.github/workflows/devcontainer-cache.yml' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + packages: write + id-token: write + +env: + DEVCONTAINER_IMAGE: ghcr.io/crunchloop/devcontainer/devcontainer + +jobs: + prebuild: + name: Build devcontainer prebuild (${{ matrix.platform }}) + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + suffix: amd64 + - platform: linux/arm64 + runner: ubuntu-24.04-arm + suffix: arm64 + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # devcontainers/ci doesn't cleanly expose digest-only push, so we push + # per-arch tags and merge them into a multi-arch manifest below. + - name: Build devcontainer prebuild image + uses: devcontainers/ci@v0.3 + with: + configFile: .devcontainer/devcontainer-build.json + imageName: ${{ env.DEVCONTAINER_IMAGE }} + imageTag: build-${{ github.run_id }}-${{ matrix.suffix }} + platform: ${{ matrix.platform }} + cacheFrom: ${{ env.DEVCONTAINER_IMAGE }}:buildcache-${{ matrix.suffix }} + push: always + + merge: + name: Merge multi-arch manifest + needs: prebuild + runs-on: ubuntu-latest + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create manifest list and push + run: | + docker buildx imagetools create \ + -t ${{ env.DEVCONTAINER_IMAGE }}:latest \ + ${{ env.DEVCONTAINER_IMAGE }}:build-${{ github.run_id }}-amd64 \ + ${{ env.DEVCONTAINER_IMAGE }}:build-${{ github.run_id }}-arm64 + + - name: Inspect manifest + run: docker buildx imagetools inspect ${{ env.DEVCONTAINER_IMAGE }}:latest diff --git a/.github/workflows/devcontainer-release.yml b/.github/workflows/devcontainer-release.yml new file mode 100644 index 0000000..16ff972 --- /dev/null +++ b/.github/workflows/devcontainer-release.yml @@ -0,0 +1,48 @@ +name: Devcontainer Features Release +on: + workflow_dispatch: + +jobs: + publish: + if: ${{ github.ref == 'refs/heads/main' }} + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + packages: write + steps: + - uses: actions/checkout@v4 + + - name: Publish Features + uses: devcontainers/action@v1 + with: + publish-features: "true" + base-path-to-features: "./.devcontainer/features" + generate-docs: "true" + features-namespace: "crunchloop/devcontainer/devcontainers-features" + + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create PR for Documentation + id: push_image_info + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -e + echo "Start." + # Configure git and Push updates + git config --global user.email github-actions[bot]@users.noreply.github.com + git config --global user.name github-actions[bot] + git config pull.rebase false + branch=automated-documentation-update-$GITHUB_RUN_ID + git checkout -b $branch + message='Automated documentation update' + # Add / update and commit + git add */**/README.md + git commit -m 'Automated documentation update [skip ci]' || export NO_UPDATES=true + # Push + if [ "$NO_UPDATES" != "true" ] ; then + git push origin "$branch" + gh pr create --title "$message" --body "$message" + fi From 1fc9d593ba14fec8305188241d04d84ab1321ebc Mon Sep 17 00:00:00 2001 From: bilby91 Date: Fri, 12 Jun 2026 13:57:46 -0300 Subject: [PATCH 2/2] devcontainer: trim to Go-only toolchain Drop node, vscode-server, and the claude-code feature, plus all vscode customizations and the claude-config volume/symlink plumbing that only existed for Claude Code. The image is now base + Go, github-cli, docker-in-docker, make, and the golangci-lint local feature. Co-Authored-By: Claude Opus 4.8 --- .devcontainer/README.md | 11 +++---- .devcontainer/devcontainer-build.json | 25 +-------------- .devcontainer/devcontainer.json | 18 +---------- .devcontainer/docker-compose.yml | 5 --- .../claude-code/devcontainer-feature.json | 17 ---------- .devcontainer/features/claude-code/install.sh | 9 ------ .devcontainer/post-create.sh | 31 ++----------------- 7 files changed, 9 insertions(+), 107 deletions(-) delete mode 100644 .devcontainer/features/claude-code/devcontainer-feature.json delete mode 100755 .devcontainer/features/claude-code/install.sh diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 28c89bb..6af7dbe 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -14,8 +14,7 @@ The devcontainer uses a **prebuild strategy** (the same one as Registry as `ghcr.io/crunchloop/devcontainer/devcontainer:latest`. 3. Developers pull that prebuild image via `devcontainer.json` → `docker-compose.yml` instead of building the toolchain locally. -4. `post-create.sh` runs lightweight, per-checkout setup (Go module download, - Claude config persistence). +4. `post-create.sh` runs lightweight, per-checkout setup (Go module download). This keeps container startup fast while the toolchain stays reproducible. @@ -25,10 +24,8 @@ This keeps container startup fast while the toolchain stays reproducible. - `devcontainer-build.json` — prebuild image configuration (used by CI). - `docker-compose.yml` — runs the prebuilt `app` service. - `post-create.sh` — per-checkout setup hook. -- `features/` — local devcontainer features: - - `golangci-lint` — installs the linter pinned to the Makefile / `ci.yml` - version (`v2.5.0`). - - `claude-code` — installs the Claude Code CLI. +- `features/golangci-lint` — local feature installing the linter pinned to the + Makefile / `ci.yml` version (`v2.5.0`). ## Toolchain @@ -40,7 +37,7 @@ The prebuild image provides everything the Linux CI jobs need: - **docker-in-docker** so the integration suite (`go test -tags=integration ./test/integration/...`) can drive `docker` / `docker compose` from inside the container. -- **GitHub CLI**, **Node.js 22** (for Claude Code), and `make`. +- **GitHub CLI** and `make`. > The Apple `container` backend (`runtime/applecontainer`) is darwin/arm64-only > and cannot be built inside this Linux container — exactly as on the Linux CI diff --git a/.devcontainer/devcontainer-build.json b/.devcontainer/devcontainer-build.json index 95f288c..c9a3035 100644 --- a/.devcontainer/devcontainer-build.json +++ b/.devcontainer/devcontainer-build.json @@ -10,36 +10,13 @@ // .github/workflows/ci.yml matrix); go.mod declares go 1.25.0. "version": "1.26" }, - "ghcr.io/devcontainers/features/node:1": { - // Required by the local claude-code feature (installs via npm). - "version": "22" - }, "ghcr.io/devcontainers/features/github-cli:1": {}, "ghcr.io/devcontainers/features/docker-in-docker:1": {}, - "ghcr.io/devcontainers-extra/features/vscode-server:1": {}, "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { "packages": "make" }, // Local features - "./features/golangci-lint": { "version": "2.5.0" }, - "./features/claude-code": { "version": "2.1.170" } - }, - - // Customize the vscode extensions that are installed. - "customizations": { - "vscode": { - "extensions": [ - "golang.go", - "streetsidesoftware.code-spell-checker", - "mhutchie.git-graph", - "eamodio.gitlens", - "GitHub.copilot", - "GitHub.copilot-chat", - "GitHub.vscode-pull-request-github", - "github.vscode-github-actions" - ], - "settings": {} - } + "./features/golangci-lint": { "version": "2.5.0" } } } diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 76dbce5..d533528 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -15,21 +15,5 @@ // Keep containers running after VS Code shuts down. "shutdownAction": "stopCompose", - "postCreateCommand": ".devcontainer/post-create.sh", - - "customizations": { - "vscode": { - "extensions": [ - "golang.go", - "streetsidesoftware.code-spell-checker", - "mhutchie.git-graph", - "eamodio.gitlens", - "GitHub.copilot", - "GitHub.copilot-chat", - "GitHub.vscode-pull-request-github", - "github.vscode-github-actions" - ], - "settings": {} - } - } + "postCreateCommand": ".devcontainer/post-create.sh" } diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 148d259..f67dd44 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -1,6 +1,3 @@ -volumes: - claude-config: - services: app: image: ghcr.io/crunchloop/devcontainer/devcontainer:latest @@ -12,5 +9,3 @@ services: command: sleep infinity volumes: - ..:/workspaces/devcontainer - # Persist Claude Code config/credentials across container rebuilds. - - claude-config:/home/vscode/.claude diff --git a/.devcontainer/features/claude-code/devcontainer-feature.json b/.devcontainer/features/claude-code/devcontainer-feature.json deleted file mode 100644 index ab3c73b..0000000 --- a/.devcontainer/features/claude-code/devcontainer-feature.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "id": "claude-code", - "version": "0.0.1", - "name": "Claude Code", - "description": "Installs the Claude Code CLI via npm. Depends on the Node.js feature.", - "documentationURL": "https://docs.anthropic.com/en/docs/claude-code", - "installsAfter": [ - "ghcr.io/devcontainers/features/node" - ], - "options": { - "version": { - "type": "string", - "default": "2.1.170", - "description": "Version of Claude Code to install (e.g., '2.1.170' or 'latest')." - } - } -} diff --git a/.devcontainer/features/claude-code/install.sh b/.devcontainer/features/claude-code/install.sh deleted file mode 100755 index 8479b33..0000000 --- a/.devcontainer/features/claude-code/install.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -e - -VERSION=${VERSION:-2.1.170} - -echo "Installing Claude Code (version: $VERSION)..." -npm install -g "@anthropic-ai/claude-code@$VERSION" - -echo "Claude Code installed successfully!" diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index ee03442..20d3083 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -6,34 +6,9 @@ set -e cd /workspaces/devcontainer -# Fix ownership of the claude-config volume (Docker named volumes default to root) -sudo chown -R vscode:vscode /home/vscode/.claude - -# Persist .claude.json across rebuilds by symlinking into the volume. -# The file lives at ~/.claude.json but the volume only mounts ~/.claude/, -# so without this symlink it's lost on every container rebuild. -if [ ! -L /home/vscode/.claude.json ]; then - # Migrate existing .claude.json into the volume if present - if [ -f /home/vscode/.claude.json ]; then - mv /home/vscode/.claude.json /home/vscode/.claude/.claude.json - fi - - # Restore from backup if no .claude.json exists in the volume yet - if [ ! -f /home/vscode/.claude/.claude.json ]; then - BACKUP=$(ls -t /home/vscode/.claude/backups/.claude.json.backup.* 2>/dev/null | head -1) - if [ -n "$BACKUP" ]; then - cp "$BACKUP" /home/vscode/.claude/.claude.json - echo "Restored .claude.json from backup: $BACKUP" - fi - fi - - ln -sf /home/vscode/.claude/.claude.json /home/vscode/.claude.json - echo "Symlinked ~/.claude.json -> ~/.claude/.claude.json" -fi - -# Warm the Go module + build caches so the first `make test` / `make lint` -# is fast. golangci-lint is baked into the image (local feature), so we only -# need to fetch dependencies here. +# Warm the Go module cache so the first `make test` / `make lint` is fast. +# golangci-lint is baked into the image (local feature), so we only need to +# fetch dependencies here. echo "Downloading Go module dependencies..." go mod download