Docker base images #3
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
| # Build the base/system images declared in etc/docker/docker-bake.hcl. | |
| # | |
| # Cells (distro, version) come from the bake matrix; each is built natively on | |
| # amd64 AND arm64 (no QEMU), pushed by digest, then the two arch digests are | |
| # merged into one multi-arch manifest list per cell. | |
| # | |
| # The cell surface is owned entirely by docker-bake.hcl. Adding a distro/version | |
| # or arch is a change to that file (and the `arch` list below for a new arch); | |
| # the job graph here is generic and stays untouched. | |
| name: Docker base images | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| push: | |
| description: "push images + create manifest lists (off = build-only dry run)" | |
| type: boolean | |
| default: false | |
| # Rebuild when the image definition changes. | |
| push: | |
| branches: [main] | |
| paths: | |
| - etc/docker/** | |
| - .github/workflows/docker-base-images.yml | |
| env: | |
| REGISTRY: ghcr.io/viamrobotics | |
| BAKE_FILE: etc/docker/docker-bake.hcl | |
| # workflow_dispatch carries inputs.push; branch pushes always publish. | |
| PUSH: ${{ github.event_name != 'workflow_dispatch' || inputs.push }} | |
| jobs: | |
| # --------------------------------------------------------------------------- | |
| # Read the cell surface straight out of the bake file. `bake --print` is the | |
| # documented contract; we never hand-maintain the cell list in YAML. | |
| # --------------------------------------------------------------------------- | |
| matrix: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| cells: ${{ steps.gen.outputs.cells }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - id: gen | |
| name: Derive cell matrix from bake --print | |
| working-directory: etc/docker | |
| run: | | |
| # Each system-* target -> { target, tag, image }. `tag` is the published | |
| # multi-arch reference the merge job creates; `image` is that tag with | |
| # the version stripped (the per-cell repo digests are pushed under). | |
| cells=$(docker buildx bake -f docker-bake.hcl system --print \ | |
| | jq -c '[ | |
| .target | |
| | to_entries[] | |
| | select(.key | startswith("system-")) | |
| | { target: .key, tag: (.value.tags[0]), image: (.value.tags[0] | split(":")[0]) } | |
| ]') | |
| echo "cells=$cells" >> "$GITHUB_OUTPUT" | |
| echo "$cells" | jq . | |
| # --------------------------------------------------------------------------- | |
| # One job per (cell, arch). Native runner per arch, build a single named bake | |
| # target, push by digest only (no tag). Digests are collected by the merge job. | |
| # --------------------------------------------------------------------------- | |
| build: | |
| needs: matrix | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| cell: ${{ fromJson(needs.matrix.outputs.cells) }} | |
| arch: [amd64, arm64] | |
| include: | |
| - arch: amd64 | |
| runner: ubuntu-24.04 | |
| - arch: arm64 | |
| runner: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| if: env.PUSH == 'true' | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build ${{ matrix.cell.target }} (${{ matrix.arch }}) | |
| working-directory: etc/docker | |
| run: | | |
| # Slice the bake matrix to this runner's native arch (no QEMU); the | |
| # bake file defaults to the full amd64+arm64 matrix. | |
| # Push by digest: strip the tag and emit a name-canonical image so the | |
| # arch builds don't race on a shared tag. Merge job stitches them. | |
| docker buildx bake -f docker-bake.hcl \ | |
| --set "${{ matrix.cell.target }}.platform=linux/${{ matrix.arch }}" \ | |
| --set "${{ matrix.cell.target }}.tags=" \ | |
| --set "${{ matrix.cell.target }}.output=type=image,name=${{ matrix.cell.image }},push-by-digest=true,name-canonical=true,push=${PUSH}" \ | |
| --metadata-file metadata.json \ | |
| "${{ matrix.cell.target }}" | |
| - name: Export digest | |
| if: env.PUSH == 'true' | |
| working-directory: etc/docker | |
| run: | | |
| digest=$(jq -r '.["${{ matrix.cell.target }}"]."containerimage.digest"' metadata.json) | |
| test -n "$digest" && test "$digest" != null | |
| mkdir -p /tmp/digests | |
| # Filename = sanitized digest; content unused, name carries the data. | |
| echo -n > "/tmp/digests/${digest#sha256:}" | |
| echo "$digest" | |
| - name: Upload digest | |
| if: env.PUSH == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| # One artifact per cell; arch jobs append their digest into it. | |
| name: digest-${{ matrix.cell.target }}-${{ matrix.arch }} | |
| path: /tmp/digests/* | |
| retention-days: 1 | |
| if-no-files-found: error | |
| # --------------------------------------------------------------------------- | |
| # Per cell, join the arch digests into one manifest list under the cell tag. | |
| # --------------------------------------------------------------------------- | |
| merge: | |
| needs: [matrix, build] | |
| if: needs.build.result == 'success' && (github.event_name != 'workflow_dispatch' || inputs.push) | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| cell: ${{ fromJson(needs.matrix.outputs.cells) }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Collect digests for ${{ matrix.cell.target }} | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: /tmp/digests | |
| pattern: digest-${{ matrix.cell.target }}-* | |
| merge-multiple: true | |
| - name: Create multi-arch manifest | |
| run: | | |
| # imagetools create -t <tag> <image>@sha256:<d1> <image>@sha256:<d2> | |
| docker buildx imagetools create -t "${{ matrix.cell.tag }}" \ | |
| $(for d in /tmp/digests/*; do printf '%s@sha256:%s ' "${{ matrix.cell.image }}" "$(basename "$d")"; done) | |
| - name: Inspect | |
| run: docker buildx imagetools inspect "${{ matrix.cell.tag }}" |