-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Test against PDM 2.8.x - Fix reading and writing of `.pre-commit-config.yaml` - Ignore `local` hooks (#4) - Support files with document separator (`---`) - Move from `PyYAML` to `strictyaml`, partially validate the config file
- Loading branch information
Showing
18 changed files
with
812 additions
and
756 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
pdm 2.7.4 | ||
pdm 2.8.1 | ||
python 3.11.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,10 +4,13 @@ build-backend = "pdm.backend" | |
|
||
[project] | ||
name = "sync-pre-commit-lock" | ||
version = "0.1.2" | ||
version = "0.2.0" | ||
description = "PDM plugin to sync your pre-commit versions with your lockfile, and install them, all automatically." | ||
authors = [{ name = "Gabriel Dugny", email = "[email protected]" }] | ||
dependencies = ["PyYAML>=6.0", "tomli>=2.0.0; python_version < \"3.11\""] | ||
dependencies = [ | ||
"tomli>=2.0.0; python_version < \"3.11\"", | ||
"strictyaml>=1.7.3", | ||
] | ||
requires-python = ">=3.10" | ||
readme = "README.md" | ||
license = { file = "LICENSE" } | ||
|
@@ -48,6 +51,7 @@ pdm = [ | |
] | ||
[tool.pdm.dev-dependencies] | ||
dev = [ | ||
"PyYAML>=6.0", | ||
"black>=23.3.0", | ||
"mypy>=1.4.1", | ||
"ruff>=0.0.275", | ||
|
@@ -73,6 +77,10 @@ target-version = "py310" | |
[tool.mypy] | ||
strict = true | ||
|
||
[[tool.mypy.overrides]] | ||
module = "strictyaml" | ||
ignore_missing_imports = true | ||
|
||
[tool.black] | ||
line-length = 120 | ||
target-version = ["py310"] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
from __future__ import annotations | ||
|
||
import difflib | ||
from functools import cached_property | ||
from typing import TYPE_CHECKING, Any, NamedTuple | ||
|
||
import strictyaml as yaml | ||
from strictyaml import Any as AnyStrictYaml | ||
from strictyaml import MapCombined, Optional, Seq, Str | ||
|
||
from sync_pre_commit_lock.utils import normalize_git_url | ||
|
||
if TYPE_CHECKING: | ||
from pathlib import Path | ||
|
||
schema = MapCombined( | ||
{ | ||
Optional("repos"): Seq( | ||
MapCombined( | ||
{ | ||
"repo": Str(), | ||
Optional("rev"): Str(), | ||
}, | ||
Str(), | ||
AnyStrictYaml(), | ||
), | ||
) | ||
}, | ||
Str(), | ||
AnyStrictYaml(), | ||
) | ||
|
||
|
||
class PreCommitRepo(NamedTuple): | ||
repo: str | ||
rev: str # Check if is not loaded as float/int/other yolo | ||
|
||
|
||
class PreCommitHookConfig: | ||
def __init__( | ||
self, | ||
raw_file_contents: str, | ||
pre_commit_config_file_path: Path, | ||
) -> None: | ||
self.raw_file_contents = raw_file_contents | ||
self.yaml = yaml.dirty_load( | ||
raw_file_contents, schema=schema, allow_flow_style=True, label=str(pre_commit_config_file_path) | ||
) | ||
|
||
self.pre_commit_config_file_path = pre_commit_config_file_path | ||
|
||
@cached_property | ||
def original_file_lines(self) -> list[str]: | ||
return self.raw_file_contents.splitlines(keepends=True) | ||
|
||
@property | ||
def data(self) -> Any: | ||
return self.yaml.data | ||
|
||
@classmethod | ||
def from_yaml_file(cls, file_path: Path) -> PreCommitHookConfig: | ||
with file_path.open("r") as stream: | ||
file_contents = stream.read() | ||
|
||
return PreCommitHookConfig(file_contents, file_path) | ||
|
||
@cached_property | ||
def repos(self) -> list[PreCommitRepo]: | ||
"""Return the repos, excluding local repos.""" | ||
return [ | ||
PreCommitRepo(repo=repo["repo"], rev=repo["rev"]) for repo in (self.data["repos"] or []) if "rev" in repo | ||
] | ||
|
||
@cached_property | ||
def repos_normalized(self) -> set[PreCommitRepo]: | ||
return {PreCommitRepo(repo=normalize_git_url(repo.repo), rev=repo.rev) for repo in self.repos} | ||
|
||
@cached_property | ||
def document_start_offset(self) -> int: | ||
"""Return the line number where the YAML document starts.""" | ||
|
||
lines = self.raw_file_contents.split("\n") | ||
for i, line in enumerate(lines): | ||
# Trim leading/trailing whitespaces | ||
line = line.rstrip() | ||
# Skip if line is a comment or empty/whitespace | ||
if line.startswith("#") or line == "": | ||
continue | ||
# If line is '---', return line number + 1 | ||
if line == "---": | ||
return i + 1 | ||
return 0 | ||
|
||
def update_pre_commit_repo_versions(self, new_versions: dict[PreCommitRepo, str]) -> None: | ||
"""Fix the pre-commit hooks to match the lockfile. Preserve comments and formatting as much as possible.""" | ||
|
||
if len(new_versions) == 0: | ||
return | ||
|
||
original_lines = self.original_file_lines | ||
updated_lines = original_lines[:] | ||
|
||
for repo_rev in self.yaml["repos"]: | ||
if "rev" not in repo_rev: | ||
continue | ||
|
||
repo, rev = repo_rev["repo"], repo_rev["rev"] | ||
normalized_repo = PreCommitRepo(normalize_git_url(str(repo)), str(rev)) | ||
if normalized_repo not in new_versions: | ||
continue | ||
|
||
rev_line_number: int = rev.end_line + self.document_start_offset | ||
rev_line_idx: int = rev_line_number - 1 | ||
original_rev_line: str = updated_lines[rev_line_idx] | ||
updated_lines[rev_line_idx] = original_rev_line.replace(str(rev), new_versions[normalized_repo]) | ||
|
||
changes = difflib.ndiff(original_lines, updated_lines) | ||
change_count = sum(1 for change in changes if change[0] in ["+", "-"]) | ||
|
||
if change_count == 0: | ||
raise RuntimeError("No changes to write, this should not happen") | ||
with self.pre_commit_config_file_path.open("w") as stream: | ||
stream.writelines(updated_lines) |
33 changes: 33 additions & 0 deletions
33
tests/fixtures/sample_pre_commit_config/pre-commit-config-document-separator.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
|
||
# Many unused lines before document separator | ||
|
||
--- | ||
default_language_version: | ||
python: python3.11 | ||
repos: | ||
- repo: https://github.com/pre-commit/pre-commit-hooks | ||
rev: v4.4.0 | ||
hooks: | ||
- id: check-toml | ||
|
||
- repo: https://github.com/psf/black | ||
rev: 23.2.0 | ||
hooks: | ||
- id: black | ||
|
||
- repo: https://github.com/charliermarsh/ruff-pre-commit | ||
rev: 'v0.0.275' | ||
hooks: | ||
- id: ruff | ||
args: [--fix, --exit-non-zero-on-fix] | ||
|
||
- repo: local | ||
hooks: | ||
- id: mypy | ||
name: mypy | ||
entry: mypy | ||
args: [src, tests, --color-output] | ||
language: system | ||
types: [python] | ||
pass_filenames: false | ||
require_serial: true |
30 changes: 30 additions & 0 deletions
30
tests/fixtures/sample_pre_commit_config/pre-commit-config-start-empty-lines.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
|
||
|
||
|
||
default_language_version: | ||
python: python3.11 | ||
repos: | ||
- repo: https://github.com/pre-commit/pre-commit-hooks | ||
rev: v4.4.0 | ||
hooks: | ||
- id: check-toml | ||
- id: trailing-whitespace | ||
- id: check-executables-have-shebangs | ||
- id: debug-statements | ||
- id: end-of-file-fixer | ||
- id: check-added-large-files | ||
- id: check-merge-conflict | ||
- id: fix-byte-order-marker | ||
|
||
- repo: https://github.com/charliermarsh/ruff-pre-commit | ||
# Ruff version. | ||
rev: 'v0.0.277' | ||
hooks: | ||
- id: ruff | ||
args: [--fix, --exit-non-zero-on-fix] | ||
- repo: https://github.com/psf/black | ||
rev: 23.3.0 | ||
hooks: | ||
- id: black | ||
|
||
# XXX Fix the issue with documents |
Oops, something went wrong.