Merge pull request #19: haproxy pin 3.0.11 (any Debian revision) + multi-arch publish [skip ci]
This commit was merged in pull request #19.
This commit is contained in:
commit
46ea498db3
3 files changed
+86
-28
No files matched your search
@@ -4,10 +4,26 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
# Manual publish (e.g. after a [skip ci] merge, or to re-publish after a
|
||||
# failed arm64 leg).
|
||||
workflow_dispatch:
|
||||
|
||||
# Multi-arch (linux/amd64 + linux/arm64) in ONE job on the trusted x86 runner.
|
||||
# Each platform is pushed BY DIGEST (untagged) to BOTH registries, then
|
||||
# scripts/assemble-multiarch-index.sh creates :latest, :<version> and :<sha>
|
||||
# from those digests (same template as build-push-coraza.yaml; see
|
||||
# docs/arm64/IMAGE-PIPELINE-PLAN.md sections 4.1 / 4.4 in the WHP repo).
|
||||
# * amd64 is required and built exactly as before; the Dockerfile build gate
|
||||
# (unit suites + `haproxy -c`) runs in it. Failure = nothing published.
|
||||
# * arm64 is continue-on-error and runs the SAME Dockerfile gate under QEMU
|
||||
# (the gate is RUN steps, so it executes on the arm64 binary). If arm64
|
||||
# fails or times out the tags are published amd64-only, exactly as before
|
||||
# multi-arch, and a previous run's arm64 is never re-attached (the digest is
|
||||
# read only from THIS run's step output).
|
||||
jobs:
|
||||
Build-and-Push:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -30,6 +46,8 @@ jobs:
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: https://github.com/docker/setup-buildx-action@v3
|
||||
@@ -59,20 +77,45 @@ jobs:
|
||||
id: ver
|
||||
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build Image
|
||||
# amd64: required. Pushed untagged by digest to both registries (same
|
||||
# digest in each); tags are only created by the assemble step below.
|
||||
- name: Build amd64 (push by digest)
|
||||
id: amd64
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
VERSION=${{ steps.ver.outputs.version }}
|
||||
PKG_CACHE=${{ steps.pkgcache.outputs.url }}
|
||||
# Three tags per registry: :latest (moving), :<version> (human-readable
|
||||
# release), :<sha> (immutable, guaranteed-unique rollback target).
|
||||
tags: |
|
||||
repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base:latest
|
||||
repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base:${{ steps.ver.outputs.version }}
|
||||
repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base:${{ gitea.sha }}
|
||||
ghcr.io/shadowdao/haproxy-manager-base:latest
|
||||
ghcr.io/shadowdao/haproxy-manager-base:${{ steps.ver.outputs.version }}
|
||||
ghcr.io/shadowdao/haproxy-manager-base:${{ gitea.sha }}
|
||||
outputs: type=image,"name=repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base,ghcr.io/shadowdao/haproxy-manager-base",push-by-digest=true,name-canonical=true,push=true
|
||||
|
||||
# arm64: best effort (QEMU, apt-based image). Must never block or alter
|
||||
# the amd64 tags.
|
||||
- name: Build arm64 (push by digest)
|
||||
id: arm64
|
||||
continue-on-error: true
|
||||
timeout-minutes: 30
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/arm64
|
||||
build-args: |
|
||||
VERSION=${{ steps.ver.outputs.version }}
|
||||
PKG_CACHE=${{ steps.pkgcache.outputs.url }}
|
||||
outputs: type=image,"name=repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base,ghcr.io/shadowdao/haproxy-manager-base",push-by-digest=true,name-canonical=true,push=true
|
||||
|
||||
# Three tags per registry: :latest (moving), :<version> (human-readable
|
||||
# release), :<sha> (immutable, guaranteed-unique rollback target).
|
||||
- name: Assemble tag indexes
|
||||
env:
|
||||
AMD64_DIGEST: ${{ steps.amd64.outputs.digest }}
|
||||
ARM64_DIGEST: ${{ steps.arm64.outputs.digest }}
|
||||
ARM64_OUTCOME: ${{ steps.arm64.outcome }}
|
||||
VERSION: ${{ steps.ver.outputs.version }}
|
||||
SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
scripts/assemble-multiarch-index.sh \
|
||||
--amd64 "$AMD64_DIGEST" \
|
||||
--arm64 "$ARM64_DIGEST" --arm64-outcome "$ARM64_OUTCOME" \
|
||||
--repo repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base \
|
||||
--repo ghcr.io/shadowdao/haproxy-manager-base \
|
||||
--tag latest --tag "$VERSION" --tag "$SHA"
|
||||
+31
-16
@@ -23,22 +23,30 @@ LABEL org.opencontainers.image.title="haproxy-manager-base" \
|
||||
org.opencontainers.image.version="${VERSION}" \
|
||||
org.opencontainers.image.licenses="MIT"
|
||||
|
||||
# haproxy is PINNED. It was previously unpinned, so the binary could move under
|
||||
# us at Debian's timing — an upstream release that rejected our config would have
|
||||
# broken an unrelated commit's build, or worse, shipped an edge that refuses to
|
||||
# start (see the `haproxy -c` gate below for why that matters: a config HAProxy
|
||||
# haproxy is PINNED to a reviewed UPSTREAM version (HAPROXY_UPSTREAM), not to one
|
||||
# Debian package revision. It was previously unpinned, so the binary could move
|
||||
# under us at Debian's timing - an upstream release that rejected our config would
|
||||
# have broken an unrelated commit's build, or worse, shipped an edge that refuses
|
||||
# to start (see the `haproxy -c` gate below for why that matters: a config HAProxy
|
||||
# rejects leaves the container Up with 80/443 unbound and /health still 200).
|
||||
#
|
||||
# Pinning does NOT make the gate redundant, and the gate does NOT make pinning
|
||||
# unnecessary — they compose. Pinned means the version moves deliberately; the
|
||||
# gate then answers immediately whether the new binary still accepts fleet config.
|
||||
# It also makes the image reproducible, which it previously was not.
|
||||
# History: this used to pin the exact Debian version (3.0.11-1+deb13u3). Debian
|
||||
# security then shipped 3.0.11-1+deb13u4 (same upstream 3.0.11, same arch set) and
|
||||
# the exact pin silently froze the image on the OLDER, unpatched revision, and
|
||||
# would hard-fail the build as soon as u3 left the archive. Debian security
|
||||
# revisions of the SAME upstream version are exactly what we want to take, so:
|
||||
# * an apt preference (Pin: version 3.0.11-*) makes apt install the NEWEST
|
||||
# Debian revision of that upstream version on any architecture;
|
||||
# * the build then FAILS unless the installed package AND `haproxy -v` both
|
||||
# report that upstream version, so a major/minor bump (3.0 -> 3.1, 3.0.12)
|
||||
# can never arrive by accident: that still needs this ARG changed and the
|
||||
# `haproxy -c` gate below to pass.
|
||||
# The pin and the gate compose: pinned = the version moves deliberately; the gate
|
||||
# answers immediately whether the new binary still accepts fleet config.
|
||||
#
|
||||
# To move it: bump the version here, rebuild, and let the gate verify. If Debian
|
||||
# security-updates the package (e.g. -1+deb13u4) the build FAILS until this pin is
|
||||
# updated — that failure is the point, not a bug. Check availability with:
|
||||
# apt-cache policy haproxy
|
||||
ARG HAPROXY_VERSION=3.0.11-1+deb13u3
|
||||
# To move to another upstream version: change HAPROXY_UPSTREAM, rebuild, let the
|
||||
# gate verify. Check what Debian offers with: apt-cache madison haproxy
|
||||
ARG HAPROXY_UPSTREAM=3.0.11
|
||||
# OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40
|
||||
# (CT130 pkg-cache, https only; CI sets it only after probing the cache with the
|
||||
# pinned CA). Unset = the upstream Debian repositories, exactly as before. The
|
||||
@@ -50,7 +58,14 @@ ARG PKG_CACHE=
|
||||
COPY ./scripts/pkg-cache-repos.sh /usr/local/sbin/pkg-cache-repos.sh
|
||||
COPY ./keys/pkg-cache/ /usr/local/share/pkg-cache/
|
||||
RUN /usr/local/sbin/pkg-cache-repos.sh apt && \
|
||||
apt update -y && apt dist-upgrade -y && apt install socat "haproxy=${HAPROXY_VERSION}" cron certbot curl jq net-tools -y && apt-mark hold haproxy && \
|
||||
apt update -y && apt dist-upgrade -y && \
|
||||
printf 'Package: haproxy\nPin: version %s-*\nPin-Priority: 1001\n' "${HAPROXY_UPSTREAM}" > /etc/apt/preferences.d/haproxy-upstream-pin && \
|
||||
apt install socat haproxy cron certbot curl jq net-tools -y && apt-mark hold haproxy && \
|
||||
rm -f /etc/apt/preferences.d/haproxy-upstream-pin && \
|
||||
deb_ver="$(dpkg-query -W -f='${Version}' haproxy)" && \
|
||||
case "${deb_ver}" in "${HAPROXY_UPSTREAM}"-*) ;; *) echo "FATAL: installed haproxy ${deb_ver} is not upstream ${HAPROXY_UPSTREAM}" >&2; exit 1;; esac && \
|
||||
haproxy -v | head -n1 | grep -F "version ${HAPROXY_UPSTREAM}-" && \
|
||||
echo "haproxy pin OK: upstream ${HAPROXY_UPSTREAM}, Debian package ${deb_ver}" && \
|
||||
/usr/local/sbin/pkg-cache-repos.sh restore && \
|
||||
apt clean && rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /haproxy
|
||||
@@ -95,8 +110,8 @@ RUN pip install -r requirements.txt
|
||||
# * it cannot be skipped, and it protects local `docker build` too;
|
||||
# * no workflow restructuring (build-push-action builds and pushes in one
|
||||
# step, so gating in CI would mean splitting build from push);
|
||||
# * it validates against the EXACT haproxy binary in this image. Line 26
|
||||
# installs haproxy unpinned, so that binary moves between builds - this
|
||||
# * it validates against the EXACT haproxy binary in this image. The
|
||||
# installed haproxy can move between builds (Debian revisions) - this
|
||||
# turns "the new haproxy rejects our config" from a silent production
|
||||
# risk into a build failure.
|
||||
#
|
||||
|
||||
Reference in new issue
Block a user