Compare commits

...

16 Commits

Author SHA1 Message Date
6f733d481d lint 2025-07-16 10:17:19 -07:00
d61ae9a2ec lint 2025-07-16 10:16:01 -07:00
a62fa46502 test commit 2025-07-16 09:54:53 -07:00
b45b26f68e Fixes 2025-07-16 11:46:25 -05:00
3c65f00a6f Make hook resilient to being launched on older branches 2025-07-16 11:45:12 -05:00
9d4fda5637 Minor fixes 2025-07-15 19:02:24 -05:00
73da8c1c12 typo fix 2025-07-15 18:57:46 -05:00
3c479b95c9 ensure pipx path 2025-07-15 18:56:59 -05:00
241250ff90 Remove CI check 2025-07-15 18:08:23 -05:00
6bcb74e2cc lint fix 2025-07-15 17:55:05 -05:00
bdb15094c6 moar good 2025-07-15 17:53:29 -05:00
cbd7ad6a27 no-op 2025-07-15 17:53:29 -05:00
c3ec715b74 fix 2025-07-15 17:53:29 -05:00
ae1fc1de26 Initial working version 2025-07-15 17:53:29 -05:00
892e11c770 update lintrunner wrapper 2025-07-15 17:53:29 -05:00
887f933fd9 test 2025-07-15 17:53:29 -05:00
4 changed files with 225 additions and 0 deletions

12
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,12 @@
repos:
- repo: local
hooks:
- id: lintrunner
name: Run Lintrunner in an isolated venv before every push. The first run may be slow...
entry: python scripts/run_lintrunner.py # wrapper below
language: python # precommit manages venv for the wrapper
additional_dependencies: [] # wrapper handles lintrunner install
always_run: true
stages: [pre-push] # fire only on prepush
pass_filenames: false # Lintrunner gets no perfile args
verbose: true # stream output as it is produced...allegedly anyways

91
scripts/run_lintrunner.py Normal file
View File

@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""
Prepush hook wrapper for Lintrunner.
✓ Installs Lintrunner once (`pip install lintrunner`) if missing
✓ Stores a hash of .lintrunner.toml in the venv
✓ Re-runs `lintrunner init` if that file's hash changes
✓ Pure Python works on macOS, Linux, and Windows
"""
from __future__ import annotations
import hashlib
import os
import shutil
import subprocess
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
TOML_PATH = REPO_ROOT / ".lintrunner.toml"
# This is the path to the pre-commit-managed venv
VENV_ROOT = Path(sys.executable).parent.parent
MARKER_PATH = VENV_ROOT / ".lintrunner_plugins_hash"
def ensure_lintrunner() -> None:
"""Fail if Lintrunner is not on PATH."""
if shutil.which("lintrunner"):
print("✅ lintrunner is already installed")
return
sys.exit(
"❌ lintrunner is required but was not found on your PATH. Please run the `python scripts/setup_hooks.py` to install to configure lintrunner before using this script."
)
def compute_file_hash(path: Path) -> str:
"""Returns SHA256 hash of a file's contents."""
hasher = hashlib.sha256()
with path.open("rb") as f:
while chunk := f.read(8192):
hasher.update(chunk)
return hasher.hexdigest()
def read_stored_hash(path: Path) -> str | None:
if not path.exists():
return None
try:
return path.read_text().strip()
except Exception:
return None
def maybe_initialize_lintrunner() -> None:
"""Runs lintrunner init if .lintrunner.toml changed since last run."""
if not TOML_PATH.exists():
print("⚠️ No .lintrunner.toml found. Skipping init.")
return
current_hash = compute_file_hash(TOML_PATH)
stored_hash = read_stored_hash(MARKER_PATH)
if current_hash == stored_hash:
print("✅ Lintrunner plugins already initialized and up to date.")
return
print("🔁 Running `lintrunner init` …", file=sys.stderr)
subprocess.check_call(["lintrunner", "init"])
MARKER_PATH.write_text(current_hash)
def main() -> None:
print(f"🐍 Lintrunner is using Python: {sys.executable}", file=sys.stderr)
# 1. Ensure lintrunner binary is available
ensure_lintrunner()
# 2. Check for plugin updates and re-init if needed
maybe_initialize_lintrunner()
# 3. Run lintrunner with any passed arguments and propagate its exit code
args = sys.argv[1:] # Forward all arguments to lintrunner
result = subprocess.call(["lintrunner"] + args)
sys.exit(result)
if __name__ == "__main__":
main()

