One job on the trusted x86 runner builds amd64 and arm64 (QEMU), pushes each platform by digest, then scripts/assemble-multiarch-index.sh creates :latest. arm64 is continue-on-error: a failed arm64 leg publishes the usual amd64-only tag. No Dockerfile change (both base images publish arm64). Adds workflow_dispatch and an offline test for the assemble script and workflow wiring. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0185ukXjXyosSkunDsdWz3mb
128 lines
5.8 KiB
YAML
128 lines
5.8 KiB
YAML
name: WHP Default Container
|
|
run-name: ${{ gitea.actor }} pushed a change to main
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
# Manual publish (also the safe way to do the first multi-arch publish after
|
|
# merging with [skip ci] in the merge title).
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
Build-and-Push:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
# Base image pinned by DIGEST, looked up directly at Docker Hub (never via
|
|
# the pkg-cache mirror), so the mirror can only serve content-addressed
|
|
# blobs that BuildKit verifies. If the lookup fails this job does NOT use
|
|
# the mirror and pulls the tag straight from Docker Hub.
|
|
# Canonical docs: cloud-apache-container docs/pkg-cache.md.
|
|
- name: Resolve base image digest (Docker Hub, direct)
|
|
id: base
|
|
env:
|
|
BASE_TAG: httpd:alpine
|
|
run: |
|
|
set -uo pipefail
|
|
if ref=$(.gitea/scripts/resolve-base-image.sh "$BASE_TAG") && [[ "$ref" =~ ^[a-z0-9][a-z0-9._/-]*@sha256:[0-9a-f]{64}$ ]]; then
|
|
echo "base-image: ${BASE_TAG} -> ${ref} (digest from registry-1.docker.io)"
|
|
echo "image=${ref}" >> "$GITHUB_OUTPUT"
|
|
echo "pinned=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "::warning::base image ${BASE_TAG}: Docker Hub digest lookup failed - pulling the tag directly from Docker Hub, pkg-cache registry mirror NOT used"
|
|
echo "image=${BASE_TAG}" >> "$GITHUB_OUTPUT"
|
|
echo "pinned=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
# Internal package cache (CT130 pkg-cache). Used ONLY if it answers over
|
|
# TLS with our pinned CA; otherwise this job builds from Docker Hub exactly
|
|
# as before. This image installs no OS packages, so only the registry
|
|
# mirror is used (repos= is probed and logged for parity with the other
|
|
# repos but not passed to the build).
|
|
- name: Probe package cache
|
|
id: pkgcache
|
|
# The registry mirror is only used for a digest-pinned base image.
|
|
env:
|
|
BASE_PINNED: ${{ steps.base.outputs.pinned }}
|
|
run: |
|
|
url=""; mirror=""
|
|
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi
|
|
if [ "$BASE_PINNED" = true ] && curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi
|
|
echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<not used: Docker Hub direct>}"
|
|
[ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories"
|
|
echo "url=${url}" >> "$GITHUB_OUTPUT"
|
|
echo "mirror=${mirror}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3
|
|
|
|
# BuildKit (docker-container driver) ignores dockerd's registry-mirrors,
|
|
# so the mirror is configured on the builder; BuildKit itself still falls
|
|
# back to Docker Hub if the mirror stops answering mid-run.
|
|
- name: Set up Docker Buildx (Docker Hub via pkg-cache mirror)
|
|
if: steps.pkgcache.outputs.mirror != ''
|
|
uses: https://github.com/docker/setup-buildx-action@v3
|
|
with:
|
|
buildkitd-config-inline: |
|
|
[registry."docker.io"]
|
|
mirrors = ["192.168.1.40:5000"]
|
|
[registry."192.168.1.40:5000"]
|
|
ca = ["${{ github.workspace }}/keys/pkg-cache/ca.crt"]
|
|
|
|
- name: Set up Docker Buildx
|
|
if: steps.pkgcache.outputs.mirror == ''
|
|
uses: https://github.com/docker/setup-buildx-action@v3
|
|
|
|
- name: Login to Gitea
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: repo.anhonesthost.net
|
|
username: ${{ secrets.CI_USER }}
|
|
password: ${{ secrets.CI_TOKEN }}
|
|
|
|
# Multi-arch (linux/amd64 + linux/arm64) in ONE job on the trusted x86
|
|
# runner (arm64 under QEMU, set up above). Each platform is pushed BY
|
|
# DIGEST (untagged); the :latest tag is created only by
|
|
# scripts/assemble-multiarch-index.sh. Template + rules:
|
|
# docs/arm64/IMAGE-PIPELINE-PLAN.md sections 4.1 / 4.4 (whp repo).
|
|
#
|
|
# amd64: required. Failure here fails the job and publishes nothing, as
|
|
# before.
|
|
- name: Build amd64 (push by digest)
|
|
id: amd64
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
platforms: linux/amd64
|
|
build-args: |
|
|
BASE_IMAGE=${{ steps.base.outputs.image }}
|
|
outputs: type=image,"name=repo.anhonesthost.net/cloud-hosting-platform/whp-default",push-by-digest=true,name-canonical=true,push=true
|
|
|
|
# arm64: best effort. Must never block or alter the amd64 tag.
|
|
- name: Build arm64 (push by digest)
|
|
id: arm64
|
|
continue-on-error: true
|
|
timeout-minutes: 20
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
platforms: linux/arm64
|
|
build-args: |
|
|
BASE_IMAGE=${{ steps.base.outputs.image }}
|
|
outputs: type=image,"name=repo.anhonesthost.net/cloud-hosting-platform/whp-default",push-by-digest=true,name-canonical=true,push=true
|
|
|
|
# The arm64 digest is read only from THIS run's step output, so a previous
|
|
# run's arm64 is never re-attached; a failed arm64 leg publishes :latest
|
|
# amd64-only exactly as before multi-arch.
|
|
- name: Assemble :latest index
|
|
env:
|
|
AMD64_DIGEST: ${{ steps.amd64.outputs.digest }}
|
|
ARM64_DIGEST: ${{ steps.arm64.outputs.digest }}
|
|
ARM64_OUTCOME: ${{ steps.arm64.outcome }}
|
|
run: |
|
|
scripts/assemble-multiarch-index.sh \
|
|
--amd64 "$AMD64_DIGEST" \
|
|
--arm64 "$ARM64_DIGEST" --arm64-outcome "$ARM64_OUTCOME" \
|
|
--repo repo.anhonesthost.net/cloud-hosting-platform/whp-default \
|
|
--tag latest
|