feat: add workflow to publish templates to ghcr.io #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Push Dev Containers | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - '.devcontainer/**' | |
| - '.github/workflows/build-container.yml' | |
| schedule: | |
| # Every 24h at 2am UTC | |
| - cron: '0 2 * * *' | |
| workflow_dispatch: | |
| env: | |
| REGISTRY: ghcr.io | |
| jobs: | |
| discover-containers: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| containers: ${{ steps.find.outputs.containers }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Find all Dockerfiles | |
| id: find | |
| run: | | |
| # Find all directories containing a Dockerfile | |
| CONTAINERS=$(find .devcontainer -mindepth 2 -name "Dockerfile" -printf '%h\n' | \ | |
| sed 's|.devcontainer/||' | \ | |
| jq -R -s -c 'split("\n") | map(select(length > 0))') | |
| echo "containers=$CONTAINERS" >> $GITHUB_OUTPUT | |
| echo "Found containers: $CONTAINERS" | |
| check-updates: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should_build: ${{ steps.check.outputs.should_build }} | |
| claude_version: ${{ steps.versions.outputs.claude_version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Restore cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| .claude-version-cache | |
| .dockerfile-hash-cache | |
| key: build-cache-${{ github.run_id }} | |
| restore-keys: | | |
| build-cache- | |
| - name: Get current versions | |
| id: versions | |
| run: | | |
| # Get latest Claude Code version | |
| CLAUDE_VERSION=$(npm view @anthropic-ai/claude-code version 2>/dev/null || echo "unknown") | |
| echo "claude_version=$CLAUDE_VERSION" >> $GITHUB_OUTPUT | |
| echo "Claude Code version: $CLAUDE_VERSION" | |
| - name: Check if rebuild is needed | |
| id: check | |
| run: | | |
| # For push events, always rebuild | |
| if [[ "${{ github.event_name }}" == "push" ]]; then | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| echo "Push event - will rebuild" | |
| exit 0 | |
| fi | |
| # For workflow_dispatch, always rebuild | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| echo "Manual trigger - will rebuild" | |
| exit 0 | |
| fi | |
| # For cron, check versions and hashes | |
| echo "Checking for changes..." | |
| CURRENT_CLAUDE_VERSION="${{ steps.versions.outputs.claude_version }}" | |
| CACHE_FILE=".claude-version-cache" | |
| # Check if Claude Code version changed | |
| if [[ -f "$CACHE_FILE" ]]; then | |
| CACHED_VERSION=$(cat "$CACHE_FILE") | |
| if [[ "$CACHED_VERSION" != "$CURRENT_CLAUDE_VERSION" ]]; then | |
| echo "Claude Code version changed: $CACHED_VERSION -> $CURRENT_CLAUDE_VERSION" | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| else | |
| echo "No version cache found - will build" | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Check hash of all Dockerfiles | |
| DOCKERFILE_HASH=$(find .devcontainer -name "Dockerfile" -exec sha256sum {} \; | sort | sha256sum | cut -d' ' -f1) | |
| CACHE_DOCKERFILE=".dockerfile-hash-cache" | |
| if [[ -f "$CACHE_DOCKERFILE" ]]; then | |
| CACHED_HASH=$(cat "$CACHE_DOCKERFILE") | |
| if [[ "$CACHED_HASH" != "$DOCKERFILE_HASH" ]]; then | |
| echo "Dockerfiles changed" | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| else | |
| echo "No Dockerfile cache found - will build" | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| echo "No changes detected - skipping build" | |
| echo "should_build=false" >> $GITHUB_OUTPUT | |
| build-and-push: | |
| needs: [discover-containers, check-updates] | |
| if: needs.check-updates.outputs.should_build == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| matrix: | |
| container: ${{ fromJson(needs.discover-containers.outputs.containers) }} | |
| fail-fast: false | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set image name | |
| id: image | |
| run: | | |
| CONTAINER="${{ matrix.container }}" | |
| OWNER="${{ github.repository_owner }}" | |
| # Generate image name based on folder | |
| case "$CONTAINER" in | |
| "base") | |
| IMAGE_NAME="devcontainer-claude-code" | |
| ;; | |
| "base-with-bmad") | |
| IMAGE_NAME="devcontainer-claude-code-bmad" | |
| ;; | |
| *) | |
| IMAGE_NAME="devcontainer-claude-code-${CONTAINER}" | |
| ;; | |
| esac | |
| echo "name=${OWNER}/${IMAGE_NAME}" >> $GITHUB_OUTPUT | |
| echo "Image name: ${OWNER}/${IMAGE_NAME}" | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ steps.image.outputs.name }} | |
| tags: | | |
| type=raw,value=latest | |
| type=raw,value={{date 'YYYYMMDD'}} | |
| type=sha,prefix= | |
| - name: Build and push | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: .devcontainer/${{ matrix.container }} | |
| file: .devcontainer/${{ matrix.container }}/Dockerfile | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: | | |
| ${{ steps.meta.outputs.labels }} | |
| org.opencontainers.image.claude-code-version=${{ needs.check-updates.outputs.claude_version }} | |
| platforms: linux/amd64,linux/arm64 | |
| cache-from: type=gha,scope=${{ matrix.container }} | |
| cache-to: type=gha,mode=max,scope=${{ matrix.container }} | |
| update-cache: | |
| needs: [check-updates, build-and-push] | |
| if: needs.check-updates.outputs.should_build == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Update version cache | |
| run: | | |
| echo "${{ needs.check-updates.outputs.claude_version }}" > .claude-version-cache | |
| find .devcontainer -name "Dockerfile" -exec sha256sum {} \; | sort | sha256sum | cut -d' ' -f1 > .dockerfile-hash-cache | |
| - name: Save cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| .claude-version-cache | |
| .dockerfile-hash-cache | |
| key: build-cache-${{ github.run_id }} |