name: Deploy Claude Dev (Dev) on: push: branches: [dev] paths: - 'claude-dev/**' - '.gitea/workflows/deploy-claude-dev-dev.yml' workflow_dispatch: inputs: rollback_image: description: 'Optional full image ref (for rollback), e.g. claude-dev:dev-abc123' required: false concurrency: group: deploy-claude-dev-dev cancel-in-progress: true jobs: deploy-claude-dev-dev: runs-on: nas steps: - name: Checkout repository run: | if [ ! -d .git ]; then git clone --branch "${GITHUB_REF_NAME}" --depth=1 "http://localhost:3300/${GITHUB_REPOSITORY}.git" . fi - name: Resolve image tags run: | set -euo pipefail if [ -n "${{ inputs.rollback_image }}" ]; then image_ref="${{ inputs.rollback_image }}" image_tag="${image_ref#claude-dev:}" if [ "$image_ref" = "$image_tag" ]; then echo "rollback_image must be in claude-dev: format" >&2 exit 1 fi echo "ROLLBACK_ONLY=1" >> "$GITHUB_ENV" else short_sha="$(git rev-parse --short=12 HEAD)" date_tag="$(date +%Y%m%d%H%M%S)" image_tag="dev-${date_tag}-${short_sha}" image_ref="claude-dev:${image_tag}" echo "ROLLBACK_ONLY=0" >> "$GITHUB_ENV" fi echo "CLAUDE_DEV_IMAGE_TAG=${image_tag}" >> "$GITHUB_ENV" echo "CLAUDE_DEV_IMAGE=${image_ref}" >> "$GITHUB_ENV" - name: Build claude-dev image if: env.ROLLBACK_ONLY == '0' run: | set -euo pipefail export DOCKER_API_VERSION=1.43 # Check if base image exists, build if missing # DOCKER_BUILDKIT=0 is required for Synology ContainerManager's Docker daemon, # which does not support BuildKit syntax. Remove when Synology updates to a # Docker Engine version with full BuildKit support (currently 24.x, needs 23+). # --cache-to type=inline is a no-op while BuildKit is disabled. if ! docker image inspect claude-dev-base:latest >/dev/null 2>&1; then echo "Base image not found, building claude-dev-base:latest..." DOCKER_BUILDKIT=0 docker build \ -f claude-dev/Dockerfile.base \ -t claude-dev-base:latest \ claude-dev/ else echo "Using existing claude-dev-base:latest" fi # Build runtime image (fast, just copies scripts) DOCKER_BUILDKIT=0 docker build \ --cache-from claude-dev:latest \ -t "$CLAUDE_DEV_IMAGE" \ -t claude-dev:latest \ claude-dev/ - name: Smoke test required tools if: env.ROLLBACK_ONLY == '0' run: | set -euo pipefail export DOCKER_API_VERSION=1.43 docker run --rm "$CLAUDE_DEV_IMAGE" bash /opt/claude-dev/scripts/smoke-tools.sh - name: Smoke test network assumptions if: env.ROLLBACK_ONLY == '0' run: | set -euo pipefail export DOCKER_API_VERSION=1.43 docker run --rm --network host "$CLAUDE_DEV_IMAGE" bash /opt/claude-dev/scripts/smoke-network.sh - name: Smoke test auth and mount expectations if: env.ROLLBACK_ONLY == '0' run: | set -euo pipefail export DOCKER_API_VERSION=1.43 docker run --rm --network host \ -e REQUIRE_MOUNTS=1 \ -v /volume1/docker/claude-dev/claude-config:/root/.claude \ -v /volume1/docker/claude-dev/claude-config/.claude.json:/root/.claude.json \ -v /volume1/docker/claude-dev/gitea_token:/root/.gitea_token:ro \ "$CLAUDE_DEV_IMAGE" \ bash /opt/claude-dev/scripts/smoke-auth.sh - name: Record previous image run: | set -euo pipefail export DOCKER_API_VERSION=1.43 mkdir -p /volume1/docker/claude-dev previous_image="$(docker inspect -f '{{.Config.Image}}' claude-dev 2>/dev/null || true)" if [ -n "$previous_image" ]; then printf '%s\n' "$previous_image" > /volume1/docker/claude-dev/previous-image.txt echo "Saved previous image: $previous_image" else echo "No existing claude-dev container found" fi - name: Sync compose and target tag run: | set -euo pipefail export DOCKER_API_VERSION=1.43 cp claude-dev/docker-compose.yml /volume1/docker/claude-dev/docker-compose.yml printf '%s\n' "$CLAUDE_DEV_IMAGE_TAG" > /volume1/docker/claude-dev/target-tag.txt - name: Validate compose config run: | set -euo pipefail export DOCKER_API_VERSION=1.43 if ! docker compose -f /volume1/docker/claude-dev/docker-compose.yml config >/dev/null; then echo "❌ docker-compose config validation failed" docker compose -f /volume1/docker/claude-dev/docker-compose.yml config exit 1 fi echo "✅ docker-compose config is valid" - name: Remove stale claude-dev container run: | export DOCKER_API_VERSION=1.43 docker rm -f claude-dev || true - name: Deploy claude-dev run: | set -euo pipefail export DOCKER_API_VERSION=1.43 CLAUDE_DEV_IMAGE_TAG="$CLAUDE_DEV_IMAGE_TAG" docker compose -f /volume1/docker/claude-dev/docker-compose.yml up -d --pull never - name: Post-deploy runtime checks run: | set -euo pipefail export DOCKER_API_VERSION=1.43 docker exec claude-dev bash -lc 'claude --version' docker exec claude-dev bash -lc 'gh --version' docker exec claude-dev bash -lc 'gh auth status || true'