yams/.github/workflows/docker.yml
trvon ae08f99971
ci: relax workflow shell lint
Signed-off-by: trvon <me@trevon.dev>
2026-05-21 11:26:16 -04:00

372 lines
14 KiB
YAML

name: Build and Push Docker Images
on:
push:
branches: [main, master]
release:
types: [published]
pull_request:
branches: [main, master]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
ZIG_VERSION: 0.15.2
permissions:
contents: read
packages: write
jobs:
# Build AMD64 image on Linux x86_64 runner
build-amd64:
name: Build Docker Image (linux/amd64)
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0 # Ensure tags are available for version derivation
submodules: false # Submodules not needed for Docker build
- name: Initialize required submodules
shell: bash
run: |
set -euo pipefail
retry_cmd() {
local attempts="$1"
shift
local attempt=1
until "$@"; do
if [ "$attempt" -ge "$attempts" ]; then
return 1
fi
echo "Command failed (attempt ${attempt}/${attempts}): $*" >&2
sleep $((attempt * 5))
attempt=$((attempt + 1))
done
}
git config --global url."https://github.com/".insteadOf "git@github.com:"
retry_cmd 4 git submodule update --init --depth 1 third_party/sqlite-vec-cpp
retry_cmd 4 git submodule update --init --depth 1 third_party/simeon
retry_cmd 4 git submodule update --init --depth 1 third_party/symspell
- name: Determine version
id: version
shell: bash
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}"
else
VERSION="dev-$(git rev-parse --short HEAD)"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Determined version: $VERSION"
- name: Verify Docker daemon is running
run: |
if ! docker info >/dev/null 2>&1; then
echo "Docker daemon is not available. Ensure Docker is installed and running, and the runner user has permission to access /var/run/docker.sock."
exit 1
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
if: github.event_name != 'pull_request' && env.ACT != 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
flavor: |
suffix=-amd64
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ steps.version.outputs.version }}
- name: Compute tags (local fallback)
id: tags
shell: bash
run: |
if [[ "${ACT:-}" == "true" ]]; then
T="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:local-amd64"
else
T="${{ steps.meta.outputs.tags }}"
# Guard against invalid tags generated under limited local metadata
if [[ -z "$T" || "$T" == *":-amd64" || "$T" =~ :$ ]]; then
V="${{ steps.version.outputs.version }}"
if [[ -z "$V" ]]; then V="dev-${GITHUB_SHA::7}"; fi
T="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${V}-amd64"
fi
fi
# Write possibly multi-line tag list safely to GITHUB_OUTPUT
{
echo "tags<<__TAGS__"
echo "$T"
echo "__TAGS__"
} >> "$GITHUB_OUTPUT"
- name: Build and push AMD64 Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
push: ${{ env.ACT != 'true' && github.event_name != 'pull_request' }}
load: ${{ env.ACT == 'true' }}
tags: ${{ steps.tags.outputs.tags }}
labels: |
org.opencontainers.image.title=YAMS
org.opencontainers.image.description=Yet Another Memory System - High-performance content-addressed storage
org.opencontainers.image.url=https://github.com/${{ github.repository }}
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.version=${{ steps.version.outputs.version }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.architecture=amd64
build-args: |
YAMS_VERSION=${{ steps.version.outputs.version }}
GIT_COMMIT=${{ github.sha }}
GIT_TAG=${{ github.ref_name }}
ZIG_VERSION=${{ env.ZIG_VERSION }}
# Use GitHub Actions cache for BuildKit layers (scoped per arch)
cache-from: type=gha,scope=docker-${{ runner.os }}-amd64
cache-to: type=gha,mode=max,scope=docker-${{ runner.os }}-amd64
# Enable BuildKit inline caching and leverage Conan layer reuse; future enhancement could mount host cache
provenance: false
# Build ARM64 image on native Linux ARM64 runner (no QEMU)
build-arm64:
name: Build Docker Image (linux/arm64 native)
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: false
- name: Initialize required submodules
shell: bash
run: |
set -euo pipefail
retry_cmd() {
local attempts="$1"
shift
local attempt=1
until "$@"; do
if [ "$attempt" -ge "$attempts" ]; then
return 1
fi
echo "Command failed (attempt ${attempt}/${attempts}): $*" >&2
sleep $((attempt * 5))
attempt=$((attempt + 1))
done
}
git config --global url."https://github.com/".insteadOf "git@github.com:"
retry_cmd 4 git submodule update --init --depth 1 third_party/sqlite-vec-cpp
retry_cmd 4 git submodule update --init --depth 1 third_party/simeon
retry_cmd 4 git submodule update --init --depth 1 third_party/symspell
- name: Verify Docker daemon is running
run: |
if ! docker info >/dev/null 2>&1; then
echo "Docker daemon is not available. Ensure Docker is installed and running, and the runner user has permission to access /var/run/docker.sock."
exit 1
fi
- name: Determine version
id: version
shell: bash
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}"
else
VERSION="dev-$(git rev-parse --short HEAD)"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Determined version: $VERSION"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: |
image=moby/buildkit:latest
- name: Log in to Container Registry
if: github.event_name != 'pull_request' && env.ACT != 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
flavor: |
suffix=-arm64
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ steps.version.outputs.version }}
- name: Compute tags (local fallback)
id: tags
shell: bash
run: |
if [[ "${ACT:-}" == "true" ]]; then
T="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:local-arm64"
else
T="${{ steps.meta.outputs.tags }}"
# Guard against invalid tags generated under limited local metadata
if [[ -z "$T" || "$T" == *":-arm64" || "$T" =~ :$ ]]; then
V="${{ steps.version.outputs.version }}"
if [[ -z "$V" ]]; then V="dev-${GITHUB_SHA::7}"; fi
T="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${V}-arm64"
fi
fi
# Write possibly multi-line tag list safely to GITHUB_OUTPUT
{
echo "tags<<__TAGS__"
echo "$T"
echo "__TAGS__"
} >> "$GITHUB_OUTPUT"
- name: Build and push ARM64 Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/arm64
push: ${{ env.ACT != 'true' && github.event_name != 'pull_request' }}
load: ${{ env.ACT == 'true' }}
tags: ${{ steps.tags.outputs.tags }}
labels: |
org.opencontainers.image.title=YAMS
org.opencontainers.image.description=Yet Another Memory System - High-performance content-addressed storage
org.opencontainers.image.url=https://github.com/${{ github.repository }}
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.version=${{ steps.version.outputs.version }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.architecture=arm64
build-args: |
YAMS_VERSION=${{ steps.version.outputs.version }}
GIT_COMMIT=${{ github.sha }}
GIT_TAG=${{ github.ref_name }}
ZIG_VERSION=${{ env.ZIG_VERSION }}
# Use GitHub Actions cache for BuildKit layers (scoped per arch)
cache-from: type=gha,scope=docker-${{ runner.os }}-arm64
cache-to: type=gha,mode=max,scope=docker-${{ runner.os }}-arm64
provenance: false
# Create multi-arch manifest
create-manifest:
name: Create Multi-arch Manifest
needs: [build-amd64, build-arm64]
runs-on: ubuntu-latest
if: github.event_name == 'release' || (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'))
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
shell: bash
env:
REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
for attempt in 1 2 3; do
if printf '%s' "$REGISTRY_TOKEN" | docker login "${{ env.REGISTRY }}" -u "${{ github.actor }}" --password-stdin; then
exit 0
fi
if [ "$attempt" -eq 3 ]; then
echo "docker login to ${{ env.REGISTRY }} failed after ${attempt} attempts" >&2
exit 1
fi
sleep $((attempt * 10))
done
- name: Create and push manifest
env:
VERSION: ${{ needs.build-amd64.outputs.version }}
run: |
# Base image name
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
# For main branch pushes, create 'latest' manifest
if [[ "${{ github.event_name }}" == "push" && ("${{ github.ref }}" == "refs/heads/main" || "${{ github.ref }}" == "refs/heads/master") ]]; then
docker buildx imagetools create -t ${IMAGE}:latest \
${IMAGE}:latest-amd64 \
${IMAGE}:latest-arm64
echo "Created multi-arch manifest: ${IMAGE}:latest"
fi
# For releases, create versioned manifests
if [[ "${{ github.event_name }}" == "release" ]]; then
TAG="${{ github.event.release.tag_name }}"
TAG="${TAG#v}"
# Create manifest for full version
docker buildx imagetools create -t ${IMAGE}:${TAG} \
${IMAGE}:${TAG}-amd64 \
${IMAGE}:${TAG}-arm64
echo "Created multi-arch manifest: ${IMAGE}:${TAG}"
# Also create major.minor manifest
MAJOR_MINOR=$(echo ${TAG} | sed -E 's/^([0-9]+\.[0-9]+).*/\1/')
if [[ "${MAJOR_MINOR}" != "${TAG}" ]]; then
docker buildx imagetools create -t ${IMAGE}:${MAJOR_MINOR} \
${IMAGE}:${TAG}-amd64 \
${IMAGE}:${TAG}-arm64
echo "Created multi-arch manifest: ${IMAGE}:${MAJOR_MINOR}"
fi
# And major-only manifest
MAJOR=$(echo ${TAG} | sed -E 's/^([0-9]+).*/\1/')
if [[ "${MAJOR}" != "${TAG}" ]]; then
docker buildx imagetools create -t ${IMAGE}:${MAJOR} \
${IMAGE}:${TAG}-amd64 \
${IMAGE}:${TAG}-arm64
echo "Created multi-arch manifest: ${IMAGE}:${MAJOR}"
fi
fi
# For dev builds
if [[ "$VERSION" == dev-* ]]; then
docker buildx imagetools create -t ${IMAGE}:${VERSION} \
${IMAGE}:${VERSION}-amd64 \
${IMAGE}:${VERSION}-arm64
echo "Created multi-arch manifest: ${IMAGE}:${VERSION}"
fi
- name: Test multi-arch image
run: |
# Test that the multi-arch image works
if [[ "${{ github.event_name }}" == "push" && ("${{ github.ref }}" == "refs/heads/main" || "${{ github.ref }}" == "refs/heads/master") ]]; then
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest --version
echo "Multi-arch image test completed successfully"
elif [[ "${{ github.event_name }}" == "release" ]]; then
TAG="${{ github.event.release.tag_name }}"
TAG="${TAG#v}"
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG} --version
echo "Multi-arch image test completed successfully"
fi