github2gerrit 2.2.0__tar.gz → 2.2.1__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 (150) hide show
  1. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/PKG-INFO +11 -10
  2. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/README.md +10 -9
  3. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/docs/cli.md +34 -32
  4. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/docs/features.md +82 -8
  5. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/cli.py +118 -23
  6. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/config.py +285 -349
  7. github2gerrit-2.2.1/src/github2gerrit/config_ini.py +263 -0
  8. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/core.py +221 -57
  9. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/duplicate_detection.py +7 -8
  10. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/gerrit_pr_closer.py +56 -45
  11. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/gerrit_query.py +6 -6
  12. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/gerrit_rest.py +8 -2
  13. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/gitreview.py +130 -0
  14. github2gerrit-2.2.1/src/github2gerrit/project_names.py +505 -0
  15. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/utils.py +33 -0
  16. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/conftest.py +32 -0
  17. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_config_helpers.py +717 -15
  18. github2gerrit-2.2.1/tests/test_core_integration_fixture_repo.py +727 -0
  19. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_dns_validation_and_no_gerrit.py +16 -0
  20. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_duplicate_detection.py +42 -1
  21. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_fork_pr_hardening.py +131 -5
  22. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_gerrit_pr_closer.py +46 -13
  23. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_issue_157_regressions.py +48 -10
  24. github2gerrit-2.2.1/tests/test_pr_closed_handling.py +259 -0
  25. github2gerrit-2.2.1/tests/test_project_names.py +642 -0
  26. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/unit/test_config_integration.py +23 -16
  27. github2gerrit-2.2.0/tests/test_core_integration_fixture_repo.py +0 -200
  28. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/.aislop/config.yml +0 -0
  29. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/.editorconfig +0 -0
  30. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/.gitignore +0 -0
  31. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/.gitlint +0 -0
  32. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/.markdownlint.yaml +0 -0
  33. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/.pre-commit-config.yaml +0 -0
  34. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/.yamllint +0 -0
  35. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/LICENSE +0 -0
  36. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/LICENSES/Apache-2.0.txt +0 -0
  37. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/REUSE.toml +0 -0
  38. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/SECURITY.md +0 -0
  39. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/action.yaml +0 -0
  40. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/docs/COMMIT_RULES.md +0 -0
  41. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/docs/development.md +0 -0
  42. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/docs/github2gerrit_token_permissions_classic.png +0 -0
  43. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/pyproject.toml +0 -0
  44. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/sitecustomize.py +0 -0
  45. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/__init__.py +0 -0
  46. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/approvers.py +0 -0
  47. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/commit_normalization.py +0 -0
  48. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/commit_rules.py +0 -0
  49. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/constants.py +0 -0
  50. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/error_codes.py +0 -0
  51. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/external_api.py +0 -0
  52. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/gerrit_ssh.py +0 -0
  53. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/gerrit_urls.py +0 -0
  54. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/github_api.py +0 -0
  55. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/gitutils.py +0 -0
  56. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/mapping_comment.py +0 -0
  57. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/models.py +0 -0
  58. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/netrc.py +0 -0
  59. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/orchestrator/__init__.py +0 -0
  60. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/orchestrator/reconciliation.py +0 -0
  61. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/pr_approval.py +0 -0
  62. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/pr_commands.py +0 -0
  63. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/pr_content_filter.py +0 -0
  64. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/pr_directives.py +0 -0
  65. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/reconcile_matcher.py +0 -0
  66. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/rich_display.py +0 -0
  67. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/rich_logging.py +0 -0
  68. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/similarity.py +0 -0
  69. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/ssh_agent_setup.py +0 -0
  70. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/ssh_common.py +0 -0
  71. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/ssh_config_parser.py +0 -0
  72. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/ssh_discovery.py +0 -0
  73. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/trailers.py +0 -0
  74. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/src/github2gerrit/trust.py +0 -0
  75. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/fixtures/__init__.py +0 -0
  76. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/fixtures/make_repo.py +0 -0
  77. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/fixtures/ssh_config_samples.py +0 -0
  78. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_action_environment_mapping.py +0 -0
  79. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_action_outputs.py +0 -0
  80. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_action_pr_number_handling.py +0 -0
  81. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_action_step_validation.py +0 -0
  82. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_approver_sources.py +0 -0
  83. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_automation_only.py +0 -0
  84. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_cache_isolation.py +0 -0
  85. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_change_id_deduplication.py +0 -0
  86. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_clean_squash_title.py +0 -0
  87. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_cli.py +0 -0
  88. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_cli_helpers.py +0 -0
  89. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_cli_netrc_options.py +0 -0
  90. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_cli_outputs_file.py +0 -0
  91. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_cli_url_and_dryrun.py +0 -0
  92. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_commit_normalization.py +0 -0
  93. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_commit_rules.py +0 -0
  94. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_composite_action_coverage.py +0 -0
  95. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_config_and_reviewers.py +0 -0
  96. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_core_close_pr_policy.py +0 -0
  97. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_core_config_and_errors.py +0 -0
  98. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_core_gerrit_backref_comment.py +0 -0
  99. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_core_gerrit_push_errors.py +0 -0
  100. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_core_gerrit_rest_results.py +0 -0
  101. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_core_prepare_commits.py +0 -0
  102. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_core_shallow_clone.py +0 -0
  103. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_core_ssh_setup.py +0 -0
  104. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_core_ssrf_protection.py +0 -0
  105. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_dependency_supersession.py +0 -0
  106. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_email_case_normalization.py +0 -0
  107. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_error_codes.py +0 -0
  108. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_external_api_framework.py +0 -0
  109. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_final_state_reconciliation.py +0 -0
  110. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_force_flag_cli.py +0 -0
  111. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_fork_approval_gate.py +0 -0
  112. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_gerrit_change_id_footer.py +0 -0
  113. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_gerrit_change_status_checks.py +0 -0
  114. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_gerrit_rest_client.py +0 -0
  115. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_gerrit_ssh_abandon.py +0 -0
  116. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_gerrit_urls.py +0 -0
  117. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_gerrit_urls_more.py +0 -0
  118. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_ghe_and_gitreview_args.py +0 -0
  119. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_github_api_error_handling.py +0 -0
  120. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_github_api_helpers.py +0 -0
  121. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_github_api_retry_and_helpers.py +0 -0
  122. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_gitreview.py +0 -0
  123. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_gitutils_helpers.py +0 -0
  124. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_mapping_comment_additional.py +0 -0
  125. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_mapping_comment_digest_and_backref.py +0 -0
  126. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_metadata_and_reconciliation.py +0 -0
  127. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_metadata_trailer_separation_bug.py +0 -0
  128. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_misc_small_coverage.py +0 -0
  129. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_netrc.py +0 -0
  130. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_orphan_rest_side_effects.py +0 -0
  131. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_pr_command_authorisation.py +0 -0
  132. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_pr_commands.py +0 -0
  133. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_pr_content_filter.py +0 -0
  134. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_pr_content_filter_integration.py +0 -0
  135. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_pr_update_detection.py +0 -0
  136. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_precommit_mypy_pins.py +0 -0
  137. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_reconciliation_extracted_module.py +0 -0
  138. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_reconciliation_plan_and_orphans.py +0 -0
  139. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_reconciliation_scenarios.py +0 -0
  140. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_ssh_agent.py +0 -0
  141. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_ssh_agent_ownership.py +0 -0
  142. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_ssh_artifact_prevention.py +0 -0
  143. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_ssh_common.py +0 -0
  144. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_ssh_discovery.py +0 -0
  145. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_ssh_discovery_dry_run.py +0 -0
  146. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_trailers_additional.py +0 -0
  147. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_url_parser.py +0 -0
  148. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/test_utils.py +0 -0
  149. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/tests/unit/test_ssh_config_parser.py +0 -0
  150. {github2gerrit-2.2.0 → github2gerrit-2.2.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: github2gerrit
3
- Version: 2.2.0
3
+ Version: 2.2.1
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
@@ -276,9 +276,9 @@ uvx github2gerrit https://github.com/onap/portal-ng-bff/pull/33
276
276
  | `GERRIT_KNOWN_HOSTS` | No | — | Known hosts entries for Gerrit SSH (auto-populated when empty) |
277
277
  | `GERRIT_SSH_USER_G2G` | No | `""` | Gerrit SSH username; derived when not supplied |
278
278
  | `GERRIT_SSH_USER_G2G_EMAIL` | No | `""` | Gerrit user email address; derived when not supplied |
279
- | `GERRIT_SERVER` | No | `""` | Gerrit server hostname; `.gitreview` preferred |
279
+ | `GERRIT_SERVER` | No | `""` | Gerrit server hostname; overrides `.gitreview` when set |
280
280
  | `GERRIT_SERVER_PORT` | No | `"29418"` | Gerrit SSH port |
281
- | `GERRIT_PROJECT` | No | `""` | Gerrit project name; `.gitreview` preferred |
281
+ | `GERRIT_PROJECT` | No | `""` | Gerrit project name; overrides `.gitreview` when set |
282
282
  | `GERRIT_HTTP_BASE_PATH` | No | `""` | HTTP base path for Gerrit REST API (e.g. `/r`) |
283
283
  | `GERRIT_HTTP_USER` | No | `""` | Gerrit HTTP user for REST queries |
284
284
  | `GERRIT_HTTP_PASSWORD` | No | `""` | Gerrit HTTP password/token for REST queries |
@@ -326,14 +326,15 @@ environment variables do not cross the `workflow_call` boundary. See
326
326
  [docs/cli.md](docs/cli.md) for the full option reference and
327
327
  [docs/features.md](docs/features.md) for feature-specific settings.
328
328
 
329
- Nine `G2G_`-prefixed settings can come from repository or organisation
329
+ Ten `G2G_`-prefixed settings can come from repository or organisation
330
330
  **variables**, which the reusable workflow reads and passes to the
331
- action — among them `G2G_TRUSTED_ASSOCIATIONS`, `G2G_TOPIC_PREFIX`
332
- and `G2G_ENABLE_DERIVATION`, plus the `G2G_NO_GERRIT` and
333
- `G2G_DISABLED` kill switches. An organisation can set one once for
334
- every repository beneath it. A workflow calling the composite action
335
- directly does not inherit those variables; map them in with `env:`
336
- yourself. See [docs/cli.md](docs/cli.md) for the full list.
331
+ action — among them `G2G_TRUSTED_ASSOCIATIONS`, `G2G_TOPIC_PREFIX`,
332
+ `G2G_ENABLE_DERIVATION` and the `G2G_RESOLVE_PROJECT_VIA_GERRIT`
333
+ opt-in, plus the `G2G_NO_GERRIT` and `G2G_DISABLED` kill switches. An
334
+ organisation can set one once for every repository beneath it. A
335
+ workflow calling the composite action directly does not inherit those
336
+ variables; map them in with `env:` yourself. See
337
+ [docs/cli.md](docs/cli.md) for the full list.
337
338
 
338
339
  The three `G2G_APPROVER*` inputs widen who may clear the fork approval
339
340
  gate, and each defaults to off. `G2G_INFO_YAML_MATCH_LFID` carries a
@@ -229,9 +229,9 @@ uvx github2gerrit https://github.com/onap/portal-ng-bff/pull/33
229
229
  | `GERRIT_KNOWN_HOSTS` | No | — | Known hosts entries for Gerrit SSH (auto-populated when empty) |
230
230
  | `GERRIT_SSH_USER_G2G` | No | `""` | Gerrit SSH username; derived when not supplied |
231
231
  | `GERRIT_SSH_USER_G2G_EMAIL` | No | `""` | Gerrit user email address; derived when not supplied |
232
- | `GERRIT_SERVER` | No | `""` | Gerrit server hostname; `.gitreview` preferred |
232
+ | `GERRIT_SERVER` | No | `""` | Gerrit server hostname; overrides `.gitreview` when set |
233
233
  | `GERRIT_SERVER_PORT` | No | `"29418"` | Gerrit SSH port |
234
- | `GERRIT_PROJECT` | No | `""` | Gerrit project name; `.gitreview` preferred |
234
+ | `GERRIT_PROJECT` | No | `""` | Gerrit project name; overrides `.gitreview` when set |
235
235
  | `GERRIT_HTTP_BASE_PATH` | No | `""` | HTTP base path for Gerrit REST API (e.g. `/r`) |
236
236
  | `GERRIT_HTTP_USER` | No | `""` | Gerrit HTTP user for REST queries |
237
237
  | `GERRIT_HTTP_PASSWORD` | No | `""` | Gerrit HTTP password/token for REST queries |
@@ -279,14 +279,15 @@ environment variables do not cross the `workflow_call` boundary. See
279
279
  [docs/cli.md](docs/cli.md) for the full option reference and
280
280
  [docs/features.md](docs/features.md) for feature-specific settings.
281
281
 
282
- Nine `G2G_`-prefixed settings can come from repository or organisation
282
+ Ten `G2G_`-prefixed settings can come from repository or organisation
283
283
  **variables**, which the reusable workflow reads and passes to the
284
- action — among them `G2G_TRUSTED_ASSOCIATIONS`, `G2G_TOPIC_PREFIX`
285
- and `G2G_ENABLE_DERIVATION`, plus the `G2G_NO_GERRIT` and
286
- `G2G_DISABLED` kill switches. An organisation can set one once for
287
- every repository beneath it. A workflow calling the composite action
288
- directly does not inherit those variables; map them in with `env:`
289
- yourself. See [docs/cli.md](docs/cli.md) for the full list.
284
+ action — among them `G2G_TRUSTED_ASSOCIATIONS`, `G2G_TOPIC_PREFIX`,
285
+ `G2G_ENABLE_DERIVATION` and the `G2G_RESOLVE_PROJECT_VIA_GERRIT`
286
+ opt-in, plus the `G2G_NO_GERRIT` and `G2G_DISABLED` kill switches. An
287
+ organisation can set one once for every repository beneath it. A
288
+ workflow calling the composite action directly does not inherit those
289
+ variables; map them in with `env:` yourself. See
290
+ [docs/cli.md](docs/cli.md) for the full list.
290
291
 
291
292
  The three `G2G_APPROVER*` inputs widen who may clear the fork approval
292
293
  gate, and each defaults to off. `G2G_INFO_YAML_MATCH_LFID` carries a
@@ -100,9 +100,9 @@ set by GitHub Actions) and undergo proper boolean parsing.
100
100
 
