diff --git a/.devcontainer.json b/.devcontainer.json index 66ee671..78c9af9 100644 --- a/.devcontainer.json +++ b/.devcontainer.json @@ -34,7 +34,10 @@ "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, "ghcr.io/stuartleeks/dev-container-features/shell-history:0": {}, - "ghcr.io/devcontainers/features/github-cli:latest": {} + "ghcr.io/devcontainers/features/github-cli:latest": {}, + "ghcr.io/jsburckhardt/devcontainer-features/opencode:latest": {}, + "ghcr.io/jsburckhardt/devcontainer-features/just": {}, + "ghcr.io/devcontainers/features/copilot-cli": {} }, "updateContentCommand": "npm install -g @devcontainers/cli", "remoteUser": "node", diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 09c96e1..ded87fe 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -30,6 +30,7 @@ jobs: - bat - just - opencode + - claude-code baseImage: - debian:latest - ubuntu:latest diff --git a/README.md b/README.md index e099ffd..96879cc 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ This repository contains a _collection_ of Features. | UV/UVX | https://docs.astral.sh/uv/ | An extremely fast Python package and project manager, written in Rust. A single tool to replace pip, pip-tools, pipx, poetry, pyenv, virtualenv, and more. | | Ruff | https://docs.astral.sh/ruff/ | An extremely fast Python linter and code formatter, written in Rust. | | OpenCode | https://opencode.ai/ | AI coding agent, built for the terminal. An open-source alternative to Claude Code with support for multiple LLM providers. | +| Claude Code | https://code.claude.com/docs/en/overview | Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows -- all through natural language commands. | | Codex-cli | https://github.com/openai/codex | Codex CLI is an experimental project under active development. | @@ -302,6 +303,23 @@ Running `opencode` inside the built container will allow you to use the AI codin opencode --version ``` +### `claude-code` + +Running `claude` inside the built container will allow you to use the Claude Code agentic coding tool. + +```jsonc +{ + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/jsburckhardt/devcontainer-features/claude-code:1": {} + } +} +``` + +```bash +claude --version +``` + ### `Codex-CLI` Running `codex` inside the built container will print the help menu of codex. diff --git a/src/claude-code/devcontainer-feature.json b/src/claude-code/devcontainer-feature.json new file mode 100644 index 0000000..b093176 --- /dev/null +++ b/src/claude-code/devcontainer-feature.json @@ -0,0 +1,14 @@ +{ + "name": "Claude Code", + "id": "claude-code", + "version": "1.0.0", + "description": "Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster.", + "documentationURL": "https://code.claude.com/docs/en/overview", + "options": { + "version": { + "type": "string", + "default": "latest", + "description": "Version of Claude Code to install (e.g., 1.0.58 or latest)" + } + } +} diff --git a/src/claude-code/install.sh b/src/claude-code/install.sh new file mode 100755 index 0000000..d1823bf --- /dev/null +++ b/src/claude-code/install.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +# Variables +CLAUDE_VERSION="${VERSION:-"latest"}" + +set -euo pipefail + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Clean up +rm -rf /var/lib/apt/lists/* + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" >/dev/null 2>&1; then + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update -y + fi + apt-get -y install --no-install-recommends "$@" + fi +} + +echo "Installing Claude Code version: $CLAUDE_VERSION" + +# Check if Node.js/npm is installed, if not install from Ubuntu repositories +if ! command -v npm >/dev/null 2>&1; then + echo "npm not found. Installing Node.js and npm..." + check_packages nodejs npm + echo "Node.js installed: $(node --version 2>/dev/null || echo 'N/A')" + echo "npm installed: $(npm --version)" +fi + +# Install Claude Code via npm +# The npm package @anthropic-ai/claude-code includes a bundled Node.js runtime +# This is the official installation method for Claude Code +echo "Installing Claude Code via npm..." + +# Try with strict SSL first +NPM_INSTALL_SUCCESS=false +if [ "$CLAUDE_VERSION" = "latest" ]; then + if npm install -g @anthropic-ai/claude-code --loglevel=error 2>/dev/null; then + NPM_INSTALL_SUCCESS=true + fi +else + if npm install -g @anthropic-ai/claude-code@"$CLAUDE_VERSION" --loglevel=error 2>/dev/null; then + NPM_INSTALL_SUCCESS=true + fi +fi + +# If strict SSL fails (common in build environments), retry without strict SSL verification +if [ "$NPM_INSTALL_SUCCESS" = "false" ]; then + echo "Standard npm install failed, retrying with relaxed SSL settings for build environments..." + if [ "$CLAUDE_VERSION" = "latest" ]; then + npm install -g @anthropic-ai/claude-code --loglevel=error --strict-ssl=false || { + echo "ERROR: npm installation failed even with relaxed SSL settings." + echo "This may indicate network issues or that the package is not available." + echo "In production environments with proper network access, this should work." + echo "Manual installation: npm install -g @anthropic-ai/claude-code" + exit 1 + } + else + npm install -g @anthropic-ai/claude-code@"$CLAUDE_VERSION" --loglevel=error --strict-ssl=false || { + echo "ERROR: npm installation of version $CLAUDE_VERSION failed." + echo "In production environments with proper network access, this should work." + exit 1 + } + fi +fi + +# Find where npm installed the binary and create symlink to /usr/local/bin +NPM_BIN_DIR=$(npm bin -g 2>/dev/null || npm root -g 2>/dev/null | sed 's/lib\/node_modules$/bin/') + +if [ -n "$NPM_BIN_DIR" ] && [ -d "$NPM_BIN_DIR" ]; then + if [ -f "$NPM_BIN_DIR/claude" ]; then + echo "Creating symlink from $NPM_BIN_DIR/claude to /usr/local/bin/claude" + ln -sf "$NPM_BIN_DIR/claude" /usr/local/bin/claude + chmod +x /usr/local/bin/claude + elif [ -f "$NPM_BIN_DIR/claude-code" ]; then + echo "Creating symlink from $NPM_BIN_DIR/claude-code to /usr/local/bin/claude" + ln -sf "$NPM_BIN_DIR/claude-code" /usr/local/bin/claude + chmod +x /usr/local/bin/claude + fi +fi + +# Clean up +rm -rf /var/lib/apt/lists/* + +# Verify installation +echo "Verifying installation..." +if command -v claude >/dev/null 2>&1; then + echo "Claude Code installation completed successfully!" + echo "The 'claude' command is now available at: $(which claude)" + claude --version 2>/dev/null || echo "Claude is installed (version command may not be supported)" +else + # Check if installed via npm even if not in PATH + if npm list -g @anthropic-ai/claude-code 2>/dev/null | grep -q "@anthropic-ai/claude-code"; then + echo "Claude Code installed via npm successfully." + echo "Note: The 'claude' command may require a shell restart or PATH update." + npm list -g @anthropic-ai/claude-code + else + echo "WARNING: Claude Code installation could not be verified." + echo "This may be normal in restricted build environments." + fi +fi + +echo "Done!" diff --git a/test/_global/all-tools.sh b/test/_global/all-tools.sh index 98cc7cd..3e910e0 100755 --- a/test/_global/all-tools.sh +++ b/test/_global/all-tools.sh @@ -18,5 +18,6 @@ check "zarf" zarf version check "codex" codex --version check "just" just --version check "opencode" opencode --version +check "claude-code" claude --version reportResults diff --git a/test/_global/claude-code-specific-version.sh b/test/_global/claude-code-specific-version.sh new file mode 100755 index 0000000..6b00a19 --- /dev/null +++ b/test/_global/claude-code-specific-version.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e +source dev-container-features-test-lib +check "claude-code with specific version" /bin/bash -c "claude --version | grep '1.0.58'" + +reportResults diff --git a/test/_global/scenarios.json b/test/_global/scenarios.json index a4b85c3..803eaf4 100644 --- a/test/_global/scenarios.json +++ b/test/_global/scenarios.json @@ -19,7 +19,8 @@ "zarf": {}, "codex": {}, "just": {}, - "opencode": {} + "opencode": {}, + "claude-code": {} } }, "flux-specific-version": { @@ -147,5 +148,13 @@ "version": "1.0.107" } } + }, + "claude-code-specific-version": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "claude-code": { + "version": "1.0.58" + } + } } } diff --git a/test/claude-code/test.sh b/test/claude-code/test.sh new file mode 100755 index 0000000..0b477dd --- /dev/null +++ b/test/claude-code/test.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +source dev-container-features-test-lib +check "claude-code" claude --version +reportResults