Skip to content

fix(toolshed): keep the pre-commit-installed check advisory instead of failing the run - #2548

Open
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:toolshed-precommit-check-nonfatal
Open

fix(toolshed): keep the pre-commit-installed check advisory instead of failing the run#2548
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:toolshed-precommit-check-nonfatal

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Stacking note: the one-line ci-nightly.yml change (adding toolshed/tests to the tooling test job) is byte-identical to the one in #2539, so the two merge cleanly in either order.

Problem

The module docstring is explicit about the contract:

"""Warn (without failing) if the pre-commit git hook is not installed.

and main() returns 0 on both of its branches. Three paths contradict that.

1. check=True on a command that legitimately fails.

def _git_hooks_dir() -> str:
    result = subprocess.run(
        ["git", "rev-parse", "--git-path", "hooks"],
        capture_output=True,
        check=True,
        text=True,
    )

git rev-parse exits 128 outside a work tree, so the advisory check raises CalledProcessError and fails the whole pre-commit run.

2. Missing git hits the wrong handler. If git is not on PATH, subprocess.run raises FileNotFoundError — which is not the FileNotFoundError the code catches. That one guards open(), further down. So it escapes as a traceback.

3. The open() guard is too narrow. It catches only FileNotFoundError, so any other reason the hooks path is unreadable — a directory, a dangling symlink, restrictive permissions — also escapes.

All three reproduced by calling main() directly:

outside a git repo              -> CalledProcessError: Command '['git', 'rev-parse', '--git-path', 'hooks']' returned non-zero exit status 128.
git missing on PATH             -> FileNotFoundError: [Errno 2] No such file or directory: 'git'
hooks/pre-commit is a directory -> IsADirectoryError: [Errno 21] Is a directory: '.../pre-commit'

The hook is configured with always_run: true and pass_filenames: false, so it fires on every local pre-commit run. Any of these turns a nudge to run pre-commit install into a hard failure of an otherwise-fine commit — and the failure text points at git rev-parse, not at anything the contributor did.

Fix

Treat "cannot determine whether the hook is installed" as "not installed". That is exactly the situation the existing warning describes, and it is the only outcome consistent with a check that by design cannot block.

  • _git_hooks_dir() returns None on OSError or subprocess.SubprocessError (covering both the exit-128 and the no-git cases) and on an empty answer.
  • The open() guard widens from FileNotFoundError to OSError.

No change when git answers normally and the hook is present or absent — the existing quiet/warn behavior is untouched.

Tests

toolshed/check_precommit_installed.py had no tests. Added toolshed/tests/test_check_precommit_installed.py covering:

  • an installed pre-commit hook (quiet, exit 0);
  • no hook file (warns, exit 0);
  • a hand-written hook without the pre-commit marker (warns, exit 0);
  • running outside a git work tree (monkeypatch.chdir(tmp_path));
  • git missing from PATH (monkeypatched subprocess.run, so no git binary is required and the test is hermetic);
  • the hooks path being unreadable.

The new directory is added to the existing nightly tooling job alongside ci/tools/tests.

Verification

Executed in full (pure Python, no GPU, no CUDA):

# with the fix
6 passed

# with toolshed/check_precommit_installed.py restored from upstream/main
FAILED test_outside_a_git_work_tree_warns_and_succeeds
FAILED test_git_missing_from_path_warns_and_succeeds
FAILED test_unreadable_hook_path_warns_and_succeeds
3 failed, 3 passed

ruff check / ruff format --check clean on both changed Python files; index verified clean before committing (restore done with cp aside + git show upstream/main:<path> >, never git checkout --).

The module docstring says this check should "Warn (without failing)", and
main() returns 0 on both of its branches. Three paths contradict that:

1. `_git_hooks_dir` runs `git rev-parse --git-path hooks` with `check=True`.
   Outside a work tree git exits 128, so the advisory hook raises
   CalledProcessError and fails the whole `pre-commit run`.

2. If git is not on PATH, `subprocess.run` raises FileNotFoundError. That is
   not the FileNotFoundError the code catches -- that one guards `open()` --
   so it escapes as a traceback.

3. The `open()` guard catches only FileNotFoundError, so any other reason the
   hooks path is unreadable (a directory, a dangling symlink, restrictive
   permissions) also escapes.

All three were reproduced by calling `main()` directly:

    outside a git repo  -> CalledProcessError: ... exit status 128
    git missing on PATH -> FileNotFoundError: [Errno 2] ... 'git'
    hooks/pre-commit is a directory -> IsADirectoryError: [Errno 21]

The hook is configured with `always_run: true`, so it fires on every local
`pre-commit run`. Any of these turns a nudge to run `pre-commit install` into
a hard failure of an unrelated commit.

Treat "cannot determine whether the hook is installed" as "not installed":
that is the situation the warning already describes, and it is the only
outcome consistent with a check that cannot block.

Adds tests under toolshed/tests/ covering the installed, missing,
foreign-hook, no-work-tree, no-git, and unreadable-path cases.
@copy-pr-bot

copy-pr-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

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

Labels

CI/CD CI/CD infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant