372 lines
14 KiB
YAML
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
|