Skip to content

Commit

Permalink
ENH: automatically sort pre-commit hooks (#235)
Browse files Browse the repository at this point in the history
* MAINT: remove redundant update step
  • Loading branch information
redeboer authored Dec 3, 2023
1 parent 87329b5 commit cf04dc0
Showing 1 changed file with 36 additions and 51 deletions.
87 changes: 36 additions & 51 deletions src/repoma/check_dev_files/precommit.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"""Check content of :code:`.pre-commit-config.yaml` and related files."""

from pathlib import Path
from textwrap import dedent
from typing import List, Set
from typing import Iterable, List, Optional, Set, Tuple

from ruamel.yaml.comments import CommentedMap, CommentedSeq
from ruamel.yaml.scalarstring import DoubleQuotedScalarString
Expand All @@ -13,49 +12,40 @@
from repoma.utilities.precommit import PrecommitConfig
from repoma.utilities.yaml import create_prettier_round_trip_yaml

__NON_SKIPPED_HOOKS = {
"editorconfig-checker",
}


def main() -> None:
cfg = PrecommitConfig.load()
executor = Executor()
executor(_check_plural_hooks_first, cfg)
executor(_check_single_hook_sorting, cfg)
executor(_check_skipped_hooks, cfg)
executor(_sort_hooks)
executor(_update_conda_environment, cfg)
executor(_update_precommit_ci_skip, cfg)
executor.finalize()


def _check_plural_hooks_first(config: PrecommitConfig) -> None:
if config.ci is None:
def _sort_hooks() -> None:
yaml = create_prettier_round_trip_yaml()
contents: CommentedMap = yaml.load(CONFIG_PATH.precommit)
repos: Optional[CommentedSeq] = contents.get("repos")
if repos is None:
return
plural_hook_repos = [r for r in config.repos if len(r.hooks) > 1]
n_plural_repos = len(plural_hook_repos)
if config.repos[:n_plural_repos] != plural_hook_repos:
msg = (
"Please bundle repos with multiple hooks at the top of the pre-commit"
" config"
)
sorted_repos: List[CommentedMap] = sorted(repos, key=__repo_def_sorting)
contents["repos"] = sorted_repos
if sorted_repos != repos:
yaml.dump(contents, CONFIG_PATH.precommit)
msg = f"Sorted pre-commit hooks in {CONFIG_PATH.precommit}"
raise PrecommitError(msg)


def _check_single_hook_sorting(config: PrecommitConfig) -> None:
if config.ci is None:
return
single_hook_repos = [r for r in config.repos if len(r.hooks) == 1]
expected_repo_order = sorted(
(r for r in single_hook_repos),
key=lambda r: r.hooks[0].id,
)
if single_hook_repos != expected_repo_order:
msg = "Pre-commit hooks are not sorted. Should be as follows:\n\n "
msg += "\n ".join(f"{r.hooks[0].id:20s} {r.repo}" for r in expected_repo_order)
raise PrecommitError(msg)
def __repo_def_sorting(repo_def: CommentedMap) -> Tuple[int, str]:
if repo_def["repo"] == "meta":
return (0, "meta")
hooks: CommentedSeq = repo_def["hooks"]
if len(hooks) > 1:
return 1, repo_def["repo"]
return (2, hooks[0]["id"])


def _check_skipped_hooks(config: PrecommitConfig) -> None:
def _update_precommit_ci_skip(config: PrecommitConfig) -> None:
if config.ci is None:
return
local_hooks = get_local_hooks(config)
Expand All @@ -73,26 +63,21 @@ def _check_skipped_hooks(config: PrecommitConfig) -> None:
return
existing_skips = __get_precommit_ci_skips(config)
if existing_skips != expected_skips:
yaml = create_prettier_round_trip_yaml()
contents = yaml.load(CONFIG_PATH.precommit)
ci_section: CommentedMap = contents["ci"]
if "skip" in ci_section.ca.items:
del ci_section.ca.items["skip"]
skips = CommentedSeq(sorted(expected_skips))
ci_section["skip"] = skips
contents.yaml_set_comment_before_after_key("repos", before="\n")
yaml.dump(contents, CONFIG_PATH.precommit)
msg = f"Updated ci.skip section in {CONFIG_PATH.precommit}"
raise PrecommitError(msg)
hooks_to_execute = __NON_SKIPPED_HOOKS & existing_skips
if hooks_to_execute:
msg = f"""
Please remove the following hooks from the ci.skip section of {CONFIG_PATH.precommit}:
{', '.join(sorted(hooks_to_execute))}
"""
msg = dedent(msg)
raise PrecommitError(msg)
__update_precommit_ci_skip(expected_skips)


def __update_precommit_ci_skip(expected_skips: Iterable[str]) -> None:
yaml = create_prettier_round_trip_yaml()
contents = yaml.load(CONFIG_PATH.precommit)
ci_section: CommentedMap = contents["ci"]
if "skip" in ci_section.ca.items:
del ci_section.ca.items["skip"]
skips = CommentedSeq(sorted(expected_skips))
ci_section["skip"] = skips
contents.yaml_set_comment_before_after_key("repos", before="\n")
yaml.dump(contents, CONFIG_PATH.precommit)
msg = f"Updated ci.skip section in {CONFIG_PATH.precommit}"
raise PrecommitError(msg)


def __get_precommit_ci_skips(config: PrecommitConfig) -> Set[str]:
Expand Down

0 comments on commit cf04dc0

Please sign in to comment.