name: Publish Docker image
on:
workflow_dispatch:
push:
workflow_call:
jobs:
build:
name: Build Docker image (${{ matrix.platform }})
runs-on: ${{ matrix.runner }}
permissions:
contents: read
strategy:
fail-fast: true
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
artifact: kodit-image-amd64
- platform: linux/arm64
runner: ubuntu-24.04-arm
artifact: kodit-image-arm64
steps:
- name: Check out the repo
uses: actions/checkout@v6
with:
fetch-tags: true
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract version from git
id: version
run: |
VERSION=$(git describe --tags --always 2>/dev/null || echo "dev")
COMMIT=$(git rev-parse --short HEAD)
BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
echo "COMMIT=${COMMIT}" >> $GITHUB_OUTPUT
echo "BUILD_TIME=${BUILD_TIME}" >> $GITHUB_OUTPUT
- name: Build image
uses: docker/build-push-action@v6
with:
platforms: ${{ matrix.platform }}
context: .
file: ./Dockerfile
tags: kodit:test
outputs: type=docker,dest=/tmp/image.tar
build-args: |
VERSION=${{ steps.version.outputs.VERSION }}
COMMIT=${{ steps.version.outputs.COMMIT }}
BUILD_TIME=${{ steps.version.outputs.BUILD_TIME }}
cache-from: type=gha,scope=${{ matrix.platform }}
cache-to: type=gha,mode=max,scope=${{ matrix.platform }}
- name: Upload image
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: /tmp/image.tar
if-no-files-found: error
retention-days: 1
smoke-test:
name: Smoke test
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
timeout-minutes: 10
steps:
- name: Check out the repo
uses: actions/checkout@v6
- name: Download image
uses: actions/download-artifact@v4
with:
name: kodit-image-amd64
path: /tmp
- name: Load image
run: docker load -i /tmp/image.tar
- name: Cache Ollama models
uses: actions/cache@v5
with:
path: /tmp/ollama-models
key: ollama-models-qwen2.5-0.5b
- name: Start compose stack
run: docker compose -f test/smoke/docker-compose.yml up -d
env:
OLLAMA_MODELS: /tmp/ollama-models
- name: Pull Ollama models
run: |
docker compose -f test/smoke/docker-compose.yml exec ollama ollama pull qwen2.5:0.5b
sudo chown -R $(id -u):$(id -g) /tmp/ollama-models
- name: Wait for kodit to be healthy
run: |
for i in $(seq 1 30); do
if curl -sf http://localhost:8080/healthz; then
echo ""
echo "kodit is healthy"
exit 0
fi
echo "waiting for kodit... ($i/30)"
sleep 5
done
echo "kodit failed to become healthy"
docker compose -f test/smoke/docker-compose.yml logs kodit
exit 1
- name: Verify health endpoint
run: |
STATUS=$(curl -sf http://localhost:8080/healthz | jq -r '.status')
if [ "$STATUS" != "healthy" ]; then
echo "expected status 'healthy', got '$STATUS'"
exit 1
fi
echo "healthz returned status: $STATUS"
- name: Tear down
if: always()
run: docker compose -f test/smoke/docker-compose.yml down -v
push_to_registry:
name: Push Docker image to registry
runs-on: ubuntu-latest
needs: smoke-test
environment:
name: docker
permissions:
packages: write
contents: read
attestations: write
id-token: write
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
steps:
- name: Check out the repo
uses: actions/checkout@v6
with:
fetch-tags: true
fetch-depth: 0
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{ vars.REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Download amd64 image
uses: actions/download-artifact@v4
with:
name: kodit-image-amd64
path: /tmp/amd64
- name: Download arm64 image
uses: actions/download-artifact@v4
with:
name: kodit-image-arm64
path: /tmp/arm64
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ vars.REGISTRY }}/${{ vars.REGISTRY_ORG }}/${{ github.event.repository.name }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=ref,event=branch
type=ref,event=pr
type=sha
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
flavor: |
latest=false
- name: Push platform images and create manifests
id: push
run: |
IMAGE="${{ vars.REGISTRY }}/${{ vars.REGISTRY_ORG }}/${{ github.event.repository.name }}"
docker load -i /tmp/amd64/image.tar
docker tag kodit:test "${IMAGE}:buildcache-amd64"
docker push "${IMAGE}:buildcache-amd64"
docker load -i /tmp/arm64/image.tar
docker tag kodit:test "${IMAGE}:buildcache-arm64"
docker push "${IMAGE}:buildcache-arm64"
readarray -t TAGS <<< "${{ steps.meta.outputs.tags }}"
for TAG in "${TAGS[@]}"; do
[ -z "$TAG" ] && continue
docker manifest create "$TAG" \
"${IMAGE}:buildcache-amd64" \
"${IMAGE}:buildcache-arm64"
DIGEST=$(docker manifest push --purge "$TAG")
done
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v3
with:
subject-name: ${{ vars.REGISTRY }}/${{ vars.REGISTRY_ORG }}/${{ github.event.repository.name }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true