2026-03-23 20:55:50 -07:00
|
|
|
# Unified container build workflow
|
2026-02-20 22:56:20 -08:00
|
|
|
# Triggers on pushes to main that modify containers/*, or via manual dispatch.
|
2026-03-23 20:55:50 -07:00
|
|
|
# Detects which containers changed and routes to the correct runner:
|
|
|
|
|
# - Dockerfile containers build on k8s (indri) via Dagger
|
|
|
|
|
# - Nix containers build on nix-container-builder (ringtail) via nix-build + skopeo
|
2026-01-24 16:54:35 -08:00
|
|
|
name: Build Container
|
|
|
|
|
|
|
|
|
|
on:
|
|
|
|
|
push:
|
2026-02-20 22:56:20 -08:00
|
|
|
branches: [main]
|
|
|
|
|
paths: ['containers/**']
|
|
|
|
|
workflow_dispatch:
|
|
|
|
|
inputs:
|
|
|
|
|
container:
|
|
|
|
|
description: 'Container name (directory under containers/)'
|
|
|
|
|
required: true
|
|
|
|
|
type: string
|
|
|
|
|
ref:
|
|
|
|
|
description: 'Commit SHA to build (defaults to current HEAD)'
|
|
|
|
|
required: false
|
|
|
|
|
type: string
|
2026-01-24 16:54:35 -08:00
|
|
|
|
|
|
|
|
jobs:
|
2026-02-20 22:56:20 -08:00
|
|
|
detect:
|
2026-01-25 19:56:17 -08:00
|
|
|
runs-on: k8s
|
2026-02-20 22:56:20 -08:00
|
|
|
outputs:
|
2026-04-11 17:11:56 -07:00
|
|
|
dagger: ${{ steps.classify.outputs.dagger }}
|
2026-03-23 20:55:50 -07:00
|
|
|
nix: ${{ steps.classify.outputs.nix }}
|
2026-01-24 16:54:35 -08:00
|
|
|
steps:
|
2026-02-20 22:56:20 -08:00
|
|
|
- name: Checkout
|
2026-03-24 08:11:46 -07:00
|
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
2026-02-20 22:56:20 -08:00
|
|
|
with:
|
2026-03-24 13:27:36 -07:00
|
|
|
ref: ${{ inputs.ref || github.sha }}
|
2026-02-20 22:56:20 -08:00
|
|
|
fetch-depth: 2
|
2026-01-24 16:54:35 -08:00
|
|
|
|
2026-03-23 20:55:50 -07:00
|
|
|
- name: Detect and classify changed containers
|
|
|
|
|
id: classify
|
2026-02-20 22:56:20 -08:00
|
|
|
run: |
|
|
|
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
2026-03-23 20:55:50 -07:00
|
|
|
CHANGED='["${{ inputs.container }}"]'
|
2026-02-20 22:56:20 -08:00
|
|
|
else
|
2026-03-23 20:55:50 -07:00
|
|
|
CHANGED=$(git diff --name-only HEAD~1 HEAD -- containers/ \
|
2026-02-20 22:56:20 -08:00
|
|
|
| cut -d/ -f2 | sort -u \
|
|
|
|
|
| jq -R -s -c 'split("\n") | map(select(length > 0))')
|
|
|
|
|
fi
|
2026-01-24 16:54:35 -08:00
|
|
|
|
2026-03-23 20:55:50 -07:00
|
|
|
echo "Changed containers: $CHANGED"
|
|
|
|
|
|
|
|
|
|
# Classify each container by build type (a container can appear in both)
|
2026-04-11 17:11:56 -07:00
|
|
|
DAGGER='[]'
|
2026-03-23 20:55:50 -07:00
|
|
|
NIX='[]'
|
|
|
|
|
for name in $(echo "$CHANGED" | jq -r '.[]'); do
|
|
|
|
|
has_any=false
|
2026-04-11 17:11:56 -07:00
|
|
|
if [ -f "containers/$name/container.py" ] || [ -f "containers/$name/Dockerfile" ]; then
|
|
|
|
|
DAGGER=$(echo "$DAGGER" | jq -c --arg n "$name" '. + [$n]')
|
2026-03-23 20:55:50 -07:00
|
|
|
has_any=true
|
|
|
|
|
fi
|
|
|
|
|
if [ -f "containers/$name/default.nix" ]; then
|
|
|
|
|
NIX=$(echo "$NIX" | jq -c --arg n "$name" '. + [$n]')
|
|
|
|
|
has_any=true
|
|
|
|
|
fi
|
|
|
|
|
if [ "$has_any" = "false" ]; then
|
2026-04-11 17:11:56 -07:00
|
|
|
echo "Warning: $name has neither container.py, Dockerfile, nor default.nix — skipping"
|
2026-03-23 20:55:50 -07:00
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
2026-04-11 17:11:56 -07:00
|
|
|
echo "dagger=$DAGGER" >> "$GITHUB_OUTPUT"
|
2026-03-23 20:55:50 -07:00
|
|
|
echo "nix=$NIX" >> "$GITHUB_OUTPUT"
|
2026-04-11 17:11:56 -07:00
|
|
|
echo "Dagger builds: $DAGGER"
|
2026-03-23 20:55:50 -07:00
|
|
|
echo "Nix builds: $NIX"
|
|
|
|
|
|
2026-04-11 17:11:56 -07:00
|
|
|
build-dagger:
|
2026-02-20 22:56:20 -08:00
|
|
|
needs: detect
|
2026-04-11 17:11:56 -07:00
|
|
|
if: needs.detect.outputs.dagger != '[]'
|
2026-02-20 22:56:20 -08:00
|
|
|
runs-on: k8s
|
2026-04-13 08:27:12 -07:00
|
|
|
env:
|
|
|
|
|
# Send Dagger OTLP telemetry to Tempo. Without a real backend the
|
|
|
|
|
# engine's internal proxy returns 500 on /v1/metrics, causing noisy
|
|
|
|
|
# retry warnings in every build.
|
|
|
|
|
OTEL_EXPORTER_OTLP_ENDPOINT: http://tempo.tracing.svc.cluster.local:4318
|
2026-02-20 22:56:20 -08:00
|
|
|
strategy:
|
|
|
|
|
matrix:
|
2026-04-11 17:11:56 -07:00
|
|
|
container: ${{ fromJson(needs.detect.outputs.dagger) }}
|
2026-02-20 22:56:20 -08:00
|
|
|
steps:
|
2026-01-24 16:54:35 -08:00
|
|
|
- name: Checkout
|
2026-03-24 08:11:46 -07:00
|
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
2026-02-23 17:23:13 -08:00
|
|
|
with:
|
|
|
|
|
ref: ${{ inputs.ref || github.sha }}
|
2026-01-24 16:54:35 -08:00
|
|
|
|
2026-02-20 22:56:20 -08:00
|
|
|
- name: Extract version and SHA
|
|
|
|
|
id: meta
|
2026-01-24 16:54:35 -08:00
|
|
|
run: |
|
2026-04-11 17:11:56 -07:00
|
|
|
CONTAINER="${{ matrix.container }}"
|
|
|
|
|
|
|
|
|
|
# Try native Dagger pipeline (container.py) first, fall back to Dockerfile
|
|
|
|
|
if [ -f "containers/$CONTAINER/container.py" ]; then
|
|
|
|
|
VERSION=$(dagger call container-version --container-name="$CONTAINER")
|
|
|
|
|
elif [ -f "containers/$CONTAINER/Dockerfile" ]; then
|
|
|
|
|
VERSION=$(grep -m1 '^ARG CONTAINER_APP_VERSION=' \
|
|
|
|
|
"containers/$CONTAINER/Dockerfile" \
|
|
|
|
|
| sed 's/^ARG CONTAINER_APP_VERSION=//')
|
|
|
|
|
fi
|
2026-02-20 22:56:20 -08:00
|
|
|
|
|
|
|
|
if [ -z "$VERSION" ]; then
|
2026-04-11 17:11:56 -07:00
|
|
|
echo "Error: Could not extract version for $CONTAINER"
|
2026-02-20 22:56:20 -08:00
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
REF="${{ inputs.ref }}"
|
|
|
|
|
if [ -z "$REF" ]; then
|
|
|
|
|
REF="${GITHUB_SHA}"
|
|
|
|
|
fi
|
|
|
|
|
SHORT_SHA=$(echo "$REF" | head -c 7)
|
|
|
|
|
|
|
|
|
|
# Ensure version starts with 'v'
|
|
|
|
|
case "$VERSION" in
|
2026-03-23 20:55:50 -07:00
|
|
|
v*) ;;
|
2026-02-20 22:56:20 -08:00
|
|
|
*) VERSION="v${VERSION}" ;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
|
|
|
echo "sha=$SHORT_SHA" >> "$GITHUB_OUTPUT"
|
|
|
|
|
echo "Version: $VERSION, SHA: $SHORT_SHA"
|
2026-01-24 16:54:35 -08:00
|
|
|
|
2026-02-11 15:38:31 -08:00
|
|
|
- name: Publish
|
2026-02-21 12:20:29 -08:00
|
|
|
env:
|
|
|
|
|
ZOT_CI_API_KEY: ${{ secrets.ZOT_CI_API_KEY }}
|
2026-02-11 15:38:31 -08:00
|
|
|
run: |
|
|
|
|
|
dagger call publish \
|
|
|
|
|
--src=. \
|
2026-02-20 22:56:20 -08:00
|
|
|
--container-name=${{ matrix.container }} \
|
|
|
|
|
--version=${{ steps.meta.outputs.version }} \
|
2026-02-21 12:20:29 -08:00
|
|
|
--commit-sha=${{ steps.meta.outputs.sha }} \
|
|
|
|
|
--registry-password=env:ZOT_CI_API_KEY
|
2026-03-23 20:55:50 -07:00
|
|
|
|
|
|
|
|
build-nix:
|
|
|
|
|
needs: detect
|
|
|
|
|
if: needs.detect.outputs.nix != '[]'
|
|
|
|
|
runs-on: nix-container-builder
|
|
|
|
|
strategy:
|
|
|
|
|
matrix:
|
|
|
|
|
container: ${{ fromJson(needs.detect.outputs.nix) }}
|
|
|
|
|
steps:
|
|
|
|
|
- name: Checkout
|
2026-03-24 08:11:46 -07:00
|
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
2026-03-23 20:55:50 -07:00
|
|
|
with:
|
|
|
|
|
ref: ${{ inputs.ref || github.sha }}
|
|
|
|
|
|
|
|
|
|
- name: Extract version and SHA
|
|
|
|
|
id: meta
|
|
|
|
|
run: |
|
|
|
|
|
CONTAINER="${{ matrix.container }}"
|
|
|
|
|
NIX_FILE="containers/$CONTAINER/default.nix"
|
|
|
|
|
|
|
|
|
|
# Extract version = "..." from the nix file
|
|
|
|
|
VERSION=$(grep -m1 '^\s*version\s*=\s*"' "$NIX_FILE" \
|
|
|
|
|
| sed 's/.*"\(.*\)".*/\1/' || true)
|
|
|
|
|
|
|
|
|
|
if [ -z "$VERSION" ]; then
|
|
|
|
|
echo "Error: No version declaration found in $NIX_FILE"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
REF="${{ inputs.ref }}"
|
|
|
|
|
if [ -z "$REF" ]; then
|
|
|
|
|
REF="${GITHUB_SHA}"
|
|
|
|
|
fi
|
|
|
|
|
SHORT_SHA=$(echo "$REF" | head -c 7)
|
|
|
|
|
|
|
|
|
|
# Ensure version starts with 'v'
|
|
|
|
|
case "$VERSION" in
|
|
|
|
|
v*) ;;
|
|
|
|
|
*) VERSION="v${VERSION}" ;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
|
|
|
echo "sha=$SHORT_SHA" >> "$GITHUB_OUTPUT"
|
|
|
|
|
echo "Version: $VERSION, SHA: $SHORT_SHA"
|
|
|
|
|
|
|
|
|
|
- name: Resolve nixpkgs
|
|
|
|
|
id: nixpkgs
|
|
|
|
|
run: |
|
|
|
|
|
NIXPKGS_PATH=$(nix flake metadata nixpkgs --json | jq -r '.path')
|
|
|
|
|
echo "Resolved nixpkgs: $NIXPKGS_PATH"
|
|
|
|
|
echo "path=$NIXPKGS_PATH" >> "$GITHUB_OUTPUT"
|
|
|
|
|
|
|
|
|
|
- name: Build with nix
|
|
|
|
|
env:
|
|
|
|
|
NIX_PATH: "nixpkgs=${{ steps.nixpkgs.outputs.path }}"
|
|
|
|
|
run: |
|
|
|
|
|
echo "Building containers/${{ matrix.container }}/default.nix"
|
|
|
|
|
echo "NIX_PATH=$NIX_PATH"
|
|
|
|
|
nix-build "containers/${{ matrix.container }}/default.nix" -o result
|
|
|
|
|
echo "Build complete: $(readlink result)"
|
|
|
|
|
|
|
|
|
|
- name: Push to registry
|
|
|
|
|
env:
|
|
|
|
|
ZOT_CI_API_KEY: ${{ secrets.ZOT_CI_API_KEY }}
|
|
|
|
|
run: |
|
|
|
|
|
CONTAINER="${{ matrix.container }}"
|
|
|
|
|
VERSION="${{ steps.meta.outputs.version }}"
|
|
|
|
|
SHORT_SHA="${{ steps.meta.outputs.sha }}"
|
|
|
|
|
IMAGE="registry.ops.eblu.me/blumeops/$CONTAINER:${VERSION}-${SHORT_SHA}-nix"
|
|
|
|
|
|
|
|
|
|
echo "Pushing to $IMAGE"
|
|
|
|
|
skopeo copy \
|
|
|
|
|
--dest-creds="zot-ci:$ZOT_CI_API_KEY" \
|
|
|
|
|
"docker-archive:result" \
|
|
|
|
|
"docker://$IMAGE"
|
|
|
|
|
echo "Push complete: $IMAGE"
|