From 8e78946bc7012332467805a828a07af2b1917264 Mon Sep 17 00:00:00 2001 From: Rob Kazirut Date: Tue, 2 Sep 2025 09:28:31 -0400 Subject: [PATCH 1/3] precommit enhancements #32 --- .djlintrc | 17 +++++ .pre-commit-config.yaml | 135 +++++++++++++++++++++++++++++++++++++++- .secrets.baseline | 112 +++++++++++++++++++++++++++++++++ mypy.ini | 32 ++++++++++ vulture_allowlist.py | 46 ++++++++++++++ 5 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 .djlintrc create mode 100644 .secrets.baseline create mode 100644 mypy.ini create mode 100644 vulture_allowlist.py diff --git a/.djlintrc b/.djlintrc new file mode 100644 index 0000000..6d3a1db --- /dev/null +++ b/.djlintrc @@ -0,0 +1,17 @@ +{ + "blank_line_after_tag": "load,extends,include", + "blank_line_before_tag": "load,extends,include", + "close_void_tags": true, + "format_attribute_template_tags": true, + "format_css": true, + "format_js": true, + "ignore": "H006,H030,H031", + "include": "H017,H035", + "indent": 2, + "max_line_length": 120, + "profile": "django", + "preserve_blank_lines": true, + "preserve_leading_space": false, + "use_tabs": false, + "extension": "html" +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b63c221..a901cfe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,139 @@ +exclude: | + (?x)^( + .*/migrations/.*| + .*/static/.*| + .*/media/.*| + .*/node_modules/.*| + .*/venv/.*| + .*/\.venv/.*| + .*\.min\.(js|css)$| + vulture_allowlist\.py$ + )$ + repos: + # Basic file checks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: check-toml + - id: check-xml + - id: check-case-conflict + - id: check-merge-conflict + - id: check-docstring-first + - id: debug-statements + - id: check-executables-have-shebangs + + # Secret detection + - repo: https://github.com/Yelp/detect-secrets + rev: v1.4.0 + hooks: + - id: detect-secrets + args: ['--baseline', '.secrets.baseline'] + exclude: | + (?x)^( + .*/migrations/.*| + .*\.lock$| + .*\.min\.(js|css)$| + package-lock\.json$ + )$ + + # Import sorting + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + args: ["--profile", "django", "--multi-line", "3", "--trailing-comma", "--force-grid-wrap", "0", "--combine-as", "--line-width", "88"] + + # Code formatting and linting - repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. rev: v0.12.1 hooks: - # Run the linter. - id: ruff-check args: [--fix] - # Run the formatter. - id: ruff-format + + # Type checking + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.8.0 + hooks: + - id: mypy + additional_dependencies: [django-stubs, types-requests, psycopg2-binary] + args: [--config-file=mypy.ini, --ignore-missing-imports] + + # Security analysis + - repo: https://github.com/PyCQA/bandit + rev: 1.7.5 + hooks: + - id: bandit + additional_dependencies: [pbr] + args: ['-r', '.', '-f', 'json', '--skip', 'B101,B601'] + exclude: | + (?x)^( + .*/migrations/.*| + .*/tests/.*| + .*/test_.*\.py$| + .*_test\.py$ + )$ + + # Django-specific checks + - repo: https://github.com/adamchainz/django-upgrade + rev: 1.15.0 + hooks: + - id: django-upgrade + args: [--target-version, "5.0"] + + # Template linting for Django + - repo: https://github.com/Riverside-Healthcare/djlint + rev: v1.34.1 + hooks: + - id: djlint-reformat-django + - id: djlint-django + + # Code complexity and quality + - repo: https://github.com/PyCQA/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + additional_dependencies: + - flake8-bugbear + - flake8-comprehensions + - flake8-django + - flake8-pie + - flake8-simplify + args: [--max-complexity=10, --max-line-length=88, --extend-ignore, E203,W503] + + # Dead code detection + - repo: https://github.com/jendrikseipp/vulture + rev: v2.10 + hooks: + - id: vulture + args: [--min-confidence, '80', --sort-by-size, vulture_allowlist.py] + + # README and documentation checks + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + + # Markdown linting (Python-based alternative) + - repo: https://github.com/executablebooks/mdformat + rev: 0.7.17 + hooks: + - id: mdformat + additional_dependencies: + - mdformat-gfm + - mdformat-black + args: [--wrap=100] + + # Python docstring formatting + - repo: https://github.com/pycqa/pydocstyle + rev: 6.3.0 + hooks: + - id: pydocstyle + args: [--convention=google, --add-ignore=D100,D104,D105,D107] diff --git a/.secrets.baseline b/.secrets.baseline new file mode 100644 index 0000000..3c9f2bd --- /dev/null +++ b/.secrets.baseline @@ -0,0 +1,112 @@ +{ + "version": "1.4.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": {}, + "generated_at": "2024-01-01T00:00:00Z" +} diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..ef223c6 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,32 @@ +[mypy] +python_version = 3.13 +check_untyped_defs = true +disallow_any_generics = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true +no_implicit_optional = true +warn_redundant_casts = true +warn_unused_ignores = true +warn_return_any = true +warn_unreachable = true +strict_equality = true +show_error_codes = true + +# Django-specific settings +plugins = mypy_django_plugin.main + +[mypy.plugins.django-stubs] +django_settings_module = "fables.settings" + +# Ignore missing imports for third-party libraries +[mypy-*.migrations.*] +ignore_errors = true + +[mypy-manage] +ignore_errors = true + +# Third-party libraries without stubs +[mypy-psycopg2.*] +ignore_missing_imports = true diff --git a/vulture_allowlist.py b/vulture_allowlist.py new file mode 100644 index 0000000..049b279 --- /dev/null +++ b/vulture_allowlist.py @@ -0,0 +1,46 @@ +# Vulture allowlist for Django project +# This file contains code that vulture should not report as dead code + +# Django model fields that appear unused but are accessed via ORM +_.objects # Django model manager +_.DoesNotExist # Django model exception +_.MultipleObjectsReturned # Django model exception + +# Django admin +_.admin # Django admin module imports + +# Django URL patterns +_.urlpatterns # Django URL configuration +_.app_name # Django app namespace + +# Django settings +_.DATABASES # Django database configuration +_.INSTALLED_APPS # Django installed applications +_.MIDDLEWARE # Django middleware +_.ROOT_URLCONF # Django root URL configuration +_.TEMPLATES # Django template configuration +_.STATIC_URL # Django static files URL +_.STATIC_ROOT # Django static files root +_.MEDIA_URL # Django media files URL +_.MEDIA_ROOT # Django media files root + +# Django management commands +_.handle # Django management command method +_.add_arguments # Django management command method + +# Django migrations +_.dependencies # Django migration dependencies +_.operations # Django migration operations + +# Django forms +_.clean # Django form validation methods +_.save # Django form save method + +# Django views +_.get_context_data # Django class-based view method +_.get_queryset # Django class-based view method +_.get_object # Django class-based view method + +# Django tests +_.setUp # Django test setup method +_.tearDown # Django test teardown method From 0536c950aab9d1b4adfaf61dd8f5d00d5658b514 Mon Sep 17 00:00:00 2001 From: Rob Kazirut Date: Tue, 2 Sep 2025 09:35:31 -0400 Subject: [PATCH 2/3] exclude vulture_allowlist --- .pre-commit-config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a901cfe..af2f244 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,7 +54,9 @@ repos: hooks: - id: ruff-check args: [--fix] + exclude: vulture_allowlist\.py$ - id: ruff-format + exclude: vulture_allowlist\.py$ # Type checking - repo: https://github.com/pre-commit/mirrors-mypy From 2b16eb9148abc4e6f721a96a92cf6ce9f527c3ef Mon Sep 17 00:00:00 2001 From: Rob Kazirut Date: Tue, 2 Sep 2025 09:36:57 -0400 Subject: [PATCH 3/3] exclude vulture_allowlist CI --- pyproject.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 9c2483e..c2f7b74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,3 +9,11 @@ dependencies = [ "psycopg2-binary>=2.9.0", "ruff>=0.12.0", ] + +[tool.ruff] +exclude = [ + "vulture_allowlist.py", + "migrations", + ".venv", + "venv", +]