github2gerrit 1.4.0__tar.gz → 1.4.2__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 (135) hide show
  1. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/.pre-commit-config.yaml +2 -2
  2. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/PKG-INFO +6 -4
  3. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/README.md +5 -3
  4. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/action.yaml +13 -3
  5. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/docs/cli.md +27 -20
  6. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/docs/features.md +7 -8
  7. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/pyproject.toml +6 -4
  8. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/cli.py +21 -50
  9. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/commit_normalization.py +0 -16
  10. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/commit_rules.py +0 -24
  11. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/config.py +36 -8
  12. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/core.py +252 -157
  13. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/duplicate_detection.py +0 -9
  14. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/error_codes.py +19 -7
  15. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/external_api.py +0 -7
  16. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/gerrit_pr_closer.py +4 -45
  17. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/gerrit_query.py +54 -15
  18. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/github_api.py +0 -1
  19. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/gitreview.py +0 -56
  20. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/gitutils.py +0 -5
  21. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/mapping_comment.py +0 -2
  22. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/netrc.py +12 -21
  23. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/orchestrator/reconciliation.py +13 -19
  24. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/pr_content_filter.py +0 -17
  25. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/reconcile_matcher.py +0 -6
  26. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/rich_display.py +5 -3
  27. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/rich_logging.py +5 -5
  28. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/similarity.py +0 -4
  29. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/ssh_agent_setup.py +0 -6
  30. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/ssh_common.py +0 -1
  31. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/ssh_config_parser.py +0 -3
  32. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/ssh_discovery.py +2 -7
  33. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/trailers.py +0 -7
  34. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_config_helpers.py +2 -2
  35. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_config_and_errors.py +143 -0
  36. github2gerrit-1.4.2/tests/test_final_state_reconciliation.py +333 -0
  37. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_pr_update_detection.py +2 -2
  38. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_reconciliation_extracted_module.py +43 -0
  39. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/uv.lock +149 -143
  40. github2gerrit-1.4.0/.readthedocs.yml +0 -32
  41. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/.editorconfig +0 -0
  42. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/.gitignore +0 -0
  43. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/.gitlint +0 -0
  44. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/.markdownlint.yaml +0 -0
  45. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/.yamllint +0 -0
  46. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/LICENSE +0 -0
  47. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/LICENSES/Apache-2.0.txt +0 -0
  48. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/REUSE.toml +0 -0
  49. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/SECURITY.md +0 -0
  50. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/docs/COMMIT_RULES.md +0 -0
  51. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/docs/development.md +0 -0
  52. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/docs/github2gerrit_token_permissions_classic.png +0 -0
  53. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/sitecustomize.py +0 -0
  54. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/__init__.py +0 -0
  55. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/constants.py +0 -0
  56. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/gerrit_rest.py +0 -0
  57. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/gerrit_ssh.py +0 -0
  58. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/gerrit_urls.py +0 -0
  59. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/models.py +0 -0
  60. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/orchestrator/__init__.py +0 -0
  61. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/pr_commands.py +0 -0
  62. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/src/github2gerrit/utils.py +0 -0
  63. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/conftest.py +0 -0
  64. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/fixtures/__init__.py +0 -0
  65. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/fixtures/make_repo.py +0 -0
  66. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/fixtures/ssh_config_samples.py +0 -0
  67. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_action_environment_mapping.py +0 -0
  68. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_action_outputs.py +0 -0
  69. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_action_pr_number_handling.py +0 -0
  70. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_action_step_validation.py +0 -0
  71. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_automation_only.py +0 -0
  72. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_change_id_deduplication.py +0 -0
  73. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_clean_squash_title.py +0 -0
  74. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_cli.py +0 -0
  75. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_cli_helpers.py +0 -0
  76. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_cli_netrc_options.py +0 -0
  77. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_cli_outputs_file.py +0 -0
  78. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_cli_url_and_dryrun.py +0 -0
  79. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_commit_normalization.py +0 -0
  80. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_commit_rules.py +0 -0
  81. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_composite_action_coverage.py +0 -0
  82. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_config_and_reviewers.py +0 -0
  83. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_close_pr_policy.py +0 -0
  84. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_gerrit_backref_comment.py +0 -0
  85. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_gerrit_push_errors.py +0 -0
  86. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_gerrit_rest_results.py +0 -0
  87. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_integration_fixture_repo.py +0 -0
  88. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_prepare_commits.py +0 -0
  89. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_shallow_clone.py +0 -0
  90. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_ssh_setup.py +0 -0
  91. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_core_ssrf_protection.py +0 -0
  92. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_dependency_supersession.py +0 -0
  93. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_dns_validation_and_no_gerrit.py +0 -0
  94. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_duplicate_detection.py +0 -0
  95. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_email_case_normalization.py +0 -0
  96. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_error_codes.py +0 -0
  97. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_external_api_framework.py +0 -0
  98. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_force_flag_cli.py +0 -0
  99. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_gerrit_change_id_footer.py +0 -0
  100. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_gerrit_change_status_checks.py +0 -0
  101. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_gerrit_pr_closer.py +0 -0
  102. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_gerrit_rest_client.py +0 -0
  103. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_gerrit_ssh_abandon.py +0 -0
  104. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_gerrit_urls.py +0 -0
  105. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_gerrit_urls_more.py +0 -0
  106. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_ghe_and_gitreview_args.py +0 -0
  107. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_github_api_error_handling.py +0 -0
  108. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_github_api_helpers.py +0 -0
  109. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_github_api_retry_and_helpers.py +0 -0
  110. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_gitreview.py +0 -0
  111. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_gitutils_helpers.py +0 -0
  112. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_issue_157_regressions.py +0 -0
  113. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_mapping_comment_additional.py +0 -0
  114. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_mapping_comment_digest_and_backref.py +0 -0
  115. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_metadata_and_reconciliation.py +0 -0
  116. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_metadata_trailer_separation_bug.py +0 -0
  117. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_misc_small_coverage.py +0 -0
  118. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_netrc.py +0 -0
  119. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_orphan_rest_side_effects.py +0 -0
  120. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_pr_commands.py +0 -0
  121. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_pr_content_filter.py +0 -0
  122. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_pr_content_filter_integration.py +0 -0
  123. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_reconciliation_plan_and_orphans.py +0 -0
  124. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_reconciliation_scenarios.py +0 -0
  125. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_ssh_agent.py +0 -0
  126. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_ssh_agent_ownership.py +0 -0
  127. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_ssh_artifact_prevention.py +0 -0
  128. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_ssh_common.py +0 -0
  129. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_ssh_discovery.py +0 -0
  130. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_ssh_discovery_dry_run.py +0 -0
  131. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_trailers_additional.py +0 -0
  132. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_url_parser.py +0 -0
  133. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/test_utils.py +0 -0
  134. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/unit/test_config_integration.py +0 -0
  135. {github2gerrit-1.4.0 → github2gerrit-1.4.2}/tests/unit/test_ssh_config_parser.py +0 -0
