diff --git a/CLAUDE.example.md b/CLAUDE.example.md index 4870b5e..b50dcf6 100644 --- a/CLAUDE.example.md +++ b/CLAUDE.example.md @@ -208,3 +208,95 @@ versions without warning in your own values file. Grouping by layer (rather than by day or by "misc fixes") makes it much easier to `git log --oneline -- ` your way back to *why* a given piece of config looks the way it does, months later. + +## Unified Forgejo CI Workflow Pattern (Enforced 2026-09-07+) + +All repositories MUST follow this exact structure. No variations. + +```yaml +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + REGISTRY: + IMAGE: // + +jobs: + test: + name: Test + runs-on: [golang|node|rust] + steps: + - name: Install Node.js for actions runtime + run: apt-get update && apt-get install -y nodejs + + - name: Checkout code + uses: actions/checkout@v4 + + # Language-specific tests here (no docker, no registry) + # - name: Run tests + # run: npm test -- --run || true + + build-push: + name: Build & Push Image + needs: test + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: [golang|node|rust] + steps: + - name: Install Node.js and Docker + run: | + apt-get update + apt-get install -y nodejs docker.io + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get short SHA + id: sha + run: | + SHORT_SHA=$(git rev-parse --short HEAD) + echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT + + - name: Registry login + run: | + echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ + --username "${REGISTRY_USER}" --password-stdin + env: + REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} + REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} + + - name: Build Docker image + run: | + docker build --no-cache \ + -t "${IMAGE}:${{ steps.sha.outputs.short_sha }}" \ + -t "${IMAGE}:latest" \ + . + + - name: Push Docker image + run: | + docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE}:latest" + + - name: Prune unused images + run: docker image prune -a --force 2>&1 | tail -3 || true +``` + +### Anti-Patterns (DO NOT USE) + +- ❌ `container: image: golang:1.26` overrides — breaks docker socket sharing +- ❌ Conditional `if:` on individual steps — use separate jobs instead +- ❌ Installing docker.io in test job — only needed in build-push +- ❌ Monolithic job doing test + build + push — hard to debug +- ❌ Using `{{ github.sha }}` for image tag — use short commit SHA for readability + +### How It Works + +1. **PR to feature branch** → test job runs, build-push skipped, nothing pushed +2. **Push to main** → test runs, build-push runs after test passes, image pushed +3. Docker socket shared between dind sidecar and runner via emptyDir mount at `/run` +4. `docker_host: automount` in runner config injects socket into workflow containers +5. Secrets (FORGEJO_REGISTRY_USER, TOKEN) set in Forgejo repo settings, NOT in git