Skip to content

ci: gate committed scripts on shellcheck and a Python 3.9 floor - #36

Open
avrabe wants to merge 2 commits into
mainfrom
ci/py39-shellcheck-floor
Open

avrabe wants to merge 2 commits into
mainfrom
ci/py39-shellcheck-floor

Conversation

@avrabe

@avrabe avrabe commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Why

Committed scripts get run on macOS, where /usr/bin/python3 is 3.9. CI runs newer Python, so nothing caught a script that only works there.

What

Python

  • tools/check-manifest.py, tools/scan-pins.py, tools/test_check_manifest.py and tools/test_scan_pins.py: tomllib is now guarded. It falls back to tomli and otherwise exits with needs Python 3.11+ (tomllib) or the tomli package, instead of a traceback.
  • tomli is optional, not a new dependency. The repo declares no Python version.

Shell

  • tools/next-layer-id.sh: drop STATUS_TYPE (SC2034, the only -S warning finding). It was assigned and never read; the baseline layer is picked by its eu.pulseengine.varve.role annotation.

CI: a new script-portability job

  • shellcheck 0.11.0, pinned by sha256 and called by path, at -S warning over tracked *.sh.
  • tools/check-py-floor.py runs under actions/setup-python 3.9, by interpreter path, after its own --self-test (26 fixtures, red and green).
  • The checker compiles each file with the real 3.9 parser and then catches:
    • unguarded tomllib;
    • evaluated X | Y annotations without __future__;
    • type unions used as values;
    • zip(strict=), datetime.UTC (including aliased imports), and 3.10+/3.11 typing/enum names.
  • vermin was tried first and rejected: it ignores from __future__ import annotations and flags a guarded tomllib import, so it reports fixed files as broken.

Negative controls, run locally with the job's commands. Each injected violation turned the job red, and removing it by re-editing the file turned it green again:

  • SC2034 in a tracked .sh
  • an unguarded import tomllib
  • an int | None annotation in a file with no __future__ import
  • The job is in check.yml. main has no branch protection, so it is not a required check.

Exit codes

"3.9+deps" is a 3.9 venv with tomli.

command 3.9 before 3.9 after 3.9+deps after 3.11 after
python3 -m unittest discover -s tools -p 'test_*.py' (as CI runs it) 1 (2 import errors) 1 (clean exit at import) 0 0
tools/check-manifest.py 1 (ModuleNotFoundError) 1 (clean "needs 3.11+ or tomli") 0 0
tools/scan-pins.py (no --apply) 1 (ModuleNotFoundError) 1 (clean "needs 3.11+ or tomli") 0 0

Before the fix, 3.9+deps also gave 1, because nothing tried tomli. actionlint 1.7.12: clean on check.yml.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G5qpB7zvxLvdrvn5YRyj4R

avrabe and others added 2 commits October 29, 2026 11:53
macOS runs committed scripts with /usr/bin/python3 3.9; CI runs newer
Python. check-manifest.py, scan-pins.py and their two test modules died
on import there (`No module named 'tomllib'`). tomllib is now guarded:
it falls back to tomli and otherwise exits with a one-line "needs
Python 3.11+ (tomllib) or the tomli package".

next-layer-id.sh: drop STATUS_TYPE, assigned and never read; the
baseline layer is chosen by its role annotation (SC2034).

New script-portability job in check.yml: shellcheck 0.11.0 pinned by
sha256 and called by path, and tools/check-py-floor.py under
setup-python 3.9 after its self-test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5qpB7zvxLvdrvn5YRyj4R
ubuntu-latest is now Ubuntu 26.04, and setup-python has no 3.9 build
for it, so the job failed before checking anything.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5qpB7zvxLvdrvn5YRyj4R
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.

1 participant