refactor(tools): drop PyYAML from lint version check

- parse .pre-commit-config.yaml rev fields with a regex instead of
  yaml.safe_load; the file is repo-controlled and flat, so stdlib
  covers it and the lint extra stays two pins (pyyaml removed)
- normalize YAML-quoted and v-prefixed revs so a quoting change
  can't produce a false mismatch
- derive the checked tool list from REPO_TO_TOOL instead of a
  third hardcoded copy
- drop the Makefile PyYAML probe made moot by the above
- halve persist-step comments in ci.yml; kept detached-HEAD reset,
  never-reopen, and sed fail-by-name rationale
This commit is contained in:
xgboosted
2026-07-04 20:00:21 +10:00
parent 7f2231a20f
commit 1f49218fce
4 changed files with 23 additions and 40 deletions
+11 -21
View File
@@ -347,23 +347,17 @@ jobs:
run: twine upload dist/*
- name: Open PR to persist requires-python floor
# The "Update pyproject.toml" step above only patches this job's ephemeral
# checkout to build the wheel — it never touches the repository, so the
# committed requires-python silently goes stale each time
# LATEST_PYTHON_VERSION advances in this file. Once the release has
# published successfully, open a PR with just that one-line bump so a
# human reviews and merges it — no direct push to main. Classifier
# placeholders ({{LATEST-N}}) are intentionally left untouched here;
# they are re-resolved fresh at every future release.
# The wheel-building steps above never touch the repository, so the
# committed requires-python goes stale each time LATEST_PYTHON_VERSION
# advances. Open a PR (never push main directly) with that one-line bump;
# {{LATEST-N}} classifier placeholders are re-resolved at every release.
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
NEW_FLOOR="${{ fromJson(needs.generate-matrix.outputs.python-versions)[0] }}"
# This job checked out the release tag (detached HEAD) and the
# classifier-patching step above dirtied pyproject.toml in that tree.
# -f discards those local edits; -B pins local main exactly to
# origin/main so the sed below starts from a clean, current base.
# Checkout is the release tag (detached HEAD) with pyproject.toml
# dirtied by the classifier-patching step; -f -B resets to clean main.
git fetch origin main
git checkout -f -B main origin/main
@@ -373,11 +367,9 @@ jobs:
exit 0
fi
# If a previous bump PR for this same floor was closed without merging,
# its branch still exists and this guard no-ops on every later release
# until that branch is deleted or merged. Deliberate: closing that PR
# is a maintainer decision not to take the bump, and this job should
# not keep reopening it.
# Existing branch = a PR for this floor is open, or was closed without
# merging (a maintainer decision not to take the bump). Either way,
# skip — never reopen.
BRANCH="chore/requires-python-$NEW_FLOOR"
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
echo "Branch $BRANCH already exists on origin, skipping (PR likely already open)."
@@ -397,10 +389,8 @@ jobs:
git checkout -b "$BRANCH"
git add pyproject.toml
git commit -m "chore: bump requires-python floor to >=$NEW_FLOOR"
# Auth: the push reuses the credentials actions/checkout persisted in
# this clone, authorized by this job's `contents: write`; gh uses
# GH_TOKEN from env, authorized by `pull-requests: write`. No explicit
# token-in-URL setup is needed (unlike mirror.yml's cross-host push).
# Push auth: credentials persisted by actions/checkout + this job's
# `contents: write`; gh auth: GH_TOKEN + `pull-requests: write`.
git push origin "$BRANCH"
gh pr create \
-1
View File
@@ -123,7 +123,6 @@ lint:
ruff check pandas_ta_classic --select E9,F63,F7,F82
ruff check pandas_ta_classic --extend-select C901,E501 --exit-zero
@echo "Checking black/ruff versions match .pre-commit-config.yaml..."
@$(PYTHON) -c "import yaml" 2>/dev/null || { echo "PyYAML missing — run 'make install-dev' (or pip install -e '.[lint]') first"; exit 1; }
$(PYTHON) tools/check_lint_versions.py
format:
-3
View File
@@ -60,9 +60,6 @@ oracle = [
lint = [
"ruff>=0.15.20",
"black>=26.5.1",
# Not a linter: tools/check_lint_versions.py (run by the CI code-quality
# job, which installs only this extra) parses .pre-commit-config.yaml.
"pyyaml>=6.0",
]
# PyPI publishing tools
+12 -15
View File
@@ -14,8 +14,6 @@ import sys
import tomllib
from pathlib import Path
import yaml
ROOT = Path(__file__).parent.parent
# Repos whose pre-commit `rev:` mirrors a pin in the lint extra. Any other
@@ -32,25 +30,24 @@ def main() -> int:
lint_pins = pyproject["project"]["optional-dependencies"]["lint"]
# A pin that doesn't match (or a missing tool) leaves its entry unset,
# which the mismatch check below reports as `tool: ...=None` and fails.
pyproject_versions = {}
for pin in lint_pins:
match = re.match(r"([a-zA-Z_-]+)>=(.+)", pin)
if match:
pyproject_versions[match.group(1)] = match.group(2)
pyproject_versions = {m[1]: m[2] for pin in lint_pins if (m := re.match(r"([a-zA-Z_-]+)>=(.+)", pin))}
precommit = yaml.safe_load((ROOT / ".pre-commit-config.yaml").read_text())
precommit_versions = {}
for entry in precommit.get("repos", []):
tool = REPO_TO_TOOL.get(entry.get("repo", ""))
if tool is not None:
# ruff-pre-commit tags are v-prefixed (v0.15.20); black's are bare.
precommit_versions[tool] = str(entry.get("rev", "")).lstrip("v")
# The config is a repo-controlled flat file, so a regex pairing each
# `repo:` with the `rev:` on the next line beats a PyYAML dependency.
# Revs may be YAML-quoted, and ruff-pre-commit tags are v-prefixed
# (v0.15.20) while black's are bare — normalize both away.
precommit = (ROOT / ".pre-commit-config.yaml").read_text()
precommit_versions = {
REPO_TO_TOOL[repo]: rev.strip("'\"").lstrip("v")
for repo, rev in re.findall(r"- repo: (\S+)\s*\n\s*rev: (\S+)", precommit)
if repo in REPO_TO_TOOL
}
# Missing on either side is a failure too — `!=` alone would let a tool
# absent from BOTH files pass as None == None.
mismatches = [
f"{tool}: pyproject.toml={pyproject_versions.get(tool)} vs .pre-commit-config.yaml={precommit_versions.get(tool)}"
for tool in ("black", "ruff")
for tool in sorted(REPO_TO_TOOL.values())
if pyproject_versions.get(tool) != precommit_versions.get(tool) or pyproject_versions.get(tool) is None
]
if mismatches: