Skip to content

Replace pre-commit with prek - #149

Draft
zeevmoney wants to merge 8 commits into
mainfrom
per-16772/prek
Draft

zeevmoney wants to merge 8 commits into
mainfrom
per-16772/prek

Conversation

@zeevmoney

Copy link
Copy Markdown
Member

Linear issues

  • Fixes PER-16772: replace the pre-commit runner with prek, which reads the same .pre-commit-config.yaml.
  • Part of PER-16336: the SDK upgrade guide, section 10 (packaging and tooling).

Targets main; not stacked on #136–#144.

Why

The SDK upgrade guide (PER-16336) and the team's tooling standard use prek, a Rust runner that reads the same .pre-commit-config.yaml.

What changed

  • pyproject.toml: the dev group pins prek==0.5.3 instead of pre-commit==4.6.2. 0.5.3 is the newest release outside the 7-day exclude-newer cooldown. In uv.lock, re-locking removed pre-commit and its dependencies (cfgv, distlib, filelock, identify, nodeenv, platformdirs, python-discovery, pyyaml, virtualenv). Nothing in the repo imports them.
  • .pre-commit-config.yaml keeps the same file name, the same 17 hooks and the same revs:
    • minimum_prek_version: "0.5.3" replaces minimum_pre_commit_version, which prek ignores.
    • The local hooks use language: system instead of language: unsupported, which prek treats as an alias of system.
    • Each of the 12 pre-commit-hooks hooks sets language: python. Without it, prek runs its own Rust versions of those hooks. Those accept unknown YAML tags, TOML 1.1 and empty JSON files, which v6.0.0 rejects.
    • default_language_version: python: "3.11": prek builds the pre-commit-hooks environment with whichever Python uv picks first and ignores .python-version. pre-commit used the 3.11 from .venv.
  • CI (.github/workflows/pre-commit.yml):
    • The job id stays pre-commit, the required status check.
    • It runs uv run --locked --only-dev prek run --all-files --show-diff-on-failure --color=always, so the prek version comes from uv.lock.
    • The cache path is PREK_HOME (~/.cache/prek). The cache key is the runner OS, the Python version, the config hash and the uv.lock hash. restore-keys falls back to the newest cache for the same config, so a uv.lock bump reuses the hook environments.
    • The pydantic-v1 mypy step is unchanged.
  • Dependabot: prek is added to the lint-tools group, so its updates get their own PR. The pre-commit ecosystem entry stays, because Dependabot only parses the config file and rewrites rev and the # frozen: comment.
  • CONTRIBUTING.md now uses uv run prek install and uv run prek run --all-files, and adds a note for clones whose Git hook was installed by pre-commit.

The pre-commit mentions that remain name one of these:

  • the config file
  • the pre-commit/pre-commit-hooks repository
  • the Dependabot ecosystem
  • Git's .git/hooks/pre-commit path
  • the workflow and required-check name
  • the old tool, in the migration note

Behaviour changes

SDK: none. Nothing under permit/ or tests/ changed.

Contributor tooling:

  • uv run prek install and uv run prek run --all-files replace the pre-commit commands.
  • A clone whose .git/hooks/pre-commit was installed by pre-commit needs uv sync and then uv run prek install --force once. Without --force, prek keeps the old hook as pre-commit.legacy and runs it too, and every commit fails with "No module named pre_commit".
  • prek updates get their own Dependabot PR (lint-tools) instead of joining minor-and-patch.

