Skip to content

Commit 7c1c826

Browse files
committed
ci: add pg-upgrade-stepwise (§6c-bis) -- binary pg_upgrade climb 12→18
Adds the binary pg_upgrade coverage this PR previously deferred: ONE cluster starting at the floor PostgreSQL major with 0.1.0 installed, updated straight to the current version, then climbing every later supported major in sequence via a real binary pg_upgrade, running the full suite (existing mode) and re-proving the dependency guard after every step. Per review of advanced-extension-testing.md's guidance: "unlikely to catch anything today" (no SELECT * over a system catalog found in object_reference's views/functions) is a weaker, non-self-correcting reason to skip a cheap job than a genuine cost argument -- an extension can grow catalog-touching code later without anyone revisiting a stale "skip, it's simple" decision. - bin/test_existing: add `prepare-old DB [INSTALL_VERSION]`, refactored out of update-scenario's existing create+guard logic. Simpler than cat_tools's own (no BRIDGE_TO parameter) since 0.1.0 has no identified pg_upgrade-unsafe construct to bridge away from. - .github/workflows/ci.yml: - `changes` job now also derives `climb_pg`, an ascending PG-major list from the same NEWEST/CURRENT_FLOOR constants the `test` job's matrix already uses -- no separate LEGACY_FLOOR, since 0.1.0 installs cleanly across the whole supported range. - New `pg-upgrade-stepwise` job, gated behind lint+test like extension-update-test. - `all-checks-passed` needs updated to include it. - Top-of-file "Test strategy" comment updated: pg_upgrade coverage is no longer deferred. make lint clean.
1 parent 95133fd commit 7c1c826

2 files changed

Lines changed: 206 additions & 46 deletions

File tree

.github/workflows/ci.yml

Lines changed: 161 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44
# An object_reference install can be arrived at more than one way, each of
55
# which can break differently, so each is exercised by its own job below:
66
#
7-
# - `lint`: the cheapest possible check (no database, no container beyond
8-
# a plain checkout, seconds to run) -- gates everything else so a broken
9-
# style baseline never ties up runner slots on the heavier jobs below.
10-
#
117
# - `changes`: cheap docs-only gate, PLUS the single source of truth for
128
# the supported-PostgreSQL-major list every other job's matrix derives
139
# from (see its own "Derive ..." step).
@@ -34,18 +30,28 @@
3430
# crossing this axis against every major would just multiply job count
3531
# for no added coverage.
3632
#
37-
# - No binary pg_upgrade job (the cat_tools reference this effort is
38-
# modeled on has `pg-upgrade-test` / `pg-upgrade-stepwise`) exists yet.
39-
# Deliberate, not an oversight: that job exists to catch a view/function
40-
# that breaks across a PostgreSQL major specifically because it touches
41-
# catalog internals (SELECT * over a system catalog whose columns get
42-
# added/exposed/removed between majors). object_reference has no such
43-
# construct in either its current or 0.1.0 install script (checked
44-
# directly -- no view or function selects * from a system catalog; every
45-
# object_reference table/view is an ordinary user object), so the risk
46-
# that job protects against is correspondingly low here. Left as
47-
# explicitly-noted future work rather than built preemptively; revisit
48-
# if/when object_reference grows a catalog-touching view or function.
33+
# - `pg-upgrade-stepwise`: BINARY pg_upgrade coverage. ONE cluster starts
34+
# at the oldest supported PostgreSQL major with the one real historical
35+
# PGXN release (0.1.0) installed, updates straight to the current
36+
# version, then climbs every later supported major in sequence
37+
# (e.g. 12→13→...→18) via a REAL binary pg_upgrade per step, running the
38+
# full suite (existing mode) and re-proving the dependency guard after
39+
# EVERY step. This catches a regression specific to one particular
40+
# major-to-major boundary that a single before/after snapshot would
41+
# never exercise -- a view/function that breaks because it touches
42+
# catalog internals (columns added/exposed/removed between majors) is
43+
# the concrete risk. No such construct has been found in object_reference
44+
# today (checked directly -- no view or function selects * from a system
45+
# catalog; every object_reference table/view is an ordinary user
46+
# object), but that is a fact about the code today, not a permanent
47+
# property of it, and this job is cheap (the same install+pg_upgrade
48+
# shape as any other pg_upgrade leg, just run once per step) -- so
49+
# "unlikely to catch anything today" is not treated as a reason to skip
50+
# it; only genuine cost would be. There is no separate "single big jump"
51+
# pg_upgrade job (cat_tools's `pg-upgrade-test`) -- with only one
52+
# historical extension version and no identified PostgreSQL-version
53+
# floor for it, a big-jump leg would add a second job with no coverage
54+
# the stepwise climb doesn't already provide.
4955
#
5056
# - `all-checks-passed`: single stable required-status-check name; see its
5157
# own comment below.
@@ -66,15 +72,17 @@ jobs:
6672
# all-checks-passed check would then never report and get stuck Pending in
6773
# branch protection.
6874
#
69-
# Also derives the supported-PostgreSQL-major list the test job's matrix
70-
# consumes, from a single pair of constants below, so adding or dropping a
71-
# major is a one-line edit here instead of touching the matrix directly.
75+
# Also derives the supported-PostgreSQL-major lists the test job's matrix
76+
# and the pg-upgrade-stepwise job's climb consume, from a single pair of
77+
# constants below, so adding or dropping a major is a one-line edit here
78+
# instead of touching either job directly.
7279
changes:
7380
name: 🔍 Detect changes & derive PG matrix
7481
runs-on: ubuntu-latest
7582
outputs:
7683
docs_only: ${{ steps.diff.outputs.docs_only }}
7784
supported_pg: ${{ steps.pg.outputs.supported_pg }}
85+
climb_pg: ${{ steps.pg.outputs.climb_pg }}
7886
steps:
7987
- name: Check out the repo
8088
uses: actions/checkout@v4
@@ -123,30 +131,46 @@ jobs:
123131
echo "changed files:"
124132
echo "$CHANGED"
125133
echo "docs_only=$DOCS_ONLY" >> "$GITHUB_OUTPUT"
126-
- name: Derive the supported-PostgreSQL-major list
134+
- name: Derive the supported-PostgreSQL-major lists
127135
id: pg
128136
run: |
129137
# SINGLE SOURCE OF TRUTH for the supported PostgreSQL majors. To
130-
# add or drop a major, edit only the two constants below; the test
131-
# job's matrix derives its version list from them. Do NOT hardcode
132-
# a supported major directly in a job matrix.
138+
# add or drop a major, edit only the two constants below; every
139+
# job's matrix/climb derives its version list from them. Do NOT
140+
# hardcode a supported major directly in a job matrix or loop.
133141
#
134142
# NEWEST -- highest PostgreSQL major tested.
135143
# CURRENT_FLOOR -- oldest major supported. object_reference
136144
# requires cat_tools at both build and runtime,
137145
# and cat_tools's own current release declares
138146
# PostgreSQL 12 as its build floor, so
139147
# object_reference can't usefully claim support
140-
# for anything older either.
148+
# for anything older either. 0.1.0 (the one
149+
# real historical PGXN release) has no
150+
# PostgreSQL-version floor of its own (no
151+
# SELECT * over a system catalog, no ALTER
152+
# TYPE ... ADD VALUE in its update script), so
153+
# unlike cat_tools's reference implementation
154+
# there is no separate, older LEGACY_FLOOR --
155+
# 0.1.0 installs cleanly across the whole
156+
# CURRENT_FLOOR..NEWEST range, and the stepwise
157+
# climb (pg-upgrade-stepwise) starts right at
158+
# CURRENT_FLOOR too.
141159
NEWEST=18
142160
CURRENT_FLOOR=12
143161
144162
supported=$(seq "$NEWEST" -1 "$CURRENT_FLOOR")
163+
# Ascending (ties floor-to-newest, opposite order from $supported
164+
# above): pg-upgrade-stepwise reads the first element as its
165+
# starting major and binary-pg_upgrades through the rest in turn.
166+
climb=$(seq "$CURRENT_FLOOR" "$NEWEST")
145167
146168
# Emit a JSON array for the test job's matrix to consume via
147169
# fromJSON.
148170
json=$(printf '%s\n' $supported | paste -sd, - | sed 's/^/[/; s/$/]/')
149171
echo "supported_pg=$json" >> "$GITHUB_OUTPUT"
172+
# Space-separated for direct iteration in the stepwise bash loop.
173+
echo "climb_pg=$(echo $climb)" >> "$GITHUB_OUTPUT"
150174
151175
# Style linter (https://github.com/Postgres-Extensions/linter, vendored at
152176
# .vendor/linter -- lint.mk is the thin local hand-off, see its comment).
@@ -231,14 +255,126 @@ jobs:
231255
- name: Update 0.1.0 -> stable, structurally compare, run the suite (existing mode)
232256
run: bin/test_existing update-scenario object_reference_update 0.1.0
233257

