ruff-pre-commit/mirror.py
Calum Young 6299d2ca23
Update mirror.py to a uv script (#110)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

From my reading of the [UV
docs](https://docs.astral.sh/uv/guides/scripts/), `mirror.py` would be a
good example of a standalone script. This PR updates `mirror.py` and the
`main.yml` workflow to be a UV script and makes use of UV in the update
process.

## Test Plan

- Update the uv version (currently 0.8.4) to an older version e.g. 0.8.3
in both `pyproject.toml` and `README.md`
- Run `uv run --no-project mirror.py` to check the update process still
works as expected
2024-12-30 10:48:39 -05:00

85 lines
2.6 KiB
Python

# /// script
# requires-python = ">=3.12"
# dependencies = [
# "packaging==23.1",
# "urllib3==2.0.5",
# ]
# ///
"""Update ruff-pre-commit to the latest version of ruff."""
import re
import subprocess
import tomllib
import typing
from pathlib import Path
import urllib3
from packaging.requirements import Requirement
from packaging.version import Version
def main():
with open(Path(__file__).parent / "pyproject.toml", "rb") as f:
pyproject = tomllib.load(f)
all_versions = get_all_versions()
current_version = get_current_version(pyproject=pyproject)
target_versions = [v for v in all_versions if v > current_version]
for version in target_versions:
paths = process_version(version)
if subprocess.check_output(["git", "status", "-s"]).strip():
subprocess.run(["git", "add", *paths], check=True)
subprocess.run(["git", "commit", "-m", f"Mirror: {version}"], check=True)
subprocess.run(["git", "tag", f"v{version}"], check=True)
else:
print(f"No change v{version}")
def get_all_versions() -> list[Version]:
response = urllib3.request("GET", "https://pypi.org/pypi/ruff/json")
if response.status != 200:
raise RuntimeError("Failed to fetch versions from pypi")
versions = [Version(release) for release in response.json()["releases"]]
return sorted(versions)
def get_current_version(pyproject: dict) -> Version:
requirements = [Requirement(d) for d in pyproject["project"]["dependencies"]]
requirement = next((r for r in requirements if r.name == "ruff"), None)
assert requirement is not None, "pyproject.toml does not have ruff requirement"
specifiers = list(requirement.specifier)
assert (
len(specifiers) == 1 and specifiers[0].operator == "=="
), f"ruff's specifier should be exact matching, but `{requirement}`"
return Version(specifiers[0].version)
def process_version(version: Version) -> typing.Sequence[str]:
def replace_pyproject_toml(content: str) -> str:
return re.sub(r'"ruff==.*"', f'"ruff=={version}"', content)
def replace_readme_md(content: str) -> str:
content = re.sub(r"rev: v\d+\.\d+\.\d+", f"rev: v{version}", content)
return re.sub(r"/ruff/\d+\.\d+\.\d+\.svg", f"/ruff/{version}.svg", content)
paths = {
"pyproject.toml": replace_pyproject_toml,
"README.md": replace_readme_md,
}
for path, replacer in paths.items():
with open(path) as f:
content = replacer(f.read())
with open(path, mode="w") as f:
f.write(content)
return tuple(paths.keys())
if __name__ == "__main__":
main()