@@ -59,7 +59,7 @@ repos:
59
59
  types: [yaml]
60
60
 
61
61
  - repo: https://github.com/astral-sh/ruff-pre-commit
62
- rev: c59bba8fb259db0fec2bbb77ad8ba51ea7341b56 # frozen: v0.15.20
62
+ rev: 01a675ea018f2fb714478a5ffb83fcea8374bb06 # frozen: v0.15.21
63
63
  hooks:
64
64
  - id: ruff
65
65
  files: ^(src|scripts|tests)/.+\.py$
@@ -68,7 +68,7 @@ repos:
68
68
  files: ^(src|scripts|tests)/.+\.py$
69
69
 
70
70
  - repo: https://github.com/pre-commit/mirrors-mypy
71
- rev: d2823d321df3af8f878f7ee3414dc94d037145b9 # frozen: v2.1.0
71
+ rev: 41e691678310dfd3833f7ab4e180ddb014310356 # frozen: v2.3.0
72
72
  hooks:
73
73
  - id: mypy
74
74
  files: ^src/.+\.py$
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github2gerrit
3
- Version: 1.4.0
3
+ Version: 1.4.2
4
4
  Summary: Submit a GitHub pull request to a Gerrit repository.
5
5
  Project-URL: Homepage, https://github.com/lfreleng-actions/github2gerrit-action
6
6
  Project-URL: Repository, https://github.com/lfreleng-actions/github2gerrit-action
@@ -353,9 +353,11 @@ dispatches the caller workflow to report a merged or abandoned
353
353
  change, forward these dispatch inputs and the tool closes the source
354
354
  GitHub PR instead of processing pull requests.
355
355
 
356
- Setting the repository variable `G2G_NO_GERRIT` to `true` makes runs
357
- skip Gerrit interaction. Test-only settings (`CI_TESTING`,
358
- `USE_LOCAL_ACTION`) remain composite-action-only by design.
356
+ Repository variables provide operational kill switches: set
357
+ `G2G_NO_GERRIT` to `true` to skip Gerrit interaction, or
358
+ `G2G_DISABLED` to `true` to exit immediately without processing.
359
+ Test-only settings (`CI_TESTING`, `USE_LOCAL_ACTION`) remain
360
+ composite-action-only by design.
359
361
 
360
362
  ## Documentation
361
363
 
@@ -307,9 +307,11 @@ dispatches the caller workflow to report a merged or abandoned
307
307
  change, forward these dispatch inputs and the tool closes the source
308
308
  GitHub PR instead of processing pull requests.
309
309
 
310
- Setting the repository variable `G2G_NO_GERRIT` to `true` makes runs
311
- skip Gerrit interaction. Test-only settings (`CI_TESTING`,
312
- `USE_LOCAL_ACTION`) remain composite-action-only by design.
310
+ Repository variables provide operational kill switches: set
311
+ `G2G_NO_GERRIT` to `true` to skip Gerrit interaction, or
312
+ `G2G_DISABLED` to `true` to exit immediately without processing.
313
+ Test-only settings (`CI_TESTING`, `USE_LOCAL_ACTION`) remain
314
+ composite-action-only by design.
313
315
 
314
316
  ## Documentation
315
317
 
@@ -22,6 +22,15 @@ inputs:
22
22
  description: "Fetch depth for checkout"
23
23
  required: false
24
24
  default: "10"
25
+ SKIP_CHECKOUT:
26
+ description: >-
27
+ Skip the internal repository checkout. Set when the caller has
28
+ already checked out the target repository at the desired ref
29
+ (e.g. the bundled reusable workflow, which also checks out this
30
+ action into the workspace and must not have it deleted by a
31
+ nested checkout)
32
+ required: false
33
+ default: "false"
25
34
  PR_NUMBER:
26
35
  description: "Pull request number to process, use 0 to process all open"
27
36
  required: false
@@ -108,7 +117,7 @@ inputs:
108
117
  required: false
109
118
  default: ""
110
119
  GERRIT_SERVER_PORT:
111
- description: "Gerrit serverSSH TCP/port"
120
+ description: "Gerrit server SSH TCP port"
112
121
  required: false
113
122
  default: "29418"
114
123
  GERRIT_PROJECT:
@@ -205,7 +214,8 @@ runs:
205
214
  fi
206
215
 
207
216
  - name: "Checkout repository"
208
- if: steps.disabled-check.outputs.disabled != 'true'
217
+ # yamllint disable-line rule:line-length
218
+ if: steps.disabled-check.outputs.disabled != 'true' && inputs.SKIP_CHECKOUT != 'true'
209
219
  # yamllint disable-line rule:line-length
210
220
  uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
211
221
  with:
@@ -227,7 +237,7 @@ runs:
227
237
  - name: "Setup uv"
228
238
  if: steps.disabled-check.outputs.disabled != 'true'
229
239
  # yamllint disable-line rule:line-length
230
- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
240
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
231
241
  with:
232
242
  enable-cache: false
233
243
 
@@ -159,26 +159,33 @@ Some settings have no CLI flag and use environment variables:
159
159
 
160
160
  <!-- markdownlint-disable MD013 -->
161
161
 
162
- | Variable | Default | Description |
163
- | -------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------- |
164
- | `G2G_CONFIG_PATH` | `~/.config/github2gerrit/configuration.txt` | Path to the per-organization configuration file |
165
- | `G2G_LOG_LEVEL` | `WARNING` | Logging level; set `DEBUG` for verbose output |
166
- | `G2G_TOPIC_PREFIX` | `GH` | Prefix used when generating Gerrit topics |
167
- | `G2G_SKIP_GERRIT_COMMENTS` | `false` | Skip posting back-reference comments on Gerrit changes |
168
- | `G2G_ENABLE_DERIVATION` | `true` | Enable automatic derivation of Gerrit parameters from organization defaults |
169
- | `G2G_AUTO_SAVE_CONFIG` | context-dependent | Save derived parameters back to the configuration file |
170
- | `G2G_RESPECT_USER_SSH` | `false` (`true` in direct URL mode) | Use the local user's SSH configuration and keys instead of provided key material |
171
- | `G2G_USE_SSH_AGENT` | `true` | Use SSH agent authentication; set `false` to force file-based SSH keys |
172
- | `G2G_NO_GERRIT` | `false` | Run the full pipeline without contacting Gerrit (forces dry-run behavior) |
173
- | `G2G_DISABLED` | `false` | Exit immediately with success without processing anything |
174
- | `CLEANUP_ABANDONED` | `true` | Close GitHub PRs whose Gerrit changes have status abandoned |
175
- | `CLEANUP_GERRIT` | `true` | Abandon Gerrit changes for closed GitHub PRs |
176
- | `PR_NUMBER` | unset | Pull request number to process in CI/CD mode (`0` means bulk mode) |
177
- | `SYNC_ALL_OPEN_PRS` | `false` | Process all open PRs in the repository (bulk mode) |
178
- | `GERRIT_HTTP_USER` | unset | Username for the Gerrit REST API (when required) |
179
- | `GERRIT_HTTP_PASSWORD` | unset | HTTP password/token for the Gerrit REST API |
180
- | `GERRIT_HTTP_BASE_PATH` | unset | Gerrit REST base path for non-standard deployments (e.g., `/r`) |
181
- | `GITHUB_TOKEN` | unset | GitHub API token; required for PR queries, comments, and closing PRs |
162
+ | Variable | Default | Description |
163
+ | ----------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------- |
164
+ | `G2G_CONFIG_PATH` | `~/.config/github2gerrit/configuration.txt` | Path to the per-organization configuration file |
165
+ | `G2G_LOG_LEVEL` | `WARNING` | Logging level; set `DEBUG` for verbose output |
166
+ | `G2G_TOPIC_PREFIX` | `GH` | Prefix used when generating Gerrit topics |
167
+ | `G2G_SKIP_GERRIT_COMMENTS` | `false` | Skip posting back-reference comments on Gerrit changes |
168
+ | `G2G_ENABLE_DERIVATION` | `true` | Enable automatic derivation of Gerrit parameters from organization defaults |
169
+ | `G2G_AUTO_SAVE_CONFIG` | context-dependent | Save derived parameters back to the configuration file |
170
+ | `G2G_RESPECT_USER_SSH` | `false` (`true` in direct URL mode) | Use the local user's SSH configuration and keys instead of provided key material |
171
+ | `G2G_USE_SSH_AGENT` | `true` | Use SSH agent authentication; set `false` to force file-based SSH keys |
172
+ | `G2G_NO_GERRIT` | `false` | Run the full pipeline without contacting Gerrit (forces dry-run behavior) |
173
+ | `G2G_DISABLED` | `false` | Exit immediately with success without processing anything |
174
+ | `CLEANUP_ABANDONED` | `true` | Close GitHub PRs whose Gerrit changes have status abandoned |
175
+ | `CLEANUP_GERRIT` | `true` | Abandon Gerrit changes for closed GitHub PRs |
176
+ | `PR_NUMBER` | unset | Pull request number to process in CI/CD mode (`0` means bulk mode) |
177
+ | `SYNC_ALL_OPEN_PRS` | `false` | Process all open PRs in the repository (bulk mode) |
178
+ | `GERRIT_HTTP_USER` | unset | Username for the Gerrit REST API (when required) |
179
+ | `GERRIT_HTTP_PASSWORD` | unset | HTTP password/token for the Gerrit REST API |
180
+ | `GERRIT_HTTP_BASE_PATH` | unset | Gerrit REST base path for non-standard deployments (e.g., `/r`) |
181
+ | `GITHUB_TOKEN` | unset | GitHub API token; required for PR queries, comments, and closing PRs |
182
+ | `GH_TOKEN` | unset | Fallback source for `GITHUB_TOKEN` when the latter is unset |
183
+ | `GERRIT_CHANGE_URL` | unset | Gerrit change URL for Gerrit-event dispatch mode (close the source PR) |
184
+ | `GERRIT_EVENT_TYPE` | unset | Gerrit event type for dispatch mode (`change-merged` or `change-abandoned`) |
185
+ | `GERRIT_BRANCH` | unset | Gerrit branch for dispatch mode change queries |
186
+ | `G2G_DRYRUN_DISABLE_NETWORK` | `false` | Skip DNS/network checks in dry-run; set automatically by `G2G_NO_GERRIT` |
187
+ | `G2G_ANON_SUPERSEDE_FALLBACK` | `true` | Allow anonymous supersession queries when Gerrit credentials are unavailable |
188
+ | `VERIFY_DIGEST_STRICT` | `true` | Fail (rather than warn) on reconciliation digest mismatch after push |
182
189
 
183
190
  <!-- markdownlint-enable MD013 -->
184
191
 
@@ -44,8 +44,8 @@ When a PR updates (for example, Dependabot rebases or updates dependencies):
44
44
  1. The `synchronize` event triggers UPDATE mode.
45
45
  2. The tool recovers the existing Gerrit change using five strategies,
46
46
  in order:
47
- - Topic-based query (`GH-<owner>-<repo>-<pr-number>`, where `<repo>` is
48
- the GitHub repository name without the owner prefix)
47
+ - Topic-based query (`<prefix>-<project>-<pr-number>`, matching the
48
+ topic the tool pushed; see "Topic naming" under Behavior Details)
49
49
  - `GitHub-Hash` trailer matching
50
50
  - `GitHub-PR` trailer URL matching
51
51
  - Mapping comment parsing from PR comments
@@ -702,12 +702,11 @@ Lookup logic:
702
702
 
703
703
  - Branch resolution: uses `GITHUB_BASE_REF` as the target branch for Gerrit,
704
704
  defaulting to `master` when unset.
705
- - Topic naming: pushes use `<prefix>-<project>-<pr-number>`, where
706
- `<prefix>` comes from `G2G_TOPIC_PREFIX` (default `GH`) and `<project>`
707
- is the Gerrit project path with `/` replaced by `-` (or the GitHub
708
- repository name when `.gitreview` is unavailable). Change recovery
709
- queries use `GH-<owner>-<repo>-<pr-number>` with the GitHub owner and
710
- repository name.
705
+ - Topic naming: pushes and change recovery queries use
706
+ `<prefix>-<project>-<pr-number>`, where `<prefix>` comes from
707
+ `G2G_TOPIC_PREFIX` (default `GH`) and `<project>` is the Gerrit
708
+ project path with `/` replaced by `-` (or the GitHub repository name
709
+ when `.gitreview` is unavailable).
711
710
  - GitHub Enterprise support: direct URL mode accepts enterprise GitHub hosts
712
711
  when explicitly enabled via `--allow-ghe-urls` or `ALLOW_GHE_URLS="true"`
713
712
  (default: off). The tool determines the GitHub API base URL from
@@ -199,10 +199,6 @@ ignore_missing_imports = true
199
199
  module = ["typer", "typer.*"]
200
200
  ignore_missing_imports = true
201
201
 
202
- [[tool.mypy.overrides]]
203
- module = ["importlib_metadata"]
204
- ignore_missing_imports = true
205
-
206
202
  [[tool.mypy.overrides]]
207
203
  module = ["pytest", "pytest.*"]
208
204
  ignore_missing_imports = true
@@ -220,6 +216,12 @@ disallow_untyped_calls = false
220
216
 
221
217
  [tool.coverage.run]
222
218
  branch = true