101
101
  | Flag(s) | Env var | Default | Description |
102
102
  | ------------------------------------ | --------------------------- | ------- | ------------------------------------------------------------ |
103
- | `--gerrit-server TEXT` | `GERRIT_SERVER` | `""` | Gerrit server hostname (optional; `.gitreview` preferred) |
103
+ | `--gerrit-server TEXT` | `GERRIT_SERVER` | `""` | Gerrit server hostname (optional; overrides `.gitreview`) |
104
104
  | `--gerrit-server-port INTEGER` | `GERRIT_SERVER_PORT` | `29418` | Gerrit SSH port |
105
- | `--gerrit-project TEXT` | `GERRIT_PROJECT` | `""` | Gerrit project (optional; `.gitreview` preferred) |
105
+ | `--gerrit-project TEXT` | `GERRIT_PROJECT` | `""` | Gerrit project (optional; overrides `.gitreview` when set) |
106
106
  | `--gerrit-ssh-user-g2g TEXT` | `GERRIT_SSH_USER_G2G` | `""` | Gerrit SSH username (e.g., automation bot account) |
107
107
  | `--gerrit-ssh-user-g2g-email TEXT` | `GERRIT_SSH_USER_G2G_EMAIL` | `""` | Email address for the Gerrit SSH user |
