Skip to content

Commit

Permalink
FEAT: enforce Taplo as a TOML formatter (#116)
Browse files Browse the repository at this point in the history
* ENH: sort VS Code extensions

* FEAT: automatically update `ci.skip` section

* FIX: correct syntax of `any_of` value in PR linting workflow

* FIX: write VS Code instead of VSCode
  https://en.wikipedia.org/wiki/Visual_Studio_Code

* MAINT: remove `unwantedRecommendations`
  Seems to have no effect in VS Code

* MAINT: set PAT for style job

* MAINT: sort `_ConfigFilePaths` attributes

---------

Co-authored-by: GitHub <[email protected]>
  • Loading branch information
redeboer and web-flow authored Mar 8, 2023
1 parent 4ed7378 commit 9c365e7
Show file tree
Hide file tree
Showing 19 changed files with 181 additions and 65 deletions.
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"setuptools",
"showcode",
"showtags",
"taplo",
"unittests",
"venv"
]
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ jobs:
specific-pip-packages: ${{ inputs.specific-pip-packages }}
style:
if: inputs.specific-pip-packages == ''
secrets:
token: ${{ secrets.PAT }}
uses: ComPWA/actions/.github/workflows/pre-commit.yml@v1
4 changes: 2 additions & 2 deletions .github/workflows/pr-linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ jobs:
steps:
- uses: docker://agilepathway/pull-request-label-checker:latest
with:
any_of: 🐛 Bug,✨ Feature,⚙️ Enhancement,⚠️ Interface,❗ Behavior,📝 Docs,🔨
Maintenance,🖱️ DX
any_of: >-
🐛 Bug,✨ Feature,⚙️ Enhancement,⚠️ Interface,❗ Behavior,📝 Docs,🔨 Maintenance,🖱️ DX
none_of: Epic,💫 Good first issue
repo_token: ${{ secrets.GITHUB_TOKEN }}

Expand Down
2 changes: 1 addition & 1 deletion .gitpod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ github:

vscode:
extensions:
- bungcip.better-toml
- christian-kohler.path-intellisense
- cschleiden.vscode-github-actions
- davidanson.vscode-markdownlint
Expand All @@ -31,6 +30,7 @@ vscode:
- ryanluker.vscode-coverage-gutters
- stkb.rewrap
- streetsidesoftware.code-spell-checker
- tamasfe.even-better-toml
- travisillig.vscode-json-stable-stringify
- tyriar.sort-lines
- yzhang.markdown-all-in-one
10 changes: 7 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ ci:
autoupdate_commit_msg: "MAINT: autoupdate pre-commit hooks"
autoupdate_schedule: monthly
skip:
# local hooks
- check-dev-files
- flake8
- format-setup-cfg
- mypy
- pylint
- repoma-self-check
# hooks that don't work on pre-commit.ci
- pyright
- repoma-self-check
- taplo

repos:
- repo: meta
Expand Down Expand Up @@ -156,3 +155,8 @@ repos:
- id: pyupgrade
args:
- --py36-plus

- repo: https://github.com/ComPWA/mirrors-taplo
rev: v0.8.0
hooks:
- id: taplo
16 changes: 16 additions & 0 deletions .taplo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
exclude = [
"Cargo.toml",
"Manifest.toml",
"Project.toml",
"labels*.toml",
]

