Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .github/workflows/build-pg16-focal.yml
Original file line number Diff line number Diff line change
@@ -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_<version>.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
62 changes: 62 additions & 0 deletions dockerfiles/pg16-focal-builder/Dockerfile
Original file line number Diff line number Diff line change
@@ -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_<NEW>.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"]
158 changes: 158 additions & 0 deletions scripts/build_pg16_focal
Original file line number Diff line number Diff line change
@@ -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 <dsc-url> <filename> -> prints the sha256 recorded for <filename>
# 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
Loading