CI: binary pg_upgrade testing, docs-only gate, single-source PG matrix - #35
Open
jnasbyupgrade wants to merge 7 commits into
Open
CI: binary pg_upgrade testing, docs-only gate, single-source PG matrix#35jnasbyupgrade wants to merge 7 commits into
jnasbyupgrade wants to merge 7 commits into
Conversation
…trix Implements the remaining CI-structure items from the advanced update+upgrade testing pattern (modeled on Postgres-Extensions/cat_tools's ci.yml/ bin/test_existing and what has landed on its master since), stacked on the foundation from PR #22 (TEST_LOAD_SOURCE, dependency guard, load.sql): - Scope `push` to `branches: [master]`; `pull_request` stays unrestricted -- fixes the double-triggered-CI-on-every-PR-commit bug (test_factory was named as one of the repos still needing this). - Job-level `changes` gate: computes a per-push docs-only diff (fail-safe = not-docs-only as the literal first line) instead of a workflow-level `paths-ignore`, so heavy jobs can skip on doc-only pushes without leaving all-checks-passed stuck Pending. - Single source of truth for the supported-PostgreSQL-major list (NEWEST=17, FLOOR=10, matching the existing matrix), derived once in `changes` and consumed via fromJSON by both the `test` and new `pg-upgrade-test` matrices. - `all-checks-passed` gate, self-checking that its `needs` list matches the actual job set. - New `pg-upgrade-test` job: binary pg_upgrade legs (10->17, 16->17) -- install the current version on an old cluster, pg_upgrade to a newer major, then run the suite against the migrated objects in existing mode. No bridge step needed: test_factory has shipped only one version, so every leg installs current directly on the old cluster. Mechanics factored into `.github/scripts/pg_upgrade_cluster` (generic, modeled on cat_tools's script of the same name) and `bin/test_existing` (test_factory-specific, much smaller than cat_tools's own since there's no bridge/multi-origin machinery to carry -- prepare-old + run-suite is the whole surface). - `test` job now gates on `make verify-results` instead of pgxn-tools' `pg-build-test`: pgxntool marks installcheck `.IGNORE`, so the old job was silently exiting 0 even when regression.diffs was nonempty. PGXNTOOL_ENABLE_VERIFY_RESULTS is already pgxntool's own default but is now pinned explicitly in the Makefile, matching ENABLE_TEST_INSTALL's existing explicit-over-implicit convention. - Dynamic version assertion (bin/test_existing's assert_version): the installed version is always derived from `make -s print-PGXNVERSION`, never hardcoded, with empty-value guards on both sides. Real bug found by actually running the pg_upgrade dry run locally (PG12/16 -> PG17, using throwaway data directories, per the verification requirement -- not just written and trusted): test/helpers/create.sql's security-definer function check had no ORDER BY, so its row order depended on pg_proc's physical layout. That happens to match creation order on a fresh CREATE EXTENSION but is NOT preserved by pg_upgrade's dump/restore, which produced a real (but harmless -- every individual assertion still said "ok") text diff against the fresh-install expected output. Fixed with an explicit ORDER BY, which turns out to make one set of expected-output files valid for fresh, existing, AND pg_upgraded modes alike -- no third numbered alternate file needed, simpler than it first looked. Skipped, per the scoping decided before starting (see PR description for full reasoning): extension-update-test job (no second version has ever shipped), bridge/multi-origin update machinery (cat_tools-specific technical debt test_factory doesn't have), pg-upgrade-stepwise (test_factory has no catalog-internals-touching views/functions), pg_tle testing (not a deployment target), the `stable` pseudo-version (real feature work, not requested). Verified locally: make verify-results passes cleanly on both PG12 and PG17 (shared clusters); a full old-cluster-install -> pg_upgrade -> new-cluster existing-mode suite run (PG12->PG17 and PG12->PG16, using scratch data directories, never touching the shared clusters) passes with zero raw "not ok" TAP lines using the actual committed bin/test_existing and pg_upgrade_cluster scripts, not just ad hoc commands. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- test job: `make verify-results` alone races install vs installcheck under this container's ambient parallel make (they're independent prerequisites of the same `test` target) -- pg_regress could start, and fail with "extension ... is not available", before install's file copy finished. Split into two separate `make` invocations, which can't race with each other. Reproduced the failure mode's shape locally (though not the race itself -- couldn't get this container's make to lose the race on demand) and confirmed the two-step form still passes. - pg-upgrade-test job: never installed pgtap system-wide on either cluster. It worked by accident in local dry-runs only because this dev container already had pgtap installed for some PG majors from earlier testing -- confirmed by deliberately clearing /usr/share/postgresql/16/extension/ (a major this container had never used) and re-running the full recreate-old -> prepare-old -> pg_upgrade -> run-suite cycle end to end: it failed the same way PR #23's CI did ("extension pgtap is not available"), then passed once both `pgxn install pgtap --sudo --pg_config ...` steps were added (old cluster before prepare-old, new cluster before its make install -- pg_upgrade itself needs pgtap available on the new cluster too, not just post-upgrade). --pg_config is explicit on both, not left to rely on pg-start's PATH-switching, since that's exactly the kind of ambient-state assumption that already broke once in this same job. Verified locally end-to-end (real pg_ctlcluster/pg_createcluster/pg_upgrade, not just make test): old=12/new=16, a pair this container had never exercised before, all the way through bin/test_existing run-suite with zero raw "not ok" TAP lines. Also re-confirmed plain `make test` still passes on PG12 and PG17 after these changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The ORDER BY rationale added for the pg_upgrade row-ordering fix used consecutive -- lines for one continuous remark; convert it to a /* */ block per the repo's comment convention (see the sibling foundation fix in advanced-testing/foundation).
The pgtap.sql rebase conflict resolution two commits back had to pick a side for test/expected/pgtap.out arbitrarily to unblock the rebase; it kept the pre-ORDER-BY (creation-order) content instead of the alphabetically-sorted order test/helpers/create.sql's new `ORDER BY p.oid::regproc::text` actually produces. test/expected/pgtap_1.out (the existing-mode alternate) already had the correct sorted order -- it merged cleanly during the rebase without needing this fix. Verified fresh mode, existing mode (against a real pre-populated database), and -j4 parallel make all pass cleanly with the regenerated file.
…bump checkout - ci.yml: pg-upgrade-test now needs [changes, test], not just [changes] -- a trivially-broken PR (fails the cheap fresh-install matrix) no longer also burns the full, expensive binary pg_upgrade matrix. Added a top-level concurrency block (cancel-in-progress) so a superseded push cancels an in-flight run instead of letting that expensive matrix run to completion for nothing. all-checks-passed's own needs/self-check invariant is unaffected (its needs list and job set didn't change shape). - bin/test_existing: run_suite() called make test then make verify-results back to back -- verify-results already depends on test (pgxntool's base.mk), so every pg-upgrade-test CI leg paid for two full pg_regress --use-existing runs against the real migrated database instead of one. Dropped the redundant call. - actions/checkout@v4 -> @v7 (current latest major), all 5 occurrences (lint, changes, test, pg-upgrade-test, all-checks-passed -- one more than the brief's "4" since the lint job wasn't part of this branch when that count was written). Verified locally: full make lint + make verify-results (fresh mode) still pass. Reproduced a REAL binary pg_upgrade leg end to end (16 -> 17, using throwaway pg_createcluster data directories on custom ports, never touching this container's shared main clusters): recreated old cluster with data checksums, installed pgtap + test_factory, prepared it via bin/test_existing prepare-old, created the new cluster, installed pgtap + test_factory there too, ran the actual pg_upgrade binary, started the new cluster, then ran bin/test_existing run-suite against it -- all 3 tests passed with the deduplicated run_suite().
The advanced-extension-testing doc's reference implementation (cat_tools) is on NEWEST=18; this repo was still on 17. Rather than assume pg-start's `apt.postgresql.org.sh -i -p -v "$PGVERSION"` can install PG18 (a matrix expansion that silently failed to install would be a much worse failure mode than not bumping), confirmed it via an actual CI run: pushed NEWEST=18 alone first and watched the new "PostgreSQL 18" job -- it installed postgresql-18 18.4-1.pgdg13+1 via pg-start and the full fresh-install suite passed. With NEWEST=18, also shifted pg-upgrade-test's "newest-boundary" leg from 16->17 to 17->18 and its "oldest-to-newest" leg from 10->17 to 10->18, keeping both legs matching the job's own stated rationale (widest catalog distance; most likely to hit a *new* major's catalog change first) now that 18 is the newest major instead of 17.
Caught by the job's own self-verification step on the first full CI run of this rebased branch (confirmed via a real run, not just local YAML parsing): the `lint` job (SQL Lint, inherited via the pgxntool 2.3.0 sync stacked below this PR -- not part of this PR's own original commits) was present in the workflow but missing from all-checks-passed's needs: list, since that list was carried over unmodified from before `lint` existed on this branch. all-checks-passed would otherwise silently ignore SQL Lint results entirely -- exactly the class of bug its own self-check step exists to catch, which is what actually caught it here.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebase note + follow-up fixes
Rebased onto the updated
advanced-testing/foundation(this stack issync-pgxntool-2.3.0->add-test-build(#26) ->advanced-testing/foundation(#22) -> this PR; coordinator is handlinggh stack linkseparately).advanced-testing/cihad a merge commit bringing in the OLDadvanced-testing/foundationhistory directly, so a plaingit rebasetried to replay that entire flattened history against the new base -- usedgit rebase --onto <new-foundation> <old-foundation-tip>instead, to replay only this branch's own unique commits.Real conflicts requiring judgment (not mechanical):
.github/workflows/ci.yml(this PR'schanges/pg-upgrade-test/all-checks-passedstructure vs. the now-simplifiedlint+teststructure inherited from #26/the pgxntool 2.3.0 sync -- merged both),test/expected/pgtap.out(this PR'sORDER BYfix fortest/helpers/create.sql's security-definer-function check reorders that output; regenerated the fresh-mode expected file from a real run once the conflict was resolved --pgtap_1.out, the existing-mode alternate, already had the correct sorted order and needed no change), and the sametest/sql/install.sql/pgtap.sqlmode-gating reconciliation #22 already needed against #26's trim (nothing new here, just replayed).Fixes applied per this round of review, all verified locally before pushing
(a)
pg-upgrade-testnow gates ontest, plus addedconcurrency. Previouslyneeds: [changes]only, so a trivially-broken PR still burned the full binarypg_upgradematrix. Nowneeds: [changes, test]. Added a top-levelconcurrency: {group: "${{ github.workflow }}-${{ github.ref }}", cancel-in-progress: true}so a superseded push cancels an in-flight run.all-checks-passed's ownneeds/self-check invariant is unaffected (job set didn't change shape).(b)
bin/test_existing'srun_suite()was running the full suite twice.make testfollowed immediately bymake verify-results--verify-resultsalready depends ontest(pgxntool'sbase.mk), so everypg-upgrade-testleg paid for two fullpg_regress --use-existingruns against the real migrated database. Dropped the redundantmake testcall. Verified this is still correct post-pgxntool-2.3.0 by reproducing a REALpg_upgradeleg end to end locally (16 -> 17 at the time, using throwawaypg_createclusterdata directories on custom ports -- never touching this container's shared clusters): recreated the old cluster with data checksums, installed pgtap + test_factory, prepared it viabin/test_existing prepare-old, created the new cluster, installed pgtap + test_factory there too, ran the actualpg_upgradebinary, started the new cluster, then ranbin/test_existing run-suiteagainst it -- all 3 tests passed with the deduplicatedrun_suite().(c)
actions/checkout@v4->@v7(current latest major -- v5/v6 have both released since v4), all 5 occurrences (lint,changes,test,pg-upgrade-test,all-checks-passed-- one more than the original "4" since thelintjob wasn't part of this branch when that count was written; it arrived via the pgxntool 2.3.0 sync / #26 stacked below). Coordination note: PR #29 (ci/bump-actions-versions, someone else's, out of scope for me to touch) also bumps this same pin elsewhere in this file for the original, not-yet-restructured job set -- whichever of these two efforts merges second will need a finalgrep -rn 'actions/checkout@v4'sweep to make sure nothing was missed.(d)
NEWESTbumped 17 -> 18. The advanced-extension-testing doc's reference implementation (cat_tools) is already onNEWEST=18. Rather than assume PG18 installs cleanly in thepgxn/pgxn-toolsimage, confirmed it via an actual CI run first (pushed theNEWEST=18bump alone, watched the new "PostgreSQL 18" job) -- it installedpostgresql-18 18.4-1.pgdg13+1viapg-startand the fresh-install suite passed. With 18 now the newest major, also shiftedpg-upgrade-test's two legs to match its own stated rationale (widest catalog distance; most likely to hit a new major's catalog change first): oldest-to-newest is now10 -> 18(was10 -> 17), and the newest-boundary leg is now17 -> 18(was16 -> 17).Verification after all fixes
make lintandmake verify-results(fresh mode) pass locally.bash -non both shell scripts, YAML parse check onci.yml.pg_upgradeleg (see (b) above) passes with the deduplicated script.changes, alltestmatrix legs (10-18),pg-upgrade-test's two legs (10->18, 17->18), andall-checks-passed.Summary
PR 2 of the advanced update+upgrade (U&U) testing stack, on top of #22
(
advanced-testing/foundation, not yet merged -- this PR targets thatbranch, not
master). Implements the remaining CI-structure items from theadvanced-extension-testing pattern, modeled on
Postgres-Extensions/cat_tools'sactual
ci.yml/bin/test_existing(read directly off itsmaster, not justprose about it) and scoped down deliberately per the plan agreed before
starting.
Note:
advanced-testing/foundationwas pushed to this repo (unchanged, samecommit as the fork's branch backing #22) purely so this PR's base could
reference it directly -- it is not new work, just a ref needed for a clean
stacked diff. Once #22 merges, this PR should be retargeted to
masteras afollow-up (not done here).
What shipped
pushscoped tobranches: [master];pull_requeststays unrestricted. test_factory was explicitlynamed as one of the repos still needing this.
changes/docs-only gate instead of workflow-levelpaths-ignore-- computes the real per-push diff, fail-safe (docs_only=false)written as the literal first line so any early exit/error leaves the safe
default in place. Avoids the stuck-Pending-required-check trap a
workflow-level
paths-ignorewould cause.(
NEWEST=18,FLOOR=10), derivedonce in the
changesjob and consumed viafromJSONby both thetestand new
pg-upgrade-testmatrices.all-checks-passedgate, self-verifying its ownneeds:list matchesthe actual job set. This is the check to wire up as required in branch
protection -- I don't have permission to change that setting myself.
pg-upgrade-testjob: binarypg_upgradelegs (10 -> 18,17 -> 18, updated from the original10 -> 17/16 -> 17onceNEWESTwas bumped -- see the rebase note above) -- install the current version on an old cluster,pg_upgradeto a newer major, run the suite against the migrated objects in
existingmode. No bridge step: test_factory has shipped only one version (0.5.0),
so every leg installs current directly on the old cluster -- there's no
older,
pg_upgrade-unsafe install to carry forward.bin/test_existing(new, test_factory-specific) and.github/scripts/pg_upgrade_cluster(new, generic pg_upgrade CImechanics) -- the install -> pg_upgrade -> assert -> run-suite flow
factored into committed scripts instead of inline YAML per job, per the
doc's own guidance.
bin/test_existingis much smaller than cat_tools'sown: no bridge/multi-origin subcommands, just
prepare-old+run-suite.testjob now gates onmake verify-results, not pgxn-tools'pg-build-test. Found while rewriting this job:pg-build-test's ownmake installcheck || status=$?never actually catches a failure, becausepgxntool marks
installcheck.IGNORE--makeitself exits 0 thereregardless of
regression.diffs. The old CI was silently green on a realregression.
PGXNTOOL_ENABLE_VERIFY_RESULTSwas already pgxntool's owndefault, but I pinned it explicitly in the Makefile (matching
ENABLE_TEST_INSTALL's existing explicit-over-implicit convention) so afuture pgxntool default change can't silently disable the gate.
bin/test_existing'sassert_versionderives the expected version from
make -s print-PGXNVERSION, neverhardcoded, with empty-value guards on both sides (
"" != ""is false, so abroken extraction can't silently pass).
A real bug the local pg_upgrade dry run actually found
Per the brief, I ran the full old-cluster-install ->
pg_upgrade->new-cluster-suite-run cycle locally before trusting any of this in CI (PG12
-> PG17 and PG12 -> PG16, using throwaway
initdbdata directories, nevertouching the container's shared clusters) -- and it surfaced a real, if
low-stakes, bug:
test/helpers/create.sql's security-definer-function checkhad no
ORDER BY, so its row order depended onpg_proc's physical layout.That happens to match creation order on a fresh
CREATE EXTENSIONbut isnot preserved by
pg_upgrade's dump/restore (which reconstructs it in adifferent, apparently name-sorted, order) -- producing a real but harmless
text diff (every individual pgTAP assertion still said
ok, just reordered)against the fresh-install expected output. Fixed with an explicit
ORDER BY p.oid::regproc::text.Bonus: this makes ONE set of expected-output files valid for fresh,
existing, and pg_upgraded modes alike -- no third numbered alternate file
needed (unlike the doc's more general guidance for genuinely different
axes like
TEST_SCHEMA), since the divergence here was pure incidentalnon-determinism, not a legitimate different-but-correct scenario. Simpler
than it first looked once actually run.
Convergence / divergence from cat_tools PR #16 (and its
mastersince)Took near-verbatim (generic CI mechanics, no cat_tools-specific
content): the shape of
.github/scripts/pg_upgrade_cluster(
recreate-old/upgradesubcommands,INITDB_OPTSconvention, pg_upgradelog capture for both PG17+'s
pg_upgrade_output.d/and older's CWD), thechangesjob's docs-only fail-safe-first-line pattern, theall-checks-passedself-verifyingneedscheck, and the dynamicversion-assertion empty-guard pattern.
Adapted:
bin/test_existingkeeps only two subcommands(
prepare-old/run-suite) instead of cat_tools's six -- noplant-guard/update/update-scenario/update-check, becausetest_factory's dependency guard is planted and proved entirely inside
test/install/load.sql's existing-mode branch (from PR #22), not by thisscript, and there's no update path to exercise yet. The
changesjob alsodrops cat_tools's "find the last commit where real code changed" reporting
machinery -- useful polish, but not part of the checklist items this PR
scoped to; noting it here as a real simplification, not an oversight, in
case a human wants it added later.
Skipped, and why (all decided before starting, confirmed still correct
after doing the work):
extension-update-testjob -- test_factory has shipped only one version(0.5.0), so
TEST_LOAD_SOURCE=updatehas no real historical update scriptto exercise yet. The mechanism exists (PR Add test/install foundation for update+upgrade testing (fresh/update/existing) #22); no CI job drives it.
(recovering from old
pg_upgrade-unsafe releases). test_factory has nocatalog-touching views and only one shipped version, so there's nothing to
bridge from.
pg-upgrade-stepwise(every-major climb) -- test_factory has nocatalog-internals-touching views/functions (no
SELECT *over a systemcatalog), so the per-major-boundary risk this protects against is low. A
human may disagree and ask for it later; flagging explicitly rather than
silently omitting.
deployment.
stablepseudo-version -- real feature work, not part of atesting-infrastructure PR, not requested.
Verification
make verify-resultspasses cleanly (fresh mode) on both PG12 and PG17.pg_upgrade-> new-cluster-suite-run cycle,using the actual committed
bin/test_existingand.github/scripts/pg_upgrade_cluster(not just ad hoc commands), passeswith zero raw
not okTAP lines -- run twice, PG12->PG17 and PG12->PG16,in scratch data directories that never touched the container's shared
clusters.
.github/workflows/ci.ymlparses cleanly under PyYAML; both new shellscripts pass
bash -n.Test plan
test+pg-upgrade-test+all-checks-passed)master(follow-up, not done here)all-checks-passedas a required status check inbranch protection
🤖 Generated with Claude Code
Recreated from #23 (fork-headed) as an upstream-branch PR so it can be part of a formal GitHub stack (
gh stack link), which refuses fork PRs. Same content/commits, same CI results.