117
scripts/setup_hooks.py Normal file
View File

@ -0,0 +1,117 @@
#!/usr/bin/env python3
"""
Bootstrap Git prepush hook.
✓ Installs pipx automatically (via Homebrew on macOS)
✓ Installs/updates precommit with pipx (global, venvproof)
✓ Registers the repo's prepush hook and freezes hook versions
Run this from the repo root (inside or outside any project venv):
python scripts/setup_hooks.py
"""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
# ───────────────────────────────────────────
# Helper utilities
# ───────────────────────────────────────────
def run(cmd: list[str]) -> None:
print(f"$ {' '.join(cmd)}")
subprocess.check_call(cmd)
def which(cmd: str) -> bool:
return shutil.which(cmd) is not None
# ───────────────────────────────────────────
# 1. Ensure pipx exists (install via brew on macOS)
# ───────────────────────────────────────────
def ensure_pipx() -> None:
if which("pipx"):
return
# If we're on a mac
if sys.platform == "darwin":
# Try Homebrew installation
if which("brew"):
print("pipx not found installing with Homebrew …")
run(["brew", "install", "pipx"])
run(["pipx", "ensurepath"])
else:
sys.exit(
"\n❌ pipx is required but neither pipx nor Homebrew were found.\n"
" Please install Homebrew (https://brew.sh) or pipx manually:\n"
" https://pipx.pypa.io/stable/installation/\n"
)
else:
# NonmacOS: ask user to install pipx manually
sys.exit(
"\n❌ pipx is required but was not found on your PATH.\n"
" Install pipx first (https://pipx.pypa.io/stable/installation/),\n"
" then rerun python scripts/setup_hooks.py\n"
)
if not which("pipx"):
sys.exit(
"\n❌ pipx installation appeared to succeed, but it's still not on PATH.\n"
" Restart your terminal or add pipx's bin directory to PATH and retry.\n"
)
def ensure_tool_installed(tool: str, force_update: bool = False) -> None:
if force_update or not which(tool):
print(f"Ensuring latest {tool} via pipx …")
run(["pipx", "install", "--quiet", "--force", tool])
if not which(tool):
sys.exit(
f"\n{tool} installation appeared to succeed, but it's still not on PATH.\n"
" Restart your terminal or add pipx's bin directory to PATH and retry.\n"
)
ensure_pipx()
# Ensure the path pipx installs binaries to is part of the system path.
# Modifies the shell's configuration files (like ~/.bashrc, ~/.zshrc, etc.)
# to include the directory where pipx installs executables in your PATH
# variable.
run(["pipx", "ensurepath"])
# Ensure pre-commit is installed globally via pipx
ensure_tool_installed("pre-commit", force_update=True)
# Don't force a lintrunner update because it might break folks
# who already have it installed in a different way
ensure_tool_installed("lintrunner")
# ───────────────────────────────────────────
# 3. Activate (or refresh) the prepush hook
# ───────────────────────────────────────────
# ── Activate (or refresh) the repos prepush hook ──────────────────────────
# Creates/overwrites .git/hooks/prepush with a tiny shim that will call
# `pre-commit run --hook-stage pre-push` on every `git push`.
# This is why we need to install pre-commit globally.
#
# The --allow-missing-config flag lets pre-commit succeed if someone changes to
# a branch that doesn't have pre-commit installed
run(["pre-commit", "install", "--hook-type", "pre-push", "--allow-missing-config"])
# ── Pin remotehook versions for reproducibility ────────────────────────────
# 1. `autoupdate` bumps every remote hooks `rev:` in .pre-commit-config.yaml
# to the latest commit on its default branch.
# (Note: we don't have remote hooks right now, but this future-proofs
# this script)
# 2. `--freeze` immediately rewrites each `rev:` to the exact commit SHA,
# ensuring all contributors and CI run identical hook code.
run(["pre-commit", "autoupdate", "--freeze"])
print(
"\n✅ precommit is installed globally via pipx and the prepush hook is active.\n"
" Lintrunner will now run automatically on every `git push`.\n"
)

5
scripts/test_camyll.py Normal file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env python3
def main():
print("hello world")