Skip to content

103 test scripts are not executable, so 30 documented invocations fail with Permission denied #852

Description

@OffgridwithJD

docs/testing.md tells a reader to run a suite by naming it. Thirty of those invocations fail, because 103 of the harness scripts do not carry the executable bit. CI never meets this, so nothing has reported it.

What happens

$ test/run_all_versions.sh /usr/local/pg18a/bin/pg_config
bash: test/run_all_versions.sh: Permission denied

$ test/parquet_export.sh /usr/local/pg18a/bin/pg_config
bash: test/parquet_export.sh: Permission denied

Both are the exact command docs/testing.md gives (lines 156 and 27). test/smoke.sh, which is mode 100755, runs.

Who this affects, and who it does not

A person following the documentation, on a fresh clone. That is the whole of it.

CI and the version matrix are unaffected, and it is worth being explicit about why, because it is the reason this has been invisible:

  • .github/workflows/ci.yml:485 and nightly.yml:188 both invoke it as bash test/run_all_versions.sh ....
  • run_all_versions.sh runs each suite as bash "$builddir/test/${s}.sh" "$pgc" (lines 712 and 749), so a suite's own mode never matters inside the matrix.

So every green run in the project's history is honest. The gap is between the documentation and a shell.

The measurement

At 0e4884c1, counted from the ref rather than a working tree:

100755 100644
test/*.sh 153 95
test/*.py 4 8

30 documented invocations name one of those 103 files: 29 in docs/testing.md (lines 13, 20-24, 26-28, 33, 51-55, 57, 58, 60-65, 156, 188, 189, 217, 272, 412) and one in docs/limitations.md:79. Two further mentions — docs/administration.md:422 and CONTEXT.md:166 — are prose references, not commands, and are fine.

Cause: two different situations, not one

I searched the whole history of test/ for mode transitions. There is exactly one 100755 -> 100644 change:

56ae5f8eb  2026-08-16  perf: iterate needed columns in the batch fold, not all natts
   test/run_all_versions.sh   100755 -> 100644

That commit is mine. It touched four files, added one line to SUITES, and took the bit off run_all_versions.sh on the way past — no part of the change had anything to do with it. run_all_versions.sh was added executable on 2026-07-19 in 1d1b31b and stayed that way for four weeks until I broke it. test/native_batch_fold_projection.sh, added in that same commit, was created 100644.

The other 102 were born 100644 and never regressed. Spot-checked at their own add commits: parquet_export.sh (b6a3b30), native_agg.sh (85b6f29), hardening.sh (d2cb212), native_batch_fold_projection.sh (56ae5f8) — all 100644 from the first commit that introduced them.

So this is not one accident. It is one regression plus a long-standing habit of adding suites without the bit, and nothing anywhere notices either.

Why nothing caught it

test/harness_selftest.sh has no mode check — no chmod, no 100755, no -x test anywhere in it. The suite that exists to pin harness invariants does not pin this one, which is why a mode could drift out of a perf commit unremarked and why 102 files could arrive without it.

Suggested fix, in this order

  1. chmod +x the 103 files and commit the mode change alone, so the diff is reviewable as exactly that.
  2. Add a harness_selftest check: every test/*.sh and test/*.py that docs/testing.md names must be executable. Run it over the tree before committing step 1 and it flags 103, which is the false-positive budget measured rather than assumed; after step 1 it flags none.
  3. Decide the rule and write it in CONTEXT.md beside the "register every suite in SUITES" line, since that is where someone adding a suite is already reading.

An alternative to 1 and 2 is to change the 30 documented invocations to bash test/..., matching what CI does. I would rather not: docs/testing.md presents these as commands to type, a reader will type them, and chmod +x is the smaller change to make the documentation true.

Happy to send the PR for any or all three; step 2 is the part that stops it recurring, and it is the part I would not want left out.


Found while gating #851, whose two new files (test/parquet_export_stats.sh, test/parquet_stats.py) are both correctly 100755. My own gate script died on run_all_versions.sh before it ran a single suite, which is how I noticed.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions