Skip to content
Open
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
188 changes: 188 additions & 0 deletions .github/workflows/build-tdigest-focal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
name: Build tdigest (focal)

# Builds tdigest .deb packages for Ubuntu 20.04 (focal) from upstream source
# (PGDG removed focal entirely -- focal-pgdg 404s), then signs them with
# debsigs (--sign=maint) using the
# existing packaging key.
#
# There is no per-major matrix: the tdigest Debian packaging is multi-version by
# design (debian/pgversions + pg_buildext), so one source build emits
# postgresql-<major>-tdigest for every requested major in a single pass. That
# also means no assemble/de-duplicate job is needed -- each runtime package is
# self-contained and produced exactly once.

on:
workflow_dispatch:
inputs:
tdigest_version:
description: "tdigest upstream version; blank or latest discovers the latest stable release"
required: false
default: "latest"
pg_versions:
description: "Space-separated PostgreSQL majors (supported: 11–16)"
required: false
default: "11 12 13 14 15 16"
Comment thread
aybuket marked this conversation as resolved.
tdigest_sha256:
description: "Optional SHA-256 for the GitHub tag tarball. Blank = use the release ZIP and its GitHub-published digest."
required: false
default: ""
run_tests:
description: "Run upstream regression suite (1=yes, much slower)"
required: false
default: "0"
push:
branches:
- tdigest-focal

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-sign:
name: Build & sign tdigest ${{ github.event.inputs.tdigest_version || 'latest' }} (focal)
runs-on: ubuntu-latest
outputs:
tdigest_version: ${{ steps.source.outputs.TDIGEST_VERSION }}
env:
PACKAGING_SECRET_KEY: ${{ secrets.PACKAGING_SECRET_KEY }}
PACKAGING_PASSPHRASE: ${{ secrets.PACKAGING_PASSPHRASE }}
TDIGEST_VERSION: ${{ github.event.inputs.tdigest_version || 'latest' }}
PG_VERSIONS: ${{ github.event.inputs.pg_versions || '11 12 13 14 15 16' }}
TDIGEST_SHA256: ${{ github.event.inputs.tdigest_sha256 || '' }}
RUN_TESTS: ${{ github.event.inputs.run_tests || '0' }}
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Validate majors and resolve tdigest source
id: source
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
run: bash scripts/resolve_tdigest_focal | tee -a "$GITHUB_ENV" "$GITHUB_OUTPUT"

- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USER_NAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

- name: Build focal tdigest builder image
run: |
docker build -t focal-tdigest-builder \
-f dockerfiles/focal-tdigest-builder/Dockerfile .

- name: Build tdigest packages
run: |
mkdir -p packages
docker run --rm \
-e TDIGEST_VERSION="${TDIGEST_VERSION}" \
-e PG_VERSIONS="${PG_VERSIONS}" \
-e TDIGEST_SHA256="${TDIGEST_SHA256}" \
-e UPSTREAM_URL="${UPSTREAM_URL}" \
-e UPSTREAM_FORMAT="${UPSTREAM_FORMAT}" \
-e RUN_TESTS="${RUN_TESTS}" \
-v "${PWD}/packages:/packages" \
focal-tdigest-builder
echo "Built packages:"
ls -1 packages/focal/tdigest/*.deb

- name: Sign packages (debsigs --sign=maint)
# Use the prebuilt, deployed debsigner image (the one all Citus signing
# uses). Its entrypoint signs exactly "/packages/*/*.deb" (one dir level
# deep), so mount the parent of the output dir: with
# "${PWD}/packages/focal:/packages" the debs land at
# /packages/tdigest/*.deb, which is what that glob expects.
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/focal:/packages" \
citusdata/packaging:debsigner

