diff --git a/.github/workflows/build-pg16-focal.yml b/.github/workflows/build-pg16-focal.yml new file mode 100644 index 00000000..84eadb4e --- /dev/null +++ b/.github/workflows/build-pg16-focal.yml @@ -0,0 +1,107 @@ +name: Build PostgreSQL 16 (focal) + +# Builds PostgreSQL 16 *core* .deb packages for Ubuntu 20.04 (focal) by rebuilding +# the official Debian source package (PGDG dropped focal binaries upstream), then +# signs them with debsigs (--sign=maint) using the existing packaging key. +# +# This is intentionally a *standalone* pipeline: it does not use the extension +# build flow (citus_package / pg_buildext / the build-package.yml matrix), which +# assumes PostgreSQL itself comes from PGDG. + +on: + workflow_dispatch: + inputs: + pg_upstream_version: + description: "PostgreSQL 16 minor version to build (e.g. 16.14, 16.15)" + required: true + default: "16.14" + pg_orig_sha256: + description: "Optional: pin sha256 of postgresql-16_.orig.tar.bz2 (leave empty to auto-resolve from the official .dsc)" + required: false + default: "" + run_tests: + description: "Run upstream regression suite (1=yes, slower)" + required: false + default: "0" + push: + branches: + - pg16-focal + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-sign: + name: Build & sign PostgreSQL ${{ github.event.inputs.pg_upstream_version || '16.14' }} (focal) + runs-on: ubuntu-latest + env: + PACKAGING_SECRET_KEY: ${{ secrets.PACKAGING_SECRET_KEY }} + PACKAGING_PASSPHRASE: ${{ secrets.PACKAGING_PASSPHRASE }} + PG_UPSTREAM_VERSION: ${{ github.event.inputs.pg_upstream_version || '16.14' }} + PG_ORIG_SHA256: ${{ github.event.inputs.pg_orig_sha256 || '' }} + RUN_TESTS: ${{ github.event.inputs.run_tests || '0' }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USER_NAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Build focal builder image + run: | + docker build -t pg16-focal-builder \ + -f dockerfiles/pg16-focal-builder/Dockerfile . + + - name: Build PostgreSQL 16 packages + run: | + mkdir -p packages + docker run --rm \ + -e PG_UPSTREAM_VERSION="${PG_UPSTREAM_VERSION}" \ + -e PG_ORIG_SHA256="${PG_ORIG_SHA256}" \ + -e RUN_TESTS="${RUN_TESTS}" \ + -v "${PWD}/packages:/packages" \ + pg16-focal-builder + echo "Built packages:" + ls -1 packages/focal/*.deb + + - name: Sign packages (debsigs --sign=maint) + # Use the prebuilt, deployed debsigner image (the one all Citus signing + # uses), not a locally built copy of dockerfiles/debsigner, which has + # drifted from it. Mirrors tools.packaging_automation.citus_package. + run: | + if [ -z "${PACKAGING_SECRET_KEY}" ] || [ -z "${PACKAGING_PASSPHRASE}" ]; then + echo "::error::PACKAGING_SECRET_KEY / PACKAGING_PASSPHRASE secrets are not set" >&2 + exit 1 + fi + printf '%s' "${PACKAGING_PASSPHRASE}" | docker run --rm -i \ + -e PACKAGING_SECRET_KEY \ + -e PACKAGING_PASSPHRASE \ + -v "${PWD}/packages:/packages" \ + citusdata/packaging:debsigner + + - name: Verify signatures are embedded + run: | + rc=0 + for deb in packages/focal/*.deb; do + if ar t "$deb" | grep -q '^_gpgmaint$'; then + echo "signed: $deb" + else + echo "::error::missing _gpgmaint signature in $deb" >&2 + rc=1 + fi + done + exit $rc + + - name: Upload signed packages + uses: actions/upload-artifact@v4 + with: + name: postgresql-16-focal-deb + path: | + packages/focal/*.deb + packages/focal/*.changes + packages/focal/*.buildinfo + if-no-files-found: error diff --git a/dockerfiles/pg16-focal-builder/Dockerfile b/dockerfiles/pg16-focal-builder/Dockerfile new file mode 100644 index 00000000..f14258d4 --- /dev/null +++ b/dockerfiles/pg16-focal-builder/Dockerfile @@ -0,0 +1,62 @@ +# vim:set ft=dockerfile: +# +# Builder image for PostgreSQL 16 *core* packages targeting Ubuntu 20.04 (focal). +# +# Why this exists: +# apt.postgresql.org (PGDG) no longer ships PostgreSQL 16 binaries for focal +# (focal reached EOL standard support 2025-04; the last official focal build +# was 16.9-1.pgdg20.04+1). To get a newer 16.x on focal we rebuild the official +# Debian source package ourselves. +# +# Strategy (validated): +# - Upstream tarball: postgresql-16_.orig.tar.bz2 (e.g. 16.14) +# - Debian packaging: the focal-era debian/ from 16.9-1.pgdg20.04+1, because its +# build profile uses focal's *default* toolchain (clang/llvm-dev = LLVM 10), +# so the resulting JIT depends on focal's libllvm10 (installable on focal), +# unlike newer packaging which requires clang-19/llvm-19. +# - Build tooling (debhelper 13, dh-exec, postgresql-common-dev) is restored from +# the PGDG *archive* (apt-archive.postgresql.org), which keeps the removed +# focal-pgdg suite. +# +# The heavy lifting lives in scripts/build_pg16_focal (the entrypoint). +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive + +# PGDG repository signing key fingerprint: +# B97B 0AFC AA1A 47F0 44F2 44A0 7FCC 7D46 ACCC 4CF8 +RUN set -ex; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates curl gnupg; \ + install -d /usr/share/keyrings; \ + curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \ + | gpg --dearmor -o /usr/share/keyrings/pgdg-archive.gpg; \ + echo "deb [signed-by=/usr/share/keyrings/pgdg-archive.gpg] https://apt-archive.postgresql.org/pub/repos/apt focal-pgdg main 16" \ + > /etc/apt/sources.list.d/pgdg-archive.list; \ + # make sure 'universe' is enabled (clang / llvm-dev live there on focal) + sed -i 's/^# deb \(.*universe\)/deb \1/' /etc/apt/sources.list; \ + apt-get update; \ + # base build tooling; the per-build Build-Depends are resolved at run time + # by scripts/build_pg16_focal via mk-build-deps against debian/control. + apt-get install -y --no-install-recommends \ + build-essential \ + devscripts \ + equivs \ + fakeroot \ + quilt \ + dpkg-dev \ + debhelper \ + dh-exec \ + postgresql-common-dev \ + xz-utils \ + bzip2; \ + rm -rf /var/lib/apt/lists/* + +# Fail the image build early if the archived focal-pgdg debhelper (>= 13) is not +# what we picked up (debhelper-compat (= 13) is required by the packaging). +RUN dpkg-query -W -f='${Package} ${Version}\n' debhelper postgresql-common-dev dh-exec + +COPY scripts/build_pg16_focal /usr/local/bin/build_pg16_focal +RUN chmod +x /usr/local/bin/build_pg16_focal + +VOLUME /packages +ENTRYPOINT ["/usr/local/bin/build_pg16_focal"] diff --git a/scripts/build_pg16_focal b/scripts/build_pg16_focal new file mode 100755 index 00000000..e7ff19ea --- /dev/null +++ b/scripts/build_pg16_focal @@ -0,0 +1,158 @@ +#!/bin/bash +# +# build_pg16_focal -- rebuild PostgreSQL 16 *core* Debian packages for Ubuntu 20.04 (focal). +# +# Approach (see dockerfiles/pg16-focal-builder/Dockerfile for the rationale): +# newer upstream orig.tar.bz2 + focal-era debian/ packaging (16.9-1.pgdg20.04+1) +# -> dpkg-buildpackage inside a focal environment that has the archived +# focal-pgdg build tooling available. +# +# Output: unsigned *.deb in ${OUTPUT_DIR} (default /packages/focal). Signing is a +# separate step performed by the debsigner image (debsigs --sign=maint), so that +# the build and the signing key never live in the same container. +# +# All inputs are overridable via environment variables. +# +# To build a different 16.x minor version (e.g. 16.15) you only need to set +# PG_UPSTREAM_VERSION=16.15 +# The orig-tarball checksum is resolved automatically from PostgreSQL's official +# .dsc unless you pin it explicitly via PG_ORIG_SHA256 (recommended for releases). + +set -euo pipefail + +# ---------------------------------------------------------------------------- +# Inputs (override via env) +# ---------------------------------------------------------------------------- +UPSTREAM="${PG_UPSTREAM_VERSION:-16.14}" # upstream PostgreSQL version to build +DEBIAN_BASE="${PG_DEBIAN_BASE:-16.9-1.pgdg20.04+1}" # focal-era packaging to reuse (kept constant across 16.x) +TARGET_VERSION="${PG_TARGET_VERSION:-${UPSTREAM}-1.pgdg20.04+1}" # produced package version + +# sha256 of the artifacts we download. Leave empty to auto-resolve from the +# official .dsc; set to pin a specific value (integrity / reproducibility). +ORIG_SHA256="${PG_ORIG_SHA256:-}" +DEBIAN_SHA256="${PG_DEBIAN_SHA256:-}" + +# Set RUN_TESTS=1 to run the upstream regression suite (slower, needs more deps). +RUN_TESTS="${RUN_TESTS:-0}" + +OUTPUT_DIR="${OUTPUT_DIR:-/packages/focal}" + +# Sources: orig tarball from the live pool (shared across distros), focal-era +# debian/ packaging from the archive (removed from the live pool with focal). +LIVE_POOL="https://apt.postgresql.org/pub/repos/apt/pool/main/p/postgresql-16" +ARCHIVE_POOL="https://apt-archive.postgresql.org/pub/repos/apt/pool/main/p/postgresql-16" + +ORIG_TARBALL="postgresql-16_${UPSTREAM}.orig.tar.bz2" +DEBIAN_TARBALL="postgresql-16_${DEBIAN_BASE}.debian.tar.xz" + +WORK="${WORK_DIR:-/build}" +SRCDIR="${WORK}/postgresql-16-${UPSTREAM}" + +# changelog identity (only cosmetic for a binary build) +export DEBEMAIL="${DEBEMAIL:-pgsql-pkg-debian@lists.postgresql.org}" +export DEBFULLNAME="${DEBFULLNAME:-PostgreSQL focal rebuild}" + +case "${UPSTREAM}" in + 16.*) : ;; + *) echo "ERROR: this pipeline builds PostgreSQL 16.x only (got '${UPSTREAM}')." >&2; exit 64 ;; +esac + +# dsc_sha256 -> prints the sha256 recorded for +# in the Checksums-Sha256 stanza of a Debian .dsc (served over HTTPS by PGDG). +dsc_sha256() { + curl -fsSL "$1" | awk -v f="$2" ' + /^Checksums-Sha256:/ { insec=1; next } + /^[A-Za-z]/ { insec=0 } + insec && $3==f { print $1; exit }' +} + +# Find any released .dsc for this upstream version (the orig tarball is shared +# across every per-distro revision, so any of them carries its checksum). +find_orig_dsc_url() { + local v="${UPSTREAM//./\\.}" + curl -fsSL "${LIVE_POOL}/" \ + | grep -oE "postgresql-16_${v}-[0-9]+\.pgdg[A-Za-z0-9.+~]*\.dsc" \ + | sort -u | tail -1 +} + +echo "==> Building PostgreSQL ${UPSTREAM} as ${TARGET_VERSION} (focal)" +mkdir -p "${WORK}" "${OUTPUT_DIR}" +cd "${WORK}" + +echo "==> [1/6] Download source artifacts" +curl -fSL "${LIVE_POOL}/${ORIG_TARBALL}" -o "${ORIG_TARBALL}" +curl -fSL "${ARCHIVE_POOL}/${DEBIAN_TARBALL}" -o "${DEBIAN_TARBALL}" + +echo "==> [2/6] Resolve & verify checksums" +if [ -z "${ORIG_SHA256}" ]; then + orig_dsc="$(find_orig_dsc_url || true)" + [ -n "${orig_dsc}" ] || { echo "ERROR: could not find a .dsc for ${ORIG_TARBALL} (is ${UPSTREAM} released?)." >&2; exit 1; } + ORIG_SHA256="$(dsc_sha256 "${LIVE_POOL}/${orig_dsc}" "${ORIG_TARBALL}")" + [ -n "${ORIG_SHA256}" ] || { echo "ERROR: ${ORIG_TARBALL} not listed in ${orig_dsc}." >&2; exit 1; } + echo " orig sha256 resolved from ${orig_dsc}: ${ORIG_SHA256}" +else + echo " orig sha256 pinned: ${ORIG_SHA256}" +fi +if [ -z "${DEBIAN_SHA256}" ]; then + DEBIAN_SHA256="$(dsc_sha256 "${ARCHIVE_POOL}/postgresql-16_${DEBIAN_BASE}.dsc" "${DEBIAN_TARBALL}")" + [ -n "${DEBIAN_SHA256}" ] || { echo "ERROR: ${DEBIAN_TARBALL} not listed in postgresql-16_${DEBIAN_BASE}.dsc." >&2; exit 1; } + echo " debian sha256 resolved from postgresql-16_${DEBIAN_BASE}.dsc: ${DEBIAN_SHA256}" +else + echo " debian sha256 pinned: ${DEBIAN_SHA256}" +fi +echo "${ORIG_SHA256} ${ORIG_TARBALL}" | sha256sum -c - +echo "${DEBIAN_SHA256} ${DEBIAN_TARBALL}" | sha256sum -c - + +echo "==> [3/6] Assemble 3.0 (quilt) source tree" +rm -rf "${SRCDIR}" +mkdir -p "${SRCDIR}" +tar -xf "${ORIG_TARBALL}" -C "${SRCDIR}" --strip-components=1 +tar -xf "${DEBIAN_TARBALL}" -C "${SRCDIR}" # unpacks debian/ +# NB: ${ORIG_TARBALL} is already named postgresql-16_${UPSTREAM}.orig.tar.bz2, +# exactly what dpkg-buildpackage expects one level above ${SRCDIR}. + +cd "${SRCDIR}" + +# 'hurd-iovec' (GNU/Hurd-only) was merged upstream as of 16.14: pg_iovec.h now +# guards IOV_MAX with '#ifndef IOV_MAX', so the 16.9-era patch no longer applies. +# Irrelevant to linux/amd64; drop it so the quilt series stays consistent. +if grep -qx 'hurd-iovec' debian/patches/series 2>/dev/null; then + sed -i '/^hurd-iovec$/d' debian/patches/series + rm -f debian/patches/hurd-iovec + echo " dropped obsolete patch: hurd-iovec" +fi + +echo "==> [4/6] Verify the quilt patch series applies cleanly to ${UPSTREAM}" +export QUILT_PATCHES=debian/patches +if [ -s debian/patches/series ]; then + quilt push -a + quilt pop -a +fi + +echo "==> [5/6] Set version to ${TARGET_VERSION} and install Build-Depends" +dch --newversion "${TARGET_VERSION}" --distribution focal --force-distribution \ + "Rebuild of PostgreSQL ${UPSTREAM} for focal (upstream PGDG focal-pgdg discontinued)." + +BUILD_PROFILES="" +BUILD_OPTIONS="parallel=$(nproc)" +if [ "${RUN_TESTS}" != "1" ]; then + BUILD_PROFILES="nocheck" + BUILD_OPTIONS="${BUILD_OPTIONS} nocheck" +fi +export DEB_BUILD_PROFILES="${BUILD_PROFILES}" + +apt-get update +mk-build-deps --install --remove \ + --tool 'apt-get -o Debug::pkgProblemResolver=yes --yes --no-install-recommends' \ + debian/control + +echo "==> [6/6] Build binary packages (DEB_BUILD_OPTIONS='${BUILD_OPTIONS}')" +export DEB_BUILD_OPTIONS="${BUILD_OPTIONS}" +dpkg-buildpackage -b -uc -us + +echo "==> Collect artifacts into ${OUTPUT_DIR}" +cp -v "${WORK}"/*.deb "${OUTPUT_DIR}/" +cp -v "${WORK}"/*.buildinfo "${WORK}"/*.changes "${OUTPUT_DIR}/" 2>/dev/null || true + +echo "==> DONE. Packages:" +ls -1 "${OUTPUT_DIR}"/*.deb