Skip to content

ci: the nightly's coverage job installs pg_partman, hll and tdigest (#1382) - #1385

Merged
jdatcmd merged 2 commits into
mainfrom
ci/1382-coverage-extensions
Oct 14, 2026
Merged

jdatcmd merged 2 commits into
mainfrom
ci/1382-coverage-extensions

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1382. For @OffgridwithJD to review.

What was wrong

Last night's run went red on one job, the coverage report (PG 18). Two suites failed there:

FAIL  tdigest is not installed for this major
FAIL  pg_partman is not installed for this major

A missing dependency is a FAIL, not a skip (#1215). #1291 and #1296 added postgresql-N-partman, -hll and -tdigest to every job that runs the suites except this one. The other jobs in the run passed.

The change

  • nightly.yml, coverage job: installs the three packages, and hashes their versions into the apt cache key, as the suites job does.
  • New harness selftest part 640-every-suite-job-installs-the-extensions.sh: every job that runs the suites must install every postgresql-<major>-<extension> package that any such job installs. A job runs the suites if it calls run_all_versions.sh or run_coverage.sh, or is a pytest cluster job. Comment lines are skipped, because two jobs that run no suite mention run_all_versions.sh in comments.

Evidence

  • Red against main's nightly.yml (PG15 and PG18):
    FAIL every job that runs the suites installs every extension package (#1382): got [nightly.yml:coverage:hll nightly.yml:coverage:partman nightly.yml:coverage:tdigest] want []
  • Green: harness_selftest 1243/1243 on each of PG15–19.
  • The part's own checks can fail:
    • A fixture workflow, inside the part, proves the detector names the job and the package it lacks.
    • With _ej_missing made to report nothing: FAIL the detector names a suite job that lacks a package the others install: got [] want [w.yml:b:hll]
    • With the job parser matching no job key, that FAIL again, plus FAIL at least five suite-running jobs were parsed across both workflows: got [no (0 parsed)] want [yes]
  • Counts:
    • Ledger: four rows, three dated. 2464 rows / 1894 never on d7cd8f3.
    • The gate against origin/main: rc=0. Census 1894 agrees; ceiling 244 → 244.
    • Database-free guard set: 429 passed.

Not verified here

The fix itself runs only in the nightly. After merge I'll dispatch the nightly workflow and post the coverage job's result on #1382. The issue closes itself on the next green run.

🤖 Generated with Claude Code

https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N

@OffgridwithJD OffgridwithJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix itself is right: the coverage job installs the three packages, and their versions go into its cache key as the suites job's do. Deriving the list instead of trusting it is the right shape. I checked the detector on main's nightly.yml (copied to a file): nightly_main.yml:coverage:hll …:partman …:tdigest, the three the issue names. Reviewed at 560f520. Two things to change:

  1. The final arm expects the empty string, so it passes on any capture that comes back empty. I sourced _ej_scan and _ej_missing from the part and fed them inputs they can't read:

    _ej_missing /nonexistent/ci.yml /nonexistent/nightly.yml  -> []
    _ej_missing / /                                          -> []
    

    Both are identical to "no job lacks a package". In place, the premise and the five-jobs arm catch a missing file or a parser that finds no jobs. Nothing catches _ej_missing itself coming back empty: its all pipeline, or the while read, broken while _ej_scan still works. It's the shape you flagged in my debug_hook_privilege owner call on #1381, which now expects ok. Something like "$(m="$(_ej_missing …)"; echo "${m:-none}")" "none" closes it, and the pytest corpus refuses an empty expected value anyway.

  2. The part needs its pytest twin. Selftest parts are twinned like the suites: 600/610 → test_capability_decline.py (#1286), 630 → test_build_needs_pg_config.py. A database-free twin that reads both workflow files independently (YAML, or the same line rules) and asserts the same three things would do. Remember the TESTS.md row, guard_tests and the NO_CLUSTER list.

Minor, not blocking: "runs pytest against a cluster" is detected as /pytest -q/ in a job whose key contains cluster. A cluster job renamed without that word drops out of the population quietly. The five-jobs floor would only notice if that took the count below five. Naming the jobs the scan is expected to find, as a set, would make a rename red.

🤖 Generated with Claude Code

@jdatcmd

jdatcmd commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, both points are done, plus the minor one. The new head is 3e9d9f1, one commit on df6a02f.

1. An empty answer can no longer pass. You were right that ${m:-none} alone wouldn't close it: a broken _ej_missing still returns empty, which becomes "none". So the detector now names what it couldn't compare: file:unreadable, or file:no-suite-job when it parses no suite job. The final arm expects none. Two new arms prove it on a missing file and on a workflow with no suite job. On a broken union (all empty), the fixture arm still goes red (got [] want [w.yml:b:hll]), because it calls the same function.

2. The pytest twin: test_every_suite_job_installs_the_extensions.py. It's database-free, with its own Python line reader, and uses the shell's check names. It's in NO_CLUSTER, guard_tests 429 → 433 (433 passed), and TESTS.md §106.

Minor: "at least five" became "the suite-running jobs are exactly the five this part knows". A cluster job renamed without cluster in its key is now red.

Evidence:

shell part 640 (harness_selftest, PG18) twin
clean, PG15–19 1245/1245 each 4 passed
main's nightly.yml job arm: got [nightly.yml:coverage:hll …:partman …:tdigest] want [none] same arm, by name
scan stops counting cluster pytest jobs 1244 + 1: the job-set arm only —
unreadable-file guard removed 1244 + 1: got [absent.yml:no-suite-job] want [absent.yml:unreadable] same, by name
no-suite-job guard removed 1244 + 1: got [] want [nosuite.yml:no-suite-job] same, by name
union emptied fixture arm red same, by name

I first dated the job-set arm by renaming pytest-cluster. That also reddens parts 240 and 450, so the ledger refused it as collateral. A narrower mutation dates it instead.

Ledger: part 640 has six checks: five dated, the file premise never. 2466 + 6 = 2472; never 1895 → 1896; dated 576. The gate is clean against df6a02f, and the ceiling stays 244.

After it merges, I'll dispatch the nightly and post the coverage job's result on #1382.

🤖 Generated with Claude Code

@OffgridwithJD OffgridwithJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nearly there. I verified 3e9d9f1: the guard set is 433/433, and the twin, pointed at a copy of main's nightly.yml, names exactly coverage:hll, :partman and :tdigest. The unreadable / no-suite-job sentinels close the empty-answer hole, and the job set as a literal closes the rename. One CI red to fix:

pytest (cluster tests) fails on both majors, 1 failed and 540 passed:

FAILED test_harness_deps.py::test_the_harness_independence_inventory_is_exactly_what_the_corpus_does
  the files that reach into the shell harness are exactly the declared ones:
  got [..., 'test_every_suite_job_installs_the_extensions.py', ...] want [...]

The twin's RUNS = re.compile(r"run_all_versions\.sh|run_coverage\.sh") is a string literal in code, and _SHELL_NAMES matches run_all_versions\.sh in it. Docstrings are exempt; literals aren't. It doesn't read or run the shell harness, so I'd declare it next to test_suite_exit_trap.py (test_harness_deps.py, around line 1280) with its reason. Something like: "names the matrix and coverage runners' file names as data, to recognise a workflow step that runs the suites; it never reads or executes them". Otherwise build the pattern so it doesn't spell the name, but declaring it is the honest version.

It didn't show in your guard run, or mine, because that test runs in the cluster job, not in the database-free set. I'll re-approve once CI is green on the fix.

🤖 Generated with Claude Code

jdatcmd and others added 2 commits October 14, 2026 18:30
…1382)

The coverage job ran partman_columnar, approx_aggregates and the other
extension suites without their PGDG packages, so the nightly went red.
It now installs postgresql-18-partman, -hll and -tdigest, and their
versions join its cache key, as in the suites job.

Selftest part 640 derives the list instead of trusting it: every job that
runs the suites must install every extension package any such job
installs. Its detector names a workflow it cannot read or finds no suite
job in, so an empty answer can only mean nothing is missing, and the
suite-running jobs are named as a set. The part has a database-free
pytest twin with its own Python reader:
test_every_suite_job_installs_the_extensions.py (NO_CLUSTER, guard_tests
429 -> 433, TESTS.md section 106).

Fixes #1382.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
…rs as data (#1385 review)

Its RUNS pattern spells run_all_versions.sh and run_coverage.sh to
recognise a workflow step that runs the suites, so the harness-
independence inventory counted it as reaching into the shell harness.
It reads only the two workflow files and never the runners.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VvWuX1zUwk6Y513waRd4N
@jdatcmd
jdatcmd force-pushed the ci/1382-coverage-extensions branch from 3e9d9f1 to 577c404 Compare October 14, 2026 18:31
@jdatcmd

jdatcmd commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, fixed as you suggested. The new head is 577c404, on 7065fbe (after #1356).

  • The declaration: test_harness_deps.py now declares test_every_suite_job_installs_the_extensions.py next to test_suite_exit_trap.py, with its reason. It names the runners' file names as data and reads only the two workflow files.
  • Red, then green: -k independence_inventory fails without the declaration, with the twin listed in got, as on CI. With it, the test passes.
  • Restated on main: 2466 / 1894 / 572 gives 2472 / 1895 / 577. The gate is clean, the ceiling stays 244, and the guard set gives 433 passed.

test_a_cluster_test_still_needs_the_driver fails here on main too, because this run has no cluster, so the cluster job is the real check for the rest of that file.

🤖 Generated with Claude Code

@OffgridwithJD OffgridwithJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at 577c404, with full CI green (15/15), including both pytest (cluster tests) jobs that were red at 3e9d9f1.

  • The declaration is as suggested, and its reason is accurate: the twin names the runners as data and reads only the two workflow files.
  • Everything else I verified at 3e9d9f1 still holds:
    • the guard set is 433/433;
    • the twin names exactly coverage:hll, :partman and :tdigest against main's old nightly.yml;
    • the unreadable / no-suite-job sentinels close the empty-answer hole;
    • the job set is pinned by name.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nightly deep gate is red

2 participants