From ac56aac0a725431e25ac18999d05cdf210016251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=BChl?= Date: Wed, 4 Mar 2026 12:05:31 +0100 Subject: [PATCH] feat: add docker-build reusable action Add reusable action for building and pushing Docker images with: - S3 build cache support (SeaweedFS) - Optional Nix/Attic cache configuration - Auto-tagging based on branches, PRs, and semver tags - Multi-registry support Co-Authored-By: Claude Sonnet 4.5 --- docker-build/README.md | 143 +++++++++++++++++++++++++++++++++++++++ docker-build/action.yaml | 138 +++++++++++++++++++++++++++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 docker-build/README.md create mode 100644 docker-build/action.yaml diff --git a/docker-build/README.md b/docker-build/README.md new file mode 100644 index 0000000..b0a15c2 --- /dev/null +++ b/docker-build/README.md @@ -0,0 +1,143 @@ +# docker-build + +Build and push Docker images with S3 caching and optional Nix cache support. + +## Features + +- **S3 build cache**: Uses your self-hosted S3 (SeaweedFS) for fast layer caching +- **Nix cache integration**: Automatically configures Attic cache for Dockerfiles using Nix +- **Auto-tagging**: Smart tagging based on branches, PRs, and semver tags +- **Multi-registry support**: Works with Forgejo Docker registry or any other + +## Usage + +### Basic example + +```yaml +name: Build Docker Image +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: https://git.toph.so/toph/ci-actions/docker-build@main + with: + dockerfile: ./Dockerfile + image-name: git.toph.so/${{ gitea.repository }} + registry-password: ${{ secrets.GITEA_TOKEN }} +``` + +### With Nix cache support + +For Dockerfiles that use Nix/Lix (e.g., `FROM ghcr.io/lix-project/lix`): + +```yaml +- uses: https://git.toph.so/toph/ci-actions/docker-build@main + with: + dockerfile: ./Dockerfile.nix + image-name: git.toph.so/${{ gitea.repository }}/nix + registry-password: ${{ secrets.GITEA_TOKEN }} + enable-nix-cache: 'true' +``` + +### Multiple images from one repo + +```yaml +jobs: + build-web: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: https://git.toph.so/toph/ci-actions/docker-build@main + with: + dockerfile: ./deploy/Dockerfile + image-name: git.toph.so/${{ gitea.repository }} + registry-password: ${{ secrets.GITEA_TOKEN }} + + build-worker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: https://git.toph.so/toph/ci-actions/docker-build@main + with: + dockerfile: ./deploy/Dockerfile.worker + image-name: git.toph.so/${{ gitea.repository }}/worker + registry-password: ${{ secrets.GITEA_TOKEN }} +``` + +### Custom tags + +```yaml +- uses: https://git.toph.so/toph/ci-actions/docker-build@main + with: + dockerfile: ./Dockerfile + image-name: git.toph.so/myorg/myapp + registry-password: ${{ secrets.GITEA_TOKEN }} + tags: | + git.toph.so/myorg/myapp:latest + git.toph.so/myorg/myapp:v1.0.0 + git.toph.so/myorg/myapp:stable +``` + +## Inputs + +| Input | Required | Default | Description | +|-------|----------|---------|-------------| +| `dockerfile` | ✅ | - | Path to Dockerfile | +| `image-name` | ✅ | - | Full image name (e.g., `git.toph.so/user/repo`) | +| `registry-password` | ✅ | - | Registry password/token | +| `context` | ❌ | `.` | Build context path | +| `registry` | ❌ | `git.toph.so` | Docker registry | +| `registry-username` | ❌ | `${{ gitea.actor }}` | Registry username | +| `push` | ❌ | `true` | Push image to registry | +| `tags` | ❌ | auto-generated | Custom tags (newline-separated) | +| `s3-cache-bucket` | ❌ | `docker-cache` | S3 bucket for build cache | +| `s3-endpoint` | ❌ | `https://s3.toph.so` | S3 endpoint URL | +| `enable-nix-cache` | ❌ | `false` | Configure Nix binary cache | +| `attic-endpoint` | ❌ | `https://cache.toph.so` | Attic/Nix cache endpoint | + +## Auto-generated tags + +When `tags` input is not provided, tags are auto-generated based on the event: + +| Event | Generated tags | +|-------|----------------| +| Push to `main` | `main`, `` | +| Push tag `v1.2.3` | `1.2.3`, `1.2`, `1`, `latest`, `` | +| Pull request #42 | `pr-42`, `` | + +## S3 Cache + +The action uses your self-hosted S3 (SeaweedFS) for Docker build cache layers, which: +- Speeds up builds by reusing unchanged layers +- Works across different runners and workflow runs +- Automatically managed by Docker Buildx + +**No additional setup required** — the cache is automatically used if S3 is accessible. + +## Nix Cache Integration + +When `enable-nix-cache: 'true'`, the action configures: +- `cache.nixos.org` (official Nix cache) +- `cache.toph.so/ci` (your CI cache, 90d retention) +- `cache.toph.so/toph` (your trusted cache, 180d retention) + +This allows Dockerfiles using Nix to fetch pre-built derivations instead of rebuilding from source. + +## Prerequisites + +**For all builds:** +- S3 service running at `s3.toph.so` (SeaweedFS) +- `GITEA_TOKEN` secret configured (auto-available in Forgejo Actions) + +**For Nix-enabled builds:** +- Attic cache service running at `cache.toph.so` + +## Examples + +See the main [ci-actions README](../README.md) for infrastructure setup details. diff --git a/docker-build/action.yaml b/docker-build/action.yaml new file mode 100644 index 0000000..89b5a59 --- /dev/null +++ b/docker-build/action.yaml @@ -0,0 +1,138 @@ +name: Build and Push Docker Image +description: Build and push a Docker image with S3 caching and Nix cache support + +inputs: + context: + description: 'Build context path' + required: false + default: '.' + + dockerfile: + description: 'Path to Dockerfile' + required: true + + image-name: + description: 'Full image name (e.g., git.toph.so/user/repo or git.toph.so/user/repo/image)' + required: true + + registry: + description: 'Docker registry' + required: false + default: 'git.toph.so' + + registry-username: + description: 'Registry username' + required: false + default: ${{ gitea.actor }} + + registry-password: + description: 'Registry password/token' + required: true + + push: + description: 'Push image to registry' + required: false + default: 'true' + + tags: + description: 'Custom tags (newline-separated), overrides auto-tagging' + required: false + + s3-cache-bucket: + description: 'S3 bucket for Docker build cache' + required: false + default: 'docker-cache' + + s3-endpoint: + description: 'S3 endpoint URL' + required: false + default: 'https://s3.toph.so' + + enable-nix-cache: + description: 'Configure Nix binary cache for builds using Nix' + required: false + default: 'false' + + attic-endpoint: + description: 'Attic/Nix cache endpoint' + required: false + default: 'https://cache.toph.so' + +runs: + using: composite + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Configure Nix cache + if: inputs.enable-nix-cache == 'true' + shell: bash + run: | + mkdir -p ~/.config/nix + cat > ~/.config/nix/nix.conf <> $GITHUB_OUTPUT + else + # Auto-generate tags based on event type + TAGS="" + IMAGE_BASE="${{ inputs.image-name }}" + + # Branch name tag + if [ "${{ github.event_name }}" = "push" ]; then + BRANCH_NAME="${GITHUB_REF#refs/heads/}" + TAGS="${TAGS}${IMAGE_BASE}:${BRANCH_NAME}\n" + fi + + # PR tag + if [ "${{ github.event_name }}" = "pull_request" ]; then + TAGS="${TAGS}${IMAGE_BASE}:pr-${{ github.event.pull_request.number }}\n" + fi + + # Tag-based versioning + if [[ "${{ github.ref }}" == refs/tags/v* ]]; then + VERSION="${GITHUB_REF#refs/tags/v}" + TAGS="${TAGS}${IMAGE_BASE}:${VERSION}\n" + MAJOR="${VERSION%%.*}" + MINOR="${VERSION#*.}" + MINOR="${MINOR%%.*}" + TAGS="${TAGS}${IMAGE_BASE}:${MAJOR}.${MINOR}\n" + TAGS="${TAGS}${IMAGE_BASE}:${MAJOR}\n" + TAGS="${TAGS}${IMAGE_BASE}:latest\n" + fi + + # SHA tag + SHORT_SHA="${GITHUB_SHA:0:7}" + TAGS="${TAGS}${IMAGE_BASE}:${SHORT_SHA}" + + echo -e "tags<> $GITHUB_OUTPUT + echo -e "$TAGS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + fi + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: ${{ inputs.context }} + file: ${{ inputs.dockerfile }} + push: ${{ inputs.push }} + tags: ${{ steps.tags.outputs.tags }} + cache-from: type=s3,region=auto,bucket=${{ inputs.s3-cache-bucket }},endpoint_url=${{ inputs.s3-endpoint }} + cache-to: type=s3,region=auto,bucket=${{ inputs.s3-cache-bucket }},endpoint_url=${{ inputs.s3-endpoint }},mode=max