Make pre-commit scripts more OS-agnostic (#6724)

# Description of Changes
Fix #6723
This commit is contained in:
James Brunton
2026-06-23 08:42:18 +00:00
committed by GitHub
parent 1816bad1ba
commit f2b65f4a77
5 changed files with 145 additions and 105 deletions
-31
View File
@@ -1,31 +0,0 @@
# Download, checksum-verify and extract the gitleaks binary.
#
# Usage: install-gitleaks.ps1 -Url <url> -Sha <sha256> -Dest <dest>
#
# Called by the pre-commit:gitleaks-bin Task target, which owns the pinned
# version and per-platform checksums and passes the resolved values in.
param(
[Parameter(Mandatory)] [string]$Url,
[Parameter(Mandatory)] [string]$Sha,
[Parameter(Mandatory)] [string]$Dest
)
$ErrorActionPreference = 'Stop'
if (-not $Sha) {
throw 'No pinned gitleaks checksum for this platform'
}
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Dest) | Out-Null
$archive = New-TemporaryFile
$extract = Join-Path $env:TEMP 'gitleaks-extract'
try {
Invoke-WebRequest -Uri $Url -OutFile $archive
if ((Get-FileHash $archive -Algorithm SHA256).Hash -ne $Sha) {
throw 'gitleaks checksum mismatch'
}
Expand-Archive -Force -Path $archive -DestinationPath $extract
Move-Item -Force -Path (Join-Path $extract 'gitleaks.exe') -Destination $Dest
} finally {
Remove-Item -Force -ErrorAction SilentlyContinue $archive, $extract -Recurse
}
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
# Download, checksum-verify and extract the gitleaks binary.
#
# Usage: install-gitleaks.sh <url> <sha256> <dest>
#
# Called by the pre-commit:gitleaks-bin Task target, which owns the pinned
# version and per-platform checksums and passes the resolved values in.
set -euo pipefail
url=$1
sha=$2
dest=$3
if [ -z "$sha" ]; then
echo "No pinned gitleaks checksum for this platform" >&2
exit 1
fi
mkdir -p "$(dirname "$dest")"
archive=$(mktemp)
trap 'rm -f "$archive"' EXIT
curl -fsSL "$url" -o "$archive"
actual=$(shasum -a 256 "$archive" | awk '{print $1}')
if [ "$actual" != "$sha" ]; then
echo "gitleaks checksum mismatch: expected $sha, got $actual" >&2
exit 1
fi
tar -xzO -f "$archive" gitleaks > "$dest"
chmod +x "$dest"
+113
View File
@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""Download the pinned gitleaks binary into .task/bin, verifying its checksum.
gitleaks is a Go binary with no PyPI package, so it can't be locked like the
other tools (ruff/codespell/toml-sort live in scripts/pre-commit/pyproject.toml).
This script is the single source of truth for the gitleaks version and the
SHA-256 of each release asset. It is cross-platform (stdlib only) and idempotent:
if the cached binary already reports the pinned version it does nothing, so
`task pre-commit` can call it every run.
Bump the version by editing VERSION and the SHA256 map (values come from the
release's gitleaks_<version>_checksums.txt).
"""
from __future__ import annotations
import hashlib
import platform
import subprocess
import sys
import tarfile
import urllib.request
import zipfile
from pathlib import Path
VERSION = "8.30.0"
# SHA-256 of each release asset, keyed by "<os>_<arch>" (gitleaks' own naming).
SHA256 = {
"linux_x64": "79a3ab579b53f71efd634f3aaf7e04a0fa0cf206b7ed434638d1547a2470a66e",
"linux_arm64": "b4cbbb6ddf7d1b2a603088cd03a4e3f7ce48ee7fd449b51f7de6ee2906f5fa2f",
"darwin_x64": "ca221d012d247080c2f6f61f4b7a83bffa2453806b0c195c795bbe9a8c775ed5",
"darwin_arm64": "b251ab2bcd4cd8ba9e56ff37698c033ebf38582b477d21ebd86586d927cf87e7",
"windows_x64": "54fe94f644b832dd08e8c3a5915efb3bfa862386d59fb27ca0792cb687a83573",
}
REPO_ROOT = Path(__file__).resolve().parents[2]
IS_WINDOWS = platform.system() == "Windows"
BIN = REPO_ROOT / ".task" / "bin" / ("gitleaks.exe" if IS_WINDOWS else "gitleaks")
def platform_key() -> str:
os_name = {"Linux": "linux", "Darwin": "darwin", "Windows": "windows"}.get(
platform.system()
)
arch = {
"x86_64": "x64",
"amd64": "x64",
"arm64": "arm64",
"aarch64": "arm64",
"i386": "x32",
"i686": "x32",
"x86": "x32",
"armv7l": "armv7",
"armv6l": "armv6",
}.get(platform.machine().lower())
if not os_name or not arch:
raise SystemExit(
f"Unsupported platform for gitleaks: {platform.system()}/{platform.machine()}"
)
return f"{os_name}_{arch}"
def cached_version() -> str | None:
if not BIN.exists():
return None
try:
return subprocess.run(
[str(BIN), "version"], capture_output=True, text=True
).stdout.strip()
except OSError:
return None
def main() -> int:
if cached_version() == VERSION:
return 0
key = platform_key()
expected = SHA256.get(key)
if expected is None:
raise SystemExit(f"No pinned gitleaks checksum for {key}")
suffix = "zip" if key.startswith("windows") else "tar.gz"
asset = f"gitleaks_{VERSION}_{key}.{suffix}"
url = f"https://github.com/gitleaks/gitleaks/releases/download/v{VERSION}/{asset}"
print(f"Downloading gitleaks {VERSION} ({asset})", flush=True)
BIN.parent.mkdir(parents=True, exist_ok=True)
archive, _ = urllib.request.urlretrieve(url)
digest = hashlib.sha256(Path(archive).read_bytes()).hexdigest()
if digest != expected:
raise SystemExit(
f"gitleaks checksum mismatch: expected {expected}, got {digest}"
)
member = "gitleaks.exe" if IS_WINDOWS else "gitleaks"
if suffix == "zip":
with zipfile.ZipFile(archive) as zf:
data = zf.read(member)
else:
with tarfile.open(archive) as tf:
extracted = tf.extractfile(member)
if extracted is None:
raise SystemExit(f"{member} not found in {asset}")
data = extracted.read()
BIN.write_bytes(data)
BIN.chmod(0o755)
return 0
if __name__ == "__main__":
sys.exit(main())
+20 -6
View File
@@ -3,11 +3,14 @@
Replaces the end-of-file-fixer / trailing-whitespace pre-commit hooks, which
have no read-only mode. Run via `task pre-commit` (check) and `task
pre-commit:fix`; Task selects the files (with `git ls-files`) and passes them
as arguments.
pre-commit:fix`.
python scripts/whitespace.py <files>... # check: report, exit 1 if any need fixing
python scripts/whitespace.py --fix <files>... # fix: rewrite in place
Takes git pathspecs (not a file list) and runs `git ls-files` itself, so the
matched files never hit the command line - on Windows that list can be ~66KB
and exceed the ~32KB CreateProcess argv limit.
python whitespace.py <pathspec>... # check: report, exit 1 if any need fixing
python whitespace.py --fix <pathspec>... # fix: rewrite in place
Operates on bytes and only ever touches trailing spaces/tabs and the final
newline, so it never mangles content or line endings. Binary files (those with
@@ -16,10 +19,21 @@ a NUL byte) are skipped.
from __future__ import annotations
import subprocess
import sys
from pathlib import Path
def tracked_files(pathspecs: list[str]) -> list[str]:
result = subprocess.run(
["git", "ls-files", "-z", *pathspecs],
check=True,
capture_output=True,
text=True,
)
return [path for path in result.stdout.split("\0") if path]
def normalise(data: bytes) -> bytes:
# Strip trailing spaces/tabs from each line (leave \r so CRLF survives).
lines = [line.rstrip(b" \t") for line in data.split(b"\n")]
@@ -32,10 +46,10 @@ def normalise(data: bytes) -> bytes:
def main() -> int:
args = sys.argv[1:]
fix = "--fix" in args
paths = [a for a in args if a != "--fix"]
pathspecs = [a for a in args if a != "--fix"]
offenders: list[str] = []
for path in paths:
for path in tracked_files(pathspecs):
data = Path(path).read_bytes()
if b"\0" in data:
continue