gruff-py 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. gruff_py-0.1.0/.editorconfig +20 -0
  2. gruff_py-0.1.0/.gitattributes +38 -0
  3. gruff_py-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
  4. gruff_py-0.1.0/.github/ISSUE_TEMPLATE/false_positive.md +33 -0
  5. gruff_py-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  6. gruff_py-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +18 -0
  7. gruff_py-0.1.0/.github/git-commit-instructions.md +5 -0
  8. gruff_py-0.1.0/.github/workflows/ci.yml +57 -0
  9. gruff_py-0.1.0/.github/workflows/publish.yml +60 -0
  10. gruff_py-0.1.0/.gitignore +88 -0
  11. gruff_py-0.1.0/.gruff-py.yaml +710 -0
  12. gruff_py-0.1.0/.pre-commit-config.yaml +27 -0
  13. gruff_py-0.1.0/CHANGELOG.md +15 -0
  14. gruff_py-0.1.0/CODE_OF_CONDUCT.md +26 -0
  15. gruff_py-0.1.0/CONTRIBUTING.md +63 -0
  16. gruff_py-0.1.0/LICENSE.md +21 -0
  17. gruff_py-0.1.0/Makefile +64 -0
  18. gruff_py-0.1.0/PKG-INFO +279 -0
  19. gruff_py-0.1.0/README.md +242 -0
  20. gruff_py-0.1.0/SECURITY.md +44 -0
  21. gruff_py-0.1.0/SUPPORT.md +38 -0
  22. gruff_py-0.1.0/bin/gruff-py +28 -0
  23. gruff_py-0.1.0/docs/CONFIGURATION.md +159 -0
  24. gruff_py-0.1.0/docs/DASHBOARD.md +64 -0
  25. gruff_py-0.1.0/docs/RELEASING.md +81 -0
  26. gruff_py-0.1.0/docs/REPORTING.md +144 -0
  27. gruff_py-0.1.0/docs/RULES.md +1948 -0
  28. gruff_py-0.1.0/pyproject.toml +138 -0
  29. gruff_py-0.1.0/scripts/bump-version.sh +253 -0
  30. gruff_py-0.1.0/scripts/performance-baselines/linux-x86_64.json +16 -0
  31. gruff_py-0.1.0/scripts/preflight-checks.sh +573 -0
  32. gruff_py-0.1.0/scripts/publish-pypi.sh +271 -0
  33. gruff_py-0.1.0/scripts/test-performance.sh +505 -0
  34. gruff_py-0.1.0/src/gruffpy/__init__.py +5 -0
  35. gruff_py-0.1.0/src/gruffpy/__main__.py +9 -0
  36. gruff_py-0.1.0/src/gruffpy/analysis/__init__.py +15 -0
  37. gruff_py-0.1.0/src/gruffpy/analysis/report.py +163 -0
  38. gruff_py-0.1.0/src/gruffpy/analysis/run_diagnostic.py +37 -0
  39. gruff_py-0.1.0/src/gruffpy/analysis/runner.py +286 -0
  40. gruff_py-0.1.0/src/gruffpy/analysis/schema.py +5 -0
  41. gruff_py-0.1.0/src/gruffpy/cli.py +741 -0
  42. gruff_py-0.1.0/src/gruffpy/cli_menu.py +131 -0
  43. gruff_py-0.1.0/src/gruffpy/cli_options.py +769 -0
  44. gruff_py-0.1.0/src/gruffpy/cli_state.py +54 -0
  45. gruff_py-0.1.0/src/gruffpy/command/__init__.py +4 -0
  46. gruff_py-0.1.0/src/gruffpy/command/dashboard_page_renderer.py +229 -0
  47. gruff_py-0.1.0/src/gruffpy/command/dashboard_server.py +282 -0
  48. gruff_py-0.1.0/src/gruffpy/command/metric_calibration.py +723 -0
  49. gruff_py-0.1.0/src/gruffpy/command/rule_docs.py +322 -0
  50. gruff_py-0.1.0/src/gruffpy/config/__init__.py +13 -0
  51. gruff_py-0.1.0/src/gruffpy/config/analysis_config.py +182 -0
  52. gruff_py-0.1.0/src/gruffpy/config/dead_code_allowlist.py +66 -0
  53. gruff_py-0.1.0/src/gruffpy/config/exceptions.py +5 -0
  54. gruff_py-0.1.0/src/gruffpy/config/loader.py +382 -0
  55. gruff_py-0.1.0/src/gruffpy/config/rule_selection.py +69 -0
  56. gruff_py-0.1.0/src/gruffpy/config/rule_settings.py +180 -0
  57. gruff_py-0.1.0/src/gruffpy/config/yaml_loader.py +41 -0
  58. gruff_py-0.1.0/src/gruffpy/finding/__init__.py +19 -0
  59. gruff_py-0.1.0/src/gruffpy/finding/confidence.py +9 -0
  60. gruff_py-0.1.0/src/gruffpy/finding/fail_threshold.py +50 -0
  61. gruff_py-0.1.0/src/gruffpy/finding/finding.py +98 -0
  62. gruff_py-0.1.0/src/gruffpy/finding/fingerprint.py +48 -0
  63. gruff_py-0.1.0/src/gruffpy/finding/output_format.py +31 -0
  64. gruff_py-0.1.0/src/gruffpy/finding/pillar.py +20 -0
  65. gruff_py-0.1.0/src/gruffpy/finding/rule_tier.py +7 -0
  66. gruff_py-0.1.0/src/gruffpy/finding/severity.py +27 -0
  67. gruff_py-0.1.0/src/gruffpy/parser/__init__.py +4 -0
  68. gruff_py-0.1.0/src/gruffpy/parser/analysis_unit.py +49 -0
  69. gruff_py-0.1.0/src/gruffpy/parser/python_parser.py +61 -0
  70. gruff_py-0.1.0/src/gruffpy/py.typed +0 -0
  71. gruff_py-0.1.0/src/gruffpy/reporting/__init__.py +19 -0
  72. gruff_py-0.1.0/src/gruffpy/reporting/finding_display_filter.py +103 -0
  73. gruff_py-0.1.0/src/gruffpy/reporting/github_annotations_reporter.py +55 -0
  74. gruff_py-0.1.0/src/gruffpy/reporting/hotspot_reporter.py +36 -0
  75. gruff_py-0.1.0/src/gruffpy/reporting/html_reporter.py +442 -0
  76. gruff_py-0.1.0/src/gruffpy/reporting/json_reporter.py +41 -0
  77. gruff_py-0.1.0/src/gruffpy/reporting/markdown_reporter.py +108 -0
  78. gruff_py-0.1.0/src/gruffpy/reporting/sarif_reporter.py +184 -0
  79. gruff_py-0.1.0/src/gruffpy/reporting/text_reporter.py +115 -0
  80. gruff_py-0.1.0/src/gruffpy/rule/__init__.py +6 -0
  81. gruff_py-0.1.0/src/gruffpy/rule/_python_dynamism.py +309 -0
  82. gruff_py-0.1.0/src/gruffpy/rule/builtins.py +11 -0
  83. gruff_py-0.1.0/src/gruffpy/rule/catalog.py +574 -0
  84. gruff_py-0.1.0/src/gruffpy/rule/complexity/__init__.py +0 -0
  85. gruff_py-0.1.0/src/gruffpy/rule/complexity/_halstead.py +213 -0
  86. gruff_py-0.1.0/src/gruffpy/rule/complexity/_walks.py +88 -0
  87. gruff_py-0.1.0/src/gruffpy/rule/complexity/cognitive_complexity_rule.py +310 -0
  88. gruff_py-0.1.0/src/gruffpy/rule/complexity/cyclomatic_complexity_rule.py +159 -0
  89. gruff_py-0.1.0/src/gruffpy/rule/complexity/halstead_volume_rule.py +106 -0
  90. gruff_py-0.1.0/src/gruffpy/rule/complexity/maintainability_index_rule.py +133 -0
  91. gruff_py-0.1.0/src/gruffpy/rule/complexity/nesting_depth_rule.py +191 -0
  92. gruff_py-0.1.0/src/gruffpy/rule/complexity/npath_complexity_rule.py +248 -0
  93. gruff_py-0.1.0/src/gruffpy/rule/context.py +34 -0
  94. gruff_py-0.1.0/src/gruffpy/rule/dead_code/__init__.py +0 -0
  95. gruff_py-0.1.0/src/gruffpy/rule/dead_code/unused_private_attribute_rule.py +209 -0
  96. gruff_py-0.1.0/src/gruffpy/rule/dead_code/unused_private_function_rule.py +300 -0
  97. gruff_py-0.1.0/src/gruffpy/rule/definition.py +70 -0
  98. gruff_py-0.1.0/src/gruffpy/rule/design/__init__.py +3 -0
  99. gruff_py-0.1.0/src/gruffpy/rule/design/single_implementor_protocol_rule.py +454 -0
  100. gruff_py-0.1.0/src/gruffpy/rule/docs/__init__.py +0 -0
  101. gruff_py-0.1.0/src/gruffpy/rule/docs/_comment_scanner.py +50 -0
  102. gruff_py-0.1.0/src/gruffpy/rule/docs/_docstring_parser.py +138 -0
  103. gruff_py-0.1.0/src/gruffpy/rule/docs/_helpers.py +182 -0
  104. gruff_py-0.1.0/src/gruffpy/rule/docs/complex_branch_rationale_rule.py +227 -0
  105. gruff_py-0.1.0/src/gruffpy/rule/docs/dataclass_attributes_rule.py +270 -0
  106. gruff_py-0.1.0/src/gruffpy/rule/docs/ignore_directive_reason_rule.py +173 -0
  107. gruff_py-0.1.0/src/gruffpy/rule/docs/missing_class_docstring_rule.py +115 -0
  108. gruff_py-0.1.0/src/gruffpy/rule/docs/missing_function_docstring_rule.py +158 -0
  109. gruff_py-0.1.0/src/gruffpy/rule/docs/missing_module_docstring_rule.py +105 -0
  110. gruff_py-0.1.0/src/gruffpy/rule/docs/missing_param_doc_rule.py +191 -0
  111. gruff_py-0.1.0/src/gruffpy/rule/docs/missing_raises_doc_rule.py +126 -0
  112. gruff_py-0.1.0/src/gruffpy/rule/docs/missing_readme_rule.py +86 -0
  113. gruff_py-0.1.0/src/gruffpy/rule/docs/missing_return_doc_rule.py +129 -0
  114. gruff_py-0.1.0/src/gruffpy/rule/docs/stale_param_doc_rule.py +136 -0
  115. gruff_py-0.1.0/src/gruffpy/rule/docs/todo_density_rule.py +108 -0
  116. gruff_py-0.1.0/src/gruffpy/rule/docs/useless_docstring_rule.py +298 -0
  117. gruff_py-0.1.0/src/gruffpy/rule/naming/__init__.py +0 -0
  118. gruff_py-0.1.0/src/gruffpy/rule/naming/_allowlists.py +23 -0
  119. gruff_py-0.1.0/src/gruffpy/rule/naming/_identifier_tokenizer.py +64 -0
  120. gruff_py-0.1.0/src/gruffpy/rule/naming/abbreviation_rule.py +205 -0
  121. gruff_py-0.1.0/src/gruffpy/rule/naming/boolean_prefix_rule.py +315 -0
  122. gruff_py-0.1.0/src/gruffpy/rule/naming/confusing_name_rule.py +109 -0
  123. gruff_py-0.1.0/src/gruffpy/rule/naming/generic_function_rule.py +116 -0
  124. gruff_py-0.1.0/src/gruffpy/rule/naming/hungarian_notation_rule.py +200 -0
  125. gruff_py-0.1.0/src/gruffpy/rule/naming/identifier_quality_rule.py +181 -0
  126. gruff_py-0.1.0/src/gruffpy/rule/naming/module_name_mismatch_rule.py +267 -0
  127. gruff_py-0.1.0/src/gruffpy/rule/naming/parameter_type_name_rule.py +393 -0
  128. gruff_py-0.1.0/src/gruffpy/rule/naming/short_variable_rule.py +178 -0
  129. gruff_py-0.1.0/src/gruffpy/rule/naming/test_naming_consistency_rule.py +142 -0
  130. gruff_py-0.1.0/src/gruffpy/rule/project_rule.py +19 -0
  131. gruff_py-0.1.0/src/gruffpy/rule/registry.py +198 -0
  132. gruff_py-0.1.0/src/gruffpy/rule/rule.py +21 -0
  133. gruff_py-0.1.0/src/gruffpy/rule/security/__init__.py +0 -0
  134. gruff_py-0.1.0/src/gruffpy/rule/security/_security_metadata.py +127 -0
  135. gruff_py-0.1.0/src/gruffpy/rule/security/_security_node_helper.py +265 -0
  136. gruff_py-0.1.0/src/gruffpy/rule/security/_security_taint_helper.py +376 -0
  137. gruff_py-0.1.0/src/gruffpy/rule/security/cors_wildcard_with_credentials_rule.py +156 -0
  138. gruff_py-0.1.0/src/gruffpy/rule/security/dangerous_function_call_rule.py +122 -0
  139. gruff_py-0.1.0/src/gruffpy/rule/security/disabled_ssl_verification_rule.py +383 -0
  140. gruff_py-0.1.0/src/gruffpy/rule/security/django_mark_safe_rule.py +161 -0
  141. gruff_py-0.1.0/src/gruffpy/rule/security/django_raw_sql_rule.py +139 -0
  142. gruff_py-0.1.0/src/gruffpy/rule/security/error_suppression_rule.py +134 -0
  143. gruff_py-0.1.0/src/gruffpy/rule/security/extract_compact_user_input_rule.py +121 -0
  144. gruff_py-0.1.0/src/gruffpy/rule/security/flask_debug_enabled_rule.py +175 -0
  145. gruff_py-0.1.0/src/gruffpy/rule/security/hardcoded_bind_all_interfaces_rule.py +172 -0
  146. gruff_py-0.1.0/src/gruffpy/rule/security/hardcoded_framework_secret_key_rule.py +149 -0
  147. gruff_py-0.1.0/src/gruffpy/rule/security/header_injection_rule.py +114 -0
  148. gruff_py-0.1.0/src/gruffpy/rule/security/insecure_random_rule.py +132 -0
  149. gruff_py-0.1.0/src/gruffpy/rule/security/insecure_temp_file_rule.py +186 -0
  150. gruff_py-0.1.0/src/gruffpy/rule/security/insecure_tls_protocol_rule.py +132 -0
  151. gruff_py-0.1.0/src/gruffpy/rule/security/jinja2_autoescape_off_rule.py +185 -0
  152. gruff_py-0.1.0/src/gruffpy/rule/security/paramiko_no_host_key_check_rule.py +175 -0
  153. gruff_py-0.1.0/src/gruffpy/rule/security/path_traversal_rule.py +191 -0
  154. gruff_py-0.1.0/src/gruffpy/rule/security/shell_injection_rule.py +137 -0
  155. gruff_py-0.1.0/src/gruffpy/rule/security/silent_except_rule.py +109 -0
  156. gruff_py-0.1.0/src/gruffpy/rule/security/sql_concatenation_rule.py +160 -0
  157. gruff_py-0.1.0/src/gruffpy/rule/security/ssrf_rule.py +172 -0
  158. gruff_py-0.1.0/src/gruffpy/rule/security/unsafe_pickle_rule.py +166 -0
  159. gruff_py-0.1.0/src/gruffpy/rule/security/unsafe_yaml_load_rule.py +355 -0
  160. gruff_py-0.1.0/src/gruffpy/rule/security/variable_import_rule.py +103 -0
  161. gruff_py-0.1.0/src/gruffpy/rule/security/weak_crypto_rule.py +260 -0
  162. gruff_py-0.1.0/src/gruffpy/rule/security/xxe_rule.py +231 -0
  163. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/__init__.py +0 -0
  164. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/_secret_scanner_helper.py +138 -0
  165. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/api_key_pattern_rule.py +108 -0
  166. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/aws_access_key_rule.py +90 -0
  167. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/database_url_password_rule.py +137 -0
  168. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/hardcoded_env_value_rule.py +120 -0
  169. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/high_entropy_string_rule.py +120 -0
  170. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/jwt_token_rule.py +80 -0
  171. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/phi_pattern_rule.py +103 -0
  172. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/pii_test_fixture_rule.py +132 -0
  173. gruff_py-0.1.0/src/gruffpy/rule/sensitive_data/private_key_rule.py +86 -0
  174. gruff_py-0.1.0/src/gruffpy/rule/size/__init__.py +3 -0
  175. gruff_py-0.1.0/src/gruffpy/rule/size/_lines.py +89 -0
  176. gruff_py-0.1.0/src/gruffpy/rule/size/attribute_count_rule.py +172 -0
  177. gruff_py-0.1.0/src/gruffpy/rule/size/average_function_length_rule.py +150 -0
  178. gruff_py-0.1.0/src/gruffpy/rule/size/class_length_rule.py +129 -0
  179. gruff_py-0.1.0/src/gruffpy/rule/size/file_length_rule.py +88 -0
  180. gruff_py-0.1.0/src/gruffpy/rule/size/function_length_rule.py +135 -0
  181. gruff_py-0.1.0/src/gruffpy/rule/size/parameter_count_rule.py +128 -0
  182. gruff_py-0.1.0/src/gruffpy/rule/size/public_method_count_rule.py +134 -0
  183. gruff_py-0.1.0/src/gruffpy/rule/test_quality/__init__.py +0 -0
  184. gruff_py-0.1.0/src/gruffpy/rule/test_quality/_pytest_config.py +159 -0
  185. gruff_py-0.1.0/src/gruffpy/rule/test_quality/_test_quality_node_helper.py +359 -0
  186. gruff_py-0.1.0/src/gruffpy/rule/test_quality/_test_quality_scope.py +33 -0
  187. gruff_py-0.1.0/src/gruffpy/rule/test_quality/conditional_logic_rule.py +97 -0
  188. gruff_py-0.1.0/src/gruffpy/rule/test_quality/eager_test_rule.py +104 -0
  189. gruff_py-0.1.0/src/gruffpy/rule/test_quality/empty_parametrize_rule.py +99 -0
  190. gruff_py-0.1.0/src/gruffpy/rule/test_quality/exception_type_only_rule.py +124 -0
  191. gruff_py-0.1.0/src/gruffpy/rule/test_quality/excessive_mocking_rule.py +98 -0
  192. gruff_py-0.1.0/src/gruffpy/rule/test_quality/extends_production_class_rule.py +149 -0
  193. gruff_py-0.1.0/src/gruffpy/rule/test_quality/global_state_mutation_rule.py +95 -0
  194. gruff_py-0.1.0/src/gruffpy/rule/test_quality/loop_assertion_without_message_rule.py +102 -0
  195. gruff_py-0.1.0/src/gruffpy/rule/test_quality/loop_in_test_rule.py +98 -0
  196. gruff_py-0.1.0/src/gruffpy/rule/test_quality/magic_number_assertion_rule.py +317 -0
  197. gruff_py-0.1.0/src/gruffpy/rule/test_quality/mock_only_test_rule.py +120 -0
  198. gruff_py-0.1.0/src/gruffpy/rule/test_quality/mock_without_expectation_rule.py +125 -0
  199. gruff_py-0.1.0/src/gruffpy/rule/test_quality/mocking_domain_object_rule.py +136 -0
  200. gruff_py-0.1.0/src/gruffpy/rule/test_quality/multiple_aaa_cycles_rule.py +129 -0
  201. gruff_py-0.1.0/src/gruffpy/rule/test_quality/mystery_guest_rule.py +121 -0
  202. gruff_py-0.1.0/src/gruffpy/rule/test_quality/naming_consistency_rule.py +103 -0
  203. gruff_py-0.1.0/src/gruffpy/rule/test_quality/no_assertions_rule.py +107 -0
  204. gruff_py-0.1.0/src/gruffpy/rule/test_quality/parametrize_annotation_rule.py +142 -0
  205. gruff_py-0.1.0/src/gruffpy/rule/test_quality/private_reflection_rule.py +107 -0
  206. gruff_py-0.1.0/src/gruffpy/rule/test_quality/pytest_coverage_source_missing_rule.py +86 -0
  207. gruff_py-0.1.0/src/gruffpy/rule/test_quality/pytest_deprecations_not_fatal_rule.py +85 -0
  208. gruff_py-0.1.0/src/gruffpy/rule/test_quality/pytest_strict_config_missing_rule.py +93 -0
  209. gruff_py-0.1.0/src/gruffpy/rule/test_quality/repeated_structure_missing_parametrize_rule.py +133 -0
  210. gruff_py-0.1.0/src/gruffpy/rule/test_quality/setup_bloat_rule.py +114 -0
  211. gruff_py-0.1.0/src/gruffpy/rule/test_quality/skipped_without_reason_rule.py +156 -0
  212. gruff_py-0.1.0/src/gruffpy/rule/test_quality/sleep_in_test_rule.py +98 -0
  213. gruff_py-0.1.0/src/gruffpy/rule/test_quality/sut_not_called_rule.py +273 -0
  214. gruff_py-0.1.0/src/gruffpy/rule/test_quality/tautological_type_assertion_rule.py +140 -0
  215. gruff_py-0.1.0/src/gruffpy/rule/test_quality/test_function_too_long_rule.py +105 -0
  216. gruff_py-0.1.0/src/gruffpy/rule/test_quality/test_longer_than_sut_rule.py +130 -0
  217. gruff_py-0.1.0/src/gruffpy/rule/test_quality/testdox_readability_rule.py +100 -0
  218. gruff_py-0.1.0/src/gruffpy/rule/test_quality/trivial_assertion_rule.py +104 -0
  219. gruff_py-0.1.0/src/gruffpy/rule/test_quality/trivial_snapshot_rule.py +113 -0
  220. gruff_py-0.1.0/src/gruffpy/rule/test_quality/unused_mock_rule.py +109 -0
  221. gruff_py-0.1.0/src/gruffpy/rule/waste/__init__.py +0 -0
  222. gruff_py-0.1.0/src/gruffpy/rule/waste/commented_out_code_rule.py +155 -0
  223. gruff_py-0.1.0/src/gruffpy/rule/waste/empty_class_rule.py +152 -0
  224. gruff_py-0.1.0/src/gruffpy/rule/waste/empty_function_rule.py +123 -0
  225. gruff_py-0.1.0/src/gruffpy/rule/waste/one_line_function_rule.py +166 -0
  226. gruff_py-0.1.0/src/gruffpy/rule/waste/redundant_variable_rule.py +151 -0
  227. gruff_py-0.1.0/src/gruffpy/rule/waste/unreachable_code_rule.py +231 -0
  228. gruff_py-0.1.0/src/gruffpy/rule/waste/unused_import_rule.py +208 -0
  229. gruff_py-0.1.0/src/gruffpy/rule/waste/unused_parameter_rule.py +211 -0
  230. gruff_py-0.1.0/src/gruffpy/scoring/__init__.py +25 -0
  231. gruff_py-0.1.0/src/gruffpy/scoring/composite_finding_factory.py +151 -0
  232. gruff_py-0.1.0/src/gruffpy/scoring/file_score.py +60 -0
  233. gruff_py-0.1.0/src/gruffpy/scoring/grade.py +52 -0
  234. gruff_py-0.1.0/src/gruffpy/scoring/pillar_score.py +52 -0
  235. gruff_py-0.1.0/src/gruffpy/scoring/score_calculator.py +196 -0
  236. gruff_py-0.1.0/src/gruffpy/scoring/score_report.py +48 -0
  237. gruff_py-0.1.0/src/gruffpy/source/__init__.py +20 -0
  238. gruff_py-0.1.0/src/gruffpy/source/discovery.py +343 -0
  239. gruff_py-0.1.0/src/gruffpy/source/gitignore.py +170 -0
  240. gruff_py-0.1.0/src/gruffpy/source/source_file.py +32 -0
  241. gruff_py-0.1.0/src/gruffpy/suppression/__init__.py +11 -0
  242. gruff_py-0.1.0/src/gruffpy/suppression/filter.py +37 -0
  243. gruff_py-0.1.0/src/gruffpy/suppression/parser.py +273 -0
  244. gruff_py-0.1.0/src/gruffpy/version.py +4 -0
  245. gruff_py-0.1.0/tests/__init__.py +0 -0
  246. gruff_py-0.1.0/tests/fixtures/calibration/docs.complex-branch-rationale/negative/docstring_rationale.py +30 -0
  247. gruff_py-0.1.0/tests/fixtures/calibration/docs.complex-branch-rationale/positive/no_rationale.py +25 -0
  248. gruff_py-0.1.0/tests/fixtures/calibration/docs.dataclass-attributes/negative/attributes_section.py +16 -0
  249. gruff_py-0.1.0/tests/fixtures/calibration/docs.dataclass-attributes/positive/missing_attrs.py +8 -0
  250. gruff_py-0.1.0/tests/fixtures/calibration/docs.ignore-directive-reason/negative/reexport_reason.py +3 -0
  251. gruff_py-0.1.0/tests/fixtures/calibration/docs.ignore-directive-reason/positive/unexplained.py +3 -0
  252. gruff_py-0.1.0/tests/fixtures/calibration/docs.todo-density/negative/src/gruffpy/rule/docs/todo_density_rule.py +11 -0
  253. gruff_py-0.1.0/tests/fixtures/calibration/docs.todo-density/positive/high_density.py +9 -0
  254. gruff_py-0.1.0/tests/fixtures/complexity/cc_fixture.py +76 -0
  255. gruff_py-0.1.0/tests/fixtures/complexity/radon_ground_truth.md +47 -0
  256. gruff_py-0.1.0/tests/integration/__init__.py +0 -0
  257. gruff_py-0.1.0/tests/integration/test_bin_script.py +19 -0
  258. gruff_py-0.1.0/tests/integration/test_cli_smoke.py +490 -0
  259. gruff_py-0.1.0/tests/integration/test_dashboard_server.py +85 -0
  260. gruff_py-0.1.0/tests/integration/test_gitignore_e2e.py +78 -0
  261. gruff_py-0.1.0/tests/integration/test_json_byte_equivalence.py +75 -0
  262. gruff_py-0.1.0/tests/integration/test_perf_script_smoke.py +189 -0
  263. gruff_py-0.1.0/tests/integration/test_suppression.py +140 -0
  264. gruff_py-0.1.0/tests/unit/__init__.py +0 -0
  265. gruff_py-0.1.0/tests/unit/command/__init__.py +1 -0
  266. gruff_py-0.1.0/tests/unit/command/test_dashboard_page_renderer.py +110 -0
  267. gruff_py-0.1.0/tests/unit/command/test_metric_calibration.py +121 -0
  268. gruff_py-0.1.0/tests/unit/command/test_rule_docs.py +31 -0
  269. gruff_py-0.1.0/tests/unit/config/__init__.py +0 -0
  270. gruff_py-0.1.0/tests/unit/config/test_dead_code_allowlist_loader.py +91 -0
  271. gruff_py-0.1.0/tests/unit/config/test_gruff_py_yaml_registry_coverage.py +118 -0
  272. gruff_py-0.1.0/tests/unit/config/test_loader_precedence.py +336 -0
  273. gruff_py-0.1.0/tests/unit/config/test_yaml_loader.py +71 -0
  274. gruff_py-0.1.0/tests/unit/finding/__init__.py +0 -0
  275. gruff_py-0.1.0/tests/unit/finding/test_fingerprint.py +80 -0
  276. gruff_py-0.1.0/tests/unit/parser/test_python_parser.py +42 -0
  277. gruff_py-0.1.0/tests/unit/reporting/__init__.py +1 -0
  278. gruff_py-0.1.0/tests/unit/reporting/test_reporters.py +426 -0
  279. gruff_py-0.1.0/tests/unit/rule/__init__.py +0 -0
  280. gruff_py-0.1.0/tests/unit/rule/complexity/__init__.py +0 -0
  281. gruff_py-0.1.0/tests/unit/rule/complexity/test_cognitive_complexity_rule.py +192 -0
  282. gruff_py-0.1.0/tests/unit/rule/complexity/test_complexity_pillar_integration.py +289 -0
  283. gruff_py-0.1.0/tests/unit/rule/complexity/test_cyclomatic_complexity_rule.py +133 -0
  284. gruff_py-0.1.0/tests/unit/rule/complexity/test_halstead_volume_rule.py +165 -0
  285. gruff_py-0.1.0/tests/unit/rule/complexity/test_maintainability_index_rule.py +132 -0
  286. gruff_py-0.1.0/tests/unit/rule/complexity/test_nesting_depth_rule.py +136 -0
  287. gruff_py-0.1.0/tests/unit/rule/complexity/test_npath_complexity_rule.py +126 -0
  288. gruff_py-0.1.0/tests/unit/rule/dead_code/__init__.py +0 -0
  289. gruff_py-0.1.0/tests/unit/rule/dead_code/test_dead_code_allowlist.py +158 -0
  290. gruff_py-0.1.0/tests/unit/rule/dead_code/test_unused_private_attribute_rule.py +102 -0
  291. gruff_py-0.1.0/tests/unit/rule/dead_code/test_unused_private_function_rule.py +120 -0
  292. gruff_py-0.1.0/tests/unit/rule/design/__init__.py +1 -0
  293. gruff_py-0.1.0/tests/unit/rule/design/test_single_implementor_protocol_rule.py +88 -0
  294. gruff_py-0.1.0/tests/unit/rule/docs/__init__.py +0 -0
  295. gruff_py-0.1.0/tests/unit/rule/docs/_helpers.py +52 -0
  296. gruff_py-0.1.0/tests/unit/rule/docs/test_complex_branch_rationale_rule.py +81 -0
  297. gruff_py-0.1.0/tests/unit/rule/docs/test_dataclass_attributes_rule.py +133 -0
  298. gruff_py-0.1.0/tests/unit/rule/docs/test_docs_pillar_integration.py +179 -0
  299. gruff_py-0.1.0/tests/unit/rule/docs/test_docstring_parser.py +218 -0
  300. gruff_py-0.1.0/tests/unit/rule/docs/test_ignore_directive_reason_rule.py +49 -0
  301. gruff_py-0.1.0/tests/unit/rule/docs/test_missing_class_docstring_rule.py +55 -0
  302. gruff_py-0.1.0/tests/unit/rule/docs/test_missing_function_docstring_rule.py +132 -0
  303. gruff_py-0.1.0/tests/unit/rule/docs/test_missing_module_docstring_rule.py +44 -0
  304. gruff_py-0.1.0/tests/unit/rule/docs/test_missing_param_doc_rule.py +70 -0
  305. gruff_py-0.1.0/tests/unit/rule/docs/test_missing_raises_doc_rule.py +56 -0
  306. gruff_py-0.1.0/tests/unit/rule/docs/test_missing_readme_rule.py +44 -0
  307. gruff_py-0.1.0/tests/unit/rule/docs/test_missing_return_doc_rule.py +36 -0
  308. gruff_py-0.1.0/tests/unit/rule/docs/test_stale_param_doc_rule.py +68 -0
  309. gruff_py-0.1.0/tests/unit/rule/docs/test_todo_density_rule.py +71 -0
  310. gruff_py-0.1.0/tests/unit/rule/docs/test_useless_docstring_rule.py +89 -0
  311. gruff_py-0.1.0/tests/unit/rule/naming/__init__.py +0 -0
  312. gruff_py-0.1.0/tests/unit/rule/naming/test_abbreviation_rule.py +106 -0
  313. gruff_py-0.1.0/tests/unit/rule/naming/test_boolean_prefix_rule.py +228 -0
  314. gruff_py-0.1.0/tests/unit/rule/naming/test_confusing_name_rule.py +80 -0
  315. gruff_py-0.1.0/tests/unit/rule/naming/test_generic_function_rule.py +87 -0
  316. gruff_py-0.1.0/tests/unit/rule/naming/test_hungarian_notation_rule.py +136 -0
  317. gruff_py-0.1.0/tests/unit/rule/naming/test_identifier_quality_rule.py +111 -0
  318. gruff_py-0.1.0/tests/unit/rule/naming/test_identifier_tokenizer.py +62 -0
  319. gruff_py-0.1.0/tests/unit/rule/naming/test_module_name_mismatch_rule.py +133 -0
  320. gruff_py-0.1.0/tests/unit/rule/naming/test_naming_pillar_integration.py +156 -0
  321. gruff_py-0.1.0/tests/unit/rule/naming/test_parameter_type_name_rule.py +222 -0
  322. gruff_py-0.1.0/tests/unit/rule/naming/test_short_variable_rule.py +100 -0
  323. gruff_py-0.1.0/tests/unit/rule/naming/test_test_naming_consistency_rule.py +74 -0
  324. gruff_py-0.1.0/tests/unit/rule/security/__init__.py +0 -0
  325. gruff_py-0.1.0/tests/unit/rule/security/_helpers.py +44 -0
  326. gruff_py-0.1.0/tests/unit/rule/security/test_cors_wildcard_with_credentials_rule.py +72 -0
  327. gruff_py-0.1.0/tests/unit/rule/security/test_dangerous_function_call_rule.py +38 -0
  328. gruff_py-0.1.0/tests/unit/rule/security/test_disabled_ssl_verification_rule.py +71 -0
  329. gruff_py-0.1.0/tests/unit/rule/security/test_django_mark_safe_rule.py +102 -0
  330. gruff_py-0.1.0/tests/unit/rule/security/test_django_raw_sql_rule.py +97 -0
  331. gruff_py-0.1.0/tests/unit/rule/security/test_error_suppression_rule.py +24 -0
  332. gruff_py-0.1.0/tests/unit/rule/security/test_extract_compact_user_input_rule.py +30 -0
  333. gruff_py-0.1.0/tests/unit/rule/security/test_flask_debug_enabled_rule.py +78 -0
  334. gruff_py-0.1.0/tests/unit/rule/security/test_hardcoded_bind_all_interfaces_rule.py +70 -0
  335. gruff_py-0.1.0/tests/unit/rule/security/test_hardcoded_framework_secret_key_rule.py +68 -0
  336. gruff_py-0.1.0/tests/unit/rule/security/test_header_injection_rule.py +34 -0
  337. gruff_py-0.1.0/tests/unit/rule/security/test_insecure_random_rule.py +24 -0
  338. gruff_py-0.1.0/tests/unit/rule/security/test_insecure_temp_file_rule.py +73 -0
  339. gruff_py-0.1.0/tests/unit/rule/security/test_insecure_tls_protocol_rule.py +63 -0
  340. gruff_py-0.1.0/tests/unit/rule/security/test_jinja2_autoescape_off_rule.py +71 -0
  341. gruff_py-0.1.0/tests/unit/rule/security/test_paramiko_no_host_key_check_rule.py +74 -0
  342. gruff_py-0.1.0/tests/unit/rule/security/test_path_traversal_rule.py +134 -0
  343. gruff_py-0.1.0/tests/unit/rule/security/test_security_pillar_integration.py +150 -0
  344. gruff_py-0.1.0/tests/unit/rule/security/test_security_taint_helper.py +224 -0
  345. gruff_py-0.1.0/tests/unit/rule/security/test_shell_injection_rule.py +29 -0
  346. gruff_py-0.1.0/tests/unit/rule/security/test_silent_except_rule.py +35 -0
  347. gruff_py-0.1.0/tests/unit/rule/security/test_sql_concatenation_rule.py +58 -0
  348. gruff_py-0.1.0/tests/unit/rule/security/test_ssrf_rule.py +146 -0
  349. gruff_py-0.1.0/tests/unit/rule/security/test_unsafe_pickle_rule.py +75 -0
  350. gruff_py-0.1.0/tests/unit/rule/security/test_unsafe_yaml_load_rule.py +92 -0
  351. gruff_py-0.1.0/tests/unit/rule/security/test_variable_import_rule.py +19 -0
  352. gruff_py-0.1.0/tests/unit/rule/security/test_weak_crypto_rule.py +48 -0
  353. gruff_py-0.1.0/tests/unit/rule/security/test_xxe_rule.py +86 -0
  354. gruff_py-0.1.0/tests/unit/rule/sensitive_data/__init__.py +0 -0
  355. gruff_py-0.1.0/tests/unit/rule/sensitive_data/_helpers.py +49 -0
  356. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_api_key_pattern_rule.py +40 -0
  357. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_aws_access_key_rule.py +49 -0
  358. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_database_url_password_rule.py +29 -0
  359. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_hardcoded_env_value_rule.py +31 -0
  360. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_high_entropy_string_rule.py +31 -0
  361. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_jwt_token_rule.py +25 -0
  362. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_phi_pattern_rule.py +28 -0
  363. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_pii_test_fixture_rule.py +43 -0
  364. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_private_key_rule.py +30 -0
  365. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_secret_scanner_helper.py +46 -0
  366. gruff_py-0.1.0/tests/unit/rule/sensitive_data/test_sensitive_data_pillar_integration.py +100 -0
  367. gruff_py-0.1.0/tests/unit/rule/size/__init__.py +0 -0
  368. gruff_py-0.1.0/tests/unit/rule/size/test_attribute_count_rule.py +121 -0
  369. gruff_py-0.1.0/tests/unit/rule/size/test_average_function_length_rule.py +83 -0
  370. gruff_py-0.1.0/tests/unit/rule/size/test_class_length_rule.py +81 -0
  371. gruff_py-0.1.0/tests/unit/rule/size/test_file_length_rule.py +84 -0
  372. gruff_py-0.1.0/tests/unit/rule/size/test_function_length_rule.py +139 -0
  373. gruff_py-0.1.0/tests/unit/rule/size/test_lines_helper.py +101 -0
  374. gruff_py-0.1.0/tests/unit/rule/size/test_parameter_count_rule.py +108 -0
  375. gruff_py-0.1.0/tests/unit/rule/size/test_public_method_count_rule.py +91 -0
  376. gruff_py-0.1.0/tests/unit/rule/size/test_size_pillar_integration.py +362 -0
  377. gruff_py-0.1.0/tests/unit/rule/test_calibration_fixtures.py +87 -0
  378. gruff_py-0.1.0/tests/unit/rule/test_catalog.py +155 -0
  379. gruff_py-0.1.0/tests/unit/rule/test_python_dynamism.py +145 -0
  380. gruff_py-0.1.0/tests/unit/rule/test_quality/__init__.py +0 -0
  381. gruff_py-0.1.0/tests/unit/rule/test_quality/_helpers.py +45 -0
  382. gruff_py-0.1.0/tests/unit/rule/test_quality/test_conditional_logic_rule.py +25 -0
  383. gruff_py-0.1.0/tests/unit/rule/test_quality/test_empty_parametrize_rule.py +18 -0
  384. gruff_py-0.1.0/tests/unit/rule/test_quality/test_extends_production_class_rule.py +37 -0
  385. gruff_py-0.1.0/tests/unit/rule/test_quality/test_full_pillar_integration.py +324 -0
  386. gruff_py-0.1.0/tests/unit/rule/test_quality/test_global_state_mutation_rule.py +13 -0
  387. gruff_py-0.1.0/tests/unit/rule/test_quality/test_loop_in_test_rule.py +25 -0
  388. gruff_py-0.1.0/tests/unit/rule/test_quality/test_magic_number_assertion_rule.py +89 -0
  389. gruff_py-0.1.0/tests/unit/rule/test_quality/test_memoisation_gate.py +56 -0
  390. gruff_py-0.1.0/tests/unit/rule/test_quality/test_mock_lifecycle_integration.py +96 -0
  391. gruff_py-0.1.0/tests/unit/rule/test_quality/test_naming_consistency_rule.py +19 -0
  392. gruff_py-0.1.0/tests/unit/rule/test_quality/test_no_assertions_rule.py +33 -0
  393. gruff_py-0.1.0/tests/unit/rule/test_quality/test_private_reflection_rule.py +19 -0
  394. gruff_py-0.1.0/tests/unit/rule/test_quality/test_project_config_rules.py +191 -0
  395. gruff_py-0.1.0/tests/unit/rule/test_quality/test_repeated_structure_missing_parametrize_rule.py +96 -0
  396. gruff_py-0.1.0/tests/unit/rule/test_quality/test_skipped_without_reason_rule.py +29 -0
  397. gruff_py-0.1.0/tests/unit/rule/test_quality/test_sleep_in_test_rule.py +24 -0
  398. gruff_py-0.1.0/tests/unit/rule/test_quality/test_sut_not_called_rule.py +94 -0
  399. gruff_py-0.1.0/tests/unit/rule/test_quality/test_tautological_type_assertion_rule.py +23 -0
  400. gruff_py-0.1.0/tests/unit/rule/test_quality/test_test_function_too_long_rule.py +59 -0
  401. gruff_py-0.1.0/tests/unit/rule/test_quality/test_trivial_assertion_rule.py +31 -0
  402. gruff_py-0.1.0/tests/unit/rule/test_quality/test_unused_mock_rule.py +35 -0
  403. gruff_py-0.1.0/tests/unit/rule/test_threshold_contract.py +138 -0
  404. gruff_py-0.1.0/tests/unit/rule/waste/__init__.py +0 -0
  405. gruff_py-0.1.0/tests/unit/rule/waste/test_commented_out_code_rule.py +96 -0
  406. gruff_py-0.1.0/tests/unit/rule/waste/test_dead_code_waste_pillar_integration.py +148 -0
  407. gruff_py-0.1.0/tests/unit/rule/waste/test_empty_class_and_function.py +163 -0
  408. gruff_py-0.1.0/tests/unit/rule/waste/test_one_line_function_rule.py +95 -0
  409. gruff_py-0.1.0/tests/unit/rule/waste/test_redundant_variable_rule.py +96 -0
  410. gruff_py-0.1.0/tests/unit/rule/waste/test_unreachable_code_rule.py +222 -0
  411. gruff_py-0.1.0/tests/unit/rule/waste/test_unused_import_rule.py +154 -0
  412. gruff_py-0.1.0/tests/unit/rule/waste/test_unused_parameter_rule.py +128 -0
  413. gruff_py-0.1.0/tests/unit/scoring/__init__.py +0 -0
  414. gruff_py-0.1.0/tests/unit/scoring/test_composite_finding_factory.py +146 -0
  415. gruff_py-0.1.0/tests/unit/scoring/test_score_calculator.py +43 -0
  416. gruff_py-0.1.0/tests/unit/source/__init__.py +0 -0
  417. gruff_py-0.1.0/tests/unit/source/test_discovery_gitignore.py +145 -0
  418. gruff_py-0.1.0/tests/unit/source/test_gitignore_contract.py +135 -0
  419. gruff_py-0.1.0/tests/unit/source/test_gitignore_matcher.py +176 -0
  420. gruff_py-0.1.0/tests/unit/suppression/__init__.py +0 -0
  421. gruff_py-0.1.0/tests/unit/suppression/test_filter.py +74 -0
  422. gruff_py-0.1.0/tests/unit/suppression/test_parser.py +71 -0
  423. gruff_py-0.1.0/uv.lock +499 -0
@@ -0,0 +1,20 @@
1
+ root = true
2
+
3
+ [*]
4
+ end_of_line = lf
5
+ indent_style = space
6
+ indent_size = 4
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.py]
12
+ indent_style = space
13
+ indent_size = 4
14
+ max_line_length = 100
15
+
16
+ [*.{md,toml,yaml,yml,json}]
17
+ indent_size = 2
18
+
19
+ [Makefile]
20
+ indent_style = tab
@@ -0,0 +1,38 @@
1
+ # Normalize text files to LF on commit; let git decide what's text.
2
+ * text=auto eol=lf
3
+
4
+ # Per-extension overrides (explicit > auto for source/config).
5
+ *.py text eol=lf
6
+ *.pyi text eol=lf
7
+ *.sh text eol=lf
8
+ *.yml text eol=lf
9
+ *.yaml text eol=lf
10
+ *.md text eol=lf
11
+ *.ini text eol=lf
12
+ *.toml text eol=lf
13
+ *.cfg text eol=lf
14
+ *.json text eol=lf
15
+ .editorconfig text eol=lf
16
+
17
+ # Windows-only files: keep CRLF.
18
+ *.bat text eol=crlf
19
+ *.cmd text eol=crlf
20
+ *.ps1 text eol=crlf
21
+
22
+ # Binary files — don't normalize, don't diff.
23
+ *.png binary
24
+ *.jpg binary
25
+ *.jpeg binary
26
+ *.gif binary
27
+ *.ico binary
28
+ *.pdf binary
29
+ *.whl binary
30
+ *.zip binary
31
+ *.gz binary
32
+ *.tar binary
33
+ *.tar.gz binary
34
+ *.7z binary
35
+ *.exe binary
36
+ *.dll binary
37
+ *.so binary
38
+ *.dylib binary
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report incorrect behaviour in gruff-py
4
+ title: ""
5
+ labels: bug
6
+ assignees: ""
7
+ ---
8
+
9
+ ## Version
10
+
11
+ - gruff-py:
12
+ - Python:
13
+ - OS:
14
+
15
+ ## Command
16
+
17
+ ```bash
18
+
19
+ ```
20
+
21
+ ## Expected Behaviour
22
+
23
+
24
+ ## Actual Behaviour
25
+
26
+
27
+ ## Minimal Reproduction
28
+
29
+ ```python
30
+
31
+ ```
32
+
33
+ ## Notes
34
+
35
+
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: False positive
3
+ about: Report a finding that should not be emitted
4
+ title: ""
5
+ labels: false-positive
6
+ assignees: ""
7
+ ---
8
+
9
+ ## Rule
10
+
11
+ Rule id:
12
+
13
+ ## Version
14
+
15
+ - gruff-py:
16
+ - Python:
17
+ - OS:
18
+
19
+ ## Code Sample
20
+
21
+ ```python
22
+
23
+ ```
24
+
25
+ ## Finding
26
+
27
+ ```text
28
+
29
+ ```
30
+
31
+ ## Why This Should Be Allowed
32
+
33
+
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest a rule, reporter, config option, or workflow
4
+ title: ""
5
+ labels: enhancement
6
+ assignees: ""
7
+ ---
8
+
9
+ ## Problem
10
+
11
+
12
+ ## Proposed Behaviour
13
+
14
+
15
+ ## Alternatives Considered
16
+
17
+
18
+ ## Compatibility Notes
19
+
20
+ Does this change any schema fields, rule ids, fingerprints, exit-code behaviour,
21
+ or report format names?
22
+
@@ -0,0 +1,18 @@
1
+ ## Summary
2
+
3
+ -
4
+
5
+ ## Verification
6
+
7
+ - [ ] `uv run ruff check src tests`
8
+ - [ ] `uv run ruff format --check src tests`
9
+ - [ ] `uv run mypy src`
10
+ - [ ] `uv run pytest`
11
+
12
+ ## Compatibility Checklist
13
+
14
+ - [ ] No unintended schema string changes
15
+ - [ ] No unintended fingerprint changes
16
+ - [ ] No unintended public rule id changes
17
+ - [ ] Docs updated where behaviour changed
18
+
@@ -0,0 +1,5 @@
1
+ # Git Commit Instructions
2
+
3
+ Use concise conventional commits when practical, for example `feat: add size rule` or `fix: preserve fingerprint compatibility`.
4
+
5
+ Keep commits focused by boundary: runtime code with its tests, GOAT Flow setup docs, and generated package artifacts should not be mixed unless the user explicitly asks for a single combined commit. Do not commit `node_modules/`, `dist/`, `.venv/`, or local cache directories.
@@ -0,0 +1,57 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ check:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.11", "3.12"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v3
21
+ with:
22
+ enable-cache: true
23
+
24
+ - name: Set up Python ${{ matrix.python-version }}
25
+ run: uv python install ${{ matrix.python-version }}
26
+
27
+ - name: Install project
28
+ run: uv sync --all-extras --dev --python ${{ matrix.python-version }}
29
+
30
+ - name: Bash syntax
31
+ run: bash -n scripts/*.sh
32
+
33
+ - name: Shellcheck
34
+ uses: ludeeus/action-shellcheck@master
35
+ with:
36
+ scandir: ./scripts
37
+
38
+ - name: Lint
39
+ run: uv run ruff check src tests
40
+
41
+ - name: Format check
42
+ run: uv run ruff format --check src tests
43
+
44
+ - name: Typecheck
45
+ run: uv run mypy src
46
+
47
+ - name: Rule docs
48
+ run: uv run python -m gruffpy.command.rule_docs --check docs/RULES.md
49
+
50
+ - name: Test
51
+ run: uv run pytest
52
+
53
+ - name: Gruff self-check
54
+ run: uv run gruff-py analyse src tests --fail-on advisory
55
+
56
+ - name: Package build
57
+ run: uv build
@@ -0,0 +1,60 @@
1
+ name: publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions: {}
9
+
10
+ jobs:
11
+ publish:
12
+ name: Build and publish to PyPI
13
+ runs-on: ubuntu-latest
14
+ environment:
15
+ name: pypi
16
+ url: https://pypi.org/project/gruff-py/
17
+ permissions:
18
+ id-token: write
19
+ contents: read
20
+
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v3
26
+ with:
27
+ enable-cache: true
28
+
29
+ - name: Set up Python
30
+ run: uv python install 3.11
31
+
32
+ - name: Install project
33
+ run: uv sync --all-extras --dev --python 3.11
34
+
35
+ - name: Verify tag matches project version
36
+ run: |
37
+ tag="${GITHUB_REF_NAME#v}"
38
+ pyver=$(uv run python -c 'import tomllib, pathlib; print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')
39
+ if [[ "$tag" != "$pyver" ]]; then
40
+ echo "::error::Tag '${GITHUB_REF_NAME}' does not match pyproject.toml version '$pyver'"
41
+ exit 1
42
+ fi
43
+
44
+ - name: Lint
45
+ run: uv run ruff check src tests
46
+
47
+ - name: Format check
48
+ run: uv run ruff format --check src tests
49
+
50
+ - name: Typecheck
51
+ run: uv run mypy src
52
+
53
+ - name: Test
54
+ run: uv run pytest
55
+
56
+ - name: Build sdist and wheel
57
+ run: uv build
58
+
59
+ - name: Publish to PyPI
60
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,88 @@
1
+ # ----- Build artifacts -----
2
+ build/
3
+ dist/
4
+ *.egg-info/
5
+ *.egg
6
+ *.whl
7
+
8
+ # ----- Python bytecode and shared objects -----
9
+ __pycache__/
10
+ *.py[cod]
11
+ *$py.class
12
+ *.so
13
+ *.pyd
14
+
15
+ # ----- Virtual environments -----
16
+ .venv/
17
+ venv/
18
+ env/
19
+ ENV/
20
+ __pypackages__/
21
+
22
+ # ----- Local config and secrets (dotenv-style files) -----
23
+ # (Note: NOT a virtualenv. `.env` is almost always a KEY=value file with secrets.)
24
+ .env
25
+ .env.local
26
+ .env.*.local
27
+ .envrc.local
28
+
29
+ # ----- Coverage and test caches -----
30
+ htmlcov/
31
+ .tox/
32
+ .nox/
33
+ .coverage
34
+ .coverage.*
35
+ .pytest_cache/
36
+ .hypothesis/
37
+ coverage.xml
38
+ *.cover
39
+
40
+ # ----- Type checker / linter / formatter caches -----
41
+ .mypy_cache/
42
+ .dmypy.json
43
+ dmypy.json
44
+ .pyre/
45
+ .pytype/
46
+ .ruff_cache/
47
+ cython_debug/
48
+
49
+ # ----- Publishing -----
50
+ .pypirc
51
+
52
+ # ----- Logs and pip artifacts -----
53
+ *.log
54
+ pip-log.txt
55
+ pip-delete-this-directory.txt
56
+
57
+ # ----- IDE / editor -----
58
+ .idea/
59
+ .vscode/
60
+ .spyderproject
61
+ .spyproject
62
+ .ropeproject
63
+ *.swp
64
+ *.swo
65
+ .*.swp
66
+ .*.swo
67
+ *~
68
+ *.bak
69
+
70
+ # ----- OS files -----
71
+ .DS_Store
72
+ Thumbs.db
73
+
74
+ # ----- Performance harness output -----
75
+ perf-out/
76
+
77
+ # ----- Claude Code (per-user overrides; shared .claude/settings.json stays tracked) -----
78
+ .claude/settings.local.json
79
+
80
+ # ----- Codex (per-user overrides; shared .codex/config.toml stays tracked) -----
81
+ .codex/config.local.toml
82
+
83
+ /node_modules
84
+
85
+ # ----- Not ignored on purpose (keep tracked) -----
86
+ # uv.lock → committed: gruff is a CLI app; lock = reproducible installs.
87
+ # gruff-baseline.json, .gruff-history.json → committed: project-level state.
88
+ # .python-version → if present, used for team Python pinning.