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:
+11
-21
@@ -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 \
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user