219
+ # Keep transient coverage data out of the working tree so tests that
220
+ # inspect on-disk repository content never see .coverage* files.
221
+ # During pytest runs tests/conftest.py sets COVERAGE_FILE (env), which
222
+ # takes precedence; this value covers direct coverage invocations and
223
+ # satisfies python-test-action's data_file configuration check.
224
+ data_file = "${TMPDIR-/tmp}/.coverage.github2gerrit"
223
225
  source = ["github2gerrit"]
224
226
  omit = [
225
227
  "tests/*",
@@ -88,15 +88,10 @@ from .utils import parse_bool_env
88
88
 
89
89
 
90
90
  def get_version(package: str) -> str:
91
- """Get package version, trying importlib.metadata first, then fallback."""
92
- try:
93
- from importlib.metadata import version as stdlib_version
94
-
95
- return str(stdlib_version(package))
96
- except ImportError:
97
- from importlib_metadata import version as backport_version
91
+ """Get the installed package version via importlib.metadata."""
92
+ from importlib.metadata import version
98
93
 
99
- return str(backport_version(package))
94
+ return str(version(package))
100
95
 
101
96
 
102
97
  # Legacy error handling functions - now use centralized error_codes module
@@ -184,7 +179,6 @@ def _check_automation_only(
184
179
  "pre-commit-ci",
185
180
  ]
186
181
 
187
- # Get PR author
188
182
  pr_author = getattr(getattr(pr_obj, "user", None), "login", "")
189
183
  if not pr_author:
190
184
  log.warning("Unable to determine PR author, allowing PR to proceed")
@@ -262,10 +256,8 @@ def _extract_and_display_pr_info(
262
256
  # Check if automation_only is enabled and reject non-automation PRs
263
257
  _check_automation_only(pr_obj, gh, progress_tracker)
264
258
 
265
- # Extract PR information
266
259
  title, _body = get_pr_title_body(pr_obj)
267
260
 
268
- # Get additional PR details
269
261
  pr_info = {
270
262
  "Repository": gh.repository,
271
263
  "PR Number": gh.pr_number,
@@ -364,7 +356,6 @@ def _parse_github_target(url: str) -> GitHubPRTarget | GitHubRepoTarget:
364
356
 
365
357
  owner, repo = parts[0], parts[1]
366
358
 
367
- # Check for PR URL
368
359
  if len(parts) >= 4 and parts[2] in ("pull", "pulls"):
369
360
  try:
370
361
  pr_number = int(parts[3])
@@ -378,7 +369,6 @@ def _parse_github_target(url: str) -> GitHubPRTarget | GitHubRepoTarget:
378
369
 
379
370
  APP_NAME = "github2gerrit"
380
371
 
381
-
382
372
  if TYPE_CHECKING:
383
373
  BaseGroup = object
384
374
  else:
@@ -429,10 +419,8 @@ def _resolve_issue_id_from_json(json_str: str, github_actor: str) -> str:
429
419
  return ""
430
420
 
431
421
  try:
432
- # Parse JSON
433
422
  lookup_data = json.loads(json_str)
434
423
 
435
- # Validate it's an array
436
424
  if not isinstance(lookup_data, list):
437
425
  log.warning(
438
426
  "⚠️ Warning: Issue-ID JSON was not valid (expected array)"
@@ -484,8 +472,11 @@ def _print_version_banner() -> None:
484
472
  try:
485
473
  app_version = get_version("github2gerrit")
486
474
  print(f"🏷️ github2gerrit version {app_version}")
487
- except Exception:
488
- print("⚠️ github2gerrit version information not available")
475
+ # The banner is cosmetic: a missing or unreadable package version
476
+ # must never block CLI startup.
477
+ # aislop-ignore-next-line swallowed-exception
478
+ except Exception as exc:
479
+ print(f"⚠️ github2gerrit version information not available: {exc}")
489
480
 
490
481
 
491
482
  # Show version information when --help is used or in GitHub Actions mode
@@ -531,7 +522,6 @@ def _save_derived_parameters_after_success(data: Inputs) -> None:
531
522
  """Save derived parameters to config file after successful Gerrit
532
523
  submission."""
533
524
  try:
534
- # Get the organization used for derivation
535
525
  org_for_cfg = (
536
526
  data.organization
537
527
  or os.getenv("ORGANIZATION")
@@ -756,8 +746,6 @@ def main(
756
746
  envvar="G2G_SHOW_PROGRESS",
757
747
  help="Show real-time progress updates with Rich formatting.",
758
748
  ),
759
- # BREAKING CHANGE v0.2.0: Default changed from True to False
760
- # for more flexible commit reconciliation
761
749
  similarity_files: bool = typer.Option(
762
750
  False,
763
751
  "--similarity-files/--no-similarity-files",
@@ -852,7 +840,6 @@ def main(
852
840
  - No arguments: environment variables determine behaviour (CI/CD mode)
853
841
  """
854
842
 
855
- # Handle version flag first
856
843
  if version_flag:
857
844
  try:
858
845
  app_version = get_version("github2gerrit")
@@ -931,7 +918,6 @@ def main(
931
918
 
932
919
  # Handle netrc credential loading if enabled
933
920
  if not no_netrc:
934
- # Get the Gerrit server from environment or CLI
935
921
  gerrit_host = gerrit_server or os.getenv("GERRIT_SERVER", "")
936
922
  if gerrit_host:
937
923
  try:
@@ -963,21 +949,18 @@ def main(
963
949
  safe_typer_echo(f"❌ Failed to parse .netrc file: {e}")
964
950
  sys.exit(int(ExitCode.CONFIGURATION_ERROR))
965
951
 
966
- # Set up logging level based on verbose flag
967
952
  if verbose:
968
953
  os.environ["G2G_LOG_LEVEL"] = "DEBUG"
969
954
  _reconfigure_logging()
970
955
 
971
- # Initialize Rich-aware logging system
972
956
  setup_rich_aware_logging()
973
957
 
974
- # Log version to logs in GitHub Actions environment
975
958
  if _is_github_actions_context():
976
959
  try:
977
960
  app_version = get_version("github2gerrit")
978
961
  log.debug("github2gerrit version %s", app_version)
979
- except Exception:
980
- log.warning("Version information not available")
962
+ except Exception as exc:
963
+ log.warning("Version information not available: %s", exc)
981
964
 
982
965
  # Show initial progress if Rich is available and progress is enabled
983
966
  if show_progress and not RICH_AVAILABLE:
@@ -1041,7 +1024,6 @@ def main(
1041
1024
  if reuse_strategy:
1042
1025
  os.environ["REUSE_STRATEGY"] = reuse_strategy
1043
1026
 
1044
- # Validate similarity parameters
1045
1027
  if not (0.0 <= similarity_subject <= 1.0):
1046
1028
  msg = (
1047
1029
  f"similarity_subject must be between 0.0 and 1.0, "
@@ -1231,7 +1213,6 @@ def _build_inputs_from_env() -> Inputs:
1231
1213
 
1232
1214
 
1233
1215
  def _process_bulk(data: Inputs, gh: GitHubContext) -> bool:
1234
- # Initialize progress tracker for processing
1235
1216
  show_progress = env_bool("G2G_SHOW_PROGRESS", True)
1236
1217
  target = gh.repository
1237
1218
 
@@ -1303,7 +1284,6 @@ def _process_bulk(data: Inputs, gh: GitHubContext) -> bool:
1303
1284
  gh.event_action,
1304
1285
  )
1305
1286
 
1306
- # Update progress tracker for this PR
1307
1287
  progress_tracker.update_operation(f"Processing PR #{pr_number}...")
1308
1288
  progress_tracker.pr_processed()
1309
1289
 
@@ -1391,7 +1371,6 @@ def _process_bulk(data: Inputs, gh: GitHubContext) -> bool:
1391
1371
  )
1392
1372
  pr_tasks.append((pr, per_ctx))
1393
1373
 
1394
- # Process PRs in parallel
1395
1374
  with ThreadPoolExecutor(max_workers=max_workers) as executor:
1396
1375
  log.debug(
1397
1376
  "Processing %d PRs with %d parallel workers",
@@ -1495,7 +1474,6 @@ def _process_bulk(data: Inputs, gh: GitHubContext) -> bool:
1495
1474
  }
1496
1475
  )
1497
1476
 
1498
- # Stop progress tracker and show final results
1499
1477
  if failed_count == 0:
1500
1478
  progress_tracker.update_operation("Processing completed ✅")
1501
1479
  else:
@@ -1544,7 +1522,6 @@ def _process_single(
1544
1522
  gh: GitHubContext,
1545
1523
  progress_tracker: G2GProgressTracker | DummyProgressTracker | None = None,
1546
1524
  ) -> tuple[bool, SubmissionResult]:
1547
- # Create temporary directory for all git operations
1548
1525
  with tempfile.TemporaryDirectory() as temp_dir:
1549
1526
  workspace = Path(temp_dir)
1550
1527
 
@@ -1770,7 +1747,6 @@ def _process_single(
1770
1747
 
1771
1748
 
1772
1749
  def _load_effective_inputs() -> Inputs:
1773
- # Build inputs from environment (used by URL callback path)
1774
1750
  data = _build_inputs_from_env()
1775
1751
 
1776
1752
  # Detect GitHub CI mode and configure accordingly
@@ -1789,7 +1765,6 @@ def _load_effective_inputs() -> Inputs:
1789
1765
  )
1790
1766
  cfg = load_org_config(org_for_cfg)
1791
1767
 
1792
- # Get repository for GERRIT_PROJECT derivation
1793
1768
  repository = os.getenv("GITHUB_REPOSITORY", "")
1794
1769
 
1795
1770
  # Apply dynamic parameter derivation for missing Gerrit parameters
@@ -1939,7 +1914,6 @@ def _process_close_gerrit_change(
1939
1914
  # Check if close_merged_prs is enabled
1940
1915
  close_merged_prs = env_bool("CLOSE_MERGED_PRS", True)
1941
1916
 
1942
- # Check Gerrit change status
1943
1917
  status = check_gerrit_change_status(gerrit_change_url)
1944
1918
 
1945
1919
  # Validate status: without --force, reject MERGED/ABANDONED changes
@@ -1956,7 +1930,9 @@ def _process_close_gerrit_change(
1956
1930
  error_msg,
1957
1931
  )
1958
1932
 
1959
- # Log status information
1933
+ # Branches vary in log level and nested close_merged_prs handling,
1934
+ # so a dispatch table would obscure this status logging.
1935
+ # aislop-ignore-next-line python-repetitive-dispatch
1960
1936
  if status == "ABANDONED":
1961
1937
  if close_merged_prs:
1962
1938
  log.debug(
@@ -1983,7 +1959,6 @@ def _process_close_gerrit_change(
1983
1959
 
1984
1960
  log.debug("Found GitHub PR URL: %s", pr_url)
1985
1961
 
1986
- # Parse PR URL to get owner info for auto-discovery
1987
1962
  parsed = parse_pr_url(pr_url)
1988
1963
  if not parsed:
1989
1964
  log.error("Failed to parse PR URL: %s", pr_url)
@@ -2007,6 +1982,9 @@ def _process_close_gerrit_change(
2007
1982
  progress_tracker=None,
2008
1983
  close_merged_prs=close_merged_prs,
2009
1984
  )
1985
+ # log.exception records the caught error and traceback; keep
1986
+ # processing so one PR failure cannot abort the remaining closures.
1987
+ # aislop-ignore-next-line silent-recovery
2010
1988
  except Exception:
2011
1989
  log.exception("Failed to close GitHub PR #%d", pr_number)
2012
1990
 
@@ -2028,7 +2006,6 @@ def _process_close_merged_prs(data: Inputs, gh: GitHubContext) -> None:
2028
2006
  """
2029
2007
  log.info("🔄 Processing merged Gerrit changes to close GitHub PRs")
2030
2008
 
2031
- # Initialize progress tracker
2032
2009
  show_progress = env_bool("G2G_SHOW_PROGRESS", True)
2033
2010
  target = gh.repository
2034
2011
 
@@ -2054,7 +2031,6 @@ def _process_close_merged_prs(data: Inputs, gh: GitHubContext) -> None:
2054
2031
  with gh.event_path.open() as f:
2055
2032
  event_payload = json.load(f)
2056
2033
 
2057
- # Extract commit SHAs from the push event
2058
2034
  if "commits" in event_payload:
2059
2035
  commit_shas = [
2060
2036
  commit["id"]
@@ -2141,7 +2117,6 @@ def _process_close_merged_prs(data: Inputs, gh: GitHubContext) -> None:
2141
2117
  def _process() -> None:
2142
2118
  data = _load_effective_inputs()
2143
2119
 
2144
- # Validate inputs
2145
2120
  try:
2146
2121
  _validate_inputs(data)
2147
2122
  except ConfigurationError as exc:
@@ -2151,11 +2126,11 @@ def _process() -> None:
2151
2126
 
2152
2127
  gh = _read_github_context()
2153
2128
 
2154
- # --- G2G_NO_GERRIT: leverage DRY_RUN infrastructure ---
2155
- # G2G_NO_GERRIT reuses the existing DRY_RUN + G2G_DRYRUN_DISABLE_NETWORK
2156
- # code paths so that all tool logic runs but Gerrit network operations
2157
- # are no-ops. Cleanup tasks (abandoned-PR / Gerrit-change sweeps) are
2158
- # also suppressed because they would hit a non-existent server.
2129
+ # G2G_NO_GERRIT reuses the existing DRY_RUN +
2130
+ # G2G_DRYRUN_DISABLE_NETWORK code paths so that all tool logic runs
2131
+ # but Gerrit network operations are no-ops. Cleanup tasks
2132
+ # (abandoned-PR / Gerrit-change sweeps) are also suppressed because
2133
+ # they would hit a non-existent server.
2159
2134
  no_gerrit = env_bool("G2G_NO_GERRIT", False)
2160
2135
 
2161
2136
  if no_gerrit:
@@ -2336,7 +2311,6 @@ def _process() -> None:
2336
2311
  # 2. Direct Gerrit change URL provided in CLI
2337
2312
  # 3. Gerrit event dispatched via workflow_dispatch (GERRIT_CHANGE_URL set)
2338
2313
 
2339
- # Check for Gerrit event inputs from workflow_dispatch
2340
2314
  gerrit_event_change_url = os.getenv("GERRIT_CHANGE_URL")
2341
2315
  gerrit_event_type = os.getenv("GERRIT_EVENT_TYPE")
2342
2316
 
@@ -2469,7 +2443,6 @@ def _process() -> None:
2469
2443
  ):
2470
2444
  bulk_success = _process_bulk(data, gh)
2471
2445
 
2472
- # Log external API metrics summary
2473
2446
  try:
2474
2447
  log_api_metrics_summary()
2475
2448
  except Exception as exc:
@@ -2702,7 +2675,6 @@ def _process() -> None:
2702
2675
  # Don't fail the whole pipeline if cleanup fails
2703
2676
  log.warning("Gerrit cleanup failed: %s", exc)
2704
2677
 
2705
- # Log external API metrics summary
2706
2678
  try:
2707
2679
  log_api_metrics_summary()
2708
2680
  except Exception as exc:
@@ -2911,7 +2883,6 @@ def _validate_inputs(data: Inputs) -> None:
2911
2883
  _MSG_MISSING_REQUIRED_INPUT.format(field_name=field_name)
2912
2884
  )
2913
2885
 
2914
- # Validate fetch depth is a positive integer
2915
2886
  if data.fetch_depth <= 0:
2916
2887
  log.error("Invalid FETCH_DEPTH: %s", data.fetch_depth)
2917
2888
  raise ConfigurationError(_MSG_INVALID_FETCH_DEPTH)
@@ -149,7 +149,6 @@ class CommitNormalizer:
149
149
  if self._is_conventional_commit(title):
150
150
  return False
151
151
 
152
- # Check for automation patterns
153
152
  return self._is_automation_pr(title, author)
154
153
 
155
154
  def normalize_commit_title(self, title: str, author: str) -> str:
@@ -165,7 +164,6 @@ class CommitNormalizer:
165
164
  # Determine the appropriate conventional commit type
166
165
  commit_type = self._determine_commit_type(title, author)
167
166
 
168
- # Clean and normalize the title
169
167
  normalized_title = self._clean_title(title)
170
168
 
171
169
  # Apply conventional commit format
@@ -181,7 +179,6 @@ class CommitNormalizer:
181
179
 
182
180
  def _is_automation_pr(self, title: str, author: str) -> bool:
183
181
  """Check if this is an automation PR that should be normalized."""
184
- # Check for known automation authors
185
182
  automation_authors = [
186
183
  "dependabot[bot]",
187
184
  "dependabot-preview[bot]",
@@ -195,7 +192,6 @@ class CommitNormalizer:
195
192
  ):
196
193
  return True
197
194
 
198
- # Check for automation patterns in title
199
195
  automation_patterns = [
200
196
  r"^bump\s+",
201
197
  r"^update\s+.*\s+from\s+.*\s+to\s+",
@@ -216,10 +212,8 @@ class CommitNormalizer:
216
212
  self.workspace,
217
213
  )
218
214
 
219
- # Check .pre-commit-config.yaml
220
215
  self._check_precommit_config()
221
216
 
222
- # Check .github/release-drafter.yml
223
217
  self._check_release_drafter_config()
224
218
 
225
219
  # Analyze git history
@@ -239,12 +233,10 @@ class CommitNormalizer:
239
233
 
240
234
  ci_config = config.get("ci", {})
241
235
 
242
- # Check autofix commit message
243
236
  autofix_msg = ci_config.get("autofix_commit_msg", "")
244
237
  if autofix_msg:
245
238
  self._extract_preferences_from_message(autofix_msg)
246
239
 
247
- # Check autoupdate commit message
248
240
  autoupdate_msg = ci_config.get("autoupdate_commit_msg", "")
249
241
  if autoupdate_msg:
250
242
  self._extract_preferences_from_message(autoupdate_msg)
@@ -272,7 +264,6 @@ class CommitNormalizer:
272
264
  titles = rule.get("title", [])
273
265
 
274
266
  for title_pattern in titles:
275
- # Extract conventional commit type from pattern
276
267
  if title_pattern.startswith(
277
268
  "/"
278
269
  ) and title_pattern.endswith("/i"):
@@ -292,7 +283,6 @@ class CommitNormalizer:
292
283
  def _analyze_git_history(self) -> None:
293
284
  """Analyze recent git history for conventional commit patterns."""
294
285
  try:
295
- # Get recent commit messages
296
286
  git_cmd = shutil.which("git")
297
287
  if not git_cmd:
298
288
  log.debug("git command not found in PATH")
@@ -332,7 +322,6 @@ class CommitNormalizer:
332
322
  1
333
323
  )
334
324
 
335
- # Update preferences based on analysis
336
325
  if type_counts:
337
326
  # Determine most common capitalization
338
327
  if capitalization_examples:
@@ -344,7 +333,6 @@ class CommitNormalizer:
344
333
  else:
345
334
  self.preferences.capitalization = "lower"
346
335
 
347
- # Update preferred types
348
336
  for commit_type in type_counts:
349
337
  if commit_type in CONVENTIONAL_COMMIT_TYPES:
350
338
  self.preferences.preferred_types[commit_type] = (
@@ -374,13 +362,11 @@ class CommitNormalizer:
374
362
  """Determine the appropriate conventional commit type."""
375
363
  title_lower = title.lower()
376
364
 
377
- # Check for dependabot patterns first
378
365
  if "dependabot" in author.lower() or any(
379
366
  re.search(pattern, title_lower) for pattern in DEPENDABOT_PATTERNS
380
367
  ):
381
368
  return self.preferences.dependency_type
382
369
 
383
- # Check for pre-commit.ci patterns
384
370
  if "pre-commit" in author.lower() or "pre-commit" in title_lower:
385
371
  return self.preferences.automation_type
386
372
 
@@ -395,10 +381,8 @@ class CommitNormalizer:
395
381
 
396
382
  def _clean_title(self, title: str) -> str:
397
383
  """Clean and normalize the title text."""
398
- # Remove markdown links
399
384
  title = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", title)
400
385
 
401
- # Remove trailing ellipsis
402
386
  title = re.sub(r"\s*[.]{3,}.*$", "", title)
403
387
 
404
388
  # Remove markdown bold/code formatting but preserve underscores