258+
# Proves object_reference survives EVERY individual major-to-major binary
259+
# pg_upgrade transition, not just the single newest-major snapshot the
260+
# `test`/`extension-update-test` jobs above cover: ONE cluster that starts
261+
# on the floor PostgreSQL major and climbs through every later supported
262+
# major in sequence via a REAL binary pg_upgrade. See the "Test strategy"
263+
# comment at the top of this file for why this is included even though no
264+
# catalog-touching view/function has been found in object_reference today.
265+
#
266+
# Installs 0.1.0 on the floor major, updates it straight to the current
267+
# version (0.1.0's update script has no PostgreSQL-version floor of its
268+
# own -- no ALTER TYPE ... ADD VALUE -- so, unlike cat_tools's reference
269+
# implementation, there is no reason to hold the extension at an old
270+
# version through any step of the climb), then binary-pg_upgrades one
271+
# major at a time through the rest of the range, running the full suite
272+
# (existing mode) and re-proving the dependency guard after EVERY step.
273+
pg-upgrade-stepwise:
274+
# Gated behind test, not just changes -- see extension-update-test's
275+
# needs comment above: every leg here would fail anyway against an
276+
# already-broken baseline. success() must be written explicitly --
277+
# GitHub only assumes success() as a job's default when it has no if: at
278+
# all.
279+
needs: [changes, test]
280+
if: success() && needs.changes.outputs.docs_only != 'true'
281+
name: 🪜 Stepwise pg_upgrade (0.1.0 → stable)
282+
runs-on: ubuntu-latest
283+
container: pgxn/pgxn-tools
284+
env:
285+
# Every pg_upgrade step pairs two clusters that must share initdb
286+
# options (checksums, auth) or pg_upgrade refuses to run.
287+
INITDB_OPTS: --data-checksums --auth trust
288+
DB: object_reference_stepwise
289+
# Ascending list of every supported PostgreSQL major, from the single
290+
# source in the changes job -- so a new major joins the climb with no
291+
# edit here. The first element is the climb's starting (floor) major.
292+
CLIMB_PG: ${{ needs.changes.outputs.climb_pg }}
293+
steps:
294+
- name: Read the climb's starting (floor) PostgreSQL major
295+
id: floor
296+
run: echo "pg=$(set -- $CLIMB_PG; echo "$1")" >> "$GITHUB_OUTPUT"
297+
- name: Start PostgreSQL ${{ steps.floor.outputs.pg }}
298+
run: pg-start ${{ steps.floor.outputs.pg }}
299+
- name: Recreate the floor cluster with data checksums enabled
300+
# pg-start's default "test" cluster doesn't enable data checksums,
301+
# but binary pg_upgrade requires the old and new clusters to have
302+
# MATCHING checksum/auth settings.
303+
run: |
304+
pg_ctlcluster ${{ steps.floor.outputs.pg }} test stop
305+
pg_dropcluster ${{ steps.floor.outputs.pg }} test
306+
# -p 5432: pg_createcluster assigns the next available port, which
307+
# may not be 5432 after pg-start has claimed and released it.
308+
# Force 5432 so later psql/createdb calls connect without -p.
309+
pg_createcluster -p 5432 ${{ steps.floor.outputs.pg }} test -- $INITDB_OPTS
310+
pg_ctlcluster ${{ steps.floor.outputs.pg }} test start
311+
pg_isready -t 30
312+
- name: Check out the repo
313+
uses: actions/checkout@v4
314+
- name: Install object_reference + its 0.1.0-only test dependency (count_nulls) into the floor cluster
315+
# TEST_LOAD_SOURCE=update activates the Makefile's conditional
316+
# `install: count_nulls` prerequisite -- 0.1.0's install script
317+
# needs count_nulls even though current object_reference.control no
318+
# longer declares it. count_nulls stays installed as its own
319+
# extension in the database even after updating object_reference
320+
# past 0.1.0 (nothing drops it), so every later `make install` in
321+
# this job's climb loop keeps passing TEST_LOAD_SOURCE=update too --
322+
# pg_upgrade needs count_nulls's files present in each new cluster
323+
# for as long as it remains a real extension in the test database.
324+
run: make install TEST_LOAD_SOURCE=update
325+
- name: Prepare the floor cluster (install 0.1.0, plant guard, update to current, run suite)
326+
run: |
327+
bin/test_existing prepare-old "$DB" 0.1.0
328+
bin/test_existing update "$DB"
329+
bin/test_existing run-suite "$DB"
330+
- name: Climb every later major via binary pg_upgrade
331+
# One sequential loop; each iteration binary-pg_upgrades the cluster
332+
# from $old to $new (a single major step), re-installs
333+
# object_reference + its dependencies into the new cluster first
334+
# (pg_upgrade needs their files present in the NEW cluster's
335+
# sharedir -- default pg_config on PATH may not be $new's once
336+
# several majors are installed, hence PG_CONFIG explicit), then
337+
# re-runs the full suite in existing mode -- re-proving the
338+
# dependency guard survived, and that the same suite/expected-output
339+
# still passes against the objects that just crossed a pg_upgrade.
340+
run: |
341+
set -- $CLIMB_PG
342+
old=$1
343+
shift
344+
for new in "$@"; do
345+
echo "=== binary pg_upgrade PostgreSQL $old -> $new ==="
346+
apt-get install -y postgresql-$new postgresql-server-dev-$new
347+
make install PG_CONFIG=/usr/lib/postgresql/$new/bin/pg_config TEST_LOAD_SOURCE=update
348+
pg_ctlcluster $old test stop
349+
pg_createcluster -p 5432 $new test -- $INITDB_OPTS
350+
# PG17+ writes pg_upgrade logs under the new datadir; older
351+
# versions write to CWD. Dump both on failure.
352+
mkdir -p /tmp/pg_upgrade_logs
353+
chown postgres:postgres /tmp/pg_upgrade_logs
354+
su -c "cd /tmp/pg_upgrade_logs && /usr/lib/postgresql/$new/bin/pg_upgrade \
355+
-b /usr/lib/postgresql/$old/bin \
356+
-B /usr/lib/postgresql/$new/bin \
357+
-d /var/lib/postgresql/$old/test \
358+
-D /var/lib/postgresql/$new/test \
359+
-o '-c config_file=/etc/postgresql/$old/test/postgresql.conf' \
360+
-O '-c config_file=/etc/postgresql/$new/test/postgresql.conf'" postgres \
361+
|| { find /tmp/pg_upgrade_logs \
362+
/var/lib/postgresql/$new/test/pg_upgrade_output.d \
363+
-name '*.log' 2>/dev/null | sort | xargs -r tail -n +1; exit 1; }
364+
pg_ctlcluster $new test start
365+
pg_isready -t 30
366+
bin/test_existing run-suite "$DB"
367+
old=$new
368+
done
369+
234370
# A single stable check name for use as a required status check in branch
235371
# protection rules. Matrix jobs produce check names like "🐘 PostgreSQL 14"
236372
# which would all need to be listed individually and updated whenever the
237373
# matrix changes. This job passes if all others passed or were skipped
238374
# (e.g. test, on a docs-only push), and fails if any failed or were
239375
# cancelled.
240376
all-checks-passed:
241-
needs: [changes, lint, test, extension-update-test]
377+
needs: [changes, lint, test, extension-update-test, pg-upgrade-stepwise]
242378
if: always()
243379
runs-on: ubuntu-latest
244380
steps:

0 commit comments

Comments
 (0)