Skip to content

test: raise custom-exporter coverage to 90 (entrypoint, run_server, r… #30

test: raise custom-exporter coverage to 90 (entrypoint, run_server, r…

test: raise custom-exporter coverage to 90 (entrypoint, run_server, r… #30

Workflow file for this run

name: Build images
on:
pull_request:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
packages: write
env:
REGISTRY: ghcr.io
REGISTRY_NAMESPACE: ${{ github.repository_owner }}
jobs:
# Compute which buildable units a change touched → JSON matrix.
detect:
name: Detect affected
runs-on: ubuntu-latest
outputs:
units: ${{ steps.affected.outputs.units }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # need history to diff against the base
- name: Set up uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Install ci tooling
run: uv sync
- name: Compute affected units
id: affected
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "pull_request" ]; then
base="${{ github.event.pull_request.base.sha }}"
else
base="${{ github.event.before }}"
fi
# All-zeros (first push / force-push) or unresolvable base → build everything.
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ] \
|| ! git cat-file -e "${base}^{commit}" 2>/dev/null; then
files=".github/workflows/build.yml" # matches TOOLING_GLOBS → all units
else
files="$(git diff --name-only "$base"...HEAD)"
fi
units="$(printf '%s\n' "$files" | uv run ci affected .)"
echo "units=$units" >> "$GITHUB_OUTPUT"
echo "Affected: $units"
# One job per affected image. PR builds (no push); main pushes :sha + :latest.
build:
name: Build ${{ matrix.service }}
needs: detect
if: needs.detect.outputs.units != '[]'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.detect.outputs.units) }}
steps:
- uses: actions/checkout@v4
- name: Set up uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Test ${{ matrix.service }}
# Runs unit + integration together; the project's --cov-fail-under applies
# to their COMBINED coverage. e2e is orchestration-heavy and runs separately
# (e2e.yml / `ci test --tier e2e`). Keyed on the build context so multi-image
# stacks test the right subtree; no-ops for units with no pyproject (devbox).
run: uv run ci test "${{ matrix.context }}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to ghcr.io
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build${{ github.event_name != 'pull_request' && ' & push' || '' }} ${{ matrix.image_name }}
run: |
set -euo pipefail
repo="${REGISTRY}/${REGISTRY_NAMESPACE}/${{ matrix.image_name }}"
# bake resolves relative context/dockerfile from CWD, so run it from
# the compose file's directory (compose-relative paths then resolve).
cd "$(dirname "${{ matrix.compose_file }}")"
compose="$(basename "${{ matrix.compose_file }}")"
# --allow fs.read: some units (devbox consumers) build from a context
# outside the compose dir (../../../images/devbox), which bake gates.
args=(buildx bake -f "$compose" "${{ matrix.service }}" --allow "fs.read=*"
--set "${{ matrix.service }}.cache-from=type=gha,scope=${{ matrix.image_name }}"
--set "${{ matrix.service }}.cache-to=type=gha,mode=max,scope=${{ matrix.image_name }}")
if [ "${{ github.event_name }}" = "pull_request" ]; then
# Gate only — build, don't push.
args+=(--set "${{ matrix.service }}.output=type=cacheonly")
docker "${args[@]}"
else
# Push the compose image tag (:latest), then add the immutable :sha
# as a server-side retag. (bake --set tags= doesn't split a comma list.)
args+=(--push)
docker "${args[@]}"
docker buildx imagetools create "${repo}:latest" --tag "${repo}:${GITHUB_SHA}"
fi
# Always-run required check: green when nothing built, red if any build failed.
# This (not the matrix) is what branch protection should require — a path-filtered
# matrix that is empty for an unrelated PR would otherwise never report.
gate:
name: Build gate
needs: [detect, build]
if: always()
runs-on: ubuntu-latest
steps:
- name: Verify builds
run: |
if [ "${{ needs.build.result }}" = "failure" ] || [ "${{ needs.build.result }}" = "cancelled" ]; then
echo "A build job failed."; exit 1
fi
echo "All affected builds passed (or nothing to build)."