108
108
  | `--gerrit-ssh-privkey-g2g TEXT` | `GERRIT_SSH_PRIVKEY_G2G` | `""` | SSH private key content used to authenticate to Gerrit |
@@ -160,11 +160,12 @@ Some settings have no CLI flag and use environment variables.
160
160
  When running the tool as a GitHub Action, how you set one depends on the
161
161
  integration:
162
162
 
163
- - **Reusable workflow** — the workflow reads these nine names from repository
163
+ - **Reusable workflow** — the workflow reads these ten names from repository
164
164
  or organisation **variables**:
165
165
  `G2G_TRUSTED_ASSOCIATIONS`, `G2G_TOPIC_PREFIX`, `G2G_SKIP_GERRIT_COMMENTS`,
166
166
  `G2G_ENABLE_DERIVATION`, `G2G_ANON_SUPERSEDE_FALLBACK`, `G2G_SHOW_PROGRESS`,
167
- `G2G_LOG_LEVEL`, `G2G_NO_GERRIT` and `G2G_DISABLED`.
167
+ `G2G_LOG_LEVEL`, `G2G_RESOLVE_PROJECT_VIA_GERRIT`, `G2G_NO_GERRIT` and
168
+ `G2G_DISABLED`.
168
169
 
169
170
  The last two predate the rest and behave differently: `G2G_NO_GERRIT` is
