-
Notifications
You must be signed in to change notification settings - Fork 1
935 lines (910 loc) · 52.5 KB
/
Copy pathci.yml
File metadata and controls
935 lines (910 loc) · 52.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
# ===========================================================================
# See .github/workflows/README.md for the full job list (what `test`,
# `pg-upgrade-test`, and `pg-tle-test` each prove), the docs-only/draft-PR
# reductions, and the priority-lane design - this comment covers only what
# isn't already there, or what's specific to this workflow file's own
# mechanics.
#
# During testing we always install into a freshly, randomly generated
# schema (see test/install/load.sql; test/README.md covers how
# test/expected/extension_tests.out stays schema-invariant).
#
# `changes` is a cheap gate that lets the heavy jobs above skip themselves on
# doc-only pushes, and also derives the shared PostgreSQL-major list those
# jobs consume from a single set of constants. `all-checks-passed` is the
# single stable required-status-check name.
#
# Draft PRs get a further reduction, independent of `changes`/docs_only,
# aimed at cutting shared-runner load while a PR is still being iterated on
# (this repo's org-wide Actions queue backs up easily): `lint` always runs
# in full; `test`'s matrix drops to just the newest supported PostgreSQL
# major (see its own comment) instead of running full or being skipped
# outright, since it's cheap per-leg and a draft author still wants signal
# on every push; the other heavy jobs (`pg-tle-test`, `pg-upgrade-test`)
# are skipped entirely via an added `&& github.event.pull_request.draft !=
# true` on their existing `if:`. None of this applies to a `push` event (e.g.
# the post-merge run on master) or a non-draft PR, both of which always run
# the full suite exactly as before. `pull_request.types` below includes
# `ready_for_review` specifically so marking a draft PR ready retriggers
# immediately at full scope (draft is already false by the time that event
# fires) instead of leaving the reduced draft-time result on the PR's last
# commit as its current status until some later real push.
#
# See README.md's "Priority lanes" and "Escalation" sections for why a
# `gh stack rebase` cascade is routed to a separate, lower-priority job ID
# instead of competing with real work, and for the base-retarget/
# `ready_for_review` escalation rules. Each `test`/`pg-upgrade-test`/
# `pg-tle-test` (-lowprio) job's own comment carries the mechanics specific
# to why ITS `if:`/`concurrency:` block needs two literal job IDs; the
# `escalate` and `diff` steps below carry the mechanics specific to this
# job's own escalation/priority logic.
# ===========================================================================
name: CI
on:
push:
branches:
- master
pull_request:
# Explicit (not the GitHub default of [opened, synchronize, reopened]):
# `ready_for_review` retriggers immediately when a draft PR is marked
# ready (see the top-of-file comment); `edited` catches a base retarget
# for the priority-escalation check in the `changes` job below.
types: [opened, synchronize, reopened, edited, ready_for_review]
jobs:
# Cheap gate that lets the heavy jobs below skip themselves on commits that
# touch only docs. Must run on every push/pull_request (no paths-ignore on
# the workflow itself), otherwise the required all-checks-passed check
# would never report on doc-only pushes and get stuck Pending in branch
# protection.
#
# Also derives the supported-PostgreSQL-major list every other job reads,
# from a single NEWEST constant plus META.json's own declared minimum (not
# a second hand-maintained floor constant) - every job that cares which
# majors are supported reads the SAME values, so they can't silently drift
# onto different sets, and adding a new major is a one-line change here
# instead of an edit in several jobs.
# - supported_pg: the full descending list (NEWEST down to the
# META.json-derived floor), as a JSON array, for jobs that need every
# supported major.
# - newest_pg: just the single newest major, as a bare scalar, for
# anything that needs one value instead of the full array.
# - floor_pg: just the single oldest (META.json-derived) major, as a
# bare scalar, for the same reason.
changes:
name: 🔍 Detect docs-only changes & derive PG matrix
runs-on: ubuntu-latest
permissions:
contents: read
checks: read # escalate step: has this SHA already got a full run?
outputs:
docs_only: ${{ steps.diff.outputs.docs_only }}
supported_pg: ${{ steps.pg.outputs.supported_pg }}
newest_pg: ${{ steps.pg.outputs.newest_pg }}
floor_pg: ${{ steps.pg.outputs.floor_pg }}
priority: ${{ steps.diff.outputs.priority }}
steps:
- name: Check out the repo
uses: actions/checkout@v7
with:
# Full history needed so BASE and HEAD below are both reachable
# for `git diff`.
fetch-depth: 0
- name: Base retargeted onto a new branch? Check whether this SHA already has a full run
# Fires only for `edited` WITH a base change - the gh-stack-promotion
# signal described in README.md's Escalation section. A plain
# title/body edit also fires `edited` but leaves
# `github.event.changes.base` absent, so it's excluded here and
# falls through to the diff step's edited-with-nothing-to-do branch
# below. `ready_for_review` needs no equivalent check here: it isn't
# `edited`, so it never enters this step at all - the diff step's
# default (below) already gives it `priority: high` unconditionally,
# since only a `synchronize` push can ever be downgraded to low.
id: escalate
if: >-
github.event_name == 'pull_request' &&
github.event.action == 'edited' &&
github.event.changes.base != null
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
SHA: ${{ github.event.pull_request.head.sha }}
run: |
if [ "$(bin/check_run_exists "$REPO" "$SHA" all-checks-passed)" = "true" ]; then
echo "escalate=false" >> "$GITHUB_OUTPUT"
echo "SHA $SHA already has an all-checks-passed run - nothing to escalate"
else
echo "escalate=true" >> "$GITHUB_OUTPUT"
fi
- name: Restore this PR's previously observed content patch-id
id: patchid-restore
if: github.event_name == 'pull_request' && github.event.action != 'edited'
uses: actions/cache/restore@v4
with:
path: /tmp/prev_patch_id
# A key guaranteed not to already exist, so this always falls
# through to restore-keys (prefix match, picks the MOST RECENT
# save) instead of ever hitting its own key directly.
key: unused-${{ github.run_id }}
restore-keys: |
ci-patchid-pr-${{ github.event.pull_request.number }}-
- name: Compute per-push changed files, docs-only status, and CI priority
id: diff
run: |
# Fail safe to running the full matrix at high priority: default
# both immediately, before anything below has a chance to compute
# or fail. Writing the same GITHUB_OUTPUT key twice is fine (the
# last write wins), so the only way this step ends with
# docs_only=true or priority=low is by genuinely proving it
# further down - never by skipping past an edge case with a
# default.
echo "docs_only=false" >> "$GITHUB_OUTPUT"
echo "priority=high" >> "$GITHUB_OUTPUT"
if [ "${{ github.event.action }}" = "edited" ]; then
# Either a base retarget with nothing outstanding to run
# (escalate above said false - an all-checks-passed run already
# covers this SHA), or a plain title/description edit (no base
# change at all, so escalate didn't even run). Either way,
# there's no code to test right now.
if [ "${{ steps.escalate.outputs.escalate }}" != "true" ]; then
echo "docs_only=true" >> "$GITHUB_OUTPUT"
fi
exit 0
fi
if [ "${{ github.event_name }}" = "pull_request" ] && \
[ "${{ github.event.action }}" = "synchronize" ] && \
[ -n "${{ github.event.before }}" ]; then
# A push to an already-open PR: before/after give the true
# per-push diff, same as for a branch push.
BASE="${{ github.event.before }}"
HEAD="${{ github.event.after }}"
elif [ "${{ github.event_name }}" = "pull_request" ]; then
# First run for this PR (opened/reopened/etc, or synchronize
# without a usable before): fall back to the whole base...head
# diff.
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
else
BASE="${{ github.event.before }}"
HEAD="${{ github.event.after }}"
fi
echo "base=$BASE"
echo "head=$HEAD"
# A missing HEAD, or an all-zeros BASE (e.g. a new branch's first
# push, where GitHub reports no prior commit), means we can't
# compute a real diff. docs_only is already false from above;
# just stop here rather than risk skipping tests.
if [ -z "$HEAD" ] || [ -z "$BASE" ] || [[ "$BASE" =~ ^0+$ ]]; then
exit 0
fi
DOCS_ONLY=$(bin/docs_only_diff "$BASE" "$HEAD")
echo "docs_only=$DOCS_ONLY" >> "$GITHUB_OUTPUT"
# Priority: a synchronize push whose own patch-id (the PR's
# actual contribution, independent of what it's based on) matches
# the last one observed for this PR is a pure gh-stack rebase
# cascade, not new work - safe to route to the low-priority job
# IDs (see README.md's Priority lanes section). Real new
# commits, and this PR's first appearance (nothing to compare
# against yet), keep the high priority set above. bin/ci_priority
# always overwrites /tmp/prev_patch_id with this push's patch-id,
# regardless of the verdict, so the NEXT push has something to
# compare against - only the "was it a synchronize push" gate
# below is specific to this call site.
if [ "${{ github.event_name }}" = "pull_request" ]; then
PRIORITY=$(bin/ci_priority "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" /tmp/prev_patch_id)
if [ "${{ github.event.action }}" = "synchronize" ] && [ "$PRIORITY" = "low" ]; then
echo "priority=low" >> "$GITHUB_OUTPUT"
fi
fi
- name: Save this push's content patch-id for the next push to compare against
if: github.event_name == 'pull_request' && github.event.action != 'edited'
uses: actions/cache/save@v4
with:
path: /tmp/prev_patch_id
key: ci-patchid-pr-${{ github.event.pull_request.number }}-${{ github.run_id }}
- name: Derive the supported-PostgreSQL-major list
id: pg
run: |
# A dozen-odd lines to replace what looks like a handful of version
# references, but it buys CONSISTENCY: every job that needs the
# supported-major list derives it from this ONE source, so none of
# them can silently drift onto a different list. Adding a new
# major is a one-line NEWEST bump here, not an edit in N places.
#
# The floor itself is NOT a second hardcoded constant here: it's
# read straight from META.json's own build prereq (see
# META.in.json's comment: "Depends on JSONB, created in 9.4"),
# which is the actual source of truth for count_nulls' minimum
# supported major. Hand-maintaining a duplicate floor constant in
# this workflow is exactly how it ended up testing down to 10 while
# META.json claimed 9.4 - 9.4/9.5/9.6 went untested for a while
# even though pg-start genuinely installs them fine on current
# Actions infra, and nobody noticed the gap.
NEWEST=18
DECLARED_FLOOR=$(jq -r '.prereqs.build.requires.PostgreSQL' META.json) # e.g. "9.4.0"
FLOOR_MAJOR=$(echo "$DECLARED_FLOOR" | cut -d. -f1)
if [ "$FLOOR_MAJOR" -lt 10 ]; then
# PostgreSQL's pre-10 versioning used X.Y as the major identifier
# (10+ switched to a single integer) - e.g. "9.4.0" -> major
# "9.4", not "9".
FLOOR=$(echo "$DECLARED_FLOOR" | cut -d. -f1,2)
else
FLOOR="$FLOOR_MAJOR"
fi
# A sub-10 floor needs two `seq` runs stitched together: one for
# the integer majors (10..NEWEST) and one for the X.Y majors
# (9.FLOOR_MINOR..9.6, since 9.6 was the last 9.x release).
if [[ "$FLOOR" == 9.* ]]; then
FLOOR_MINOR=$(echo "$FLOOR" | cut -d. -f2)
supported="$(seq "$NEWEST" -1 10) $(seq 6 -1 "$FLOOR_MINOR" | sed 's/^/9./')"
else
supported=$(seq "$NEWEST" -1 "$FLOOR")
fi
# Emit a JSON array of STRINGS, not bare numbers - "9.4" isn't a
# valid bare integer token, and keeping every leg (10+ and 9.x
# alike) as a string keeps matrix.pg comparisons/interpolations
# consistent regardless of which leg they're in. $supported is
# deliberately unquoted below: it word-splits (on both the spaces
# AND the newlines `seq`/the concatenation above produce) into
# individual version tokens for printf to repeat over.
json() { printf '"%s",' $supported | sed 's/,$//; s/^/[/; s/$/]/'; }
echo "supported_pg=$(json)" >> "$GITHUB_OUTPUT"
# Also emitted as a bare scalar (not a JSON array), for anything
# that needs just the single newest major instead of the full
# array - so NEWEST still only needs to change in one place.
echo "newest_pg=$NEWEST" >> "$GITHUB_OUTPUT"
# Same idea for the OLDEST supported major: emit the exact $FLOOR
# value supported_pg above already derived from META.json, as a
# bare scalar, for anything that needs just the single oldest
# major instead of the full array - rather than hand-maintaining a
# THIRD copy of this number.
echo "floor_pg=$FLOOR" >> "$GITHUB_OUTPUT"
lint:
name: 🧹 SQL lint
# Deliberately no docs_only/needs gate, unlike the heavy jobs below: a
# plain title/description edit (no base change) now triggers a real
# run of this job too, since `edited` was added to `pull_request.types`
# for the escalate check. Accepted rather than fixed - lint is cheap
# (a checkout + `make lint`, no PostgreSQL container), and gating it
# would need the SAME docs_only computation the `changes` job already
# does, on a job that's meant to be the cheap always-on baseline.
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v7
- name: make lint
# Deliberately not pre-initializing the .vendor/linter submodule via
# `submodules:` above -- letting `make lint` self-init it (lint.mk)
# is what actually proves that works from a plain clone.
run: make lint
# Fresh install, then the in-place extension update path, both across the
# PG matrix. The update leg CREATE EXTENSIONs at the oldest version we
# still ship a full install script for (0.9.6), then ALTER EXTENSION
# UPDATEs to current (no pg_upgrade, same PostgreSQL) and reruns the
# suite - a single job rather than a dedicated one, since a load mode is
# just an input the same assertions run against, not a real environment
# difference, and the per-version container/checkout setup would
# otherwise be duplicated across two jobs with the same PG matrix. Each
# install (both legs) always targets its own freshly, randomly generated
# schema (see test/install/load.sql), so both legs pass against the SAME
# test/expected/extension_tests.out (see test/README.md for how the
# suite keeps its output schema-invariant).
test:
needs: [changes]
if: needs.changes.outputs.priority != 'low' && needs.changes.outputs.docs_only != 'true'
# "ci-test-" prefix, distinct from pg-upgrade-test's/pg-tle-test's below:
# those run CONCURRENTLY with this job within the same PR's workflow run,
# so sharing a group name across job types would make them cancel/queue
# behind EACH OTHER instead of behind their own prior runs for this job.
#
# This is the HIGH-priority job ID - see test-lowprio right below for its
# low-priority counterpart, and for why the two are separate job IDs
# rather than one job with a priority-conditional `if:` here. The group
# includes ${{ matrix.pg }}:
# without it, every leg of THIS SAME push's own matrix would collide on
# one shared group name and cancel-in-progress would cancel siblings,
# not just a prior push's run - the exact opposite of "always runs the
# full matrix either way". Confirmed live: before this per-leg split,
# most legs of a normal (non-rebase) push came back cancelled instead of
# completing. cancel-in-progress is unconditionally true here (a real
# push always wants its own latest commit's result, never a stale one)
# - it's only the LOW-priority job below that needs it to be
# unconditionally false, for `queue: max` to be usable at all.
concurrency:
group: ci-test-${{ github.event.pull_request.number || github.sha }}-${{ matrix.pg }}
cancel-in-progress: true
strategy:
matrix:
# From the single source in the changes job. On a draft PR, reduced
# to just the newest supported major (never skipped outright, unlike
# the other heavy jobs below - this is the one signal a draft author
# still wants on every push): `github.event.pull_request.draft` is
# null/falsy for a push event (e.g. the post-merge run on master), so
# this expression falls through to the full list there with no extra
# guard needed.
pg: ${{ github.event.pull_request.draft && fromJSON(format('[{0}]', needs.changes.outputs.newest_pg)) || fromJSON(needs.changes.outputs.supported_pg) }}
name: 🐘 PostgreSQL ${{ matrix.pg }}
runs-on: ubuntu-latest
container: pgxn/pgxn-tools
steps:
- name: Start PostgreSQL ${{ matrix.pg }}
run: pg-start ${{ matrix.pg }}
- name: Check out the repo
uses: actions/checkout@v7
- name: Test on PostgreSQL ${{ matrix.pg }}
run: make test
- name: Install count_nulls
run: make install
- name: Update 0.9.6 -> current and run the suite
run: make verify-results TEST_LOAD_SOURCE=update
- name: Structurally compare the updated objects against a fresh install
# A fixed pgTAP suite only proves the specific behaviors it asserts
# still hold; it can't catch an update script that leaves some
# definition/comment/ACL subtly different from what a fresh install
# of the same version produces. bin/compare_fresh_vs_update installs
# both ways itself, in its own scratch databases - each an
# unqualified `CREATE EXTENSION`, so both land in the same default
# schema by construction, which is all the diff needs to isolate
# real update-vs-fresh divergence rather than a schema-name
# difference - and diffs every object the extension owns; any
# nonempty diff fails the step. Not a make target (it's a
# standalone script, not `make test`), so invoked directly here.
run: bin/compare_fresh_vs_update 0.9.6
# LOW-priority counterpart of `test` above - same steps/matrix, only the
# `if:`/`concurrency:` differ. See README.md's Priority lanes section for
# why a gh-stack rebase cascade is routed here instead of to `test`, and
# why that requires a genuinely separate job ID rather than a runtime
# branch inside one job: `queue: max` (letting up to 100 runs QUEUE in a
# concurrency group instead of cancelling all but the latest) cannot
# combine with a `cancel-in-progress` that could evaluate `true` at
# runtime - and a single job with `cancel-in-progress: ${{
# needs.changes.outputs.priority != 'low' }}` is exactly that, since the
# SAME job could run with priority=high on some other push. Splitting into
# two job IDs means THIS job's `cancel-in-progress: false` is a literal
# that can never be anything else, which is what makes `queue: max` legal
# here at all.
#
# The group is keyed by ${{ matrix.pg }} ALONE (no PR number), so it's
# shared across every PR's low-priority pushes for that same PG major -
# not a small fixed hashed set of lanes (the previous, buggy design): a
# real leg value queues against other runs of that SAME leg, repo-wide,
# while still leaving every OTHER leg of this push's own matrix to queue
# independently in its own group - preserving "still gets the FULL matrix
# either way" from README.md's Priority lanes section, which the
# previous single-hashed-lane design didn't (confirmed live: it collapsed
# a whole push's matrix onto one shared group and cancelled most of its
# own legs).
test-lowprio:
needs: [changes]
if: needs.changes.outputs.priority == 'low' && needs.changes.outputs.docs_only != 'true'
concurrency:
group: ci-test-lowprio-${{ matrix.pg }}
cancel-in-progress: false
queue: max
strategy:
matrix:
pg: ${{ github.event.pull_request.draft && fromJSON(format('[{0}]', needs.changes.outputs.newest_pg)) || fromJSON(needs.changes.outputs.supported_pg) }}
name: 🐘 PostgreSQL ${{ matrix.pg }} (low-priority)
runs-on: ubuntu-latest
container: pgxn/pgxn-tools
steps:
- name: Start PostgreSQL ${{ matrix.pg }}
run: pg-start ${{ matrix.pg }}
- name: Check out the repo
uses: actions/checkout@v4
- name: Test on PostgreSQL ${{ matrix.pg }}
run: make test
- name: Install count_nulls
run: make install
- name: Update 0.9.6 -> current and run the suite
run: make verify-results TEST_LOAD_SOURCE=update
- name: Structurally compare the updated objects against a fresh install
run: bin/compare_fresh_vs_update 0.9.6
# Proves count_nulls survives a BINARY pg_upgrade (in-place catalog
# migration to a newer PostgreSQL major). Installs 0.9.6 on the oldest
# supported PostgreSQL floor, plants a dependency guard, binary-
# pg_upgrades to the newest supported major, then runs the suite against
# the REAL migrated objects in existing mode.
#
# A single old_pg/new_pg leg (old_pg from the changes job's floor_pg
# output, new_pg from its newest_pg output - see that job's comment; NOT
# a second/third hardcoded copy of either number), which via TWIN
# databases in the SAME cluster, migrated by a SINGLE pg_upgrade call,
# compares BOTH update-vs-pg_upgrade orderings a real user could hit:
# upgrade_oldest_first -- stays at 0.9.6 through the
# binary pg_upgrade, updated to current AFTER it (on the NEW
# cluster). Proves pg_upgrade itself correctly preserves objects
# that are STILL at the oldest supported version at the moment of
# the upgrade - a real-world scenario (a user upgrading their
# PostgreSQL major while still running an old extension version).
# upgrade_current_first -- updated to current BEFORE the
# binary pg_upgrade (on the OLD cluster). Proves pg_upgrade
# correctly migrates the objects count_nulls' CURRENT code actually
# creates - migrating 0.9.6's objects and updating afterward would
# instead test whether pg_upgrade can migrate a legacy structure
# frozen in the past, which isn't actionable on its own (that
# version already shipped; nothing to fix if it turned out
# fragile) - which is exactly why this leg exists alongside
# oldest_first rather than instead of it.
# The ordering comparison is a property of pg_upgrade itself, not of
# which PG floor it runs from, so a single floor is enough to cover
# both orderings - there was never a technical reason for a second
# old_pg floor here (the previous old_pg=12 leg predated this ordering
# comparison and had no purpose once it existed).
#
# Both databases still end up at the current version by the time
# run-suite runs, so nothing downstream (the pgTAP suite, the
# dependency guard check) needs to differ - only which side(s) of the
# pg_upgrade call each database's update happens on. No bridge-update
# step first: count_nulls has always been pure SQL functions with no
# SELECT-*-over-catalog views, so it has no known pg_upgrade-unsafe old
# version to bridge past.
#
# Deliberately not doing a stepwise every-major-in-sequence climb (one
# cluster walking 10->11->12->...->newest, vs. the single big jump
# here): that would catch a regression specific to one particular
# major-to-major boundary, which would matter if count_nulls had views/
# functions touching catalog internals, but it doesn't - pure SQL
# functions over anyarray/json/jsonb, nothing version-sensitive to
# break at a specific boundary. Revisit if count_nulls ever grows
# something catalog-touching.
pg-upgrade-test:
needs: [changes]
# Skipped outright (not just matrix-reduced like `test`) on a draft PR:
# this is a heavy job, and a draft author doesn't need a real binary
# pg_upgrade re-proven on every push while still iterating.
if: needs.changes.outputs.priority != 'low' && needs.changes.outputs.docs_only != 'true' && github.event.pull_request.draft != true
# "ci-pgupgrade-" prefix, matrix.old_pg/new_pg split - see the `test`
# job's concurrency comment above (currently a single-combination
# matrix, but split the same way regardless in case that ever changes).
# This is the HIGH-priority job ID; see pg-upgrade-test-lowprio below for
# its low-priority counterpart, and for why splitting into two job IDs
# is what makes `queue: max` usable on the low-priority side at all.
concurrency:
group: ci-pgupgrade-${{ github.event.pull_request.number || github.sha }}-${{ matrix.old_pg }}-${{ matrix.new_pg }}
cancel-in-progress: true
strategy:
matrix:
# Single-element lists built from the changes job's floor_pg/
# newest_pg scalar outputs (see that job's comment), NOT a second/
# third hardcoded copy of either number - keeping this a `matrix:`
# (rather than plain `env:`) preserves the ${{ matrix.old_pg }}/
# ${{ matrix.new_pg }} interpolations used throughout the steps
# below unchanged.
old_pg: ["${{ needs.changes.outputs.floor_pg }}"]
new_pg: ["${{ needs.changes.outputs.newest_pg }}"]
name: 🔄 Binary pg_upgrade ${{ matrix.old_pg }} → ${{ matrix.new_pg }}
runs-on: ubuntu-latest
container: pgxn/pgxn-tools
env:
# Both clusters must use the same initdb options or pg_upgrade
# refuses to run.
INITDB_OPTS: --data-checksums --auth trust
steps:
- name: Install PostgreSQL ${{ matrix.old_pg }} (cluster created next, with the right initdb options from the start)
run: NO_CLUSTER=1 pg-start ${{ matrix.old_pg }}
- name: Create old cluster with data checksums enabled
run: |
# -p 5432: pg_createcluster would otherwise assign the next
# available port; force 5432 so subsequent psql/createdb calls
# connect without -p.
pg_createcluster -p 5432 ${{ matrix.old_pg }} test -- $INITDB_OPTS
pg_ctlcluster ${{ matrix.old_pg }} test start
pg_isready -t 30
- name: Check out the repo
uses: actions/checkout@v7
- name: Install count_nulls into old cluster
run: make install
- name: Prepare the old cluster (install + dependency guard), twin databases
# Two separate databases (distinct names, one per ordering) so
# both exist in the SAME cluster ahead of the single pg_upgrade
# call below - that one binary upgrade migrates both at once.
# prepare-old installs count_nulls at 0.9.6 in each, then plants +
# proves the dependency guard, so a later accidental CASCADE drop
# anywhere in this job cannot silently make the eventual
# existing-mode run test a fresh install instead.
run: |
bin/test_existing prepare-old upgrade_oldest_first 0.9.6
bin/test_existing prepare-old upgrade_current_first 0.9.6
- name: Update to current before pg_upgrade (proves pg_upgrade preserves CURRENT objects)
# Updates ONLY upgrade_current_first
# (upgrade_oldest_first must NOT be touched here - it
# stays at 0.9.6 until AFTER pg_upgrade, below). Exercises ALTER
# EXTENSION UPDATE on the OLD cluster, BEFORE the binary pg_upgrade
# below, running the 0.9.6->stable update script, so pg_upgrade
# runs against already-current objects, not 0.9.6 ones. `make
# install` above already installed the current version's update
# scripts/control file into this (old) cluster's sharedir, so
# they're in place for this ALTER EXTENSION UPDATE to use.
run: bin/test_existing update upgrade_current_first
- name: Install PostgreSQL ${{ matrix.new_pg }}
run: apt-get install -y postgresql-${{ matrix.new_pg }} postgresql-server-dev-${{ matrix.new_pg }}
- name: Install count_nulls into new cluster
# PG_CONFIG must be specified explicitly: at this point both old
# and new PostgreSQL are installed, and the default pg_config on
# PATH may not be the new version's.
run: make install PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config
- name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster
run: bin/pg_binary_upgrade ${{ matrix.old_pg }} ${{ matrix.new_pg }}
- name: Update to current after pg_upgrade (proves pg_upgrade preserves OLDEST-version objects)
# Exercises ALTER EXTENSION UPDATE AFTER the binary pg_upgrade
# above, against the NEW cluster (already started by the previous
# step, and the only cluster listening on 5432 at this point,
# since the old one was stopped in that same step), on
# upgrade_oldest_first ONLY (upgrade_current_first
# was already updated before the upgrade, above, and must not be
# updated again here) - the 0.9.6 objects prepare-old planted into
# that database are thus still at 0.9.6 at the moment pg_upgrade
# itself runs, so this proves pg_upgrade correctly preserves
# objects still frozen at the oldest supported version, not just
# already-current ones. `make install` into the new cluster above
# already installed the current version's update scripts/control
# file into ITS sharedir, so they're in place for this ALTER
# EXTENSION UPDATE to use.
run: bin/test_existing update upgrade_oldest_first
- name: Run the suite against the pg_upgraded database(s) (existing mode), twin databases
# run-suite asserts the version, re-proves the dependency guard
# still blocks a non-CASCADE drop (i.e. it survived both the update
# and pg_upgrade), drops the guard, then runs the suite against the
# REAL pg_upgraded database via --use-existing (so pg_regress does
# not drop/recreate it) - a plain fresh `make test` would silently
# test a fresh install instead of the migrated objects. Once per
# database, since each holds an independent ordering's result.
run: |
bin/test_existing run-suite upgrade_oldest_first
bin/test_existing run-suite upgrade_current_first
- name: Structurally compare the pg_upgraded databases against a fresh install
# Same rationale as the test job's own update leg's use of this tool
# (see above), but here the "other side" is a REAL database a binary
# pg_upgrade + ALTER EXTENSION UPDATE (in one order or the other)
# just produced, not a scratch database this tool created itself -
# passed as EXISTING_DB so the script queries it in place instead of
# re-deriving it, discovering that database's OWN randomly generated
# schema (each of the twin databases got an independent one from
# bin/test_existing prepare-old) rather than generating a new one, so
# both sides of each comparison still land in the same schema. Once
# per database, since each holds an independent ordering's result.
# Catches a divergence class the fixed pgTAP suite above doesn't: an
# object left subtly different (body, comment, ACL) by surviving a
# real catalog migration, as opposed to only an in-place update.
run: |
bin/compare_fresh_vs_update 0.9.6 upgrade_oldest_first
bin/compare_fresh_vs_update 0.9.6 upgrade_current_first
# LOW-priority counterpart of pg-upgrade-test above - same steps/matrix,
# only the `if:`/`concurrency:` differ. See test-lowprio's comment (right
# after `test` above) for why this needs to be a separate job ID rather
# than a runtime branch inside pg-upgrade-test itself, and README.md's
# Priority lanes section for the overall scheme. Group keyed by
# matrix.old_pg + matrix.new_pg alone (no PR number) - shared across every
# PR's low-priority pg-upgrade-test pushes for that same leg combination,
# not the small fixed hashed-lane set the previous design used.
pg-upgrade-test-lowprio:
needs: [changes]
if: needs.changes.outputs.priority == 'low' && needs.changes.outputs.docs_only != 'true' && github.event.pull_request.draft != true
concurrency:
group: ci-pgupgrade-lowprio-${{ matrix.old_pg }}-${{ matrix.new_pg }}
cancel-in-progress: false
queue: max
strategy:
matrix:
old_pg: ["${{ needs.changes.outputs.floor_pg }}"]
new_pg: ["${{ needs.changes.outputs.newest_pg }}"]
name: 🔄 Binary pg_upgrade ${{ matrix.old_pg }} → ${{ matrix.new_pg }} (low-priority)
runs-on: ubuntu-latest
container: pgxn/pgxn-tools
env:
INITDB_OPTS: --data-checksums --auth trust
steps:
- name: Install PostgreSQL ${{ matrix.old_pg }} (cluster created next, with the right initdb options from the start)
run: NO_CLUSTER=1 pg-start ${{ matrix.old_pg }}
- name: Create old cluster with data checksums enabled
run: |
pg_createcluster -p 5432 ${{ matrix.old_pg }} test -- $INITDB_OPTS
pg_ctlcluster ${{ matrix.old_pg }} test start
pg_isready -t 30
- name: Check out the repo
uses: actions/checkout@v4
- name: Install count_nulls into old cluster
run: make install
- name: Prepare the old cluster (install + dependency guard), twin databases
run: |
bin/test_existing prepare-old upgrade_oldest_first 0.9.6
bin/test_existing prepare-old upgrade_current_first 0.9.6
- name: Update to current before pg_upgrade (proves pg_upgrade preserves CURRENT objects)
run: bin/test_existing update upgrade_current_first
- name: Install PostgreSQL ${{ matrix.new_pg }}
run: apt-get install -y postgresql-${{ matrix.new_pg }} postgresql-server-dev-${{ matrix.new_pg }}
- name: Install count_nulls into new cluster
run: make install PG_CONFIG=/usr/lib/postgresql/${{ matrix.new_pg }}/bin/pg_config
- name: Stop old cluster, binary pg_upgrade to PostgreSQL ${{ matrix.new_pg }}, start new cluster
run: bin/pg_binary_upgrade ${{ matrix.old_pg }} ${{ matrix.new_pg }}
- name: Update to current after pg_upgrade (proves pg_upgrade preserves OLDEST-version objects)
run: bin/test_existing update upgrade_oldest_first
- name: Run the suite against the pg_upgraded database(s) (existing mode), twin databases
run: |
bin/test_existing run-suite upgrade_oldest_first
bin/test_existing run-suite upgrade_current_first
- name: Structurally compare the pg_upgraded databases against a fresh install
run: |
bin/compare_fresh_vs_update 0.9.6 upgrade_oldest_first
bin/compare_fresh_vs_update 0.9.6 upgrade_current_first
# Covers both a fresh install AND the 0.9.6 -> current update path, both
# purely via pg_tle. pgxntool 2.3.0's fix for installcheck's ordering bug
# (Postgres-Extensions/pgxntool#83) made `installcheck` (and so `make
# test`) unconditionally depend on `install`, which writes a real
# .control file to disk - defeating the entire point of proving a pg_tle
# deployment never touches the filesystem. There's still no upstream fix
# for that (Postgres-Extensions/pgxntool#90, open) that would let
# bin/test_existing's real pgTAP suite run without it, so the update-path
# steps below use TEST_EXISTING_DEPLOY=pgtle (see bin/test_existing),
# which instead sandboxes `make test`'s install step behind a scratch
# DESTDIR - harmless here since a pg_tle-deployed database never needs
# those files.
pg-tle-test:
needs: [changes]
# Skipped outright (not just matrix-reduced like `test` above) on a
# draft PR: this is a heavy job, and a draft author doesn't need the
# pg_tle deployment path re-proven on every push while still iterating.
if: needs.changes.outputs.priority != 'low' && needs.changes.outputs.docs_only != 'true' && github.event.pull_request.draft != true
# "ci-pgtle-" prefix, matrix.pg split - see the `test` job's concurrency
# comment above. This is the HIGH-priority job ID; see
# pg-tle-test-lowprio below for its low-priority counterpart, and for
# why splitting into two job IDs is what makes `queue: max` usable on
# the low-priority side at all.
concurrency:
group: ci-pgtle-${{ github.event.pull_request.number || github.sha }}-${{ matrix.pg }}
cancel-in-progress: true
strategy:
matrix:
# Intersection of count_nulls' own supported range (10-18, see the
# `test` job above / #21) with pg_tle 1.5.2's supported range (12-18,
# see pgxntool/pgtle_versions.md): drop 10 and 11 since pg_tle doesn't
# support them.
pg: [18, 17, 16, 15, 14, 13, 12]
name: 🧩 pg_tle ${{ matrix.pg }}
runs-on: ubuntu-latest
container: pgxn/pgxn-tools
env:
# NOT named PGTLE_VERSION: on the pgxntool version this job was
# written against, that collided with pgxntool's own Makefile variable
# of the same name (`pgtle:`'s
# `$(if $(PGTLE_VERSION),--pgtle-version $(PGTLE_VERSION))`), which Make
# auto-imports from the environment - `make run-pgtle` would silently
# generate into pg_tle/1.5.2/ (the literal version) instead of the
# correct range directory pg_tle/1.5.0+/, then fail when --run looked
# for the range directory and didn't find it. Filed as
# Postgres-Extensions/pgxntool#78, fixed upstream in pgxntool 2.3.0
# (renamed to PGXNTOOL_PGTLE_VERSION - see this repo's pgxntool bump).
# Keeping our own name distinct from EITHER of those regardless -
# still the simplest way to guarantee no future collision.
PG_TLE_BUILD_VERSION: "1.5.2"
steps:
# A dedicated cluster, never shared with the other jobs in this
# workflow: pg_tle requires shared_preload_libraries and mixing
# pg_tle/non-pg_tle extension installs on one cluster can misbehave.
- name: Start PostgreSQL ${{ matrix.pg }}
run: pg-start ${{ matrix.pg }}
- name: Check out the repo
uses: actions/checkout@v7
- name: Install rsync
run: apt-get install -y rsync
- name: Snapshot filesystem extension control files (pre-pgtap baseline)
# Whatever ships on disk by default (e.g. contrib), before installing
# pgTAP. Lets the next step prove pgTAP is the ONLY thing `make pgtap`
# puts on disk, instead of trusting that and folding whatever it did
# into the pre-pg_tle baseline unexamined -- a future pgxntool change
# to `make pgtap` that also happened to touch count_nulls' own files
# would otherwise be silently absorbed into that baseline and never
# get flagged by any later check.
run: bin/assert_fs_clean snapshot ${{ matrix.pg }} /tmp/pre_pgtap_baseline.txt
- name: Install pgtap (test harness dependency)
# pgTAP is a filesystem-installed dependency of the TEST HARNESS, not
# part of what this job proves is pg_tle-only -- it's not being
# deployed via pg_tle here, and never will be.
run: make pgtap
- name: Verify make pgtap installed exactly pgtap.control, nothing else
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/pre_pgtap_baseline.txt pgtap.control
- name: Snapshot filesystem extension control files (pre-pg_tle baseline)
# Everything on disk now that pgtap is confirmed the only addition
# (contrib, pgtap). bin/assert_fs_clean's later checks diff against
# this, so they flag ANY extension that lands on disk instead of
# being registered via pg_tle -- not just count_nulls -- without
# hardcoding contrib/pgtap names.
run: bin/assert_fs_clean snapshot ${{ matrix.pg }} /tmp/control_baseline.txt
- name: Build and install pg_tle ${{ env.PG_TLE_BUILD_VERSION }}
# flex/bison/libkrb5-dev aren't in the pgxn-tools image; pg_tle's build
# needs them (guc-file.l, and clientauth.c includes gssapi.h).
run: |
apt-get install -y flex bison libkrb5-dev
git clone --branch v${{ env.PG_TLE_BUILD_VERSION }} --depth 1 https://github.com/aws/pg_tle.git /tmp/pg_tle
make -C /tmp/pg_tle install
- name: Enable pg_tle and restart PostgreSQL ${{ matrix.pg }}
run: |
echo "shared_preload_libraries = 'pg_tle'" >> /etc/postgresql/${{ matrix.pg }}/test/postgresql.conf
pg_ctlcluster ${{ matrix.pg }} test restart
pg_isready -t 30
- name: Register pg_tle + count_nulls against template1
# template1, not the ambient default db: pg_tle's registration catalog
# is per-database, and `createdb` only inherits it because it copies
# template1 by default. Every count_nulls database used below (the
# smoke-test db) is created AFTER this step specifically so it
# inherits both registrations.
run: |
psql -d template1 -c "CREATE EXTENSION pg_tle"
PGDATABASE=template1 make run-pgtle
- name: Verify no stray extension control files landed on the filesystem
# CRITICAL, and intentionally redundant with the count_nulls-specific
# check in the next step: a filesystem control file silently wins
# over a pg_tle-registered extension of the same name, which would
# make this whole job a false pass without ever raising an error. Run
# again after every step below that could plausibly write extension
# files to disk -- never trust a single check to catch everything.
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
- name: Install count_nulls purely via pg_tle (fresh install, no filesystem trace)
# count_nulls is never `make install`ed in this job, so a successful
# CREATE EXTENSION here can only be resolving through pg_tle's
# registration, not a control file on disk. Checked explicitly here
# too (not just via the comprehensive check above) as a guard
# specifically for the extension under test, in case that check's
# exclude-list logic has a bug.
run: |
test ! -e /usr/share/postgresql/${{ matrix.pg }}/extension/count_nulls.control
createdb count_nulls_smoke
psql -d count_nulls_smoke -c "CREATE EXTENSION count_nulls"
- name: Verify count_nulls works when deployed via pg_tle
run: bin/verify_pgtle_smoke count_nulls_smoke
- name: Verify no stray extension control files after the fresh-install smoke test
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
- name: Install count_nulls at 0.9.6, purely via pg_tle (update-path prep)
# A SECOND, separate scratch database, created after the template1
# registration above so it inherits both registrations too (same
# reasoning as count_nulls_smoke). prepare-old creates the database,
# CREATE EXTENSIONs at 0.9.6 (pure SQL - resolves through pg_tle's
# catalog, no `make install` call, which would defeat the whole
# point), then plants + proves the dependency guard so a stray
# CASCADE drop anywhere below can't silently turn the eventual
# existing-mode run into a fresh install instead.
run: |
test ! -e /usr/share/postgresql/${{ matrix.pg }}/extension/count_nulls.control
bin/test_existing prepare-old count_nulls_pgtle_update 0.9.6
- name: Verify no stray extension control files after installing 0.9.6 via pg_tle
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
- name: Update 0.9.6 -> current, purely via pg_tle
# Pure SQL (ALTER EXTENSION ... UPDATE), no filesystem write either.
run: bin/test_existing update count_nulls_pgtle_update
- name: Verify no stray extension control files after the pg_tle update
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
- name: Run the real pgTAP suite against the pg_tle-updated database (existing mode)
# run-suite re-proves the dependency guard, drops it, then runs the
# FULL suite via --use-existing against the real pg_tle-deployed +
# updated database - the same suite/expected-output as every other
# leg (see test/README.md). TEST_EXISTING_DEPLOY=pgtle makes
# run-suite sandbox `make test`'s otherwise-unavoidable `install`
# step behind a scratch DESTDIR instead of writing to the real
# extension directory (see bin/test_existing's TEST_EXISTING_DEPLOY
# comment), and makes test/install/load.sql's existing-mode
# assertion cross-check pgtle.available_extensions() instead of
# pg_available_extensions (which never sees pg_tle registrations -
# see the Makefile's TEST_EXISTING_DEPLOY comment).
run: TEST_EXISTING_DEPLOY=pgtle bin/test_existing run-suite count_nulls_pgtle_update
- name: Verify no stray extension control files after the pgTAP suite
# THE step that actually proves the DESTDIR sandboxing worked: the
# real extension directory must still be clean after `make test`
# ran (with its otherwise-unavoidable `install` step) sandboxed
# behind a scratch DESTDIR.
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
# LOW-priority counterpart of pg-tle-test above - same steps/matrix, only
# the `if:`/`concurrency:` differ. See test-lowprio's comment (right after
# `test`, near the top of this file) for why this needs to be a separate
# job ID rather than a runtime branch inside pg-tle-test itself, and
# README.md's Priority lanes section for the overall scheme. Group keyed
# by matrix.pg alone (no PR number) - shared across every PR's low-priority
# pg-tle-test pushes for that same PG major, not the small fixed
# hashed-lane set the previous design used.
pg-tle-test-lowprio:
needs: [changes]
if: needs.changes.outputs.priority == 'low' && needs.changes.outputs.docs_only != 'true' && github.event.pull_request.draft != true
concurrency:
group: ci-pgtle-lowprio-${{ matrix.pg }}
cancel-in-progress: false
queue: max
strategy:
matrix:
pg: [18, 17, 16, 15, 14, 13, 12]
name: 🧩 pg_tle ${{ matrix.pg }} (low-priority)
runs-on: ubuntu-latest
container: pgxn/pgxn-tools
env:
PG_TLE_BUILD_VERSION: "1.5.2"
steps:
- name: Start PostgreSQL ${{ matrix.pg }}
run: pg-start ${{ matrix.pg }}
- name: Check out the repo
uses: actions/checkout@v4
- name: Install rsync
run: apt-get install -y rsync
- name: Snapshot filesystem extension control files (pre-pgtap baseline)
run: bin/assert_fs_clean snapshot ${{ matrix.pg }} /tmp/pre_pgtap_baseline.txt
- name: Install pgtap (test harness dependency)
run: make pgtap
- name: Verify make pgtap installed exactly pgtap.control, nothing else
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/pre_pgtap_baseline.txt pgtap.control
- name: Snapshot filesystem extension control files (pre-pg_tle baseline)
run: bin/assert_fs_clean snapshot ${{ matrix.pg }} /tmp/control_baseline.txt
- name: Build and install pg_tle ${{ env.PG_TLE_BUILD_VERSION }}
run: |
apt-get install -y flex bison libkrb5-dev
git clone --branch v${{ env.PG_TLE_BUILD_VERSION }} --depth 1 https://github.com/aws/pg_tle.git /tmp/pg_tle
make -C /tmp/pg_tle install
- name: Enable pg_tle and restart PostgreSQL ${{ matrix.pg }}
run: |
echo "shared_preload_libraries = 'pg_tle'" >> /etc/postgresql/${{ matrix.pg }}/test/postgresql.conf
pg_ctlcluster ${{ matrix.pg }} test restart
pg_isready -t 30
- name: Register pg_tle + count_nulls against template1
run: |
psql -d template1 -c "CREATE EXTENSION pg_tle"
PGDATABASE=template1 make run-pgtle
- name: Verify no stray extension control files landed on the filesystem
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
- name: Install count_nulls purely via pg_tle (fresh install, no filesystem trace)
run: |
test ! -e /usr/share/postgresql/${{ matrix.pg }}/extension/count_nulls.control
createdb count_nulls_smoke
psql -d count_nulls_smoke -c "CREATE EXTENSION count_nulls"
- name: Verify count_nulls works when deployed via pg_tle
run: bin/verify_pgtle_smoke count_nulls_smoke
- name: Verify no stray extension control files after the fresh-install smoke test
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
- name: Install count_nulls at 0.9.6, purely via pg_tle (update-path prep)
run: |
test ! -e /usr/share/postgresql/${{ matrix.pg }}/extension/count_nulls.control
bin/test_existing prepare-old count_nulls_pgtle_update 0.9.6
- name: Verify no stray extension control files after installing 0.9.6 via pg_tle
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
- name: Update 0.9.6 -> current, purely via pg_tle
run: bin/test_existing update count_nulls_pgtle_update
- name: Verify no stray extension control files after the pg_tle update
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
- name: Run the real pgTAP suite against the pg_tle-updated database (existing mode)
run: TEST_EXISTING_DEPLOY=pgtle bin/test_existing run-suite count_nulls_pgtle_update
- name: Verify no stray extension control files after the pgTAP suite
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt pg_tle.control
# A single stable check name for use as a required status check in branch
# protection rules. Matrix jobs produce check names like
# "🐘 PostgreSQL 14" (one per supported major) which would all need to be
# listed individually and updated whenever the matrix changes. This job
# passes if all others passed or were skipped (e.g. the heavy jobs gated
# off by the `changes` job on a docs-only push), and fails if any failed
# or were cancelled.
all-checks-passed:
needs: [changes, lint, test, test-lowprio, pg-upgrade-test, pg-upgrade-test-lowprio, pg-tle-test, pg-tle-test-lowprio]
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Verify all jobs are listed in needs
# Ensures this job won't silently ignore a newly-added job that was
# omitted from the needs list above.
run: |
DEFINED=$(python3 -c "
import yaml
with open('.github/workflows/ci.yml') as f:
w = yaml.safe_load(f)
print('\n'.join(sorted(j for j in w['jobs'] if j != 'all-checks-passed')))
")
NEEDED=$(echo '${{ toJson(needs) }}' | python3 -c "
import json, sys
print('\n'.join(sorted(json.load(sys.stdin))))
")
if [ "$DEFINED" != "$NEEDED" ]; then
echo "Some jobs are missing from all-checks-passed needs:"
diff <(echo "$DEFINED") <(echo "$NEEDED")
exit 1
fi
- name: Check all jobs passed or were skipped
run: |
if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
echo "One or more jobs failed or were cancelled"
exit 1
fi
# vi: expandtab ts=2 sw=2