From 9760a13047ecbad59c001949a26122c503aa21d9 Mon Sep 17 00:00:00 2001 From: Kemal Buyukkaya Date: Thu, 18 Jun 2026 14:09:12 +0000 Subject: [PATCH 1/7] Add signed PostgreSQL 16 core package build for Ubuntu focal PGDG dropped PostgreSQL 16 binaries for Ubuntu 20.04 (focal) after focal reached EOL standard support (last official focal build: 16.9-1.pgdg20.04+1). This adds a standalone, signed pipeline that rebuilds newer 16.x core packages for focal. Approach (validated locally; produces the full 13-package set that installs and runs on stock focal with working JIT): - Combine the newer upstream orig.tar.bz2 with the focal-era debian/ packaging (16.9-1.pgdg20.04+1), whose default toolchain (clang/llvm-dev = LLVM 10) yields focal-native dependencies (libicu66, libssl1.1, libldap-2.4-2, libllvm10) instead of the clang-19/llvm-19 required by newer packaging. - Restore the removed focal-pgdg build tooling (debhelper 13, dh-exec, postgresql-common-dev) from apt-archive.postgresql.org. - Drop the obsolete hurd-iovec patch (merged upstream as of 16.14) and gate on the full quilt series applying cleanly so future drift fails loudly. - Sign with the existing debsigner image (debsigs --sign=maint), using the pg-azure-storage signing secrets (PGAZ_PACKAGE_SECRET_KEY / PGAZ_PACKAGE_PASSPHRASE), matching build-pgazure-nightlies.yml. The minor version is parameterized: set PG_UPSTREAM_VERSION (workflow input pg_upstream_version) to build e.g. 16.15; the orig/debian checksums are auto-resolved from the official .dsc unless pinned. Files: - dockerfiles/pg16-focal-builder/Dockerfile: focal builder image - scripts/build_pg16_focal: fetch/verify/assemble/build entrypoint - .github/workflows/build-pg16-focal.yml: build -> sign -> verify -> upload --- .github/workflows/build-pg16-focal.yml | 105 ++++++++++++++ dockerfiles/pg16-focal-builder/Dockerfile | 62 +++++++++ scripts/build_pg16_focal | 158 ++++++++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100644 .github/workflows/build-pg16-focal.yml create mode 100644 dockerfiles/pg16-focal-builder/Dockerfile create mode 100755 scripts/build_pg16_focal diff --git a/.github/workflows/build-pg16-focal.yml b/.github/workflows/build-pg16-focal.yml new file mode 100644 index 00000000..921e260e --- /dev/null +++ b/.github/workflows/build-pg16-focal.yml @@ -0,0 +1,105 @@ +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: + # Same signing key the pg-azure-storage pipeline uses + # (see .github/workflows/build-pgazure-nightlies.yml on all-pg-azure-storage). + PACKAGING_SECRET_KEY: ${{ secrets.PGAZ_PACKAGE_SECRET_KEY }} + PACKAGING_PASSPHRASE: ${{ secrets.PGAZ_PACKAGE_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: 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: Build debsigner image + run: | + docker build -t debsigner \ + -f dockerfiles/debsigner/Dockerfile dockerfiles/debsigner + + - name: Sign packages (debsigs --sign=maint) + 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 + docker run --rm \ + -e PACKAGING_SECRET_KEY \ + -e PACKAGING_PASSPHRASE \ + -v "${PWD}/packages:/packages" \ + 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 From 461ac49478392453de6954a9f1206f548848d4c2 Mon Sep 17 00:00:00 2001 From: Kemal Buyukkaya Date: Thu, 18 Jun 2026 14:56:04 +0000 Subject: [PATCH 2/7] debsigner: accept ASCII-armored secret key, fail fast if none imported The packaging signing secrets in this repo (incl. PGAZ_PACKAGE_SECRET_KEY) are stored as raw ASCII-armored keys, not base64. import_and_sign assumed base64 and ran `base64 -d` first, which fails on armored input ("base64: invalid input" -> "no valid OpenPGP data found" -> "secret key not available"). Detect the format: import ASCII-armored keys directly, otherwise base64-decode as before (backward compatible). Also verify a PRIVATE key was actually imported and exit non-zero with an actionable message if only a public key is present, so debsigs never silently emits unsigned packages. Validated in the xenial debsigner image: armored private key -> signs (_gpgmaint added); base64 key -> signs; armored public-only key -> exits 78. --- dockerfiles/debsigner/scripts/import_and_sign | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/dockerfiles/debsigner/scripts/import_and_sign b/dockerfiles/debsigner/scripts/import_and_sign index 22dfb509..56761620 100755 --- a/dockerfiles/debsigner/scripts/import_and_sign +++ b/dockerfiles/debsigner/scripts/import_and_sign @@ -15,8 +15,29 @@ elif [ -z "${PACKAGING_SECRET_KEY+x}" ]; then exit $badconfig fi -gpg --batch --no-tty --trust-model always \ - --import <(echo "${PACKAGING_SECRET_KEY}" | base64 -d) +# PACKAGING_SECRET_KEY may be provided either as an ASCII-armored private key +# block (-----BEGIN PGP PRIVATE KEY BLOCK-----) or as base64-encoded key +# material (the historical format). Detect and import accordingly. +if printf '%s' "${PACKAGING_SECRET_KEY}" | grep -qa 'BEGIN PGP'; then + printf '%s\n' "${PACKAGING_SECRET_KEY}" \ + | gpg --batch --no-tty --trust-model always --import +else + printf '%s' "${PACKAGING_SECRET_KEY}" | base64 -d \ + | gpg --batch --no-tty --trust-model always --import +fi + +# Fail fast if no secret (private) key is available; otherwise debsigs would +# silently emit unsigned packages. +if ! gpg --list-secret-keys --with-colons 2>/dev/null | grep -q '^sec'; then + echo "$0: ERROR: no PRIVATE (secret) key was imported from PACKAGING_SECRET_KEY." >&2 + if gpg --list-keys --with-colons 2>/dev/null | grep -q '^pub'; then + echo "$0: a PUBLIC key was found instead:" >&2 + gpg --list-keys 2>/dev/null | sed 's/^/ /' >&2 + fi + echo "$0: PACKAGING_SECRET_KEY must contain the PRIVATE signing key (ASCII-armored," >&2 + echo "$0: or base64 of the armored key), e.g.: gpg --armor --export-secret-keys " >&2 + exit $badconfig +fi for deb in /packages/*/*.deb do From e71107b1a15192b9e4bc4d59ad463524330f3fb9 Mon Sep 17 00:00:00 2001 From: Kemal Buyukkaya Date: Thu, 18 Jun 2026 14:56:04 +0000 Subject: [PATCH 3/7] ci: don't run Citus extension build/test on the pg16-focal branch build-package.yml and build-package-test.yml trigger on every branch (branches: "**") and run the Citus extension build plus test_build_packages, which is unrelated to the PostgreSQL-core focal pipeline and fails here on a pre-existing PACKAGING_PASSPHRASE mismatch. Exclude pg16-focal via branches-ignore so it no longer blocks this work; workflow_dispatch stays. --- .github/workflows/build-package-test.yml | 5 ++++- .github/workflows/build-package.yml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-package-test.yml b/.github/workflows/build-package-test.yml index dd8393b8..f9b109c6 100644 --- a/.github/workflows/build-package-test.yml +++ b/.github/workflows/build-package-test.yml @@ -13,7 +13,10 @@ env: TEST: true on: push: - branches: "**" + branches-ignore: + # pg16-focal builds PostgreSQL core (see build-pg16-focal.yml); the Citus + # extension build/test here is unrelated and should not run on that branch. + - pg16-focal workflow_dispatch: concurrency: diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml index d4c67b2c..2ee324ae 100644 --- a/.github/workflows/build-package.yml +++ b/.github/workflows/build-package.yml @@ -13,7 +13,10 @@ env: TEST: false on: push: - branches: "**" + branches-ignore: + # pg16-focal builds PostgreSQL core (see build-pg16-focal.yml); the Citus + # extension build/test here is unrelated and should not run on that branch. + - pg16-focal workflow_dispatch: concurrency: From 4f51e0ba75e686413bc7c6e137626559c267940a Mon Sep 17 00:00:00 2001 From: Kemal Buyukkaya Date: Thu, 18 Jun 2026 17:11:21 +0000 Subject: [PATCH 4/7] Revert debsigner script and extension-workflow changes Roll back the earlier workarounds now that signing uses the prebuilt citusdata/packaging:debsigner image: - dockerfiles/debsigner/scripts/import_and_sign: back to upstream (we no longer build our own signer, so the armored-key handling is unnecessary). - build-package.yml / build-package-test.yml: restore branches: "**" (drop the pg16-focal branches-ignore guard) to avoid touching shared extension CI. These three files now match develop; only the PostgreSQL-core focal pipeline remains in this branch. --- .github/workflows/build-package-test.yml | 5 +--- .github/workflows/build-package.yml | 5 +--- dockerfiles/debsigner/scripts/import_and_sign | 25 ++----------------- 3 files changed, 4 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build-package-test.yml b/.github/workflows/build-package-test.yml index f9b109c6..dd8393b8 100644 --- a/.github/workflows/build-package-test.yml +++ b/.github/workflows/build-package-test.yml @@ -13,10 +13,7 @@ env: TEST: true on: push: - branches-ignore: - # pg16-focal builds PostgreSQL core (see build-pg16-focal.yml); the Citus - # extension build/test here is unrelated and should not run on that branch. - - pg16-focal + branches: "**" workflow_dispatch: concurrency: diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml index 2ee324ae..d4c67b2c 100644 --- a/.github/workflows/build-package.yml +++ b/.github/workflows/build-package.yml @@ -13,10 +13,7 @@ env: TEST: false on: push: - branches-ignore: - # pg16-focal builds PostgreSQL core (see build-pg16-focal.yml); the Citus - # extension build/test here is unrelated and should not run on that branch. - - pg16-focal + branches: "**" workflow_dispatch: concurrency: diff --git a/dockerfiles/debsigner/scripts/import_and_sign b/dockerfiles/debsigner/scripts/import_and_sign index 56761620..22dfb509 100755 --- a/dockerfiles/debsigner/scripts/import_and_sign +++ b/dockerfiles/debsigner/scripts/import_and_sign @@ -15,29 +15,8 @@ elif [ -z "${PACKAGING_SECRET_KEY+x}" ]; then exit $badconfig fi -# PACKAGING_SECRET_KEY may be provided either as an ASCII-armored private key -# block (-----BEGIN PGP PRIVATE KEY BLOCK-----) or as base64-encoded key -# material (the historical format). Detect and import accordingly. -if printf '%s' "${PACKAGING_SECRET_KEY}" | grep -qa 'BEGIN PGP'; then - printf '%s\n' "${PACKAGING_SECRET_KEY}" \ - | gpg --batch --no-tty --trust-model always --import -else - printf '%s' "${PACKAGING_SECRET_KEY}" | base64 -d \ - | gpg --batch --no-tty --trust-model always --import -fi - -# Fail fast if no secret (private) key is available; otherwise debsigs would -# silently emit unsigned packages. -if ! gpg --list-secret-keys --with-colons 2>/dev/null | grep -q '^sec'; then - echo "$0: ERROR: no PRIVATE (secret) key was imported from PACKAGING_SECRET_KEY." >&2 - if gpg --list-keys --with-colons 2>/dev/null | grep -q '^pub'; then - echo "$0: a PUBLIC key was found instead:" >&2 - gpg --list-keys 2>/dev/null | sed 's/^/ /' >&2 - fi - echo "$0: PACKAGING_SECRET_KEY must contain the PRIVATE signing key (ASCII-armored," >&2 - echo "$0: or base64 of the armored key), e.g.: gpg --armor --export-secret-keys " >&2 - exit $badconfig -fi +gpg --batch --no-tty --trust-model always \ + --import <(echo "${PACKAGING_SECRET_KEY}" | base64 -d) for deb in /packages/*/*.deb do From 7573c8864916961cf4c4687f2ed9c7cfb12ab594 Mon Sep 17 00:00:00 2001 From: Kemal Buyukkaya Date: Thu, 18 Jun 2026 17:11:21 +0000 Subject: [PATCH 5/7] build-pg16-focal: sign with prebuilt debsigner image, pin ubuntu-20.04 The signer images are maintained out-of-band (not built by this repo's image pipeline), so the deployed citusdata/packaging:debsigner has drifted from dockerfiles/debsigner. Building our own signer from that source could not import the same signing key that signs every other Citus package, while the deployed image does (pg-azure-storage nightlies are green with PGAZ_PACKAGE_SECRET_KEY). Use the prebuilt citusdata/packaging:debsigner with a Docker Hub login and pipe the passphrase via stdin + env, mirroring citus_package.sign_packages. Pin the job to ubuntu-20.04 to match the green pg-azure-storage signing pipeline. --- .github/workflows/build-pg16-focal.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-pg16-focal.yml b/.github/workflows/build-pg16-focal.yml index 921e260e..3ef23f32 100644 --- a/.github/workflows/build-pg16-focal.yml +++ b/.github/workflows/build-pg16-focal.yml @@ -34,7 +34,8 @@ concurrency: jobs: build-and-sign: name: Build & sign PostgreSQL ${{ github.event.inputs.pg_upstream_version || '16.14' }} (focal) - runs-on: ubuntu-latest + # Pinned to focal to match the (green) pg-azure-storage signing pipeline. + runs-on: ubuntu-20.04 env: # Same signing key the pg-azure-storage pipeline uses # (see .github/workflows/build-pgazure-nightlies.yml on all-pg-azure-storage). @@ -47,6 +48,12 @@ jobs: - 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 \ @@ -64,22 +71,20 @@ jobs: echo "Built packages:" ls -1 packages/focal/*.deb - - name: Build debsigner image - run: | - docker build -t debsigner \ - -f dockerfiles/debsigner/Dockerfile dockerfiles/debsigner - - 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 - docker run --rm \ + printf '%s' "${PACKAGING_PASSPHRASE}" | docker run --rm -i \ -e PACKAGING_SECRET_KEY \ -e PACKAGING_PASSPHRASE \ -v "${PWD}/packages:/packages" \ - debsigner + citusdata/packaging:debsigner - name: Verify signatures are embedded run: | From e2e96f6d445420afdd4c1e33863bad44b6cec69a Mon Sep 17 00:00:00 2001 From: Kemal Buyukkaya Date: Thu, 18 Jun 2026 17:12:22 +0000 Subject: [PATCH 6/7] build-pg16-focal: run on ubuntu-latest Revert the ubuntu-20.04 pin to ubuntu-latest to stay future-proof as GitHub retires the hosted ubuntu-20.04 image. Signing uses the prebuilt citusdata/packaging:debsigner image (same as all-citus, which signs fine on ubuntu-latest), so the runner version is not the relevant factor. --- .github/workflows/build-pg16-focal.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build-pg16-focal.yml b/.github/workflows/build-pg16-focal.yml index 3ef23f32..0ba69c8c 100644 --- a/.github/workflows/build-pg16-focal.yml +++ b/.github/workflows/build-pg16-focal.yml @@ -34,8 +34,7 @@ concurrency: jobs: build-and-sign: name: Build & sign PostgreSQL ${{ github.event.inputs.pg_upstream_version || '16.14' }} (focal) - # Pinned to focal to match the (green) pg-azure-storage signing pipeline. - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest env: # Same signing key the pg-azure-storage pipeline uses # (see .github/workflows/build-pgazure-nightlies.yml on all-pg-azure-storage). From 4a773db098fa47dbd3989693c2fc97dd3606e9fd Mon Sep 17 00:00:00 2001 From: Kemal Buyukkaya Date: Thu, 18 Jun 2026 18:02:23 +0000 Subject: [PATCH 7/7] build-pg16-focal: sign with the common PACKAGING_SECRET_KEY/PASSPHRASE PGAZ_PACKAGE_SECRET_KEY is only referenced on the pg-azure-storage branches and did not import in our run. Switch to the common signing secrets used across the other pipelines (PACKAGING_SECRET_KEY / PACKAGING_PASSPHRASE) to match the standard convention. --- .github/workflows/build-pg16-focal.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-pg16-focal.yml b/.github/workflows/build-pg16-focal.yml index 0ba69c8c..84eadb4e 100644 --- a/.github/workflows/build-pg16-focal.yml +++ b/.github/workflows/build-pg16-focal.yml @@ -36,10 +36,8 @@ jobs: name: Build & sign PostgreSQL ${{ github.event.inputs.pg_upstream_version || '16.14' }} (focal) runs-on: ubuntu-latest env: - # Same signing key the pg-azure-storage pipeline uses - # (see .github/workflows/build-pgazure-nightlies.yml on all-pg-azure-storage). - PACKAGING_SECRET_KEY: ${{ secrets.PGAZ_PACKAGE_SECRET_KEY }} - PACKAGING_PASSPHRASE: ${{ secrets.PGAZ_PACKAGE_PASSPHRASE }} + 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' }}