mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-02 21:03:11 +00:00
Ecosystem CI: Optionally diff fixes (#4193)
* Generate fixes when using --show-fixes
Example command: `cargo run --bin ruff -- --no-cache --select F401
--show-source --show-fixes
crates/ruff/resources/test/fixtures/pyflakes/F401_9.py`
Before, `--show-fixes` was ignored:
```
crates/ruff/resources/test/fixtures/pyflakes/F401_9.py:4:22: F401 [*] `foo.baz` imported but unused
|
4 | __all__ = ("bar",)
5 | from foo import bar, baz
| ^^^ F401
|
= help: Remove unused import: `foo.baz`
Found 1 error.
[*] 1 potentially fixable with the --fix option.
```
After:
```
crates/ruff/resources/test/fixtures/pyflakes/F401_9.py:4:22: F401 [*] `foo.baz` imported but unused
|
4 | __all__ = ("bar",)
5 | from foo import bar, baz
| ^^^ F401
|
= help: Remove unused import: `foo.baz`
ℹ Suggested fix
1 1 | """Test: late-binding of `__all__`."""
2 2 |
3 3 | __all__ = ("bar",)
4 |-from foo import bar, baz
4 |+from foo import bar
Found 1 error.
[*] 1 potentially fixable with the --fix option.
```
Also fixes git clone
This commit is contained in:
parent
32f1edc555
commit
625849b846
2 changed files with 33 additions and 10 deletions
|
|
@ -17,11 +17,12 @@
|
||||||
# docker buildx build -f scripts/Dockerfile.ecosystem -t ruff-ecosystem-checker --load .
|
# docker buildx build -f scripts/Dockerfile.ecosystem -t ruff-ecosystem-checker --load .
|
||||||
# docker run --rm -v ./target/x86_64-unknown-linux-musl/debug/ruff:/app/ruff-new -v ./ruff-old:/app/ruff-old ruff-ecosystem-checker
|
# docker run --rm -v ./target/x86_64-unknown-linux-musl/debug/ruff:/app/ruff-new -v ./ruff-old:/app/ruff-old ruff-ecosystem-checker
|
||||||
# ```
|
# ```
|
||||||
# You can customize this, e.g. cache the git checkouts and use a custom json file:
|
# You can customize this, e.g. cache the git checkouts, a custom json file and a glibc build:
|
||||||
# ```
|
# ```
|
||||||
# docker run -v ./target/x86_64-unknown-linux-musl/debug/ruff:/app/ruff-new -v ./ruff-old:/app/ruff-old \
|
# docker run -v ./target/debug/ruff:/app/ruff-new -v ./ruff-old:/app/ruff-old -v ./target/checkouts:/app/checkouts \
|
||||||
# -v ./target/checkouts:/app/checkouts -v ./github_search.jsonl:/app/github_search.jsonl \
|
# -v ./github_search.jsonl:/app/github_search.jsonl --rm ruff-ecosystem-checker \
|
||||||
# --rm ruff-ecosystem-checker python check_ecosystem.py -v ruff-new ruff-old --checkouts checkouts > output.txt
|
# python check_ecosystem.py --verbose ruff-new ruff-old --projects github_search.jsonl --checkouts checkouts \
|
||||||
|
# > target/ecosystem-ci.txt
|
||||||
# ```
|
# ```
|
||||||
|
|
||||||
FROM python:3.11
|
FROM python:3.11
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import time
|
||||||
from asyncio.subprocess import PIPE, create_subprocess_exec
|
from asyncio.subprocess import PIPE, create_subprocess_exec
|
||||||
from contextlib import asynccontextmanager, nullcontext
|
from contextlib import asynccontextmanager, nullcontext
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from signal import SIGINT, SIGTERM
|
||||||
from typing import TYPE_CHECKING, NamedTuple, Self
|
from typing import TYPE_CHECKING, NamedTuple, Self
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -36,6 +37,8 @@ class Repository(NamedTuple):
|
||||||
select: str = ""
|
select: str = ""
|
||||||
ignore: str = ""
|
ignore: str = ""
|
||||||
exclude: str = ""
|
exclude: str = ""
|
||||||
|
# Generating fixes is slow and verbose
|
||||||
|
show_fixes: bool = False
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def clone(self: Self, checkout_dir: Path) -> AsyncIterator[Path]:
|
async def clone(self: Self, checkout_dir: Path) -> AsyncIterator[Path]:
|
||||||
|
|
@ -102,6 +105,7 @@ async def check(
|
||||||
select: str = "",
|
select: str = "",
|
||||||
ignore: str = "",
|
ignore: str = "",
|
||||||
exclude: str = "",
|
exclude: str = "",
|
||||||
|
show_fixes: bool = False,
|
||||||
) -> Sequence[str]:
|
) -> Sequence[str]:
|
||||||
"""Run the given ruff binary against the specified path."""
|
"""Run the given ruff binary against the specified path."""
|
||||||
logger.debug(f"Checking {name} with {ruff}")
|
logger.debug(f"Checking {name} with {ruff}")
|
||||||
|
|
@ -112,6 +116,8 @@ async def check(
|
||||||
ruff_args.extend(["--ignore", ignore])
|
ruff_args.extend(["--ignore", ignore])
|
||||||
if exclude:
|
if exclude:
|
||||||
ruff_args.extend(["--exclude", exclude])
|
ruff_args.extend(["--exclude", exclude])
|
||||||
|
if show_fixes:
|
||||||
|
ruff_args.extend(["--show-fixes", "--ecosystem-ci"])
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
proc = await create_subprocess_exec(
|
proc = await create_subprocess_exec(
|
||||||
|
|
@ -169,16 +175,16 @@ async def compare(
|
||||||
|
|
||||||
# Allows to keep the checkouts locations
|
# Allows to keep the checkouts locations
|
||||||
if checkouts:
|
if checkouts:
|
||||||
checkout_dir = checkouts.joinpath(repo.org).joinpath(repo.repo)
|
checkout_parent = checkouts.joinpath(repo.org)
|
||||||
# Don't create the repodir itself, we need that for checking for existing
|
# Don't create the repodir itself, we need that for checking for existing
|
||||||
# clones
|
# clones
|
||||||
checkout_dir.parent.mkdir(exist_ok=True, parents=True)
|
checkout_parent.mkdir(exist_ok=True, parents=True)
|
||||||
location_context = nullcontext(checkout_dir)
|
location_context = nullcontext(checkout_parent)
|
||||||
else:
|
else:
|
||||||
location_context = tempfile.TemporaryDirectory()
|
location_context = tempfile.TemporaryDirectory()
|
||||||
|
|
||||||
with location_context as checkout_dir:
|
with location_context as checkout_parent:
|
||||||
checkout_dir = Path(checkout_dir)
|
checkout_dir = Path(checkout_parent).joinpath(repo.repo)
|
||||||
async with repo.clone(checkout_dir) as path:
|
async with repo.clone(checkout_dir) as path:
|
||||||
try:
|
try:
|
||||||
async with asyncio.TaskGroup() as tg:
|
async with asyncio.TaskGroup() as tg:
|
||||||
|
|
@ -190,6 +196,7 @@ async def compare(
|
||||||
select=repo.select,
|
select=repo.select,
|
||||||
ignore=repo.ignore,
|
ignore=repo.ignore,
|
||||||
exclude=repo.exclude,
|
exclude=repo.exclude,
|
||||||
|
show_fixes=repo.show_fixes,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
check2 = tg.create_task(
|
check2 = tg.create_task(
|
||||||
|
|
@ -200,6 +207,7 @@ async def compare(
|
||||||
select=repo.select,
|
select=repo.select,
|
||||||
ignore=repo.ignore,
|
ignore=repo.ignore,
|
||||||
exclude=repo.exclude,
|
exclude=repo.exclude,
|
||||||
|
show_fixes=repo.show_fixes,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
except ExceptionGroup as e:
|
except ExceptionGroup as e:
|
||||||
|
|
@ -237,6 +245,9 @@ def read_projects_jsonl(projects_jsonl: Path) -> dict[str, Repository]:
|
||||||
repository["owner"]["login"],
|
repository["owner"]["login"],
|
||||||
repository["name"],
|
repository["name"],
|
||||||
None,
|
None,
|
||||||
|
select=repository.get("select"),
|
||||||
|
ignore=repository.get("ignore"),
|
||||||
|
exclude=repository.get("exclude"),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
assert "owner" in data, "Unknown ruff-usage-aggregate format"
|
assert "owner" in data, "Unknown ruff-usage-aggregate format"
|
||||||
|
|
@ -247,6 +258,9 @@ def read_projects_jsonl(projects_jsonl: Path) -> dict[str, Repository]:
|
||||||
data["owner"],
|
data["owner"],
|
||||||
data["repo"],
|
data["repo"],
|
||||||
data.get("ref"),
|
data.get("ref"),
|
||||||
|
select=data.get("select"),
|
||||||
|
ignore=data.get("ignore"),
|
||||||
|
exclude=data.get("exclude"),
|
||||||
)
|
)
|
||||||
return repositories
|
return repositories
|
||||||
|
|
||||||
|
|
@ -414,7 +428,8 @@ if __name__ == "__main__":
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
asyncio.run(
|
loop = asyncio.get_event_loop()
|
||||||
|
main_task = asyncio.ensure_future(
|
||||||
main(
|
main(
|
||||||
ruff1=args.ruff1,
|
ruff1=args.ruff1,
|
||||||
ruff2=args.ruff2,
|
ruff2=args.ruff2,
|
||||||
|
|
@ -422,3 +437,10 @@ if __name__ == "__main__":
|
||||||
checkouts=args.checkouts,
|
checkouts=args.checkouts,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
# https://stackoverflow.com/a/58840987/3549270
|
||||||
|
for signal in [SIGINT, SIGTERM]:
|
||||||
|
loop.add_signal_handler(signal, main_task.cancel)
|
||||||
|
try:
|
||||||
|
loop.run_until_complete(main_task)
|
||||||
|
finally:
|
||||||
|
loop.close()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue