github2gerrit 1.4.3__tar.gz → 2.0.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 (136) hide show
  1. github2gerrit-2.0.0/.aislop/config.yml +28 -0
  2. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/.pre-commit-config.yaml +29 -8
  3. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/PKG-INFO +4 -4
  4. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/README.md +2 -2
  5. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/action.yaml +34 -21
  6. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/docs/cli.md +1 -1
  7. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/docs/features.md +69 -0
  8. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/pyproject.toml +5 -0
  9. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/sitecustomize.py +7 -6
  10. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/cli.py +1736 -1344
  11. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/commit_normalization.py +33 -24
  12. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/config.py +183 -119
  13. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/core.py +2609 -1805
  14. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/duplicate_detection.py +463 -322
  15. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/external_api.py +197 -120
  16. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/gerrit_pr_closer.py +764 -481
  17. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/gerrit_ssh.py +2 -2
  18. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/gerrit_urls.py +107 -91
  19. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/gitreview.py +18 -2
  20. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/models.py +61 -1
  21. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/orchestrator/reconciliation.py +125 -62
  22. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/reconcile_matcher.py +75 -40
  23. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/ssh_agent_setup.py +78 -65
  24. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/ssh_discovery.py +202 -138
  25. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/conftest.py +29 -0
  26. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_action_environment_mapping.py +6 -0
  27. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_action_step_validation.py +131 -17
  28. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_cli_netrc_options.py +25 -10
  29. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_composite_action_coverage.py +24 -16
  30. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_config_helpers.py +22 -0
  31. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_ssh_setup.py +6 -2
  32. github2gerrit-2.0.0/tests/test_fork_pr_hardening.py +580 -0
  33. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_gerrit_pr_closer.py +56 -0
  34. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_gerrit_urls_more.py +26 -0
  35. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_issue_157_regressions.py +19 -15
  36. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_ssh_discovery.py +21 -0
  37. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/uv.lock +135 -117
  38. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/.editorconfig +0 -0
  39. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/.gitignore +0 -0
  40. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/.gitlint +0 -0
  41. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/.markdownlint.yaml +0 -0
  42. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/.yamllint +0 -0
  43. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/LICENSE +0 -0
  44. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/LICENSES/Apache-2.0.txt +0 -0
  45. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/REUSE.toml +0 -0
  46. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/SECURITY.md +0 -0
  47. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/docs/COMMIT_RULES.md +0 -0
  48. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/docs/development.md +0 -0
  49. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/docs/github2gerrit_token_permissions_classic.png +0 -0
  50. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/__init__.py +0 -0
  51. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/commit_rules.py +0 -0
  52. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/constants.py +0 -0
  53. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/error_codes.py +0 -0
  54. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/gerrit_query.py +0 -0
  55. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/gerrit_rest.py +0 -0
  56. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/github_api.py +0 -0
  57. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/gitutils.py +0 -0
  58. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/mapping_comment.py +0 -0
  59. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/netrc.py +0 -0
  60. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/orchestrator/__init__.py +0 -0
  61. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/pr_commands.py +0 -0
  62. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/pr_content_filter.py +0 -0
  63. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/rich_display.py +0 -0
  64. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/rich_logging.py +0 -0
  65. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/similarity.py +0 -0
  66. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/ssh_common.py +0 -0
  67. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/ssh_config_parser.py +0 -0
  68. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/trailers.py +0 -0
  69. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/src/github2gerrit/utils.py +0 -0
  70. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/fixtures/__init__.py +0 -0
  71. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/fixtures/make_repo.py +0 -0
  72. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/fixtures/ssh_config_samples.py +0 -0
  73. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_action_outputs.py +0 -0
  74. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_action_pr_number_handling.py +0 -0
  75. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_automation_only.py +0 -0
  76. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_change_id_deduplication.py +0 -0
  77. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_clean_squash_title.py +0 -0
  78. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_cli.py +0 -0
  79. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_cli_helpers.py +0 -0
  80. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_cli_outputs_file.py +0 -0
  81. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_cli_url_and_dryrun.py +0 -0
  82. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_commit_normalization.py +0 -0
  83. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_commit_rules.py +0 -0
  84. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_config_and_reviewers.py +0 -0
  85. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_close_pr_policy.py +0 -0
  86. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_config_and_errors.py +0 -0
  87. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_gerrit_backref_comment.py +0 -0
  88. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_gerrit_push_errors.py +0 -0
  89. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_gerrit_rest_results.py +0 -0
  90. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_integration_fixture_repo.py +0 -0
  91. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_prepare_commits.py +0 -0
  92. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_shallow_clone.py +0 -0
  93. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_core_ssrf_protection.py +0 -0
  94. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_dependency_supersession.py +0 -0
  95. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_dns_validation_and_no_gerrit.py +0 -0
  96. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_duplicate_detection.py +0 -0
  97. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_email_case_normalization.py +0 -0
  98. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_error_codes.py +0 -0
  99. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_external_api_framework.py +0 -0
  100. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_final_state_reconciliation.py +0 -0
  101. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_force_flag_cli.py +0 -0
  102. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_gerrit_change_id_footer.py +0 -0
  103. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_gerrit_change_status_checks.py +0 -0
  104. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_gerrit_rest_client.py +0 -0
  105. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_gerrit_ssh_abandon.py +0 -0
  106. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_gerrit_urls.py +0 -0
  107. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_ghe_and_gitreview_args.py +0 -0
  108. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_github_api_error_handling.py +0 -0
  109. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_github_api_helpers.py +0 -0
  110. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_github_api_retry_and_helpers.py +0 -0
  111. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_gitreview.py +0 -0
  112. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_gitutils_helpers.py +0 -0
  113. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_mapping_comment_additional.py +0 -0
  114. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_mapping_comment_digest_and_backref.py +0 -0
  115. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_metadata_and_reconciliation.py +0 -0
  116. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_metadata_trailer_separation_bug.py +0 -0
  117. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_misc_small_coverage.py +0 -0
  118. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_netrc.py +0 -0
  119. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_orphan_rest_side_effects.py +0 -0
  120. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_pr_commands.py +0 -0
  121. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_pr_content_filter.py +0 -0
  122. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_pr_content_filter_integration.py +0 -0
  123. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_pr_update_detection.py +0 -0
  124. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_reconciliation_extracted_module.py +0 -0
  125. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_reconciliation_plan_and_orphans.py +0 -0
  126. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_reconciliation_scenarios.py +0 -0
  127. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_ssh_agent.py +0 -0
  128. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_ssh_agent_ownership.py +0 -0
  129. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_ssh_artifact_prevention.py +0 -0
  130. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_ssh_common.py +0 -0
  131. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_ssh_discovery_dry_run.py +0 -0
  132. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_trailers_additional.py +0 -0
  133. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_url_parser.py +0 -0
  134. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/test_utils.py +0 -0
  135. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/unit/test_config_integration.py +0 -0
  136. {github2gerrit-1.4.3 → github2gerrit-2.0.0}/tests/unit/test_ssh_config_parser.py +0 -0
@@ -0,0 +1,28 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: 2026 The Linux Foundation
3
+
4
+ # aislop quality gate configuration.
5
+ #
6
+ # The pre-commit hook and CI both run `aislop ci` against this config.
7
+ # Overly long functions and deeply nested code are refactored in the
8
+ # source tree into focused helpers rather than suppressed.
9
+ #
10
+ # maxFileLoc is raised from the 400 default to 900: this codebase is
11
+ # organised into cohesive single-responsibility modules (SSH discovery
12
+ # and setup, gerrit REST/query, reconciliation, config, etc.) that
13
+ # naturally sit in the 450-900 line range. 900 still flags egregiously
14
+ # oversized modules.
15
+ #
16
+ # Three orchestration god-files remain above this threshold: core.py,
17
+ # cli.py and gerrit_pr_closer.py. Their overly long functions and deep
18
+ # nesting are decomposed here, but a full module/class split is a large,
19
+ # self-contained refactor tracked as dedicated follow-up work; each
20
+ # carries an in-file `aislop-ignore-file complexity/file-too-large` with
21
+ # that rationale so the gate stays green in the meantime.
22
+ version: 1
23
+
24
+ quality:
25
+ maxFileLoc: 900
26
+
27
+ ci:
28
+ failBelow: 100
@@ -3,7 +3,12 @@
3
3
  # SPDX-FileCopyrightText: 2025 The Linux Foundation
4
4
 
5
5
  ci:
6
- skip: [pytest, gha-workflow-linter]
6
+ autofix_prs: false
7
+ # pre-commit.ci sandbox prevents network calls; aislop runs engines
8
+ # that can call the network at scan time, so it cannot run there
9
+ # either. gha-workflow-linter validates action calls over the
10
+ # network; pytest runs the full suite.
11
+ skip: [pytest, gha-workflow-linter, aislop]
7
12
  autofix_commit_msg: |
8
13
  Chore: pre-commit autofixes
9
14
 
@@ -59,7 +64,7 @@ repos:
59
64
  types: [yaml]
60
65
 
61
66
  - repo: https://github.com/astral-sh/ruff-pre-commit
62
- rev: 01a675ea018f2fb714478a5ffb83fcea8374bb06 # frozen: v0.15.21
67
+ rev: 65dbdb59d2f2d9c3bdc343c566821ad3319ddaa3 # frozen: v0.16.3
63
68
  hooks:
64
69
  - id: ruff
65
70
  files: ^(src|scripts|tests)/.+\.py$
@@ -68,7 +73,7 @@ repos:
68
73
  files: ^(src|scripts|tests)/.+\.py$
69
74
 
70
75
  - repo: https://github.com/pre-commit/mirrors-mypy
71
- rev: 41e691678310dfd3833f7ab4e180ddb014310356 # frozen: v2.3.0
76
+ rev: 7ff8d35ae36a7d2b968f2f90b4c723e292e594ee # frozen: v2.3.1
72
77
  hooks:
73
78
  - id: mypy
74
79
  files: ^src/.+\.py$
@@ -89,7 +94,7 @@ repos:
89
94
  - id: shellcheck
90
95
 
91
96
  - repo: https://github.com/igorshubovych/markdownlint-cli
92
- rev: a4d5d37e66ebcd6b3705204a1d6dbb56dea66338 # frozen: v0.49.0
97
+ rev: 5b5dddc4fb0f83c3ea1fc5616fa63e115dce83e0 # frozen: v0.49.1
93
98
  hooks:
94
99
  - id: markdownlint
95
100
  args: ["--fix", "--config", ".markdownlint.yaml"]
@@ -109,24 +114,40 @@ repos:
109
114
 
110
115
  # Check for misspellings in documentation files
111
116
  - repo: https://github.com/codespell-project/codespell
112
- rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2
117
+ rev: 57b21406f092110c18776e39b0bda50d37c945c8 # frozen: v2.4.3
113
118
  hooks:
114
119
  - id: codespell
115
120
 
116
121
  # Requires a mirror, primary repo lacks .pre-commit-hooks.yaml
117
122
  - repo: https://github.com/DetachHead/basedpyright-prek-mirror
118
- rev: d55eabd2531b4f4a4c49c30fba627ce3a636ab79 # frozen: 1.39.9
123
+ rev: 3c94d276aa3005fa1f5a977946c9dc106135c083 # frozen: 1.39.10
119
124
  hooks:
120
125
  - id: basedpyright
121
126
  files: ^src/.+\.py$
122
127
 
123
128
  - repo: https://github.com/lfreleng-actions/gha-workflow-linter
124
- rev: 4c3079dcb6e9e3c295f961ce5bd5b8585db698ca # frozen: v1.2.2
129
+ rev: 7f11ad46be1cd2b3d778428d40fcb09309bb3be5 # frozen: v1.4.1
125
130
  hooks:
126
131
  - id: gha-workflow-linter
127
132
 
133
+ # Catch AI slop developer-side before it reaches a pull request.
134
+ # Runs the full-repository quality gate, scored against
135
+ # .aislop/config.yml. A local hook installing the published npm
136
+ # package: the scanaislop/aislop git repository does not ship the
137
+ # built dist/, so its bundled pre-commit hook cannot install. Bump
138
+ # the pinned version here manually when new aislop releases ship.
139
+ - repo: local
140
+ hooks:
141
+ - id: aislop
142
+ name: aislop
143
+ entry: aislop ci --human
144
+ language: node
145
+ additional_dependencies: ["aislop@0.14.1"]
146
+ pass_filenames: false
147
+ always_run: true
148
+
128
149
  - repo: https://github.com/python-jsonschema/check-jsonschema