- name: Verify signatures are embedded
run: |
rc=0
for deb in packages/focal/tdigest/*.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: Verify the set is self-contained
# Every runtime must ship its own extension control file and the full SQL
# tree, so a package can never be paired with another version's SQL and
# there is no separate -scripts package to depend on. tdigest is installed
# from the base script and reaches the built version through the shipped
# upgrade chain, whose terminal script names that version, so match
# either tdigest--<previous>--<version>.sql or a direct
# tdigest--<version>.sql.
run: |
rc=0
for v in ${PG_VERSIONS}; do
deb="$(ls "packages/focal/tdigest/postgresql-${v}-tdigest_"*.deb)"
contents="$(dpkg-deb -c "$deb")"
if ! grep -q 'extension/tdigest\.control' <<<"$contents"; then
echo "::error::PG${v} runtime ships no extension control file" >&2; rc=1
fi
if ! grep -qE "extension/tdigest--([0-9.]+--)?${TDIGEST_VERSION}\.sql" <<<"$contents"; then
echo "::error::PG${v} runtime ships no SQL producing tdigest ${TDIGEST_VERSION}" >&2; rc=1
fi
done
exit $rc

- name: Upload signed packages
uses: actions/upload-artifact@v4
with:
name: tdigest-focal-deb
path: |
packages/focal/tdigest/*.deb
packages/focal/tdigest/*.changes
packages/focal/tdigest/*.buildinfo
packages/focal/tdigest/tdigest-source.env
if-no-files-found: error

install-smoke-test:
needs: build-and-sign
name: Install smoke test (focal)
runs-on: ubuntu-latest
env:
PG_VERSIONS: ${{ github.event.inputs.pg_versions || '11 12 13 14 15 16' }}
TDIGEST_VERSION: ${{ needs.build-and-sign.outputs.tdigest_version }}
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Download built packages
uses: actions/download-artifact@v4
with:
name: tdigest-focal-deb
path: debs

- name: Install and verify in a clean focal container
# The jobs above only prove the packages exist, are signed and are
# self-contained -- not that the extension can actually be created. This
# installs the set into a stock ubuntu:20.04 twice: clean, and over
# PGDG's archived tdigest (the in-place upgrade path), then runs
# CREATE EXTENSION / ALTER EXTENSION UPDATE on every major.
run: |
docker run --rm \
-v "${PWD}/debs:/debs:ro" \
-v "${PWD}/scripts/smoke_test_focal_tdigest_debs:/usr/local/bin/smoke_test_focal_tdigest_debs:ro" \
-e DEBS_DIR=/debs \
-e PG_VERSIONS="${PG_VERSIONS}" \
-e EXPECTED_TDIGEST="${TDIGEST_VERSION}" \
ubuntu:20.04 \
/usr/local/bin/smoke_test_focal_tdigest_debs
70 changes: 70 additions & 0 deletions dockerfiles/focal-tdigest-builder/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# vim:set ft=dockerfile:
#
# Builder image for tdigest packages targeting Ubuntu 20.04 (focal). One image
# builds every focal-buildable PostgreSQL major at once (PG 11..16 by default);
# the tdigest Debian packaging is multi-version by design (debian/pgversions +
# the pgxs_loop debhelper addon drive pg_buildext), so a single source build
# emits postgresql-<major>-tdigest for each major in one pass.
#
# Why this exists:
# apt.postgresql.org (PGDG) no longer ships focal binaries -- focal-pgdg 404s
# and the apt-archive mirror is frozen. New releases have to be rebuilt
# from upstream source.
#
# Strategy:
# - Upstream source: latest GitHub release asset and its published SHA-256,
# or a specific tag tarball with an explicitly supplied checksum.
# - Debian packaging: the frozen focal-era debian/ from PGDG's last focal
# tdigest source package, selected and fetched with `apt-get source`
# so it is authenticated by the archive's signed Release.
# - Build tooling restored from the PGDG *archive*, which keeps the removed
# focal-pgdg suite.
#
# The heavy lifting lives in scripts/build_tdigest_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; \
# 'main' carries the build tooling, the tdigest source package and the PG11
# binaries; PG12..16 are each a separate component.
echo "deb [signed-by=/usr/share/keyrings/pgdg-archive.gpg] https://apt-archive.postgresql.org/pub/repos/apt focal-pgdg main 11 12 13 14 15 16" \
> /etc/apt/sources.list.d/pgdg-archive.list; \
echo "deb-src [signed-by=/usr/share/keyrings/pgdg-archive.gpg] https://apt-archive.postgresql.org/pub/repos/apt focal-pgdg main 11 12 13 14 15 16" \
>> /etc/apt/sources.list.d/pgdg-archive.list; \
apt-get update; \
# base build tooling; per-build Build-Depends are resolved at run time by
# scripts/build_tdigest_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 \
jq \
unzip \
postgresql-common-dev \
postgresql-server-dev-all \
xz-utils; \
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 \
&& dpkg --compare-versions "$(dpkg-query -W -f='${Version}' debhelper)" ge 13

COPY scripts/build_tdigest_focal /usr/local/bin/build_tdigest_focal
COPY scripts/resolve_tdigest_focal /usr/local/bin/resolve_tdigest_focal
RUN chmod +x /usr/local/bin/build_tdigest_focal

VOLUME /packages
ENTRYPOINT ["/usr/local/bin/build_tdigest_focal"]
Loading
Loading