170
171
  also a workflow input, taking the input first and falling back to the
@@ -187,34 +188,35 @@ forwarded setting treats blank as unset and keeps its documented default.
187
188
 
188
189
  <!-- markdownlint-disable MD013 -->
189
190
 
190
- | Variable | Default | Description |
191
- | ----------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------- |
192
- | `G2G_CONFIG_PATH` | `~/.config/github2gerrit/configuration.txt` | Path to the per-organization configuration file |
193
- | `G2G_LOG_LEVEL` | `WARNING` | Logging level; set `DEBUG` for verbose output |
194
- | `G2G_TOPIC_PREFIX` | `GH` | Prefix used when generating Gerrit topics |
195
- | `G2G_SKIP_GERRIT_COMMENTS` | `false` | Skip posting back-reference comments on Gerrit changes |
196
- | `G2G_TRUSTED_ASSOCIATIONS` | `OWNER,MEMBER,COLLABORATOR` | Author associations trusted to issue `@github2gerrit` comment directives |
197
- | `G2G_ENABLE_DERIVATION` | `true` | Enable automatic derivation of Gerrit parameters from organization defaults |
198
- | `G2G_AUTO_SAVE_CONFIG` | context-dependent | Save derived parameters back to the configuration file |
199
- | `G2G_RESPECT_USER_SSH` | `false` (`true` in direct URL mode) | Use the local user's SSH configuration and keys instead of provided key material |
200
- | `G2G_USE_SSH_AGENT` | `true` | Use SSH agent authentication; set `false` to force file-based SSH keys |
201
- | `G2G_NO_GERRIT` | `false` | Run the full pipeline without contacting Gerrit (forces dry-run behavior) |
202
- | `G2G_DISABLED` | `false` | Exit immediately with success without processing anything |
203
- | `CLEANUP_ABANDONED` | `true` | Close GitHub PRs whose Gerrit changes have status abandoned |
204
- | `CLEANUP_GERRIT` | `true` | Abandon Gerrit changes for closed GitHub PRs |
205
- | `PR_NUMBER` | unset | Pull request number to process in CI/CD mode (`0` means bulk mode) |
206
- | `SYNC_ALL_OPEN_PRS` | `false` | Process all open PRs in the repository (bulk mode) |
207
- | `GERRIT_HTTP_USER` | unset | Username for the Gerrit REST API (when required) |
208
- | `GERRIT_HTTP_PASSWORD` | unset | HTTP password/token for the Gerrit REST API |
209
- | `GERRIT_HTTP_BASE_PATH` | unset | Gerrit REST base path for non-standard deployments (e.g., `/r`) |
210
- | `GITHUB_TOKEN` | unset | GitHub API token; required for PR queries, comments, and closing PRs |
211
- | `GH_TOKEN` | unset | Fallback source for `GITHUB_TOKEN` when the latter is unset |
212
- | `GERRIT_CHANGE_URL` | unset | Gerrit change URL for Gerrit-event dispatch mode (close the source PR) |
213
- | `GERRIT_EVENT_TYPE` | unset | Gerrit event type for dispatch mode (`change-merged` or `change-abandoned`) |
214
- | `GERRIT_BRANCH` | unset | Gerrit branch for dispatch mode change queries |
215
- | `G2G_DRYRUN_DISABLE_NETWORK` | `false` | Skip DNS/network checks in dry-run; set automatically by `G2G_NO_GERRIT` |
216
- | `G2G_ANON_SUPERSEDE_FALLBACK` | `true` | Allow anonymous supersession queries when Gerrit credentials are unavailable |
217
- | `VERIFY_DIGEST_STRICT` | `true` | Fail (rather than warn) on reconciliation digest mismatch after push |
191
+ | Variable | Default | Description |
192
+ | -------------------------------- | ------------------------------------------- | --------------------------------------------------------------------------------- |
193
+ | `G2G_CONFIG_PATH` | `~/.config/github2gerrit/configuration.txt` | Path to the per-organization configuration file |
194
+ | `G2G_LOG_LEVEL` | `WARNING` | Logging level; set `DEBUG` for verbose output |
195
+ | `G2G_TOPIC_PREFIX` | `GH` | Prefix used when generating Gerrit topics |
196
+ | `G2G_RESOLVE_PROJECT_VIA_GERRIT` | `false` | Without a `.gitreview`, ask Gerrit which project a hyphenated GitHub name maps to |
197
+ | `G2G_SKIP_GERRIT_COMMENTS` | `false` | Skip posting back-reference comments on Gerrit changes |
198
+ | `G2G_TRUSTED_ASSOCIATIONS` | `OWNER,MEMBER,COLLABORATOR` | Author associations trusted to issue `@github2gerrit` comment directives |
199
+ | `G2G_ENABLE_DERIVATION` | `true` | Enable automatic derivation of Gerrit parameters from organization defaults |
200
+ | `G2G_AUTO_SAVE_CONFIG` | context-dependent | Save derived parameters back to the configuration file |
201
+ | `G2G_RESPECT_USER_SSH` | `false` (`true` in direct URL mode) | Use the local user's SSH configuration and keys instead of provided key material |
202
+ | `G2G_USE_SSH_AGENT` | `true` | Use SSH agent authentication; set `false` to force file-based SSH keys |
203
+ | `G2G_NO_GERRIT` | `false` | Run the full pipeline without contacting Gerrit (forces dry-run behavior) |
204
+ | `G2G_DISABLED` | `false` | Exit immediately with success without processing anything |
205
+ | `CLEANUP_ABANDONED` | `true` | Close GitHub PRs whose Gerrit changes have status abandoned |
206
+ | `CLEANUP_GERRIT` | `true` | Abandon Gerrit changes for closed GitHub PRs |
207
+ | `PR_NUMBER` | unset | Pull request number to process in CI/CD mode (`0` means bulk mode) |
208
+ | `SYNC_ALL_OPEN_PRS` | `false` | Process all open PRs in the repository (bulk mode) |
209
+ | `GERRIT_HTTP_USER` | unset | Username for the Gerrit REST API (when required) |
210
+ | `GERRIT_HTTP_PASSWORD` | unset | HTTP password/token for the Gerrit REST API |
211
+ | `GERRIT_HTTP_BASE_PATH` | unset | Gerrit REST base path for non-standard deployments (e.g., `/r`) |
212
+ | `GITHUB_TOKEN` | unset | GitHub API token; required for PR queries, comments, and closing PRs |
213
+ | `GH_TOKEN` | unset | Fallback source for `GITHUB_TOKEN` when the latter is unset |
214
+ | `GERRIT_CHANGE_URL` | unset | Gerrit change URL for Gerrit-event dispatch mode (close the source PR) |
215
+ | `GERRIT_EVENT_TYPE` | unset | Gerrit event type for dispatch mode (`change-merged` or `change-abandoned`) |
216
+ | `GERRIT_BRANCH` | unset | Gerrit branch for dispatch mode change queries |
217
+ | `G2G_DRYRUN_DISABLE_NETWORK` | `false` | Skip DNS/network checks in dry-run; set automatically by `G2G_NO_GERRIT` |
218
+ | `G2G_ANON_SUPERSEDE_FALLBACK` | `true` | Allow anonymous supersession queries when Gerrit credentials are unavailable |
219
+ | `VERIFY_DIGEST_STRICT` | `true` | Fail (rather than warn) on reconciliation digest mismatch after push |
218
220
 
