web lint hook: run no check code a push reaches through a symlink or an earlier push
Summary
The pre-push web lint must run only check code that the push did not change (docs/contributor-verification.md, trust boundary). In some cases a push could get pushed code run by the hook. This PR closes them in scripts/web_lint_prepush.py:
-
Which changes count. The hook now measures changes to check code from where the pushed commit meets the reviewed branches (
<remote>/feat-cmux-next, else the remote's HEAD). Before, it measured them from the branch's last pushed head. Now a change to check code that an earlier push of the branch made also keeps later pushes of that branch out. Deletes and type changes count, and so does a change to a parent folder of check code. -
Symlinks. A pushed commit may hold no symlink at check code, inside it (outside the package source folders), or at a parent folder of it. The hook reads this from git before it plans anything.
-
Resolution before run. Before anything runs, every executed folder, and every file a package's check names, must resolve (symlinks followed) to that same path in the tree being linted. This also covers untracked files when the hook lints in place.
-
Reviewed remotes only. The base comes only from a reviewed remote: one whose URL is manaflow-ai/cmux, or one that the clone's own config names (
cmux.reviewedRemote). It never comes from the remote being pushed to. With no reviewed remote, the hook fails closed. -
Imported source files are check code. A file in
src/that check code loads (the check, fix and install scripts, the package's*.config.*files and everything they import) counts as check code. If the hook cannot tell what check code loads, it refuses the package. -
Paths are compared as a Mac file system sees them, ignoring case and Unicode normalization.
-
Files in folders above a package are check code. Tools search upward from a package for their config, so every file directly in a folder above a package or its local build, and any
node_modulesthere, is check code. In the last 410 web pushes, 2 changed such a file. A symlink at check code is allowed only when it points at another file that is itself check code (for exampleAGENTS.mdtoCLAUDE.md). -
node_modulesis check code. A path with anode_modulesfolder in it is check code for every package. A commit that tracks anode_modulesin or above a package folder is never linted with that package. The real tree tracks none. -
More config files count as check code. In
web/, every ESLint and Babel config file name, plus.npmrcandbunfig.toml. In the TypeScript bindings,.npmrcandbunfig.toml.
A refused package is named in the push output. CI's sandboxed web lint job checks it, as before. Nothing changes for a normal push. Planning a push takes up to about 1.5 s on this tree, and about 2 s for a Swift-only push, which runs the full trust check for webviews before its four Swift-reading source checks (#34046).
No CI job, step or gate is added.
Testing
Each fix has its own red test commit before it. The tests run red on the commit before them and green on the head:
(cd tests && python3 -m unittest -k symlink -k later_push -k TrustBoundary test_web_lint_prepush) # d5792039507, red on d7fe55f8612
(cd tests && python3 -m unittest -k fork -k no_reviewed_remote -k reviewed_remote test_web_lint_prepush) # 7b53690c692, red on c67abc2c325
(cd tests && python3 -m unittest -k imports_is_check_code -k only_in_case -k tools_find_first -k install_config -k sees_every_case test_web_lint_prepush) # 4d233dd2b4d, red on 54014ed3b66
(cd tests && python3 -m unittest -k folder_above -k folders_above -k regex_that -k built_up -k package_json_main -k fails_closed_when_git -k mac_file_system -k reviewed_remote_by test_web_lint_prepush) # red on 643ef7ec491
(cd tests && python3 -m unittest -k pushed_node_modules -k node_modules_anywhere -k never_links_over test_web_lint_prepush) # red on 559c0eb4152
- Base: 7 failures and 6 errors. A marker script that only pushed content can reach ran, on a build host and on a host with
CMUX_LOCAL_HEAVY_CHECKS=0. - Head: all pass, and the marker never runs.
test_the_real_check_code_has_no_symlink_and_resolves_to_itselfshows that the real tree is not refused.
Observed / Not run
Observed:
python3 tests/test_web_lint_prepush.py: 89 tests OK.- Merged with feat-cmux-next, which holds #34046: its lone source-check pieces now pass the same trust checks as their package.
- The last 12 feat-cmux-next commits and this head plan with no refusal.
python3 -m unittest test_cmux_next_fast_gates: 77 tests OK.python3 scripts/cmux-next/prepush-gate.py: no new violations.- On this tree, check code loads 35 webviews modules (8 under
src/), with no symlink and no import that the hook cannot follow.
Not run:
- A real push through an installed hook on a Mac (the tests use the real
git pushand the installed hook in a throwaway repository). - A case-insensitive file system: the tests check the folded comparison, but this host's file system is case-sensitive.
- The stacked runner PRs (#34046, #34060, #34114) are separate. This PR is based on feat-cmux-next.
Changelog
none
Proof
No capture: hook tooling only.