Skip to content

fix: run geo metrics incrementally #404

fix: run geo metrics incrementally

fix: run geo metrics incrementally #404

Workflow file for this run

# Build the zvg-immo image and push it to GitHub Container Registry.
#
# Image is consumed by the production Ansible deployment
# (roles/zvg-immo/ in the ansible repo) which `docker compose pull`s the tag
# specified in its docker-compose.yml.j2 template (default: `:latest` on
# default branch, or a pinned `:sha-<short>` for reproducible deploys).
#
# Multi-arch amd64+arm64, but unlike haex-claude-proxy's workflow this does
# NOT use QEMU to cross-build arm64 on an amd64 runner — this app's build
# (pnpm install + Vite/Nitro build) is heavy enough that QEMU emulation
# regularly hangs for hours instead of just being slow. Each platform builds
# natively on its own runner (ubuntu-24.04-arm is a free GitHub-hosted
# runner for public repos) and a final job merges both into one multi-arch
# manifest — the documented docker/build-push-action pattern for this.
name: Build zvg-immo image
on:
push:
branches:
- main
tags:
- "v*"
paths:
- "Dockerfile"
- "app.vue"
- "nuxt.config.ts"
- "tsconfig.json"
- "package.json"
- "pnpm-lock.yaml"
- "assets/**"
- "components/**"
- "composables/**"
- "i18n/**"
- "layouts/**"
- "lib/**"
- "pages/**"
- "public/**"
- "scripts/**"
- "server/**"
- "types/**"
- ".github/workflows/build-image.yml"
workflow_dispatch:
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
slug: amd64
- platform: linux/arm64
runner: ubuntu-24.04-arm
slug: arm64
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Standard OCI labels (org.opencontainers.image.revision/.created/...)
# derived from git context — without this the built image carries no
# labels at all, so there's no way to tell which commit is actually
# deployed short of comparing timestamps. Only the labels output is
# used here; tags are computed separately in the merge job below.
- name: Extract image metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository_owner }}/zvg-immo
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,format=short
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v7
with:
context: .
file: Dockerfile
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.slug }}
cache-to: type=gha,mode=max,scope=${{ matrix.slug }}
outputs: type=image,name=ghcr.io/${{ github.repository_owner }}/zvg-immo,push-by-digest=true,name-canonical=true,push=true
# Gate on the failure mode that took the site down: a broken SSR render
# that returns 500 on "/" while /api/* stays healthy. Runs the freshly
# built (native-arch) image and requires "/" to return 200. A failure
# here fails the matrix job, which skips the `merge` job below — so a
# broken build never gets the `:latest` tag that podman auto-update pulls.
# ZVG_SKIP_BOOT_TASKS=1 keeps the container from crawling any upstream
# portal during the test.
- name: Smoke test — homepage SSR must render
run: |
set -euo pipefail
img="ghcr.io/${{ github.repository_owner }}/zvg-immo@${{ steps.build.outputs.digest }}"
docker pull "$img"
docker run -d --name smoke -e ZVG_SKIP_BOOT_TASKS=1 -p 3000:3000 "$img"
# Seed a minimal list cache so "/" renders against data instead of
# blocking on a cold-cache live crawl of every upstream portal — that
# would hang the test and needlessly hammer the portals from CI.
docker exec smoke sh -c 'mkdir -p /app/.cache_zvg/list && printf "%s" "{\"platform\":\"smoke\",\"source\":\"smoke\",\"countries\":[\"de\"],\"regions\":[\"Bayern\"],\"fetchedAt\":\"2026-01-01T00:00:00.000Z\",\"totalReported\":0,\"auctions\":[]}" > /app/.cache_zvg/list/de-by.json'
for i in $(seq 1 30); do
if curl -fsS -o /dev/null http://localhost:3000/api/regions; then break; fi
if [ "$i" = 30 ]; then echo "::error::server never became ready"; docker logs smoke; exit 1; fi
sleep 2
done
code=$(curl -s -o /tmp/home.html -w '%{http_code}' --max-time 20 http://localhost:3000/)
echo "GET / -> $code"
if [ "$code" != "200" ]; then
echo "::error::homepage SSR did not return 200 (got $code)"
head -c 800 /tmp/home.html || true
docker logs smoke || true
exit 1
fi
docker rm -f smoke
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.slug }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Compute image tags
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository_owner }}/zvg-immo
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,format=short
type=raw,value=latest,enable={{is_default_branch}}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/${{ github.repository_owner }}/zvg-immo@sha256:%s ' *)
env:
DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }}
- name: Inspect image
run: |
docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/zvg-immo:${{ steps.meta.outputs.version }}