219
221
  <!-- markdownlint-enable MD013 -->
220
222
 
@@ -20,6 +20,7 @@ action in detail. For a quick start and the full inputs table, see the
20
20
  - [Duplicate Detection](#duplicate-detection)
21
21
  - [Commit Message Normalization](#commit-message-normalization)
22
22
  - [Change-ID Reconciliation](#change-id-reconciliation)
23
+ - [Repository and Project Names](#repository-and-project-names)
23
24
  - [Configuration](#configuration)
24
25
  - [Behavior Details](#behavior-details)
25
26
 
@@ -402,16 +403,19 @@ signal never earns a fork unwarranted trust.
402
403
 
403
404
  Resolution order for a fork pull request:
404
405
 
405
- 1. `.gitreview` from the base repository at the pull request's base branch,
406
+ 1. Explicit `GERRIT_SERVER` / `GERRIT_SERVER_PORT` / `GERRIT_PROJECT` inputs,
407
+ each for its own field — an operator configured them for this run
408
+ 2. `.gitreview` from the base repository at the pull request's base branch,
406
409
  via the GitHub API or `raw.githubusercontent.com`
407
- 2. Explicit `GERRIT_SERVER` / `GERRIT_SERVER_PORT` / `GERRIT_PROJECT` inputs
408
410
  3. Per-organization configuration file
409
411
  4. Derivation from the organization name (`gerrit.[org].org`)
410
412
 
411
- The lookup stops at the base branch. It does not fall back to the repository
412
- default branch, and it does not read `.gitreview` through the API without an
413
- authoritative base ref, because either would answer for a branch the pull
414
- request does not target — and `.gitreview` outranks the explicit inputs.
413
+ A value the tool's own derivation exported through those same variables does
414
+ not count as explicit; the tool tracks which is which. The lookup stops at the
415
+ base branch. It does not fall back to the repository default branch, and it
416
+ does not read `.gitreview` through the API without an authoritative base ref,
417
+ because either would answer for a branch the pull request does not target —
418
+ and `.gitreview` outranks everything but an explicit input.
415
419
 
416
420
  The base repository's copy carries the same authority and lies outside an
417
421
  attacker's reach, so this costs no accuracy. Skipping straight to derivation
@@ -421,7 +425,9 @@ repositories whose name contains a hyphen that does not mark a path separator.
421
425
  For the same reason, the tool drops branch names that originate in the fork
422
426
  (`GITHUB_HEAD_REF` and the pull request head ref) from base-repository
423
427
  lookups. Duplicate detection, which resolves `.gitreview` independently and
424
- earlier, applies the same rule.
428
+ earlier, applies the same rule, and so does parameter derivation: for an
429
+ untrusted pull request it declines the workspace copy of `.gitreview` and the
430
+ `master`/`main` fallbacks alike, reading the base branch only.
425
431
 
426
432
  Same-repository pull requests keep their previous behaviour and still read
427
433
  `.gitreview` from the pull request tree.
@@ -828,6 +834,69 @@ github2gerrit \
828
834
  https://github.com/owner/repo/pull/123
829
835
  ```
830
836
 
837
+ ## Repository and Project Names
838
+
839
+ Gerrit projects are paths, such as `multicloud/openstack`; GitHub repository
840
+ names are flat, such as `multicloud-openstack`. Linux Foundation mirrors
841
+ flatten a path by replacing every `/` with `-`. One module, `project_names`,
842
+ handles that mapping for the whole tool, so every part of it agrees on what a
843
+ name means.
844
+
845
+ ### One direction is exact, the other is not
846
+
847
+ Gerrit to GitHub is deterministic. GitHub to Gerrit is **ambiguous**: a hyphen
848
+ is both the flattened separator and an ordinary character inside a segment.
849
+ On gerrit.onap.org, `aai-aai-common` is `aai/aai-common`, not
850
+ `aai/aai/common`, and nothing in the name can tell you which.
851
+
852
+ So the tool resolves the Gerrit project from the best source it has, in this
853
+ order:
854
+
855
+ 1. An explicit `GERRIT_PROJECT` input — an operator said so. A value the
856
+ tool's own parameter derivation exported through the same variable does
857
+ not count; the tool tracks which is which.
858
+ 2. The repository's `.gitreview` — the repository says so. For a pull request
859
+ the tool reads this from the **base** repository at the base ref, never
860
+ from a fork; see [Gerrit target resolution](#gerrit-target-resolution).
861
+ 3. Gerrit's own project list, when you enable
862
+ `G2G_RESOLVE_PROJECT_VIA_GERRIT` — the server says so. The tool checks
863
+ every reading of the hyphens against `GET /projects/?p=<first segment>`;
864
+ exactly one match resolves it, and on two matches the tool refuses to
865
+ choose.
866
+ 4. A project from the per-organization configuration file, or one an earlier
867
+ derivation pass exported — somebody wrote it down, but nothing confirms
868
+ it for this repository.
869
+ 5. A guess that every hyphen is a separator, logged as a guess. A name with
870
+ no hyphens has only one reading and is not a guess.
871
+
872
+ The tool resolves the project once per run and uses the result both for the
873
+ Gerrit topic and for the push target, so the two cannot disagree. A real run
874
+ refuses to push to a guess: with no `.gitreview`, no `GERRIT_PROJECT`, and a
875
+ hyphenated name, it stops with an error naming the ambiguity and the three
876
+ ways to settle it. This holds for direct URL invocations too, since a URL
877
+ names the GitHub side and says nothing about the Gerrit project. Dry runs
878
+ proceed on the guess, since they push nothing. Queries — the close handler,
879
+ the sweeps — still use the guess, because a wrong query costs nothing.
880
+
881
+ Every Linux Foundation repository carries a `.gitreview`, so in practice step
882
+ 2 answers. Step 3 is off by default because it adds a network call to name
883
+ resolution; enable it for repositories that have no `.gitreview` and whose
884
+ names contain hyphens inside segments. It applies wherever the tool resolves a
885
+ project, including the closed-pull-request handler and the cleanup sweeps,
886
+ reads the opt-in from the configuration file as well as the environment, asks
887
+ each server once per prefix per run, and stays off under `G2G_NO_GERRIT`, which
888
+ promises no Gerrit calls at all.
889
+
890
+ ### Why this matters for cleanup
891
+
892
+ The close handler, the bulk sweep and duplicate detection all query Gerrit
893
+ with `project:<name>`. A wrong name matches nothing and the tool reports
894
+ "no change found" rather than an error — which is how 239 changes across 43
895
+ ONAP projects stayed open after their pull requests closed (#441). The tool
896
+ now resolves the project from `.gitreview` before any of those paths run,
897
+ reports at INFO level when it finds no change, naming the project it asked
898
+ about, and reports a failed lookup as a failure rather than as "no change".
899
+
831
900
  ## Configuration
832
901
 
833
902
  ### Configuration Precedence
@@ -940,7 +1009,12 @@ provided, the tool derives them from these sources, in priority order:
940
1009
 
941
1010
  Organization-based pattern, from the `ORGANIZATION` value:
942
1011
 
943
- - Gerrit server: `gerrit.{organization}.org` (or from config file)
1012
+ - Gerrit server: the repository's `.gitreview` host when the file exists,
1013
+ else the configuration file's `GERRIT_SERVER`, else
1014
+ `gerrit.{organization}.org`. The derived host travels with the derived
1015
+ project as one target, because the closed-pull-request handler queries
1016
+ them together and the pipeline pushes to the `.gitreview` host whenever
1017
+ the file exists.
944
1018
  - SSH username: `{organization}.gh2gerrit`
945
1019
  - Email: `releng+{organization}-gh2gerrit@linuxfoundation.org`
946
1020
 
@@ -120,6 +120,46 @@ def _exit_for_pr_state_error(pr_number: int, pr_state: str) -> None:
120
120
  exit_for_pr_state_error(pr_number, pr_state)
121
121
 
122
122
 
123
+ def _stop_for_pr_state(
124
+ gh: GitHubContext, pr_state: str, progress_tracker: Any = None
125
+ ) -> NoReturn:
126
+ """Stop because the pull request is not open.
127
+
128
+ On a pull request event this is not an error. GitHub delivers
129
+ several events around a close — Dependabot edits the body before
130
+ closing, for instance — and any of them may land after the
131
+ ``closed`` event whose handler already ran. Such a run has nothing
132
+ to do, and a red check on the contributor's pull request would
133
+ misreport that. The report behind #441 cited exactly this run and
134
+ took it for the close handler failing.
135
+
136
+ A dispatch or direct invocation naming a closed pull request is
137
+ different: somebody asked for that one specifically, and being told
138
+ it cannot be processed is the useful answer.
139
+ """
140
+ if gh.event_name in ("pull_request", "pull_request_target"):
141
+ log.info(
142
+ "Pull request #%s is %s; nothing to do for a '%s' event. Cleanup "
143
+ "for a closed pull request runs on the 'closed' event itself.",
144
+ gh.pr_number,
145
+ pr_state,
146
+ gh.event_action or gh.event_name,
147
+ )
148
+ safe_console_print(
149
+ f"⏩ Pull request #{gh.pr_number} is {pr_state}; nothing to do",
150
+ style="yellow",
151
+ progress_tracker=progress_tracker,
152
+ )
153
+ # The caller's own stop() is downstream of this raise, and
154
+ # safe_console_print only suspends and resumes the display, so
155
+ # an in-process caller catching the exit would otherwise be
156
+ # left with an active Rich context.
157
+ if progress_tracker is not None:
158
+ progress_tracker.stop()
159
+ raise typer.Exit(int(ExitCode.SUCCESS))
160
+ exit_for_pr_state_error(gh.pr_number or 0, pr_state)
161
+
162
+
123
163
  def _exit_for_pr_not_found(pr_number: int, repository: str) -> None:
124
164
  """Exit with error message for PR not found."""
125
165
  exit_for_pr_not_found(pr_number, repository)
@@ -756,10 +796,10 @@ def _extract_and_display_pr_info(
756
796
  repo = get_repo_from_env(client)
757
797
  pr_obj = get_pull(repo, int(gh.pr_number))
758
798
 
759
- # Check PR state and exit if not processable
799
+ # Check PR state and stop if not processable
760
800
  pr_state = getattr(pr_obj, "state", "unknown")
761
801
  if pr_state != "open":
762
- _exit_for_pr_state_error(gh.pr_number, pr_state)
802
+ _stop_for_pr_state(gh, str(pr_state), progress_tracker)
763
803
 
764
804
  # Check if automation_only is enabled and reject non-automation PRs
765
805
  _check_automation_only(pr_obj, gh, progress_tracker)
@@ -792,6 +832,12 @@ def _extract_and_display_pr_info(
792
832
  except GitHub2GerritError:
793
833
  # Let our structured errors propagate
794
834
  raise
835
+ except typer.Exit:
836
+ # display_and_exit and the clean stop above both raise this.
837
+ # It derives from Exception through click, so without this
838
+ # clause the handler below would swallow a deliberate exit 8
839
+ # and report "Failed to fetch PR details: 8" with exit 1.
840
+ raise
795
841
  except Exception as exc:
796
842
  log.debug("Failed to display PR info: %s", exc)
797
843
  if "404" in str(exc) or "Not Found" in str(exc):
@@ -1468,13 +1514,13 @@ def main(
1468
1514
  "",
1469
1515
  "--gerrit-project",
1470
1516
  envvar="GERRIT_PROJECT",
1471
- help="Gerrit project (optional; .gitreview preferred).",
1517
+ help="Gerrit project (optional; overrides .gitreview when set).",
1472
1518
  ),
1473
1519
  gerrit_server: str = typer.Option(
1474
1520
  "",
1475
1521
  "--gerrit-server",
1476
1522
  envvar="GERRIT_SERVER",
1477
- help="Gerrit server hostname (optional; .gitreview preferred).",
1523
+ help="Gerrit server hostname (optional; overrides .gitreview).",
1478
1524
  ),
1479
1525
  gerrit_server_port: int = typer.Option(
1480
1526
  29418,
@@ -3036,14 +3082,23 @@ def _log_abandoned_change_url(
3036
3082
 
3037
3083
 
3038
3084
  def _abandon_change_for_closed_pr(data: Inputs, gh: GitHubContext) -> None:
3039
- """Abandon the Gerrit change associated with a closed pull request."""
3085
+ """Abandon the Gerrit change associated with a closed pull request.
3086
+
3087
+ Three outcomes, reported distinctly: a change was abandoned, no
3088
+ open change carries this pull request's trailer, or the lookup or
3089
+ abandon itself failed. The last two used to share one message, and
3090
+ a failed query read as a clean "nothing to do". A failure does not
3091
+ say which side of the abandon it happened on -- Gerrit may have
3092
+ applied it and the response been lost -- so the report says the
3093
+ state is unconfirmed rather than claiming the change is open.
3094
+ """
3040
3095
  if gh.pr_number is None:
3041
3096
  return
3097
+ log.debug(
3098
+ "Checking for Gerrit change to abandon for PR #%s",
3099
+ gh.pr_number,
3100
+ )
3042
3101
  try:
3043
- log.debug(
3044
- "Checking for Gerrit change to abandon for PR #%s",
3045
- gh.pr_number,
3046
- )
3047
3102
  change_number = abandon_gerrit_change_for_closed_pr(
3048
3103
  pr_number=gh.pr_number,
3049
3104
  gerrit_server=data.gerrit_server,
@@ -3052,21 +3107,46 @@ def _abandon_change_for_closed_pr(data: Inputs, gh: GitHubContext) -> None:
3052
3107
  dry_run=data.dry_run,
3053
3108
  progress_tracker=None,
3054
3109
  )
3055
- if change_number:
3056
- _log_abandoned_change_url(data, gh, change_number)
3057
- # Console output already done by
3058
- # abandon_gerrit_change_for_closed_pr
3059
- else:
3060
- log.debug(
3061
- "No open Gerrit change found for pull request #%s",
3062
- gh.pr_number,
3063
- )
3064
3110
  except Exception as exc:
3065
3111
  log.warning(
3066
- "Failed to abandon Gerrit change for PR #%s: %s",
3112
+ "Failed to look up or abandon the Gerrit change for pull "
3113
+ "request #%s in project %r on %s: %s. Its state could not be "
3114
+ "confirmed; check Gerrit.",
3067
3115
  gh.pr_number,
3116
+ data.gerrit_project,
3117
+ data.gerrit_server,
3068
3118
  exc,
3069
3119
  )
3120
+ log.debug("Cleanup failure detail", exc_info=True)
3121
+ safe_console_print(
3122
+ f"⚠️ Could not confirm the Gerrit change state for PR "
3123
+ f"#{gh.pr_number}: {exc}",
3124
+ style="yellow",
3125
+ )
3126
+ return
3127
+
3128
+ if change_number:
3129
+ _log_abandoned_change_url(data, gh, change_number)
3130
+ # Console output already done by
3131
+ # abandon_gerrit_change_for_closed_pr
3132
+ return
3133
+
3134
+ # Audible on purpose. This is the outcome when the project name is
3135
+ # wrong, and at debug level it hid 239 stranded changes on one
3136
+ # server for six months (#441). Naming the project queried is what
3137
+ # lets a reader spot the mismatch.
3138
+ log.info(
3139
+ "No open Gerrit change found for pull request #%s in "
3140
+ "project %r on %s; nothing to abandon",
3141
+ gh.pr_number,
3142
+ data.gerrit_project,
3143
+ data.gerrit_server,
3144
+ )
3145
+ safe_console_print(
3146
+ f"⏩ No open Gerrit change for PR #{gh.pr_number} in "
3147
+ f"project '{data.gerrit_project}'",
3148
+ style="dim",
3149
+ )
3070
3150
 
3071
3151
 
3072
3152
  def _handle_pr_closed(
@@ -3104,13 +3184,28 @@ def _handle_pr_closed(
3104
3184
 
3105
3185
  # First, abandon the specific Gerrit change for this closed PR
3106
3186
  # Skip in G2G_NO_GERRIT: no Gerrit server to query
3107
- if (
3108
- not no_gerrit
3109
- and gh.pr_number
3187
+ if no_gerrit:
3188
+ pass
3189
+ elif not (
3190
+ gh.pr_number
3191
+ and gh.repository
3110
3192
  and data.gerrit_server
3111
3193
  and data.gerrit_project
3112
- and gh.repository
3113
3194
  ):
3195
+ # Say so. Silently skipping here is indistinguishable from a
3196
+ # successful cleanup that found nothing, and the change stays
3197
+ # open with no trace of why.
3198
+ log.warning(
3199
+ "Cannot look for a Gerrit change to abandon for PR #%s: "
3200
+ "repository=%r server=%r project=%r. Set GERRIT_SERVER and "
3201
+ "GERRIT_PROJECT, or add a .gitreview so they can be derived; "
3202
+ "the repository comes from GITHUB_REPOSITORY.",
3203
+ gh.pr_number,
3204
+ gh.repository,
3205
+ data.gerrit_server,
3206
+ data.gerrit_project,
3207
+ )
3208
+ else:
3114
3209
  _abandon_change_for_closed_pr(data, gh)
3115
3210
 
3116
3211
  _run_gerrit_cleanup_tasks(data, gh, no_gerrit=no_gerrit)