129
- rev: 1a4bb160cab6417b3045e1b37b6b72449243e658 # frozen: 0.37.4
150
+ rev: c4654a7e007c9619a5eb63c39a456572a0fa1851 # frozen: 0.38.0
130
151
  hooks:
131
152
  - id: check-github-actions
132
153
  - id: check-github-workflows
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: github2gerrit
3
- Version: 1.4.3
3
+ Version: 2.0.0
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
@@ -235,7 +235,7 @@ uvx github2gerrit https://github.com/onap/portal-ng-bff/pull/33
235
235
  | `GERRIT_HTTP_PASSWORD` | No | `""` | Gerrit HTTP password/token for REST queries |
236
236
  | `ORGANIZATION` | No | repository owner | GitHub organization/owner used for credential derivation |
237
237
  | `PR_NUMBER` | No | `"0"` | PR number to process; `0` processes all open PRs (dispatch) |
238
- | `FETCH_DEPTH` | No | `"10"` | Fetch depth for checkout |
238
+ | `FETCH_DEPTH` | No | `"10"` | Git history depth: PR fetch, push checkout, reconciliation |
239
239
  | `SUBMIT_SINGLE_COMMITS` | No | `"false"` | Submit one commit at a time to Gerrit |
240
240
  | `USE_PR_AS_COMMIT` | No | `"false"` | Use PR title and body as the commit message |
241
241
  | `PRESERVE_GITHUB_PRS` | No | `"true"` | Do not close GitHub PRs after pushing to Gerrit |
@@ -312,7 +312,7 @@ Input defaults match the composite action defaults.
312
312
  | `G2G_USE_SSH_AGENT` | boolean | `true` | Use SSH agent instead of file-based keys |
313
313
  | `ORGANIZATION` | string | repository owner | GitHub organization/owner |
314
314
  | `PR_NUMBER` | string | `"0"` | PR to process on dispatch; `0` processes all |
315
- | `FETCH_DEPTH` | string | `"10"` | Fetch depth for checkout |
315
+ | `FETCH_DEPTH` | string | `"10"` | Git depth: PR, push, reconciliation |
316
316
  | `SUBMIT_SINGLE_COMMITS` | boolean | `false` | Submit one commit at a time |
317
317
  | `USE_PR_AS_COMMIT` | boolean | `false` | Use PR title and body as the commit message |
318
318
  | `PRESERVE_GITHUB_PRS` | boolean | `true` | Do not close GitHub PRs after pushing |
@@ -189,7 +189,7 @@ uvx github2gerrit https://github.com/onap/portal-ng-bff/pull/33
189
189
  | `GERRIT_HTTP_PASSWORD` | No | `""` | Gerrit HTTP password/token for REST queries |
190
190
  | `ORGANIZATION` | No | repository owner | GitHub organization/owner used for credential derivation |
191
191
  | `PR_NUMBER` | No | `"0"` | PR number to process; `0` processes all open PRs (dispatch) |
192
- | `FETCH_DEPTH` | No | `"10"` | Fetch depth for checkout |
192
+ | `FETCH_DEPTH` | No | `"10"` | Git history depth: PR fetch, push checkout, reconciliation |
193
193
  | `SUBMIT_SINGLE_COMMITS` | No | `"false"` | Submit one commit at a time to Gerrit |
194
194
  | `USE_PR_AS_COMMIT` | No | `"false"` | Use PR title and body as the commit message |
195
195
  | `PRESERVE_GITHUB_PRS` | No | `"true"` | Do not close GitHub PRs after pushing to Gerrit |
@@ -266,7 +266,7 @@ Input defaults match the composite action defaults.
266
266
  | `G2G_USE_SSH_AGENT` | boolean | `true` | Use SSH agent instead of file-based keys |
267
267
  | `ORGANIZATION` | string | repository owner | GitHub organization/owner |
268
268
  | `PR_NUMBER` | string | `"0"` | PR to process on dispatch; `0` processes all |
269
- | `FETCH_DEPTH` | string | `"10"` | Fetch depth for checkout |
269
+ | `FETCH_DEPTH` | string | `"10"` | Git depth: PR, push, reconciliation |
270
270
  | `SUBMIT_SINGLE_COMMITS` | boolean | `false` | Submit one commit at a time |
271
271
  | `USE_PR_AS_COMMIT` | boolean | `false` | Use PR title and body as the commit message |
