Skip to content

fix(scripts): share one Python-interpreter probe across the three scripts (closes #273) - #274

Merged
cofade merged 1 commit into
schutera:mainfrom
dchaudhari7177:fix/273-shared-python-probe
Aug 28, 2026
Merged

fix(scripts): share one Python-interpreter probe across the three scripts (closes #273)#274
cofade merged 1 commit into
schutera:mainfrom
dchaudhari7177:fix/273-shared-python-probe

Conversation

@dchaudhari7177

Copy link
Copy Markdown
Contributor

Closes #273

One helper, scripts/lib/python.sh, sourced by all three scripts. Shape as suggested in the issue, with the array so py -3 splits without # shellcheck disable=SC2086 at every call site — that directive is now gone from check-duckdb-bind-claims.sh rather than multiplied.

resolve_python() returns rather than exiting. The callers genuinely differ on whether a missing interpreter is fatal (build.sh) or a skip (check-duckdb-bind-claims.sh), and that should stay their decision, not the helper's.

On the judgement call: py -3 goes in, for all three at once. That is the single-place-to-decide property the issue is actually about.

The shellcheck job had to change, and I verified that it had to

This is the part I'd have got wrong by assuming. Installed shellcheck 0.11.0 and ran the exact CI command line:

$ shellcheck -S info scripts/*.sh ESP32-CAM/build.sh      # current CI
exit 1     # SC1091 on every new `source` line — info level, so -S info catches it

So adding the helper would have turned the job red. Two changes were needed:

  • -x so shellcheck follows sourced files and analyses each in its caller's context;
  • scripts/lib/*.sh in the glob, because scripts/*.sh does not match a subdirectory.

The second matters beyond the exit code: an unlinted helper sourced by three linted scripts would have re-created this issue's gap in a new shape. It also surfaced two things worth fixing rather than suppressing — SC2148 (the helper has no shebang, correctly, since it is sourced → # shellcheck shell=bash) and SC1091 path resolution, which resolves from the CWD unless told → # shellcheck source-path=SCRIPTDIR.

$ shellcheck -x -S info scripts/*.sh scripts/lib/*.sh ESP32-CAM/build.sh
exit 0

.gitignore needed a third carve-out

lib/ is ignored as the Python build-output convention, and it silently swallowed scripts/lib/python.sh:

$ git check-ignore -v scripts/lib/python.sh
.gitignore:17:lib/	scripts/lib/python.sh

This is the third time — the comment block already records ESP32-CAM/lib/ and the round-2 P0 where homepage/src/lib/location.ts was on the author's disk and invisible to git. Same failure here would have been worse: three tracked scripts sourcing a file that exists on no machine but mine, so every one of them breaks for everyone else. Added !scripts/lib/ with a comment in the established style.

The self-contradicting comment

Removed. Its pre-existing line claimed Windows Git Bash has "only the py launcher, never python3" and asserted parity with build.sh; the paragraph #272 added beneath it explains that a stock install does put a python3 stub on PATH. Both cannot be true, and the parity claim was false when written. The rules now live in one place instead of being restated in three.

(#271 no longer deletes that line — I narrowed it to docs-only after #272 landed the script fix — so it stayed in scope here.)

build.sh keeps its short-circuit

The esptool.exe-first check stays ahead of the probe; only the fallback path changed. Its error message now lists what was actually tried, from python_candidates_tried(), rather than a hardcoded python3, python.

Verification

shellcheck -x -S info (new CI cmd)   exit 0
bash -n on all four scripts          clean
bash scripts/check-duckdb-bind-claims.sh   OK — dev LAN-reachable, prod loopback-only
bash scripts/check-python-twins.sh         OK (5 pairs)
bash scripts/check-doc-citations.sh        7 OK, 0 problems
bash scripts/ruff.sh --version             ruff 0.15.20

The last two are the ones that matter: the duckdb gate and ruff.sh both resolve their interpreter through the new helper on a box where python3 and python do not resolve at all and only py does — which is the machine the issue is about. resolve_python picks py -3 there and reports 3.12.4.

Docs

  • docs/troubleshooting.md — the symptom, in the Windows-PATH cluster next to ruff: command not found, with the exact command -v transcript and the snippet to source for a fourth script
  • scripts/README.md — a row for the helper, flagged as sourced-not-executed
  • docs/11-risks-and-technical-debt/ — an entry on the two lessons: count near-duplicates of a platform workaround rather than reviewing them one at a time, and when a defect is unreachable, check what makes it unreachable — here the mask lived in a different concern from the defect and could be removed by someone with no reason to look at the probe

Per CLAUDE.md, the issue reference is in the title only, not in the body or commit body.

…ipts

Three shell scripts hand-rolled three probes with three different candidate
lists and two validation techniques, and a fourth opted out of Python entirely
because of that fragility. A Windows contributor's toolchain could satisfy two
of them and fail the third, with nothing shared to fix.

The concrete gap was ESP32-CAM/build.sh, which tried only python3 and python.
On a stock Windows 11 + Git Bash box neither resolves and only the `py`
launcher does, so its fallback path would abort on a machine with a working
Python 3.12. It has never fired because build.sh prefers esptool.exe and skips
the interpreter whenever it finds one -- but that mask lives in a different
concern from the defect and could be removed by someone with no reason to look
at the probe.

scripts/lib/python.sh now holds one candidate list and one functional check.
resolve_python() returns rather than exiting, because the callers genuinely
differ on whether a missing interpreter is fatal (build.sh) or a skip
(check-duckdb-bind-claims.sh), and that stays their decision.

PYTHON is an array. `py -3` is two words, and a string would need
`# shellcheck disable=SC2086` at every call site to stay splittable -- which
the -S info job would otherwise require, so the array is what keeps the
suppressions out. It also removes the one such directive that already existed.

`py -3` goes first, for all three at once, which is the single-place-to-decide
property this is for. The launcher is the one interpreter a python.org install
on Windows always provides and it is never an alias stub.

The shellcheck CI job gains -x and scripts/lib/*.sh. Without -x every new
`source` line reports SC1091 at info level and the job goes red; without the
glob the helper is unlinted, since scripts/*.sh does not match a subdirectory.
Verified against shellcheck 0.11.0 with the exact new command line: clean at
-S info, and the old command line goes red first, so the CI edit is
load-bearing rather than tidying.

.gitignore needed a third carve-out. `lib/` is ignored as the Python
build-output convention, and it swallowed scripts/lib/python.sh exactly as it
once swallowed homepage/src/lib/location.ts -- an ignored helper that three
tracked scripts source would have failed on every machine but mine.

Also removes the self-contradicting comment block in
check-duckdb-bind-claims.sh: its pre-existing line claimed Windows Git Bash has
"only the py launcher, never python3" and asserted parity with build.sh, while
the paragraph schutera#272 added beneath it explains that a stock install does put a
python3 stub on PATH. Both could not be true, and the parity claim was false
when written -- build.sh has validated by execution since schutera#99 while that loop
trusted `command -v` until schutera#270.

Docs: a troubleshooting entry for the symptom, with the snippet to source for a
fourth script; a scripts/README row; and a chapter 11 entry on the two lessons
-- count near-duplicates of a platform workaround rather than reviewing them
one at a time, and when a defect is unreachable, check what makes it
unreachable.
@cofade
cofade merged commit 14cc9ba into schutera:main Aug 28, 2026
23 checks passed
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.

Three scripts hand-roll three different Python-interpreter probes; ESP32-CAM/build.sh never tries py

2 participants