[formatting]
align_entries = false
array_auto_collapse = false
array_auto_expand = true
array_trailing_comma = true
column_width = 88
indent_string = " "
reorder_arrays = true
reorder_keys = true
5 changes: 2 additions & 3 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"recommendations": [
"bungcip.better-toml",
"christian-kohler.path-intellisense",
"cschleiden.vscode-github-actions",
"davidanson.vscode-markdownlint",
Expand All @@ -18,9 +17,9 @@
"ryanluker.vscode-coverage-gutters",
"stkb.rewrap",
"streetsidesoftware.code-spell-checker",
"tamasfe.even-better-toml",
"travisillig.vscode-json-stable-stringify",
"tyriar.sort-lines",
"yzhang.markdown-all-in-one"
],
"unwantedRecommendations": ["dbaeumer.vscode-eslint"]
]
}
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"src/repoma/.template/.cspell.json": true,
"src/repoma/.template/.gitpod.yml": true,
"src/repoma/.template/.prettierrc": true,
"src/repoma/.template/.taplo.toml": true,
"src/repoma/.template/commitlint.config.js": true
},
"telemetry.telemetryLevel": "off",
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ exclude = '''
include = '\.pyi?$'
preview = true
target-version = [
'py310',
'py311',
'py36',
'py37',
'py38',
'py39',
'py310',
'py311',
]

[tool.isort]
Expand Down
2 changes: 2 additions & 0 deletions src/repoma/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ jobs:
specific-pip-packages: ${{ inputs.specific-pip-packages }}
style:
if: inputs.specific-pip-packages == ''
secrets:
token: ${{ secrets.PAT }}
uses: ComPWA/actions/.github/workflows/pre-commit.yml@v1
1 change: 1 addition & 0 deletions src/repoma/.template/.taplo.toml
2 changes: 2 additions & 0 deletions src/repoma/check_dev_files/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
pyupgrade,
release_drafter,
setup_cfg,
toml,
tox,
update_pip_constraints,
)
Expand Down Expand Up @@ -167,6 +168,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
executor(pyupgrade.main)
executor(setup_cfg.main, args.ignore_author)
executor(tox.main)
executor(toml.main)
if executor.error_messages:
print(executor.merge_messages())
return 1
Expand Down
3 changes: 1 addition & 2 deletions src/repoma/check_dev_files/black.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ def _check_target_versions(config: dict) -> None:
target_versions = config.get("target-version", [])
supported_python_versions = get_supported_python_versions()
expected_target_versions = sorted(
("py" + s.replace(".", "") for s in supported_python_versions),
key=natural_sorting,
"py" + s.replace(".", "") for s in supported_python_versions
)
if target_versions != expected_target_versions:
error_message = dedent(
Expand Down
67 changes: 27 additions & 40 deletions src/repoma/check_dev_files/precommit.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
"""Check content of :code:`.pre-commit-config.yaml` and related files."""
from io import StringIO
from textwrap import dedent, indent
from typing import Iterable, List, Set
from textwrap import dedent
from typing import List, Set

import yaml
from ruamel.yaml.comments import CommentedMap, CommentedSeq

from repoma.errors import PrecommitError
from repoma.utilities import CONFIG_PATH
from repoma.utilities.precommit import PrecommitConfig
from repoma.utilities.yaml import create_prettier_round_trip_yaml

__NON_SKIPPED_HOOKS = {
"editorconfig-checker",
}
__SKIPPED_HOOKS = {
"pyright",
"taplo",
}


def main() -> None:
cfg = PrecommitConfig.load()
_check_plural_hooks_first(cfg)
_check_single_hook_sorting(cfg)
_check_local_hooks(cfg)
_check_skipped_hooks(cfg)


Expand Down Expand Up @@ -51,42 +51,35 @@ def _check_single_hook_sorting(config: PrecommitConfig) -> None:
raise PrecommitError(msg)


def _check_local_hooks(config: PrecommitConfig) -> None:
if config.ci is None:
return
local_hook_ids = get_local_hooks(config)
if len(local_hook_ids) == 0:
return
skipped_hooks = __get_precommit_ci_skips(config)
missing_hooks = set(local_hook_ids) - skipped_hooks
if missing_hooks:
msg = f"""
The ci section in {CONFIG_PATH.precommit} should skip local hooks. These local
hooks are missing: {', '.join(sorted(missing_hooks))}.
Please add at least the following entries to {CONFIG_PATH.precommit}:
"""
msg = dedent(msg).replace("\n", " ")
expected_content = __dump_expected_skips(local_hook_ids)
raise PrecommitError(msg + "\n\n" + expected_content)


def _check_skipped_hooks(config: PrecommitConfig) -> None:
if config.ci is None:
return
local_hooks = get_local_hooks(config)
non_functional_hooks = get_non_functional_hooks(config)
skipped_hooks = __get_precommit_ci_skips(config)
missing_hooks = set(non_functional_hooks) - skipped_hooks
if missing_hooks:
expected_skips = sorted(set(non_functional_hooks) | set(local_hooks))
if not expected_skips:
return
existing_skips = __get_precommit_ci_skips(config)
missing_skips = set(expected_skips) - existing_skips
if missing_skips:
yaml = create_prettier_round_trip_yaml()
contents: CommentedMap = 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"""
The ci section in {CONFIG_PATH.precommit} should skip a few hooks that don't
work on pre-commit.ci. The following hooks are not listed:
{', '.join(sorted(missing_hooks))}. Please add at least the following entries
to {CONFIG_PATH.precommit}:
The following hooks don't work on pre-commit.ci and have been added to the
`ci.skip` section of {CONFIG_PATH.precommit}:
"""
msg = dedent(msg)
expected_content = __dump_expected_skips(non_functional_hooks)
raise PrecommitError(msg + "\n\n" + expected_content)
hooks_to_execute = __NON_SKIPPED_HOOKS & skipped_hooks
sep = "\n - "
msg += sep + sep.join(sorted(missing_skips))
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}:
Expand All @@ -97,12 +90,6 @@ def _check_skipped_hooks(config: PrecommitConfig) -> None:
raise PrecommitError(msg)


def __dump_expected_skips(hooks: Iterable[str]) -> str:
stream = StringIO()
yaml.dump({"ci": {"skip": sorted(hooks)}}, stream, sort_keys=False)
return indent(stream.getvalue(), prefix=" ")


def __get_precommit_ci_skips(config: PrecommitConfig) -> Set[str]:
if config.ci is None:
raise ValueError("Pre-commit config does not contain a ci section")
Expand Down
101 changes: 101 additions & 0 deletions src/repoma/check_dev_files/toml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""Configuration for working with TOML files."""

import os
import shutil
from pathlib import Path
from textwrap import dedent
from typing import List, Union

from ruamel.yaml.comments import CommentedSeq

from repoma.errors import PrecommitError
from repoma.utilities import CONFIG_PATH, REPOMA_DIR
from repoma.utilities.executor import Executor
from repoma.utilities.precommit import PrecommitConfig
from repoma.utilities.vscode import (
add_vscode_extension_recommendation,
remove_vscode_extension_recommendation,
)
from repoma.utilities.yaml import create_prettier_round_trip_yaml

__INCORRECT_TAPLO_CONFIG_PATHS = [
"taplo.toml",
]
__TRIGGER_FILES: List[Union[Path, str]] = [
"pyproject.toml",
CONFIG_PATH.taplo,
*__INCORRECT_TAPLO_CONFIG_PATHS,
]


def main() -> None:
if not any(os.path.exists(f) for f in __TRIGGER_FILES):
return
executor = Executor()
executor(_rename_taplo_config)
executor(_update_taplo_config)
executor(_update_precommit_config)
executor(_update_vscode_extensions)
if executor.error_messages:
raise PrecommitError(executor.merge_messages())


def _rename_taplo_config() -> None:
for path in __INCORRECT_TAPLO_CONFIG_PATHS:
if not os.path.exists(path):
continue
shutil.move(path, CONFIG_PATH.taplo)
raise PrecommitError(f"Renamed {path} to {CONFIG_PATH.taplo}")


def _update_taplo_config() -> None:
template_path = REPOMA_DIR / ".template" / CONFIG_PATH.taplo
if not CONFIG_PATH.taplo.exists():
shutil.copyfile(template_path, CONFIG_PATH.taplo)
raise PrecommitError(f"Added {CONFIG_PATH.taplo} config for TOML formatting")
with open(template_path) as f:
expected_content = f.read()
with open(CONFIG_PATH.taplo) as f:
existing_content = f.read()
if existing_content != expected_content:
with open(CONFIG_PATH.prettier, "w") as stream:
stream.write(expected_content)
raise PrecommitError(f"Updated {CONFIG_PATH.prettier} config file")


def _update_precommit_config() -> None:
if not os.path.exists(CONFIG_PATH.precommit):
return
existing_config = PrecommitConfig.load()
repo_url = "https://github.com/ComPWA/mirrors-taplo"
if existing_config.find_repo(repo_url) is not None:
return
yaml = create_prettier_round_trip_yaml()
config = yaml.load(CONFIG_PATH.precommit)
hook_definition = {
"repo": repo_url,
"rev": "v0.8.0",
"hooks": [{"id": "taplo"}],
}
repos: CommentedSeq = config["repos"]
repos.append(hook_definition)
repo_idx = len(repos) - 1
repos.yaml_set_comment_before_after_key(repo_idx, before="\n")
yaml.dump(config, CONFIG_PATH.precommit)
msg = f"""
Added Taplo TOML formatter as a pre-commit hook. Please run
pre-commit autoupdate --repo {repo_url}
to update it to the latest version.
"""
raise PrecommitError(dedent(msg).strip())


def _update_vscode_extensions() -> None:
# cspell:ignore bungcip tamasfe
executor = Executor()
executor(add_vscode_extension_recommendation, "tamasfe.even-better-toml")
executor(remove_vscode_extension_recommendation, "bungcip.better-toml")
if executor.error_messages:
raise PrecommitError(executor.merge_messages())
Loading

0 comments on commit 9c365e7

Please sign in to comment.