272
272
  | `PRESERVE_GITHUB_PRS` | boolean | `true` | Do not close GitHub PRs after pushing |
@@ -19,18 +19,13 @@ inputs:
19
19
  required: false
20
20
  default: "false"
21
21
  FETCH_DEPTH:
22
- description: "Fetch depth for checkout"
23
- required: false
24
- default: "10"
25
- SKIP_CHECKOUT:
26
22
  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)
23
+ Depth of git history fetched. Applied to the tool's own fetch of
24
+ refs/pull/<N>/head, to the push-event checkout, and as the git log
25
+ window used to find merged commits during PR reconciliation, so
26
+ raising it widens how far back a merged change is matched
32
27
  required: false
33
- default: "false"
28
+ default: "10"
34
29
  PR_NUMBER:
35
30
  description: "Pull request number to process, use 0 to process all open"
36
31
  required: false
@@ -213,31 +208,44 @@ runs:
213
208
  echo "disabled=false" >> "$GITHUB_OUTPUT"
214
209
  fi
215
210
 
216
- - name: "Checkout repository"
217
- # yamllint disable-line rule:line-length
218
- if: steps.disabled-check.outputs.disabled != 'true' && inputs.SKIP_CHECKOUT != 'true'
211
+ - name: "Checkout repository (push events)"
212
+ # Push runs reconcile merged Gerrit changes back to their GitHub
213
+ # PRs, which reads commit trailers with a local `git show` in the
214
+ # working directory. Pull request runs need no checkout: the tool
215
+ # fetches refs/pull/<N>/head into its own temporary directory, and
216
+ # checking a fork head out here would both fail under
217
+ # pull_request_target and expose a fork-controlled .gitreview.
218
+ if: >-
219
+ steps.disabled-check.outputs.disabled != 'true'
220
+ && github.event_name == 'push'
219
221
  # yamllint disable-line rule:line-length
220
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
222
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
221
223
  with:
222
224
  fetch-depth: ${{ inputs.FETCH_DEPTH }}
223
- # Ensure we are on the PR's head SHA when triggered by PR events
224
- ref: ${{ github.event.pull_request.head.sha || github.sha }}
225
- # The action authenticates to Gerrit over SSH and to GitHub via an
226
- # explicit GITHUB_TOKEN env var, so the checkout token does not need
227
- # to be persisted into the local git config.
225
+ # The action authenticates to Gerrit over SSH and to GitHub via
226
+ # an explicit GITHUB_TOKEN env var, so the checkout token does
227
+ # not need to be persisted into the local git config.
228
228
  persist-credentials: false
229
+ # Callers may place files in the workspace that this action
230
+ # depends on. The bundled reusable workflow checks the action
231
+ # itself out to .g2g-action and then runs it from there, so a
232
+ # cleaning checkout would delete the action mid-run. That
233
+ # workflow also seeds the workspace root with the target
234
+ # repository first, because actions/checkout wipes a directory
235
+ # that is not already a git repository whatever `clean` says.
236
+ clean: false
229
237
 
230
238
  - name: "Setup Python"
231
239
  if: steps.disabled-check.outputs.disabled != 'true'
232
240
  # yamllint disable-line rule:line-length
233
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
241
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
234
242
  with:
235
243
  python-version-file: '${{ github.action_path }}/pyproject.toml'
236
244
 
237
245
  - name: "Setup uv"
238
246
  if: steps.disabled-check.outputs.disabled != 'true'
239
247
  # yamllint disable-line rule:line-length
240
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
248
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
241
249
  with:
242
250
  enable-cache: false
243
251
 
@@ -396,6 +404,11 @@ runs:
396
404
  GITHUB_SHA: ${{ github.sha }}
397
405
  GITHUB_BASE_REF: ${{ github.base_ref }}
398
406
  GITHUB_HEAD_REF: ${{ github.head_ref }}
407
+ # Full owner/repo of the PR head. Differs from GITHUB_REPOSITORY
408
+ # for fork pull requests, whose tree must not influence Gerrit
409
+ # target resolution.
410
+ # yamllint disable-line rule:line-length
411
+ PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
399
412
  GITHUB_ACTOR: ${{ github.actor }}
400
413
  SYNC_ALL_OPEN_PRS: ${{ steps.extract.outputs.sync_all }}