How it was tested

  • Same results on a clean tree. pre-commit 4.6.2 (main's lock, at ef80ae2) and prek 0.5.3 (this branch) both ran the same 17 hooks: 16 Passed and check-xml Skipped, with identical output line for line.

  • Planted failures. One failure at a time; both runners failed the same hooks for each:

    Planted failure Hooks that failed (both runners)
    eval ruff check, ruff format
    PLANTED=1 ruff format
    wrong return type mypy
    teh typos
    trailing whitespace trim trailing whitespace
    unknown YAML tag check yaml
    TOML 1.1 inline table check toml
    empty JSON file check json
    stale pyproject pin, runner called directly ruff check, ruff format, mypy, typos, uv lock --check
  • Config checks:

    • Removing language: python from check-json, check-toml and check-yaml makes those three planted inputs pass, because prek then uses its Rust versions.
    • minimum_prek_version: "9.0.0" stops prek with "prek version 9.0.0 or newer is required".
    • Without default_language_version, an empty PREK_HOME got a hook environment on free-threaded CPython 3.14.7. With it, the environment uses 3.11.14. A cache that already holds the 3.14 environment gets a new 3.11 one.
    • Python 3.15.0rc2's tomllib accepts the TOML 1.1 input that 3.11 rejects.
  • Git hook, in a throwaway clone. uv run prek install blocked a commit with trailing whitespace and eval (trim trailing whitespace, ruff check and ruff format failed) and allowed a clean commit. The CONTRIBUTING note reproduced: without --force the commit failed with "No module named pre_commit"; with --force it passed.

  • Suites and linters:

    • uv lock --check: passes.
    • uv run --locked --only-dev prek run --all-files with an empty PREK_HOME: all hooks pass.
    • mypy against pydantic 2 and pydantic 1: no issues in 89 files.
    • Offline tests on pydantic-v2 and on pydantic-v1: 305 passed, 3 skipped, 0 warnings each, the same as main.
    • tests/test_typing_surface.py: 3 passed on both lanes.
    • .github/scripts tests: 108 passed. skills/tests: 86 passed, 1 skipped (a tool missing from PATH).
    • actionlint: clean. zizmor: no findings.

Owner actions before merge

  • Confirm that the required pre-commit check is green.
  • Confirm two choices:
    • language: python on the 12 pre-commit-hooks hooks. This keeps the v6.0.0 behaviour; prek's Rust versions would save about 0.6 s per warm run.
    • prek in Dependabot's lint-tools group. Main had no uv group naming pre-commit; it fell under minor-and-patch.
  • After merge, ask contributors to run uv sync and uv run prek install --force once.

🤖 Generated with Claude Code

zeevmoney and others added 8 commits October 3, 2026 03:48
prek 0.5.3 is the newest release outside the 7-day exclude-newer
cooldown. It is a single binary, so pre-commit's Python dependencies
(cfgv, identify, nodeenv, pyyaml, virtualenv and theirs) leave uv.lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
prek ignores minimum_pre_commit_version, so the floor moves to
minimum_prek_version, which prek enforces. `language: system` is prek's
name for what pre-commit 4.4 calls `unsupported`; prek reads both.

prek replaces the pre-commit-hooks hooks with built-in Rust versions
unless a hook sets its language. On a corpus of edge cases those
versions diverge from the pinned v6.0.0 hooks (unknown YAML tags, TOML
1.1 syntax, an empty JSON file, a BOM or NaN in JSON, a lone `=======`
during a merge, vertical tabs and CR-only line endings), so each hook
sets `language: python` and keeps the pinned implementation, which
fails and fixes the same files pre-commit did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The job keeps its id, the required status check. prek comes from the
locked dev group (`uv run --locked --only-dev`), so CI runs the version
in uv.lock rather than the one j178/prek-action would download. The
hook cache moves to prek's home, set explicitly so prek and the cache
step agree, and its key adds uv.lock, which pins prek and the uv that
builds the hook environments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
prek is 0.x, so a minor release may change how the hooks run; like a
new ruff rule, that must not hold up the runtime floor bumps. The
pre-commit ecosystem entry stays: Dependabot only reads and rewrites
.pre-commit-config.yaml, which prek reads unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Setup installs the Git hook with `uv run prek install`, and a clone
whose hook pre-commit installed is told to pass --force: otherwise prek
keeps that hook as pre-commit.legacy and runs it too, and it fails once
pre-commit is gone from .venv. `uv run prek run --all-files` runs every
hook as CI does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
prek builds the pre-commit-hooks environment with whichever Python uv
picks first, ignoring .python-version, so check-json and check-toml
could parse with a newer json or tomllib than the project's 3.11.
default_language_version pins it to 3.11, the interpreter pre-commit
used when run from .venv.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cache key hashed uv.lock with no restore-keys, so every uv.lock
change, including each Dependabot uv bump, started from an empty cache.
The key now hashes the config and uv.lock separately, and restore-keys
falls back to the newest cache for the same config; prek reuses the
environments that still match and the new key is saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

PER-16336

PER-16772

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown

Dependency Security Audit

Scanned: pyproject.toml dependencies + dev group, resolved at Python 3.10 (the current resolution, and the lowest versions the published specs permit under each pydantic major)

✅ No known vulnerabilities found.

Both the resolved dependency set and the lowest versions the published specs permit are clean at HIGH and CRITICAL.

This branch has not been deployed

No deployments
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