Claude Skill: GitHub CI/CD for Snoop Docker Images
Purpose: Guide the creation and maintenance of GitHub Actions workflows that build the Snoop Docker images and push them to Docker Hub.
Overview
The Snoop repo contains two services that need Docker images built and pushed:
| Service | Dockerfile | Build Context | Docker Hub Image |
|---|---|---|---|
| backend | server/Dockerfile | server/ | petdog/snoop-backend |
| frontend | client/Dockerfile | client/ | petdog/snoop-frontend |
Docker Hub org: petdog
Required GitHub Secrets
These MUST be configured in the repo under Settings > Secrets and variables > Actions:
| Secret | Description |
|---|---|
DOCKERHUB_USERNAME | Docker Hub username with push access to the petdog org |
DOCKERHUB_TOKEN | Docker Hub access token (not password) - generate at https://hub.docker.com/settings/security |
Setting up Docker Hub access tokens:
- •Log in to Docker Hub
- •Go to Account Settings > Security > New Access Token
- •Name:
snoop-github-actions - •Permissions: Read & Write
- •Copy the token and add it as
DOCKERHUB_TOKENin GitHub repo secrets
Workflow: Build and Push
This is the primary workflow. It builds both images on push to main or on any tag, using multi-platform builds and GitHub Actions cache for fast rebuilds.
# .github/workflows/docker-build-and-push.yml
name: docker-build-and-push
on:
push:
tags:
- '*'
branches:
- 'main'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
install: true
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Determine Docker tag
id: vars
run: |
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else
echo "TAG=latest" >> $GITHUB_OUTPUT
fi
# --- Backend Build ---
- name: Build and Push Backend
uses: docker/build-push-action@v5
with:
context: server
file: ./server/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: |
petdog/snoop-backend:${{ steps.vars.outputs.TAG }}
cache-from: type=gha,scope=backend
cache-to: type=gha,mode=max,scope=backend
# --- Frontend Build ---
- name: Build and Push Frontend
uses: docker/build-push-action@v5
with:
context: client
file: ./client/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: |
petdog/snoop-frontend:${{ steps.vars.outputs.TAG }}
cache-from: type=gha,scope=frontend
cache-to: type=gha,mode=max,scope=frontend
Tagging Strategy
| Trigger | Image Tag | Example |
|---|---|---|
Push to main | latest | petdog/snoop-backend:latest |
Push tag v1.2.3 | v1.2.3 | petdog/snoop-backend:v1.2.3 |
Push tag 1.0.0 | 1.0.0 | petdog/snoop-backend:1.0.0 |
How it Works
- •Checkout - clones the repo
- •QEMU - enables multi-architecture emulation (needed for cross-platform builds)
- •Buildx - Docker's extended builder with cache support
- •Login - authenticates to Docker Hub using repo secrets
- •Tag detection - if triggered by a git tag, uses that as the image tag; otherwise
latest - •Build backend - multi-stage build from
server/Dockerfile, pushes topetdog/snoop-backend - •Build frontend - multi-stage build from
client/Dockerfile, pushes topetdog/snoop-frontend
Both builds use GitHub Actions cache (type=gha) scoped per service, so unchanged layers are reused across runs.
Workflow: Build on Pull Request (No Push)
Use this as a CI check to verify images build successfully without pushing.
# .github/workflows/docker-build-pr.yml
name: docker-build-pr
on:
pull_request:
branches:
- 'main'
paths:
- 'server/**'
- 'client/**'
jobs:
build-check:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- name: backend
context: server
dockerfile: ./server/Dockerfile
- name: frontend
context: client
dockerfile: ./client/Dockerfile
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build ${{ matrix.name }}
uses: docker/build-push-action@v5
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
push: false
cache-from: type=gha,scope=${{ matrix.name }}
Multi-Platform Builds
Rule: All images MUST be built for both linux/amd64 and linux/arm64 using the platforms key. This is required because the dev environment runs on Apple Silicon (ARM64).
QEMU + Buildx handle cross-compilation automatically. The backend build takes longer (~10-15 min) due to Chromium's apt-get install under emulation, but this is expected.
Advanced Tagging with docker/metadata-action
For richer tag strategies (semver, SHA, branch name), replace the manual tag step:
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: petdog/snoop-backend
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=
- name: Build and Push Backend
uses: docker/build-push-action@v5
with:
context: server
file: ./server/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=backend
cache-to: type=gha,mode=max,scope=backend
This produces tags like:
- •
petdog/snoop-backend:main(branch push) - •
petdog/snoop-backend:1.2.3(tagv1.2.3) - •
petdog/snoop-backend:1.2(tagv1.2.3) - •
petdog/snoop-backend:abc1234(commit SHA)
Kubernetes Image References
After images are pushed, the Kubernetes manifests (see kubernetes-infrastructure skill) reference them:
# Backend deployment image: petdog/snoop-backend:latest # or specific tag # Frontend deployment image: petdog/snoop-frontend:latest # or specific tag
To deploy a specific version:
kubectl set image deployment/backend backend=petdog/snoop-backend:v1.2.3 -n snoop kubectl set image deployment/frontend frontend=petdog/snoop-frontend:v1.2.3 -n snoop
Adding a New Service
When adding a new Dockerized service to Snoop:
- •Create the
Dockerfilein the service directory - •Add a new build step to
docker-build-and-push.yml:yaml- name: Build and Push <service-name> uses: docker/build-push-action@v5 with: context: <service-dir> file: ./<service-dir>/Dockerfile push: true platforms: linux/amd64,linux/arm64 tags: | petdog/snoop-<service-name>:${{ steps.vars.outputs.TAG }} cache-from: type=gha,scope=<service-name> cache-to: type=gha,mode=max,scope=<service-name> - •Add the same entry to the PR workflow matrix
- •Update the Kubernetes deployment to use
petdog/snoop-<service-name>
Directory Layout
.github/
└── workflows/
├── docker-build-and-push.yml # Build + push on main/tags
└── docker-build-pr.yml # Build-only check on PRs
Debugging Failed Builds
# Check workflow runs gh run list --workflow=docker-build-and-push.yml # View logs of a failed run gh run view <run-id> --log-failed # Check if secrets are set (won't show values) gh secret list # Test Docker build locally docker build -t snoop-backend:test -f server/Dockerfile server/ docker build -t snoop-frontend:test -f client/Dockerfile client/
How Claude Should Use This Skill
When working on CI/CD for Snoop:
- •Workflow files go in
.github/workflows/in the snoop repo root - •Image names follow the pattern
petdog/snoop-<service> - •Always use
docker/build-push-action@v5with Buildx and GHA cache - •Never hardcode credentials - always use
${{ secrets.DOCKERHUB_USERNAME }}and${{ secrets.DOCKERHUB_TOKEN }} - •PR workflows should build but NOT push (
push: false) - •Use the existing tag detection pattern - tags become image tags, main becomes
latest - •When adding services, add build steps to both workflows
- •Reference the kubernetes-infrastructure skill for how images are consumed in deployments