401
414
  PR_NUMBER: ${{ steps.extract.outputs.pr_number }}
@@ -84,7 +84,7 @@ set by GitHub Actions) and undergo proper boolean parsing.
84
84
  | `--preserve-github-prs/--no-preserve-github-prs` | `PRESERVE_GITHUB_PRS` | `true` | Do not close GitHub PRs after pushing to Gerrit |
85
85
  | `--close-merged-prs/--no-close-merged-prs` | `CLOSE_MERGED_PRS` | `true` | Close GitHub PRs when corresponding Gerrit changes merge |
86
86
  | `--force` | `FORCE` | `false` | Force PR closure regardless of Gerrit change status (abandoned, etc.) |
87
- | `--fetch-depth INTEGER` | `FETCH_DEPTH` | `10` | Fetch depth for checkout |
87
+ | `--fetch-depth INTEGER` | `FETCH_DEPTH` | `10` | Git history depth: PR fetch, push checkout, reconciliation window |
88
88
  | `--reviewers-email TEXT` | `REVIEWERS_EMAIL` | `""` | Email address(es) of reviewers (comma-separated) |
89
89
  | `--organization TEXT` | `ORGANIZATION` | unset | Organization (defaults to `GITHUB_REPOSITORY_OWNER` when unset) |
90
90
  | `--github-actor TEXT` | `GITHUB_ACTOR` | `""` | GitHub actor (username) who triggered the workflow |
