diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml index c7a6749..12ec55a 100644 --- a/.github/workflows/build-and-push.yml +++ b/.github/workflows/build-and-push.yml @@ -26,6 +26,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build Image with buildah script + id: build env: STORAGE_DRIVER: vfs run: | @@ -34,12 +35,19 @@ jobs: # Run the build script VERSION=$(./devops/build-image.sh -repo-name $REPO_NAME | tail -n2 | head -n1 | cut -d: -f2) - + # Tag the image for GHCR buildah tag claude-code \ "ghcr.io/$REPO_NAME/claude-code:$VERSION" \ "ghcr.io/$REPO_NAME/claude-code:latest" - - # Push to GitHub Container Registry - buildah push "ghcr.io/$REPO_NAME/claude-code:$VERSION" - buildah push "ghcr.io/$REPO_NAME/claude-code:latest" + + echo "repo_name=$REPO_NAME" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Test image + run: ./tests/test-image.sh "${{ steps.build.outputs.version }}" + + - name: Push to GitHub Container Registry + run: | + buildah push "ghcr.io/${{ steps.build.outputs.repo_name }}/claude-code:${{ steps.build.outputs.version }}" + buildah push "ghcr.io/${{ steps.build.outputs.repo_name }}/claude-code:latest" diff --git a/bin/claude b/bin/claude index 41527a9..fcbe579 100755 --- a/bin/claude +++ b/bin/claude @@ -23,6 +23,7 @@ while [[ $# -gt 0 ]]; do --init-script FILE Execute initialization script --apk-packages LIST Install additional Alpine packages (comma or space separated) --podman-arg ARG Pass additional argument to podman + --version Show the Claude Code version in the image --help Display this help message EOT exit 0 @@ -52,6 +53,15 @@ while [[ $# -gt 0 ]]; do PODMAN_ARGS="$PODMAN_ARGS $2" shift 2 ;; + --version) + podman run \ + --rm \ + --user claude \ + --userns=keep-id:uid=1001,gid=1001 \ + $PODMAN_ARGS \ + "$IMAGE" --version + exit 0 + ;; *) break ;; diff --git a/devops/build-image.sh b/devops/build-image.sh index 6cb4664..1819f51 100755 --- a/devops/build-image.sh +++ b/devops/build-image.sh @@ -64,7 +64,7 @@ buildah commit \ --rm \ "$CONTAINER" "$IMAGE" -buildah tag "$IMAGE" "$CLAUDE_VERSION" +buildah tag "$IMAGE" "$IMAGE:$CLAUDE_VERSION" echo Done! echo ${IMAGE}:${CLAUDE_VERSION} diff --git a/tests/test-image.sh b/tests/test-image.sh new file mode 100755 index 0000000..822779e --- /dev/null +++ b/tests/test-image.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -euo pipefail + +IMAGE="localhost/claude-code:${1:-latest}" +PASS=0 +FAIL=0 + +run_test() { + local name="$1" + shift + printf "TEST: %s ... " "$name" + if "$@"; then + echo "PASS" + PASS=$((PASS + 1)) + else + echo "FAIL" + FAIL=$((FAIL + 1)) + fi +} + +# 1. Image exists +run_test "Image exists" \ + podman image exists "$IMAGE" + +# 2. Claude binary runs and outputs a version +test_claude_version() { + local output + output=$(podman run --rm --entrypoint "" "$IMAGE" \ + /home/claude/.local/bin/claude --version 2>&1) + echo "$output" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+' +} +run_test "Claude binary outputs version" \ + test_claude_version + +# 3. Entrypoint is set correctly +test_entrypoint() { + local entrypoint + entrypoint=$(podman inspect --format '{{range .Config.Entrypoint}}{{.}}{{end}}' "$IMAGE") + [ "$entrypoint" = "/home/claude/.local/bin/claude" ] +} +run_test "Entrypoint is /home/claude/.local/bin/claude" \ + test_entrypoint + +# 4. Claude user exists with UID 1000 +test_claude_uid() { + local output + output=$(podman run --rm --entrypoint "" "$IMAGE" id claude 2>&1) + echo "$output" | grep -q "uid=1000" +} +run_test "Claude user has UID 1000" \ + test_claude_uid + +# 5. Wrapper script starts claude and it responds +test_wrapper() { + local output + output=$(timeout 30 ./bin/claude --local --version 2>&1) + echo "$output" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+' +} +run_test "Wrapper script starts claude" \ + test_wrapper + +# Summary +echo "" +echo "Results: $PASS passed, $FAIL failed" +[ "$FAIL" -eq 0 ]