From a60804ca34eee639f46a1eadb0e53ec2267d338e Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Wed, 7 May 2025 17:04:15 -0400 Subject: [PATCH 01/10] infra: .gitignore --- .gitignore | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 170 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index b486fe1..0a19790 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,174 @@ -# Created by .ignore support plugin (hsz.mobi) +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class -# User-specific stuff -.idea +# C extensions +*.so -# IntelliJ -out/ +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST -# JIRA plugin -atlassian-ide-plugin.xml +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec -venv \ No newline at end of file +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc From b5acae6ef111e4434926f4d57a14d23b831c8bb3 Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Wed, 7 May 2025 17:05:02 -0400 Subject: [PATCH 02/10] infra: adding standard pre-commit-config --- .pre-commit-config.yaml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..1e99a28 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,41 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: debug-statements + - id: double-quote-string-fixer + - id: name-tests-test + - id: requirements-txt-fixer +- repo: https://github.com/asottile/setup-cfg-fmt + rev: v2.8.0 + hooks: + - id: setup-cfg-fmt +- repo: https://github.com/asottile/reorder-python-imports + rev: v3.14.0 + hooks: + - id: reorder-python-imports + args: [--py39-plus, --add-import, 'from __future__ import annotations'] +- repo: https://github.com/asottile/add-trailing-comma + rev: v3.1.0 + hooks: + - id: add-trailing-comma +- repo: https://github.com/asottile/pyupgrade + rev: v3.19.1 + hooks: + - id: pyupgrade + args: [--py39-plus] +- repo: https://github.com/hhatto/autopep8 + rev: v2.3.2 + hooks: + - id: autopep8 +- repo: https://github.com/PyCQA/flake8 + rev: 7.2.0 + hooks: + - id: flake8 +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.15.0 + hooks: + - id: mypy From d18455587fd7920499b26e329e353a71d4d7325f Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Thu, 8 May 2025 08:46:35 -0400 Subject: [PATCH 03/10] infra: add .pre-commit-config.yaml for python --- .pre-commit-config.yaml | 48 ++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e99a28..8977427 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,41 +1,29 @@ repos: -- repo: https://github.com/pre-commit/pre-commit-hooks + - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: debug-statements - - id: double-quote-string-fixer - - id: name-tests-test - - id: requirements-txt-fixer -- repo: https://github.com/asottile/setup-cfg-fmt + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: debug-statements + - id: double-quote-string-fixer + - id: name-tests-test + - id: requirements-txt-fixer + - repo: https://github.com/asottile/setup-cfg-fmt rev: v2.8.0 hooks: - - id: setup-cfg-fmt -- repo: https://github.com/asottile/reorder-python-imports - rev: v3.14.0 - hooks: - - id: reorder-python-imports - args: [--py39-plus, --add-import, 'from __future__ import annotations'] -- repo: https://github.com/asottile/add-trailing-comma + - id: setup-cfg-fmt + - repo: https://github.com/asottile/add-trailing-comma rev: v3.1.0 hooks: - - id: add-trailing-comma -- repo: https://github.com/asottile/pyupgrade + - id: add-trailing-comma + - repo: https://github.com/asottile/pyupgrade rev: v3.19.1 hooks: - - id: pyupgrade + - id: pyupgrade args: [--py39-plus] -- repo: https://github.com/hhatto/autopep8 - rev: v2.3.2 - hooks: - - id: autopep8 -- repo: https://github.com/PyCQA/flake8 - rev: 7.2.0 - hooks: - - id: flake8 -- repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.11.8 hooks: - - id: mypy + - id: ruff + - id: ruff-format From e4f181ee42112920e4b4162f187d43db7acb8c66 Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Thu, 8 May 2025 08:50:14 -0400 Subject: [PATCH 04/10] fix: ARG_ADDITIONAL_ARGS bug, fmt, lint --- .pre-commit-config.yaml | 2 +- pre_commit_hooks/__init__.py | 1 - pre_commit_hooks/runner.py | 26 ++++++++++++--- pre_commit_hooks/sbt_fatal_warnings.py | 40 +++++++++++++++-------- pre_commit_hooks/sbt_wartremover.py | 45 +++++++++++++++++--------- pre_commit_hooks/scalafmt.py | 20 ++++++++---- pre_commit_hooks/scalafmt_apply.py | 23 +++++++++---- 7 files changed, 109 insertions(+), 48 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8977427..49ae3db 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,6 @@ repos: - id: end-of-file-fixer - id: check-yaml - id: debug-statements - - id: double-quote-string-fixer - id: name-tests-test - id: requirements-txt-fixer - repo: https://github.com/asottile/setup-cfg-fmt @@ -26,4 +25,5 @@ repos: rev: v0.11.8 hooks: - id: ruff + args: [--fix] - id: ruff-format diff --git a/pre_commit_hooks/__init__.py b/pre_commit_hooks/__init__.py index 8b13789..e69de29 100644 --- a/pre_commit_hooks/__init__.py +++ b/pre_commit_hooks/__init__.py @@ -1 +0,0 @@ - diff --git a/pre_commit_hooks/runner.py b/pre_commit_hooks/runner.py index 850593b..f1ebd53 100644 --- a/pre_commit_hooks/runner.py +++ b/pre_commit_hooks/runner.py @@ -1,23 +1,39 @@ import subprocess -def run_sbt_command(task_def, missing_plugin_check_string=None, missing_plugin_error_msg=None): - sbt_process = subprocess.run([f"sbt '{task_def}'"], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) +def run_sbt_command( + task_def, + missing_plugin_check_string=None, + missing_plugin_error_msg=None, +): + sbt_process = subprocess.run( + [f"sbt '{task_def}'"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True, + ) raw_output = sbt_process.stdout.decode("utf-8") - if missing_plugin_check_string is not None and missing_plugin_check_string in raw_output: + if ( + missing_plugin_check_string is not None + and missing_plugin_check_string in raw_output + ): print(missing_plugin_error_msg) else: print(raw_output) return sbt_process.returncode + def run_git_add_modified(): """Adds stages files if changes are detected.""" try: # Check if there are modified files before running git add -u - status = subprocess.run(["git", "status", "--porcelain"], capture_output=True, text=True) + status = subprocess.run( + ["git", "status", "--porcelain"], + capture_output=True, + text=True, + ) if status.stdout.strip(): # If there are modified files print("Staged formatted files.") diff --git a/pre_commit_hooks/sbt_fatal_warnings.py b/pre_commit_hooks/sbt_fatal_warnings.py index 08514e5..83a771e 100644 --- a/pre_commit_hooks/sbt_fatal_warnings.py +++ b/pre_commit_hooks/sbt_fatal_warnings.py @@ -1,26 +1,40 @@ +from __future__ import annotations + import argparse -from pre_commit_hooks.runner import * -ARG_ADDITIONAL_ARGS = 'add_arg' -ARG_COMPILE_SCOPE = 'scope' -DEFAULT_COMPILE_SCOPE = 'test:compile' +from pre_commit_hooks.runner import run_sbt_command + +ARG_ADDITIONAL_ARGS = "add_arg" +ARG_COMPILE_SCOPE = "scope" +DEFAULT_COMPILE_SCOPE = "test:compile" def main(argv=None): - arg_p = argparse.ArgumentParser(description='Run SBT wartremover') - arg_p.add_argument(f'--{ARG_ADDITIONAL_ARGS}', action='append', - help='Additional arguments for scalac, such as warning flags, can be multi-valued.') - arg_p.add_argument(f'--{ARG_COMPILE_SCOPE}', default=DEFAULT_COMPILE_SCOPE, - help=f'Compile scope for the check. Default: {DEFAULT_COMPILE_SCOPE}') + arg_p = argparse.ArgumentParser(description="Run SBT wartremover") + arg_p.add_argument( + f"--{ARG_ADDITIONAL_ARGS}", + action="append", + help="Additional arguments for scalac, such as warning flags, can be multi-valued.", + ) + arg_p.add_argument( + f"--{ARG_COMPILE_SCOPE}", + default=DEFAULT_COMPILE_SCOPE, + help=f"Compile scope for the check. Default: {DEFAULT_COMPILE_SCOPE}", + ) args = arg_p.parse_args(argv).__dict__ - args[ARG_ADDITIONAL_ARGS].append("-Xfatal-warnings") + addtl_args = args.get(ARG_ADDITIONAL_ARGS, []) + if not addtl_args: + addtl_args = [] + addtl_args.append("-Xfatal-warnings") - add_args = ", ".join(f'"{a}"' for a in args[ARG_ADDITIONAL_ARGS]) + add_args = ", ".join(f'"{a}"' for a in addtl_args) - return run_sbt_command(f'; clean ; set scalacOptions ++= Seq({add_args}) ; {args[ARG_COMPILE_SCOPE]}') + return run_sbt_command( + f"; clean ; set scalacOptions ++= Seq({add_args}) ; {addtl_args}", + ) -if __name__ == '__main__': +if __name__ == "__main__": exit(main()) diff --git a/pre_commit_hooks/sbt_wartremover.py b/pre_commit_hooks/sbt_wartremover.py index ede80f6..4ba55b5 100644 --- a/pre_commit_hooks/sbt_wartremover.py +++ b/pre_commit_hooks/sbt_wartremover.py @@ -1,29 +1,44 @@ +from __future__ import annotations + import argparse -from pre_commit_hooks.runner import * -from colorama import init as colorama_init, Fore -ARG_WARTREMOVER_ARGS = 'warts' -ARG_COMPILE_SCOPE = 'scope' -DEFAULT_WARTREMOVER_ARGS = 'Warts.unsafe' -DEFAULT_COMPILE_SCOPE = 'test:compile' -MISSING_PLUGIN_CHECK_STRING = 'error: not found: value wartremoverErrors' -MISSING_PLUGIN_ERROR_MSG = f'{Fore.RED}ERROR: wartremover SBT plugin not present! See {Fore.BLUE}https://www.wartremover.org/doc/install-setup.html{Fore.RED} for installation instructions.' +from colorama import Fore +from colorama import init as colorama_init + +from pre_commit_hooks.runner import run_sbt_command + +ARG_WARTREMOVER_ARGS = "warts" +ARG_COMPILE_SCOPE = "scope" +DEFAULT_WARTREMOVER_ARGS = "Warts.unsafe" +DEFAULT_COMPILE_SCOPE = "test:compile" +MISSING_PLUGIN_CHECK_STRING = "error: not found: value wartremoverErrors" +MISSING_PLUGIN_ERROR_MSG = f"{Fore.RED}ERROR: wartremover SBT plugin not present! See {Fore.BLUE}https://www.wartremover.org/doc/install-setup.html{Fore.RED} for installation instructions." def main(argv=None): colorama_init() - arg_p = argparse.ArgumentParser(description='Run SBT wartremover') - arg_p.add_argument(f'--{ARG_WARTREMOVER_ARGS}', default=DEFAULT_WARTREMOVER_ARGS, - help=f'Value for wartremoverErrors, as per https://www.wartremover.org/doc/install-setup.html . Default: {DEFAULT_WARTREMOVER_ARGS}') - arg_p.add_argument(f'--{ARG_COMPILE_SCOPE}', default=DEFAULT_COMPILE_SCOPE, - help=f'Compile scope for the wartremover check. Default: {DEFAULT_COMPILE_SCOPE}') + arg_p = argparse.ArgumentParser(description="Run SBT wartremover") + arg_p.add_argument( + f"--{ARG_WARTREMOVER_ARGS}", + default=DEFAULT_WARTREMOVER_ARGS, + help=f"Value for wartremoverErrors, as per https://www.wartremover.org/doc/install-setup.html . Default: {DEFAULT_WARTREMOVER_ARGS}", + ) + arg_p.add_argument( + f"--{ARG_COMPILE_SCOPE}", + default=DEFAULT_COMPILE_SCOPE, + help=f"Compile scope for the wartremover check. Default: {DEFAULT_COMPILE_SCOPE}", + ) arg_p.print_help() args = arg_p.parse_args(argv).__dict__ - return run_sbt_command(f'; clean ; set wartremoverErrors ++= {args[ARG_WARTREMOVER_ARGS]}; {args[ARG_COMPILE_SCOPE]}', MISSING_PLUGIN_CHECK_STRING, MISSING_PLUGIN_ERROR_MSG) + return run_sbt_command( + f"; clean ; set wartremoverErrors ++= {args[ARG_WARTREMOVER_ARGS]}; {args[ARG_COMPILE_SCOPE]}", + MISSING_PLUGIN_CHECK_STRING, + MISSING_PLUGIN_ERROR_MSG, + ) -if __name__ == '__main__': +if __name__ == "__main__": exit(main()) diff --git a/pre_commit_hooks/scalafmt.py b/pre_commit_hooks/scalafmt.py index c0e9e29..e27d000 100644 --- a/pre_commit_hooks/scalafmt.py +++ b/pre_commit_hooks/scalafmt.py @@ -1,16 +1,24 @@ +from __future__ import annotations + +from colorama import Fore +from colorama import init as colorama_init + from pre_commit_hooks.runner import run_sbt_command -from colorama import init as colorama_init, Fore -TASK_SCALAFMT = 'scalafmtCheckAll' -MISSING_PLUGIN_CHECK_STRING = 'Not a valid key: scalafmtCheck' -MISSING_PLUGIN_ERROR_MSG = f'{Fore.RED}ERROR: scalafmt SBT plugin not present! See {Fore.BLUE}https://scalameta.org/scalafmt/docs/installation.html#sbt{Fore.RED} for installation instructions.' +TASK_SCALAFMT = "scalafmtCheckAll" +MISSING_PLUGIN_CHECK_STRING = "Not a valid key: scalafmtCheck" +MISSING_PLUGIN_ERROR_MSG = f"{Fore.RED}ERROR: scalafmt SBT plugin not present! See {Fore.BLUE}https://scalameta.org/scalafmt/docs/installation.html#sbt{Fore.RED} for installation instructions." def main(argv=None): colorama_init() - return run_sbt_command(f'; clean ; {TASK_SCALAFMT}', MISSING_PLUGIN_CHECK_STRING, MISSING_PLUGIN_ERROR_MSG) + return run_sbt_command( + f"; clean ; {TASK_SCALAFMT}", + MISSING_PLUGIN_CHECK_STRING, + MISSING_PLUGIN_ERROR_MSG, + ) -if __name__ == '__main__': +if __name__ == "__main__": exit(main()) diff --git a/pre_commit_hooks/scalafmt_apply.py b/pre_commit_hooks/scalafmt_apply.py index f7591d2..e858384 100644 --- a/pre_commit_hooks/scalafmt_apply.py +++ b/pre_commit_hooks/scalafmt_apply.py @@ -1,19 +1,28 @@ -from pre_commit_hooks.runner import run_sbt_command, run_git_add_modified -from colorama import init as colorama_init, Fore +from __future__ import annotations -TASK_SCALAFMT = 'scalafmtAll' -MISSING_PLUGIN_CHECK_STRING = 'Not a valid key: scalafmtAll' -MISSING_PLUGIN_ERROR_MSG = f'{Fore.RED}ERROR: scalafmt SBT plugin not present! See {Fore.BLUE}https://scalameta.org/scalafmt/docs/installation.html#sbt{Fore.RED} for installation instructions.' +from colorama import Fore +from colorama import init as colorama_init + +from pre_commit_hooks.runner import run_git_add_modified +from pre_commit_hooks.runner import run_sbt_command + +TASK_SCALAFMT = "scalafmtAll" +MISSING_PLUGIN_CHECK_STRING = "Not a valid key: scalafmtAll" +MISSING_PLUGIN_ERROR_MSG = f"{Fore.RED}ERROR: scalafmt SBT plugin not present! See {Fore.BLUE}https://scalameta.org/scalafmt/docs/installation.html#sbt{Fore.RED} for installation instructions." def main(argv=None): colorama_init() - sbt = run_sbt_command(f'; clean ; {TASK_SCALAFMT}', MISSING_PLUGIN_CHECK_STRING, MISSING_PLUGIN_ERROR_MSG) + sbt = run_sbt_command( + f"; clean ; {TASK_SCALAFMT}", + MISSING_PLUGIN_CHECK_STRING, + MISSING_PLUGIN_ERROR_MSG, + ) run_git_add_modified() return sbt -if __name__ == '__main__': +if __name__ == "__main__": exit(main()) From 5ff28129c145ca7e07b312a1da4334098ae9f07b Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Thu, 8 May 2025 08:54:03 -0400 Subject: [PATCH 05/10] fix: stages, fmt for .pre-commit-hooks.yaml --- .pre-commit-hooks.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 3f6b553..ddc9402 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,40 +1,40 @@ - id: sbt-fatal-warnings name: Scala fatal warnings - stages: [commit,push] + stages: [pre-commit, pre-push] language: python entry: sbt-fatal-warnings pass_filenames: false always_run: true - minimum_pre_commit_version: '0.19.0' + minimum_pre_commit_version: "0.19.0" - id: sbt-unused-imports name: Scala unused imports (+ fatal warnings) - stages: [commit,push] + stages: [pre-commit, pre-push] language: python entry: sbt-fatal-warnings --add_arg='-Ywarn-unused-import' pass_filenames: false always_run: true - minimum_pre_commit_version: '0.19.0' + minimum_pre_commit_version: "0.19.0" - id: sbt-scalafmt name: scalafmt formatting check - stages: [commit,push] + stages: [pre-commit, pre-push] language: python entry: scalafmt pass_filenames: false always_run: true - minimum_pre_commit_version: '0.19.0' + minimum_pre_commit_version: "0.19.0" - id: sbt-wartremover name: Scala WartRemover plugin check + stages: [pre-commit, pre-push] language: python - stages: [commit,push] entry: sbt-wartremover pass_filenames: false always_run: true - minimum_pre_commit_version: '0.19.0' + minimum_pre_commit_version: "0.19.0" - id: sbt-scalafmt-apply name: scalafmt formatting fix - stages: [commit,push] + stages: [pre-commit, pre-push] language: python entry: scalafmt-apply pass_filenames: false always_run: true - minimum_pre_commit_version: '0.19.0' + minimum_pre_commit_version: "0.19.0" From 9762ad7ec3734788e4c62fb88386f23544a88d92 Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Thu, 8 May 2025 08:55:21 -0400 Subject: [PATCH 06/10] docs: update docs, setup version --- README.adoc | 4 ++-- setup.cfg | 11 +++++++++-- setup.py | 3 +++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/README.adoc b/README.adoc index 1ba97e3..dddf71a 100644 --- a/README.adoc +++ b/README.adoc @@ -2,7 +2,7 @@ :repoRoot: https://github.com/softwaremill/scala-pre-commit-hooks :repoMaster: {repoRoot}/blob/master :defaultScope: test:compile -:currentVersion: v0.3.0 +:currentVersion: v0.5.0 == What for? @@ -37,7 +37,7 @@ To add one or more of the hooks into your repo: ..pre-commit-config.yaml ---- repos: -- repo: https://github.com/pre-commit/pre-commit-hooks +- repo: https://github.com/softwaremill/scala-pre-commit-hooks rev: {currentVersion} default_phase: push #change to commit if desired hooks: #mix and match any of the following: diff --git a/setup.cfg b/setup.cfg index 7e99faf..49cb88c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,12 +1,19 @@ [metadata] name = pre_commit_hooks -version = 0.8.0 +version = 0.5.0 +long_description = file: README.adoc +long_description_content_type = text/plain +license = Apache-2.0 +license_files = LICENSE +classifiers = + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only [options] packages = find: install_requires = colorama -python_requires = >=3.6 +python_requires = >=3.9 [options.entry_points] console_scripts = diff --git a/setup.py b/setup.py index 8bf1ba9..a03590f 100644 --- a/setup.py +++ b/setup.py @@ -1,2 +1,5 @@ +from __future__ import annotations + from setuptools import setup + setup() From 64809e5b635178d5c31294a4e4f2e18fae0ecf98 Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Thu, 8 May 2025 09:05:47 -0400 Subject: [PATCH 07/10] minor: aliases --- .pre-commit-hooks.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index ddc9402..71837cd 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -15,6 +15,7 @@ always_run: true minimum_pre_commit_version: "0.19.0" - id: sbt-scalafmt + alias: scalafmt name: scalafmt formatting check stages: [pre-commit, pre-push] language: python @@ -31,6 +32,7 @@ always_run: true minimum_pre_commit_version: "0.19.0" - id: sbt-scalafmt-apply + alias: scalafmt-apply name: scalafmt formatting fix stages: [pre-commit, pre-push] language: python From c4da65698a60e46fec96d6cdee9d6ac236d5cf2b Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Thu, 8 May 2025 09:06:30 -0400 Subject: [PATCH 08/10] fix: sbt_fatal_warnings.py arguments --- pre_commit_hooks/runner.py | 6 +++--- pre_commit_hooks/sbt_fatal_warnings.py | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pre_commit_hooks/runner.py b/pre_commit_hooks/runner.py index f1ebd53..b34af6f 100644 --- a/pre_commit_hooks/runner.py +++ b/pre_commit_hooks/runner.py @@ -2,9 +2,9 @@ def run_sbt_command( - task_def, - missing_plugin_check_string=None, - missing_plugin_error_msg=None, + task_def: str, + missing_plugin_check_string: str | None = None, + missing_plugin_error_msg: str | None = None, ): sbt_process = subprocess.run( [f"sbt '{task_def}'"], diff --git a/pre_commit_hooks/sbt_fatal_warnings.py b/pre_commit_hooks/sbt_fatal_warnings.py index 83a771e..03c41fe 100644 --- a/pre_commit_hooks/sbt_fatal_warnings.py +++ b/pre_commit_hooks/sbt_fatal_warnings.py @@ -27,12 +27,13 @@ def main(argv=None): addtl_args = args.get(ARG_ADDITIONAL_ARGS, []) if not addtl_args: addtl_args = [] - addtl_args.append("-Xfatal-warnings") + if "-Xfatal-warnings" not in addtl_args: + addtl_args.append("-Xfatal-warnings") add_args = ", ".join(f'"{a}"' for a in addtl_args) return run_sbt_command( - f"; clean ; set scalacOptions ++= Seq({add_args}) ; {addtl_args}", + f"; clean ; set scalacOptions ++= Seq({add_args})", ) From cc82d1d5dfe52e4415a236745a40a17c0c56b656 Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Thu, 8 May 2025 17:23:20 -0400 Subject: [PATCH 09/10] feat: --project-dir, --no-clean --- README.adoc | 7 ++++ pre_commit_hooks/runner.py | 44 ++++++++++++++++++++++++++ pre_commit_hooks/sbt_fatal_warnings.py | 38 ++++++++++++---------- pre_commit_hooks/sbt_wartremover.py | 40 +++++++++++++---------- pre_commit_hooks/scalafmt.py | 14 +++++--- pre_commit_hooks/scalafmt_apply.py | 16 +++++++--- 6 files changed, 117 insertions(+), 42 deletions(-) diff --git a/README.adoc b/README.adoc index dddf71a..d35a7ab 100644 --- a/README.adoc +++ b/README.adoc @@ -47,6 +47,7 @@ repos: args: [--scope={defaultScope}] - id: sbt-scalafmt - id: sbt-scalafmt-apply + args: [--no-clean] - id: sbt-wartremover #arguments are optional args: [--warts=Warts.unsafe, --scope={defaultScope}] ---- @@ -59,6 +60,12 @@ All hooks except for `sbt-scalafmt` and `sbt-scalafmt-apply` have the optional ` are relevant for the hook's check. The default is `{defaultScope}`. -- +[NOTE] +-- +All hooks have an optional `project-dir` arugument, which is useful for monorepos that do not have a `build.sbt` in their root. +All hooks have an optional `no-clean` arugument, which won't automatically run an `sbt clean` before executing the command. +-- + [IMPORTANT] -- Steps 1-2 are only really required for the person setting up the _pre-commit library_ integration. _pre-commit library_ plugins, once installed, are normal Git hooks and are thus "visible" to everyone using the repo. diff --git a/pre_commit_hooks/runner.py b/pre_commit_hooks/runner.py index b34af6f..ea9d472 100644 --- a/pre_commit_hooks/runner.py +++ b/pre_commit_hooks/runner.py @@ -1,16 +1,60 @@ +import argparse import subprocess +from dataclasses import dataclass, field +from typing import Callable + + +@dataclass +class Opts: + project_dir: str | None = None + clean: bool = True + varargs: dict = field(default_factory=dict) + + +def default_argparse( + description: str, + argv=None, + additional_args: list[Callable[[argparse.ArgumentParser], None]] = [], +) -> Opts: + arg_p = argparse.ArgumentParser(description=description) + arg_p.add_argument( + "--project-dir", + default=None, + help="Path to build.sbt. Default: Project root", + ) + arg_p.add_argument( + "--no-clean", + action="store_true", + default=False, + help="Turn off sbt clean. Default: False", + ) + for fn in additional_args: + fn(arg_p) + varags = vars(arg_p.parse_args(argv)) + return Opts( + project_dir=varags.pop("project_dir", None), + clean=not varags.pop("no_clean", False), + varargs=varags, + ) def run_sbt_command( task_def: str, missing_plugin_check_string: str | None = None, missing_plugin_error_msg: str | None = None, + opts: Opts = Opts(), ): + print(f"Running SBT command: {task_def} with options: {opts}") + if opts.clean: + task_def = f"; clean ; {task_def}" + else: + task_def = f"; {task_def}" sbt_process = subprocess.run( [f"sbt '{task_def}'"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, + cwd=opts.project_dir, ) raw_output = sbt_process.stdout.decode("utf-8") diff --git a/pre_commit_hooks/sbt_fatal_warnings.py b/pre_commit_hooks/sbt_fatal_warnings.py index 03c41fe..e457953 100644 --- a/pre_commit_hooks/sbt_fatal_warnings.py +++ b/pre_commit_hooks/sbt_fatal_warnings.py @@ -2,7 +2,7 @@ import argparse -from pre_commit_hooks.runner import run_sbt_command +from pre_commit_hooks.runner import default_argparse, run_sbt_command ARG_ADDITIONAL_ARGS = "add_arg" ARG_COMPILE_SCOPE = "scope" @@ -10,21 +10,26 @@ def main(argv=None): - arg_p = argparse.ArgumentParser(description="Run SBT wartremover") - arg_p.add_argument( - f"--{ARG_ADDITIONAL_ARGS}", - action="append", - help="Additional arguments for scalac, such as warning flags, can be multi-valued.", + def arg_append(arg_p: argparse.ArgumentParser) -> None: + arg_p.add_argument( + f"--{ARG_ADDITIONAL_ARGS}", + action="append", + help="Additional arguments for scalac, such as warning flags, can be multi-valued.", + ) + + def arg_compile_scope(arg_p: argparse.ArgumentParser) -> None: + arg_p.add_argument( + f"--{ARG_COMPILE_SCOPE}", + default=DEFAULT_COMPILE_SCOPE, + help=f"Compile scope for the check. Default: {DEFAULT_COMPILE_SCOPE}", + ) + + args = default_argparse( + description="Run SBT wartremover", + argv=argv, + additional_args=[arg_append, arg_compile_scope], ) - arg_p.add_argument( - f"--{ARG_COMPILE_SCOPE}", - default=DEFAULT_COMPILE_SCOPE, - help=f"Compile scope for the check. Default: {DEFAULT_COMPILE_SCOPE}", - ) - - args = arg_p.parse_args(argv).__dict__ - - addtl_args = args.get(ARG_ADDITIONAL_ARGS, []) + addtl_args = args.varargs.get(ARG_ADDITIONAL_ARGS, []) if not addtl_args: addtl_args = [] if "-Xfatal-warnings" not in addtl_args: @@ -33,7 +38,8 @@ def main(argv=None): add_args = ", ".join(f'"{a}"' for a in addtl_args) return run_sbt_command( - f"; clean ; set scalacOptions ++= Seq({add_args})", + task_def=f"set scalacOptions ++= Seq({add_args})", + opts=args, ) diff --git a/pre_commit_hooks/sbt_wartremover.py b/pre_commit_hooks/sbt_wartremover.py index 4ba55b5..2441cc9 100644 --- a/pre_commit_hooks/sbt_wartremover.py +++ b/pre_commit_hooks/sbt_wartremover.py @@ -5,7 +5,7 @@ from colorama import Fore from colorama import init as colorama_init -from pre_commit_hooks.runner import run_sbt_command +from pre_commit_hooks.runner import default_argparse, run_sbt_command ARG_WARTREMOVER_ARGS = "warts" ARG_COMPILE_SCOPE = "scope" @@ -18,25 +18,31 @@ def main(argv=None): colorama_init() - arg_p = argparse.ArgumentParser(description="Run SBT wartremover") - arg_p.add_argument( - f"--{ARG_WARTREMOVER_ARGS}", - default=DEFAULT_WARTREMOVER_ARGS, - help=f"Value for wartremoverErrors, as per https://www.wartremover.org/doc/install-setup.html . Default: {DEFAULT_WARTREMOVER_ARGS}", + def arg_wartremover(arg_p: argparse.ArgumentParser) -> None: + arg_p.add_argument( + f"--{ARG_WARTREMOVER_ARGS}", + default=DEFAULT_WARTREMOVER_ARGS, + help=f"Value for wartremoverErrors, as per https://www.wartremover.org/doc/install-setup.html . Default: {DEFAULT_WARTREMOVER_ARGS}", + ) + + def arg_compile_scope(arg_p: argparse.ArgumentParser) -> None: + arg_p.add_argument( + f"--{ARG_COMPILE_SCOPE}", + default=DEFAULT_COMPILE_SCOPE, + help=f"Compile scope for the check. Default: {DEFAULT_COMPILE_SCOPE}", + ) + + args = default_argparse( + description="Run SBT wartremover", + argv=argv, + additional_args=[arg_wartremover, arg_compile_scope], ) - arg_p.add_argument( - f"--{ARG_COMPILE_SCOPE}", - default=DEFAULT_COMPILE_SCOPE, - help=f"Compile scope for the wartremover check. Default: {DEFAULT_COMPILE_SCOPE}", - ) - arg_p.print_help() - - args = arg_p.parse_args(argv).__dict__ return run_sbt_command( - f"; clean ; set wartremoverErrors ++= {args[ARG_WARTREMOVER_ARGS]}; {args[ARG_COMPILE_SCOPE]}", - MISSING_PLUGIN_CHECK_STRING, - MISSING_PLUGIN_ERROR_MSG, + task_def=f"set wartremoverErrors ++= {args.varargs.get(ARG_WARTREMOVER_ARGS)}; {args.varargs.get(ARG_COMPILE_SCOPE)}", + missing_plugin_check_string=MISSING_PLUGIN_CHECK_STRING, + missing_plugin_error_msg=MISSING_PLUGIN_ERROR_MSG, + opts=args, ) diff --git a/pre_commit_hooks/scalafmt.py b/pre_commit_hooks/scalafmt.py index e27d000..86cc81d 100644 --- a/pre_commit_hooks/scalafmt.py +++ b/pre_commit_hooks/scalafmt.py @@ -3,7 +3,10 @@ from colorama import Fore from colorama import init as colorama_init -from pre_commit_hooks.runner import run_sbt_command +from pre_commit_hooks.runner import ( + default_argparse, + run_sbt_command, +) TASK_SCALAFMT = "scalafmtCheckAll" MISSING_PLUGIN_CHECK_STRING = "Not a valid key: scalafmtCheck" @@ -13,10 +16,13 @@ def main(argv=None): colorama_init() + args = default_argparse("Run SBT scalafmt", argv=argv) + print(f"args: {args}") return run_sbt_command( - f"; clean ; {TASK_SCALAFMT}", - MISSING_PLUGIN_CHECK_STRING, - MISSING_PLUGIN_ERROR_MSG, + task_def=f"{TASK_SCALAFMT}", + missing_plugin_check_string=MISSING_PLUGIN_CHECK_STRING, + missing_plugin_error_msg=MISSING_PLUGIN_ERROR_MSG, + opts=args, ) diff --git a/pre_commit_hooks/scalafmt_apply.py b/pre_commit_hooks/scalafmt_apply.py index e858384..d055202 100644 --- a/pre_commit_hooks/scalafmt_apply.py +++ b/pre_commit_hooks/scalafmt_apply.py @@ -3,8 +3,11 @@ from colorama import Fore from colorama import init as colorama_init -from pre_commit_hooks.runner import run_git_add_modified -from pre_commit_hooks.runner import run_sbt_command +from pre_commit_hooks.runner import ( + default_argparse, + run_git_add_modified, + run_sbt_command, +) TASK_SCALAFMT = "scalafmtAll" MISSING_PLUGIN_CHECK_STRING = "Not a valid key: scalafmtAll" @@ -14,10 +17,13 @@ def main(argv=None): colorama_init() + args = default_argparse("Run SBT scalafmt", argv=argv) + sbt = run_sbt_command( - f"; clean ; {TASK_SCALAFMT}", - MISSING_PLUGIN_CHECK_STRING, - MISSING_PLUGIN_ERROR_MSG, + task_def=f"{TASK_SCALAFMT}", + missing_plugin_check_string=MISSING_PLUGIN_CHECK_STRING, + missing_plugin_error_msg=MISSING_PLUGIN_ERROR_MSG, + opts=args, ) run_git_add_modified() From aee4b610e2395bd8ea2fdcbe6829db85feec96ca Mon Sep 17 00:00:00 2001 From: Christian Hollinger Date: Fri, 9 May 2025 10:25:48 -0400 Subject: [PATCH 10/10] minor: disable always_run --- .pre-commit-hooks.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 71837cd..aaf2c12 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -4,7 +4,6 @@ language: python entry: sbt-fatal-warnings pass_filenames: false - always_run: true minimum_pre_commit_version: "0.19.0" - id: sbt-unused-imports name: Scala unused imports (+ fatal warnings) @@ -12,7 +11,6 @@ language: python entry: sbt-fatal-warnings --add_arg='-Ywarn-unused-import' pass_filenames: false - always_run: true minimum_pre_commit_version: "0.19.0" - id: sbt-scalafmt alias: scalafmt @@ -21,7 +19,6 @@ language: python entry: scalafmt pass_filenames: false - always_run: true minimum_pre_commit_version: "0.19.0" - id: sbt-wartremover name: Scala WartRemover plugin check @@ -29,7 +26,6 @@ language: python entry: sbt-wartremover pass_filenames: false - always_run: true minimum_pre_commit_version: "0.19.0" - id: sbt-scalafmt-apply alias: scalafmt-apply @@ -38,5 +34,4 @@ language: python entry: scalafmt-apply pass_filenames: false - always_run: true minimum_pre_commit_version: "0.19.0"