Auto integration #287
Workflow file for this run
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: Docker prebuilds | |
| # Builds the cuda12 lucebox-hub Docker image defined in docker-bake.hcl | |
| # and pushes it to GHCR. The bake file is the source of | |
| # truth for arch matrices and CUDA pinning; this workflow only handles | |
| # fetching submodules, freeing runner disk, signing in to the registry, and | |
| # wiring the cache. | |
| on: | |
| # Build + push to GHCR when a GitHub Release is published. The release tag | |
| # becomes one of the image tags via docker/metadata-action's `type=ref, | |
| # event=tag` + `type=semver` rules below. | |
| release: | |
| types: [published] | |
| # Build-only CI guard on PRs that touch the docker surface. We never push | |
| # from a PR — even if we wanted to, GITHUB_TOKEN on PRs from forks lacks | |
| # `packages:write`. The point is to catch Dockerfile / bake-file / arch- | |
| # list regressions before they land on main. | |
| pull_request: | |
| paths: | |
| - Dockerfile | |
| - docker-bake.hcl | |
| - .dockerignore | |
| - .github/workflows/docker.yml | |
| - server/CMakeLists.txt | |
| - server/src/** | |
| - server/test/** | |
| - server/include/** | |
| - server/scripts/** | |
| - server/deps/** | |
| - server/pyproject.toml | |
| - pyproject.toml | |
| - uv.lock | |
| - lucebox.sh | |
| - lucebox/** | |
| # Manual trigger for one-off rebuilds or pre-release smoke tests. The | |
| # `push` input controls whether the resulting images land in GHCR or only | |
| # populate the buildx cache. | |
| workflow_dispatch: | |
| inputs: | |
| push: | |
| description: "Push images to GHCR after build" | |
| type: boolean | |
| default: false | |
| # Single in-flight build per ref. New pushes cancel the previous run so we | |
| # don't queue 30-min compiles. | |
| concurrency: | |
| group: docker-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository_owner }}/lucebox-hub | |
| jobs: | |
| build: | |
| name: ${{ matrix.variant }} | |
| # ubuntu-latest = 4 CPU / 16 GB RAM / 14 GB free disk on the GitHub- | |
| # hosted plan. The disk-free step at the top of the job claws back | |
| # ~30 GB, which is enough to land a 14 GB image with build cache. | |
| # CPU is the harder constraint: the fat-binary arch list can take hours | |
| # on hosted runners. If you outgrow this: | |
| # • Larger GitHub-hosted runners (`ubuntu-latest-8-cores`, paid) | |
| # halve wall time. | |
| # • A self-hosted runner with the host's nvcc avoids the | |
| # containerised CUDA toolkit pull entirely. | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| variant: [cuda12] | |
| steps: | |
| - name: Free runner disk space | |
| # The default ubuntu-latest image keeps ~25 GB of preinstalled | |
| # tooling (Android SDK, .NET, Haskell, ghc, etc.) we don't need. | |
| # Pinned action; check upstream releases before bumping. | |
| uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 | |
| with: | |
| tool-cache: true | |
| android: true | |
| dotnet: true | |
| haskell: true | |
| large-packages: false # slow; preinstalled apt packages we don't need | |
| swap-storage: true | |
| - uses: actions/checkout@v4 | |
| with: | |
| # Submodule contents are needed by the cmake build (llama.cpp ggml | |
| # subtree, mit-han-lab Block-Sparse-Attention). The Dockerfile | |
| # asserts they're present before running cmake. | |
| submodules: recursive | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| # Skip on PR runs: we never push from a PR and the token from a fork | |
| # PR can't `packages:write` anyway. | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Derive image metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| # Suffix every tag with the variant so future CUDA stacks can | |
| # coexist under the same image name. Examples (using cuda12): | |
| # ghcr.io/<owner>/lucebox-hub:cuda12 (moving — main/dispatch/release) | |
| # ghcr.io/<owner>/lucebox-hub:0.3.0-cuda12 (pinned — from `lucebox-v0.3.0` tag) | |
| # ghcr.io/<owner>/lucebox-hub:feat-x-cuda12 (per branch) | |
| # ghcr.io/<owner>/lucebox-hub:sha-abc1234-cuda12 (per commit) | |
| flavor: | | |
| latest=false | |
| suffix=-${{ matrix.variant }},onlatest=true | |
| tags: | | |
| # Moving variant tag — emitted on main, release, and any | |
| # workflow_dispatch with push:true. The `enable=` gate keeps | |
| # branch + PR builds from clobbering the published `:cuda12`. | |
| type=raw,value=${{ matrix.variant }},suffix=,priority=1000,enable=${{ github.event_name == 'release' || (github.ref == 'refs/heads/main' && github.event_name != 'pull_request') || (github.event_name == 'workflow_dispatch' && inputs.push) }} | |
| # Pinned version tag — extracts the version from a | |
| # `lucebox-v<X.Y.Z>` git tag push, mirroring the hatch-vcs | |
| # scheme used by luce-bench and lucebox. Yields e.g. | |
| # `0.3.0-cuda12` when `lucebox-v0.3.0` is pushed. | |
| type=match,pattern=lucebox-v(\d+\.\d+\.\d+),group=1 | |
| type=ref,event=branch | |
| type=ref,event=tag | |
| type=ref,event=pr | |
| type=sha,prefix=sha- | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| - name: Build and push | |
| uses: docker/bake-action@v5 | |
| with: | |
| files: | | |
| docker-bake.hcl | |
| ${{ steps.meta.outputs.bake-file }} | |
| targets: ${{ matrix.variant }} | |
| push: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.push) }} | |
| # gha cache stores layer blobs in the workflow's Actions cache, | |
| # scoped by variant so future CUDA stacks don't evict each other. | |
| # mode=max also caches multi-stage intermediate layers (the | |
| # builder stage with the 30-min nvcc compile), which is the whole | |
| # point of doing this. | |
| set: | | |
| ${{ matrix.variant }}.cache-from=type=gha,scope=${{ matrix.variant }} | |
| ${{ matrix.variant }}.cache-to=type=gha,scope=${{ matrix.variant }},mode=max |