@@ -16,6 +16,7 @@ action in detail. For a quick start and the full inputs table, see the
16
16
  - [Closing Merged PRs](#closing-merged-prs)
17
17
  - [Automatic Cleanup](#automatic-cleanup)
18
18
  - [Restricting PRs to Automation Tools](#restricting-prs-to-automation-tools)
19
+ - [Fork Pull Requests](#fork-pull-requests)
19
20
  - [Duplicate Detection](#duplicate-detection)
20
21
  - [Commit Message Normalization](#commit-message-normalization)
21
22
  - [Change-ID Reconciliation](#change-id-reconciliation)
@@ -284,6 +285,74 @@ Set `AUTOMATION_ONLY: "false"` to accept PRs from all users:
284
285
  GERRIT_SSH_PRIVKEY_G2G: ${{ secrets.GERRIT_SSH_PRIVKEY_G2G }}
285
286
  ```
286
287
 
288
+ ## Fork Pull Requests
289
+
290
+ A pull request raised from a fork carries a tree that someone without write
291
+ access to the base repository wrote. The tool treats that tree as untrusted
292
+ input.
293
+
294
+ ### No runner checkout for pull requests
295
+
296
+ The action does not check the target repository out into the runner workspace
297
+ for pull request events. It fetches `refs/pull/<N>/head` into a private
298
+ temporary directory instead. That ref lives on the base repository and resolves
299
+ fork pull request heads, so the tool never contacts the fork directly.
300
+
301
+ This sidesteps the `pull_request_target` checkout restriction that
302
+ `actions/checkout` enforces, and keeps fork content out of the working
303
+ directory, where the tool might otherwise read it as configuration.
304
+
305
+ Push events still check the repository out. Those runs reconcile merged Gerrit
306
+ changes back to their GitHub pull requests, which reads commit trailers from
307
+ the working directory. A push carries no fork content, so the checkout is safe
308
+ there.
309
+
310
+ ### Gerrit target resolution
311
+
312
+ For fork pull requests the tool ignores any `.gitreview` in the pull request
313
+ tree and resolves against the **base repository** instead. A fork could
314
+ otherwise redirect the push to a different Gerrit project simply by editing
315
+ `.gitreview`.
316
+
317
+ The tool applies the same treatment when it cannot establish where the pull
318
+ request head lives. Specific-PR `workflow_dispatch` runs and direct URL
319
+ invocations carry no pull request payload, and GitHub reports a null head
320
+ repository for a deleted fork. The tool resolves provenance through the API
321
+ where it can, and treats whatever remains unresolved as untrusted, so a missing
322
+ signal never earns a fork unwarranted trust.
323
+
324
+ Resolution order for a fork pull request:
325
+
326
+ 1. `.gitreview` from the base repository at the pull request's base branch,
327
+ via the GitHub API or `raw.githubusercontent.com`
328
+ 2. Explicit `GERRIT_SERVER` / `GERRIT_SERVER_PORT` / `GERRIT_PROJECT` inputs
329
+ 3. Per-organization configuration file
330
+ 4. Derivation from the organization name (`gerrit.[org].org`)
331
+
332
+ The lookup stops at the base branch. It does not fall back to the repository
333
+ default branch, and it does not read `.gitreview` through the API without an
334
+ authoritative base ref, because either would answer for a branch the pull
335
+ request does not target — and `.gitreview` outranks the explicit inputs.
336
+
337
+ The base repository's copy carries the same authority and lies outside an
338
+ attacker's reach, so this costs no accuracy. Skipping straight to derivation
339
+ would instead guess the Gerrit project name, and that guess goes wrong for
340
+ repositories whose name contains a hyphen that does not mark a path separator.
341
+
342
+ For the same reason, the tool drops branch names that originate in the fork
343
+ (`GITHUB_HEAD_REF` and the pull request head ref) from base-repository
344
+ lookups. Duplicate detection, which resolves `.gitreview` independently and
345
+ earlier, applies the same rule.
346
+
347
+ Same-repository pull requests keep their previous behaviour and still read
348
+ `.gitreview` from the pull request tree.
349
+
350
+ ### Approval gating
351
+
352
+ The tool does not yet require a maintainer approval before it transfers a fork
353
+ pull request to Gerrit. Until it does, use `AUTOMATION_ONLY` (see above) to
354
+ control which pull requests the tool accepts.
355
+
287
356
  ## Duplicate Detection
288
357
 
289
358
  Duplicate detection uses a scoring-based approach. The detector compares the
@@ -139,6 +139,11 @@ ignore = [
139
139
  "sitecustomize.py" = ["S110", "SIM103", "SIM105", "PLW2901"]
140
140
  "src/github2gerrit/duplicate_detection.py" = ["S310"]
141
141
  "src/github2gerrit/gerrit_rest.py" = ["S310", "TRY301"]
142
+ # S310 audits urlopen for non-HTTP schemes such as file:. This module
143
+ # builds the URL from a literal https://raw.githubusercontent.com/ prefix
144
+ # and passes it through _validate_raw_url, which requires scheme https and
145
+ # host raw.githubusercontent.com before the fetch runs.
146
+ "src/github2gerrit/gitreview.py" = ["S310"]
142
147
 
143
148
  [tool.ruff.lint.isort]
144
149
  known-first-party = ["github2gerrit"]
@@ -55,6 +55,7 @@ def _debug_enabled() -> bool:
55
55
  def _dbg(msg: str) -> None:
56
56
  if _debug_enabled():
57
57
  try:
58
+ # aislop-ignore-next-line python-print-debug -- gated debug logger
58
59
  print(f"[sitecustomize] {msg}", file=sys.stderr)
59
60
  except Exception:
60
61
  # Debug logging must never disrupt interpreter startup; if
@@ -90,17 +91,17 @@ def _clean_stale_coverage_files(
90
91
  for base in bases:
91
92
  try:
92
93
  base = base.resolve()
93
- except Exception:
94
+ except Exception as exc:
94
95
  # Use as-is if resolution fails
95
- pass
96
+ _dbg(f"Failed to resolve base {base}: {exc}")
96
97
  for candidate in _iter_cov_candidates(base):
97
98
  try:
98
99
  # Skip the protected target if present in candidates
99
100
  if protected and str(candidate.resolve()) == protected:
100
101
  continue
101
- except Exception:
102
+ except Exception as exc:
102
103
  # If resolve fails, fall through and try to remove
103
- pass
104
+ _dbg(f"Failed to resolve candidate {candidate}: {exc}")
104
105
  _remove_path(candidate)
105
106
 
106
107
 
@@ -110,9 +111,9 @@ def _ensure_unique_coverage_file() -> Path:
110
111
  if existing:
111
112
  try:
112
113
  return Path(existing)
113
- except Exception:
114
+ except Exception as exc:
114
115
  # Fall through to create a sane default
115
- pass
116
+ _dbg(f"Invalid COVERAGE_FILE {existing!r}: {exc}")
116
117
  unique = (
117
118
  Path(tempfile.gettempdir())
118
119
  / f".coverage.pytest.{os.getpid()}.{uuid.uuid4().hex}"