github2gerrit 1.2.3__tar.gz → 1.3.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 (134) hide show
  1. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/.pre-commit-config.yaml +4 -4
  2. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/PKG-INFO +25 -2
  3. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/README.md +23 -0
  4. github2gerrit-1.3.0/SECURITY.md +77 -0
  5. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/action.yaml +43 -30
  6. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/pyproject.toml +22 -2
  7. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/core.py +8 -2
  8. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/external_api.py +17 -3
  9. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/gerrit_pr_closer.py +23 -1
  10. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/gerrit_query.py +146 -14
  11. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/gerrit_rest.py +98 -4
  12. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/orchestrator/reconciliation.py +26 -2
  13. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/pr_content_filter.py +8 -1
  14. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/utils.py +45 -0
  15. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_action_environment_mapping.py +8 -7
  16. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_action_step_validation.py +8 -3
  17. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_dependency_supersession.py +274 -0
  18. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_external_api_framework.py +47 -0
  19. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_gerrit_rest_client.py +137 -0
  20. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_gerrit_urls_more.py +1 -2
  21. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_netrc.py +8 -7
  22. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_orphan_rest_side_effects.py +3 -0
  23. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_reconciliation_plan_and_orphans.py +6 -0
  24. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_ssh_discovery.py +9 -3
  25. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_utils.py +81 -0
  26. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/uv.lock +86 -79
  27. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/.editorconfig +0 -0
  28. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/.gitignore +0 -0
  29. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/.gitlint +0 -0
  30. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/.markdownlint.yaml +0 -0
  31. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/.readthedocs.yml +0 -0
  32. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/.yamllint +0 -0
  33. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/LICENSE +0 -0
  34. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/LICENSES/Apache-2.0.txt +0 -0
  35. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/REUSE.toml +0 -0
  36. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/docs/COMMIT_RULES.md +0 -0
  37. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/docs/COMPOSITE_ACTION_TESTING.md +0 -0
  38. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/docs/PR_UPDATE_IMPLEMENTATION.md +0 -0
  39. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/docs/RELEASE-v0.2.0.md +0 -0
  40. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/docs/github2gerrit_token_permissions_classic.png +0 -0
  41. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/sitecustomize.py +0 -0
  42. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/__init__.py +0 -0
  43. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/cli.py +0 -0
  44. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/commit_normalization.py +0 -0
  45. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/commit_rules.py +0 -0
  46. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/config.py +0 -0
  47. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/constants.py +0 -0
  48. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/duplicate_detection.py +0 -0
  49. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/error_codes.py +0 -0
  50. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/gerrit_ssh.py +0 -0
  51. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/gerrit_urls.py +0 -0
  52. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/github_api.py +0 -0
  53. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/gitreview.py +0 -0
  54. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/gitutils.py +0 -0
  55. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/mapping_comment.py +0 -0
  56. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/models.py +0 -0
  57. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/netrc.py +0 -0
  58. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/orchestrator/__init__.py +0 -0
  59. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/pr_commands.py +0 -0
  60. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/reconcile_matcher.py +0 -0
  61. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/rich_display.py +0 -0
  62. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/rich_logging.py +0 -0
  63. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/similarity.py +0 -0
  64. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/ssh_agent_setup.py +0 -0
  65. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/ssh_common.py +0 -0
  66. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/ssh_config_parser.py +0 -0
  67. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/ssh_discovery.py +0 -0
  68. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/src/github2gerrit/trailers.py +0 -0
  69. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/conftest.py +0 -0
  70. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/fixtures/__init__.py +0 -0
  71. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/fixtures/make_repo.py +0 -0
  72. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/fixtures/ssh_config_samples.py +0 -0
  73. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_action_outputs.py +0 -0
  74. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_action_pr_number_handling.py +0 -0
  75. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_automation_only.py +0 -0
  76. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_change_id_deduplication.py +0 -0
  77. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_clean_squash_title.py +0 -0
  78. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_cli.py +0 -0
  79. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_cli_helpers.py +0 -0
  80. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_cli_netrc_options.py +0 -0
  81. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_cli_outputs_file.py +0 -0
  82. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_cli_url_and_dryrun.py +0 -0
  83. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_commit_normalization.py +0 -0
  84. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_commit_rules.py +0 -0
  85. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_composite_action_coverage.py +0 -0
  86. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_config_and_reviewers.py +0 -0
  87. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_config_helpers.py +0 -0
  88. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_close_pr_policy.py +0 -0
  89. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_config_and_errors.py +0 -0
  90. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_gerrit_backref_comment.py +0 -0
  91. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_gerrit_push_errors.py +0 -0
  92. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_gerrit_rest_results.py +0 -0
  93. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_integration_fixture_repo.py +0 -0
  94. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_prepare_commits.py +0 -0
  95. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_shallow_clone.py +0 -0
  96. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_ssh_setup.py +0 -0
  97. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_core_ssrf_protection.py +0 -0
  98. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_dns_validation_and_no_gerrit.py +0 -0
  99. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_duplicate_detection.py +0 -0
  100. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_email_case_normalization.py +0 -0
  101. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_error_codes.py +0 -0
  102. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_force_flag_cli.py +0 -0
  103. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_gerrit_change_id_footer.py +0 -0
  104. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_gerrit_change_status_checks.py +0 -0
  105. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_gerrit_pr_closer.py +0 -0
  106. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_gerrit_ssh_abandon.py +0 -0
  107. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_gerrit_urls.py +0 -0
  108. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_ghe_and_gitreview_args.py +0 -0
  109. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_github_api_error_handling.py +0 -0
  110. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_github_api_helpers.py +0 -0
  111. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_github_api_retry_and_helpers.py +0 -0
  112. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_gitreview.py +0 -0
  113. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_gitutils_helpers.py +0 -0
  114. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_issue_157_regressions.py +0 -0
  115. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_mapping_comment_additional.py +0 -0
  116. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_mapping_comment_digest_and_backref.py +0 -0
  117. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_metadata_and_reconciliation.py +0 -0
  118. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_metadata_trailer_separation_bug.py +0 -0
  119. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_misc_small_coverage.py +0 -0
  120. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_pr_commands.py +0 -0
  121. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_pr_content_filter.py +0 -0
  122. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_pr_content_filter_integration.py +0 -0
  123. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_pr_update_detection.py +0 -0
  124. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_reconciliation_extracted_module.py +0 -0
  125. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_reconciliation_scenarios.py +0 -0
  126. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_ssh_agent.py +0 -0
  127. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_ssh_agent_ownership.py +0 -0
  128. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_ssh_artifact_prevention.py +0 -0
  129. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_ssh_common.py +0 -0
  130. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_ssh_discovery_dry_run.py +0 -0
  131. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_trailers_additional.py +0 -0
  132. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/test_url_parser.py +0 -0
  133. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/tests/unit/test_config_integration.py +0 -0
  134. {github2gerrit-1.2.3 → github2gerrit-1.3.0}/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: 0671d8ab202c4ac093b78433ae5baf74f3fc7246 # frozen: v0.15.15
62
+ rev: 3b3f7c3f57fe9925356faf5fe6230835138be230 # frozen: v0.15.17
63
63
  hooks:
64
64
  - id: ruff
65
65
  files: ^(src|scripts|tests)/.+\.py$
@@ -115,18 +115,18 @@ repos:
115
115
 
116
116
  # Requires a mirror, primary repo lacks .pre-commit-hooks.yaml
117
117
  - repo: https://github.com/DetachHead/basedpyright-prek-mirror
118
- rev: 78e6efd50b63647fecb7e65fc7032745d861e2c5 # frozen: 1.39.6
118
+ rev: 5f6f2cb9fa8aec15105f77fd21e8dfe29838b16d # frozen: 1.39.8
119
119
  hooks:
120
120
  - id: basedpyright
121
121
  files: ^src/.+\.py$
122
122
 
123
123
  - repo: https://github.com/lfreleng-actions/gha-workflow-linter
124
- rev: 2c315e461ec3379bf9c1682360fdc1a3899f88c9 # frozen: v1.0.4
124
+ rev: 1a307fd9eed98268b11300ab86b1a107e1d3d1ae # frozen: v1.1.1
125
125
  hooks:
126
126
  - id: gha-workflow-linter
127
127
 
128
128
  - repo: https://github.com/python-jsonschema/check-jsonschema
129
- rev: 943377262562a12b57292fc98fabd7dbf81451fe # frozen: 0.37.2
129
+ rev: 8ef330cbb7204d388aa7a620f9549bcea8009663 # frozen: 0.37.3
130
130
  hooks:
131
131
  - id: check-github-actions
132
132
  - id: check-github-workflows
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github2gerrit
3
- Version: 1.2.3
3
+ Version: 1.3.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
@@ -23,7 +23,7 @@ Classifier: Topic :: Software Development :: Version Control
23
23
  Classifier: Typing :: Typed
24
24
  Requires-Python: >=3.11
25
25
  Requires-Dist: click>=8.1.7
26
- Requires-Dist: cryptography>=46.0.5
26
+ Requires-Dist: cryptography>=48.0.1
27
27
  Requires-Dist: git-review>=2.5.0
28
28
  Requires-Dist: pygerrit2>=2.0.15
29
29
  Requires-Dist: pygithub>=2.8.1
@@ -1430,6 +1430,29 @@ variable.
1430
1430
  **Note**: Unknown configuration keys will generate warnings to help catch typos
1431
1431
  and missing functionality.
1432
1432
 
1433
+ ### Supersession Sweep Without REST Credentials
1434
+
1435
+ The dependency **supersession sweep** (which reuses or abandons older open
1436
+ Gerrit changes when GitHub2Gerrit pushes a newer update for the same
1437
+ dependency) needs to list GitHub2Gerrit's own open changes. With Gerrit REST
1438
+ credentials it does this precisely via the `owner:self` query operator.
1439
+
1440
+ `owner:self` requires an authenticated session, so without Gerrit REST
1441
+ credentials the tool falls back to an **anonymous** query: it lists the
1442
+ project/branch's open GitHub2Gerrit changes (narrowed server-side via the
1443
+ `GitHub-PR` trailer, a public read-only operation) and then keeps only those
1444
+ whose trailer points at the current repository. This scopes the sweep to
1445
+ GitHub2Gerrit changes for this repository without needing a Gerrit HTTP
1446
+ password.
1447
+
1448
+ - Use `G2G_ANON_SUPERSEDE_FALLBACK` (default `true`) to control the fallback.
1449
+ - Set it to `false` to disable the fallback; without credentials the tool then
1450
+ skips the sweep and logs a warning, as before.
1451
+ - The fallback depends on the Gerrit server allowing anonymous change queries.
1452
+ If the server rejects that query, the tool skips the sweep gracefully.
1453
+ - On large projects the query has a result cap; if it reaches that cap the
1454
+ tool warns that the sweep may be incomplete.
1455
+
1433
1456
  ### Credential Derivation
1434
1457
 
1435
1458
  When `GERRIT_SSH_USER_G2G` and `GERRIT_SSH_USER_G2G_EMAIL` are not explicitly provided,
@@ -1384,6 +1384,29 @@ variable.
1384
1384
  **Note**: Unknown configuration keys will generate warnings to help catch typos
1385
1385
  and missing functionality.
1386
1386
 
1387
+ ### Supersession Sweep Without REST Credentials
1388
+
1389
+ The dependency **supersession sweep** (which reuses or abandons older open
1390
+ Gerrit changes when GitHub2Gerrit pushes a newer update for the same
1391
+ dependency) needs to list GitHub2Gerrit's own open changes. With Gerrit REST
1392
+ credentials it does this precisely via the `owner:self` query operator.
1393
+
1394
+ `owner:self` requires an authenticated session, so without Gerrit REST
1395
+ credentials the tool falls back to an **anonymous** query: it lists the
1396
+ project/branch's open GitHub2Gerrit changes (narrowed server-side via the
1397
+ `GitHub-PR` trailer, a public read-only operation) and then keeps only those
1398
+ whose trailer points at the current repository. This scopes the sweep to
1399
+ GitHub2Gerrit changes for this repository without needing a Gerrit HTTP
1400
+ password.
1401
+
1402
+ - Use `G2G_ANON_SUPERSEDE_FALLBACK` (default `true`) to control the fallback.
1403
+ - Set it to `false` to disable the fallback; without credentials the tool then
1404
+ skips the sweep and logs a warning, as before.
1405
+ - The fallback depends on the Gerrit server allowing anonymous change queries.
1406
+ If the server rejects that query, the tool skips the sweep gracefully.
1407
+ - On large projects the query has a result cap; if it reaches that cap the
1408
+ tool warns that the sweep may be incomplete.
1409
+
1387
1410
  ### Credential Derivation
1388
1411
 
1389
1412
  When `GERRIT_SSH_USER_G2G` and `GERRIT_SSH_USER_G2G_EMAIL` are not explicitly provided,
@@ -0,0 +1,77 @@
1
+ <!--
2
+ SPDX-License-Identifier: Apache-2.0
3
+ SPDX-FileCopyrightText: 2026 The Linux Foundation
4
+ -->
5
+
6
+ # Security Policy
7
+
8
+ This document describes the security policy for this repository, including
9
+ which versions receive security updates and how to report vulnerabilities.
10
+
11
+ ## Supported Versions
12
+
13
+ Maintainers develop and merge security fixes on the default branch
14
+ (`main`) and publish those fixes in the latest tagged release. Older
15
+ releases and tags do not receive security updates. Users should track
16
+ the latest tagged release for security patches.
17
+
18
+ | Version | Supported |
19
+ | --------------------- | ------------------ |
20
+ | Latest tagged release | :white_check_mark: |
21
+ | Older releases/tags | :x: |
22
+
23
+ ## Reporting a Vulnerability
24
+
25
+ If you discover a security vulnerability in this project, please report it
26
+ **privately** so that maintainers can investigate and release a fix before
27
+ the issue becomes publicly known.
28
+
29
+ ### Preferred: GitHub Private Vulnerability Reporting
30
+
31
+ Use GitHub's private vulnerability reporting feature:
32
+
33
+ 1. Navigate to the **Security** tab of this repository.
34
+ 2. Click **Report a vulnerability**.
35
+ 3. Provide as much detail as possible (see below).
36
+
37
+ This creates a private advisory visible to maintainers.
38
+
39
+ ### Alternative: Email
40
+
41
+ If you cannot use GitHub's private reporting, send an email to the Linux
42
+ Foundation Release Engineering team at:
43
+
44
+ - **<releng@linuxfoundation.org>**
45
+
46
+ Please do **not** report security vulnerabilities through public GitHub
47
+ issues, discussions, or pull requests.
48
+
49
+ ### What to Include
50
+
51
+ To help maintainers triage and resolve the report, please include:
52
+
53
+ - A clear description of the vulnerability and its potential impact.
54
+ - Steps to reproduce the issue (proof-of-concept code or commands).
55
+ - The affected version(s), commit SHA, or release tag.
56
+ - Any known mitigations or workarounds.
57
+ - Your name and contact details for follow-up (optional).
58
+
59
+ ## Response Process
60
+
61
+ Maintainers will acknowledge receipt of vulnerability reports within
62
+ **5 business days**. We aim to:
63
+
64
+ 1. Confirm the vulnerability and determine its severity.
65
+ 2. Develop and test a fix in a private branch or advisory.
66
+ 3. Coordinate a disclosure timeline with the reporter.
67
+ 4. Release a patched version and publish a security advisory.
68
+
69
+ We follow a responsible disclosure process and credit reporters in the
70
+ published advisory unless they request to remain anonymous.
71
+
72
+ ## Scope
73
+
74
+ This policy covers the source code, configuration, and documentation
75
+ in this repository. Please report vulnerabilities in upstream
76
+ dependencies to their respective maintainers; this project will update
77
+ affected dependencies once fixes become available.
@@ -179,11 +179,13 @@ runs:
179
179
  - name: "Check if GitHub2Gerrit is disabled"
180
180
  id: disabled-check
181
181
  shell: bash
182
+ env:
183
+ INPUT_G2G_DISABLED: ${{ inputs.G2G_DISABLED }}
182
184
  run: |
183
185
  # Check if GitHub2Gerrit is disabled
184
186
  set -euo pipefail
185
187
  DISABLED_ENV="${G2G_DISABLED:-}"
186
- DISABLED_INPUT="${{ inputs.G2G_DISABLED }}"
188
+ DISABLED_INPUT="${INPUT_G2G_DISABLED:-}"
187
189
  # Normalize: accept the same truthy set as env_bool()
188
190
  # (1/true/yes/on, case-insensitive, trimmed)
189
191
  _normalize() {
@@ -205,11 +207,15 @@ runs:
205
207
  - name: "Checkout repository"
206
208
  if: steps.disabled-check.outputs.disabled != 'true'
207
209
  # yamllint disable-line rule:line-length
208
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
210
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
209
211
  with:
210
212
  fetch-depth: ${{ inputs.FETCH_DEPTH }}
211
213
  # Ensure we are on the PR's head SHA when triggered by PR events
212
214
  ref: ${{ github.event.pull_request.head.sha || github.sha }}
215
+ # The action authenticates to Gerrit over SSH and to GitHub via an
216
+ # explicit GITHUB_TOKEN env var, so the checkout token does not need
217
+ # to be persisted into the local git config.
218
+ persist-credentials: false
213
219
 
214
220
  - name: "Setup Python"
215
221
  if: steps.disabled-check.outputs.disabled != 'true'
@@ -221,7 +227,7 @@ runs:
221
227
  - name: "Setup uv"
222
228
  if: steps.disabled-check.outputs.disabled != 'true'
223
229
  # yamllint disable-line rule:line-length
224
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
230
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
225
231
  with:
226
232
  enable-cache: false
227
233
 
@@ -233,15 +239,16 @@ runs:
233
239
  # when git metadata is unavailable
234
240
  # GitHub Actions shallow checkout doesn't include .git history
235
241
  SETUPTOOLS_SCM_PRETEND_VERSION: "0.0.0+dev"
242
+ USE_LOCAL_ACTION: ${{ inputs.USE_LOCAL_ACTION }}
236
243
  run: |
237
244
  # Setup github2gerrit
238
245
  set -euo pipefail
239
246
  uv --version
240
247
  # Install locally for self-testing, use uvx for external repos
241
- if [[ "${{ inputs.USE_LOCAL_ACTION }}" == "true" ]] || \
242
- [[ "${{ github.repository }}" =~ lfreleng-actions/github2gerrit-action ]]; then
243
- echo "Installing with: uv pip install --system ${{ github.action_path }}"
244
- uv pip install --system ${{ github.action_path }}
248
+ if [[ "${USE_LOCAL_ACTION}" == "true" ]] || \
249
+ [[ "${GITHUB_REPOSITORY}" =~ lfreleng-actions/github2gerrit-action ]]; then
250
+ echo "Installing with: uv pip install --system ${GITHUB_ACTION_PATH}"
251
+ uv pip install --system "${GITHUB_ACTION_PATH}"
245
252
  else
246
253
  echo "uvx will install GitHub2Gerrit from PyPI"
247
254
  fi
@@ -258,11 +265,14 @@ runs:
258
265
 
259
266
  - name: "Normalize PR_NUMBER"
260
267
  if: ${{ steps.disabled-check.outputs.disabled != 'true' && github.event_name == 'workflow_dispatch' }}
268
+ id: normalize
261
269
  shell: bash
270
+ env:
271
+ INPUT_PR_NUMBER: ${{ inputs.PR_NUMBER }}
262
272
  run: |
263
273
  # Normalize PR_NUMBER
264
274
  set -euo pipefail
265
- pr_in="${{ inputs.PR_NUMBER }}"
275
+ pr_in="${INPUT_PR_NUMBER:-}"
266
276
  if [[ -z "${pr_in}" || "${pr_in}" == "null" ]]; then
267
277
  pr_in="0"
268
278
  fi
@@ -271,51 +281,53 @@ runs:
271
281
  exit 2
272
282
  fi
273
283
  if [[ "${pr_in}" == "0" ]]; then
274
- echo "SYNC_ALL_OPEN_PRS=true" >> "$GITHUB_ENV"
284
+ echo "sync_all=true" >> "$GITHUB_OUTPUT"
275
285
  else
276
- echo "PR_NUMBER=${pr_in}" >> "$GITHUB_ENV"
286
+ echo "pr_number=${pr_in}" >> "$GITHUB_OUTPUT"
277
287
  fi
278
288
 
279
289
  - name: "Extract PR number, validate context"
280
290
  if: steps.disabled-check.outputs.disabled != 'true'
291
+ id: extract
281
292
  shell: bash
293
+ env:
294
+ EVENT_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || '' }}
295
+ DISPATCH_PR_NUMBER: ${{ steps.normalize.outputs.pr_number }}
296
+ DISPATCH_SYNC_ALL: ${{ steps.normalize.outputs.sync_all }}
282
297
  run: |
283
298
  # Extract PR number, validate context
284
299
  set -euo pipefail
285
300
 
286
301
  # Push events don't need PR_NUMBER (used for closing merged PRs)
287
302
  # The CLI handles push events specially via _process_close_merged_prs()
288
- if [[ "${{ github.event_name }}" == "push" ]]; then
303
+ if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
289
304
  echo "Push event detected - will process merged commits for PR closure"
290
- # Set PR_NUMBER to empty to indicate this is intentional for push events
291
- echo "PR_NUMBER=" >> "$GITHUB_ENV"
305
+ # Emit an empty PR number to signal intentional push handling
306
+ echo "pr_number=" >> "$GITHUB_OUTPUT"
292
307
  exit 0
293
308
  fi
294
309
 
295
- # Honor PR_NUMBER or SYNC_ALL_OPEN_PRS set by workflow_dispatch
296
- if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
297
- if [[ -n "${SYNC_ALL_OPEN_PRS:-}" ]]; then
310
+ # Honor PR_NUMBER or sync-all from workflow_dispatch normalization
311
+ if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
312
+ if [[ -n "${DISPATCH_SYNC_ALL:-}" ]]; then
298
313
  echo "Processing all open pull requests via workflow_dispatch."
314
+ echo "sync_all=true" >> "$GITHUB_OUTPUT"
299
315
  else
300
- if [[ -z "${PR_NUMBER:-}" || "${PR_NUMBER}" == "null" ]]; then
316
+ if [[ -z "${DISPATCH_PR_NUMBER:-}" || "${DISPATCH_PR_NUMBER}" == "null" ]]; then
301
317
  echo "Error: provide PR_NUMBER or set 0 to process all PRs."
302
318
  exit 2
303
319
  fi
304
- echo "PR_NUMBER=${PR_NUMBER}" >> "$GITHUB_ENV"
320
+ echo "pr_number=${DISPATCH_PR_NUMBER}" >> "$GITHUB_OUTPUT"
305
321
  fi
306
322
  else
307
- PR_NUMBER_EVT="${{ github.event.pull_request.number || github.event.issue.number || '' }}"
308
- # Do not override PR_NUMBER if previously set
309
- if [[ -z "${PR_NUMBER:-}" ]]; then
310
- PR_NUMBER="${PR_NUMBER_EVT}"
311
- echo "PR_NUMBER=${PR_NUMBER}" >> "$GITHUB_ENV"
312
- fi
313
- if [[ -z "${PR_NUMBER}" || "${PR_NUMBER}" == "null" ]]; then
323
+ pr_number="${EVENT_PR_NUMBER}"
324
+ if [[ -z "${pr_number}" || "${pr_number}" == "null" ]]; then
314
325
  echo "Error: PR_NUMBER is empty."
315
326
  echo "This action requires a valid pull request context."
316
- echo "Current event: ${{ github.event_name }}"
327
+ echo "Current event: ${GITHUB_EVENT_NAME}"
317
328
  exit 2
318
329
  fi
330
+ echo "pr_number=${pr_number}" >> "$GITHUB_OUTPUT"
319
331
  fi
320
332
 
321
333
  - name: "Run github2gerrit Python CLI"
@@ -375,16 +387,17 @@ runs:
375
387
  GITHUB_BASE_REF: ${{ github.base_ref }}
376
388
  GITHUB_HEAD_REF: ${{ github.head_ref }}
377
389
  GITHUB_ACTOR: ${{ github.actor }}
378
- SYNC_ALL_OPEN_PRS: ${{ env.SYNC_ALL_OPEN_PRS }}
379
- PR_NUMBER: ${{ env.PR_NUMBER }}
390
+ SYNC_ALL_OPEN_PRS: ${{ steps.extract.outputs.sync_all }}
391
+ PR_NUMBER: ${{ steps.extract.outputs.pr_number }}
380
392
  G2G_TEST_MODE: "false"
381
393
  G2G_NO_GERRIT: ${{ inputs.G2G_NO_GERRIT }}
394
+ USE_LOCAL_ACTION: ${{ inputs.USE_LOCAL_ACTION }}
382
395
  run: |
383
396
  # Run github2gerrit Python CLI
384
397
  set -euo pipefail
385
398
  # Use different invocation methods based on repository or USE_LOCAL_ACTION flag
386
- if [[ "${{ inputs.USE_LOCAL_ACTION }}" == "true" ]] || \
387
- [[ "${{ github.repository }}" =~ lfreleng-actions/github2gerrit-action ]]; then
399
+ if [[ "${USE_LOCAL_ACTION}" == "true" ]] || \
400
+ [[ "${GITHUB_REPOSITORY}" =~ lfreleng-actions/github2gerrit-action ]]; then
388
401
  echo "Running:python -m github2gerrit.cli"
389
402
  python -m github2gerrit.cli
390
403
  else
@@ -58,8 +58,13 @@ dependencies = [
58
58
  # Security: Fix CVE-2026-21441 (decompression-bomb vulnerability)
59
59
  "urllib3>=2.6.3",
60
60
 
61
- # Security: Fix CVE-2026-26007 (SECT curve subgroup attack)
62
- "cryptography>=46.0.5",
61
+ # Security: cryptography bundles OpenSSL in its wheels, so OpenSSL
62
+ # security fixes ship as new cryptography releases. Floor at the
63
+ # patched release: fixes CVE-2026-26007 (SECT curve subgroup attack,
64
+ # 46.0.5) and GHSA-537c-gmf6-5ccf (vulnerable OpenSSL in wheels, no
65
+ # CVE assigned, 48.0.1). No upper bound, so future security releases
66
+ # are not held back.
67
+ "cryptography>=48.0.1",
63
68
  ]
64
69
 
65
70
  [project.urls]
@@ -268,6 +273,21 @@ reportUnsupportedDunderAll = "error"
268
273
 
269
274
 
270
275
  [tool.uv]
276
+ # Supply-chain cooldown: do not resolve anything published in the last
277
+ # 7 days (rolling window; uv records it as a relative span in uv.lock).
278
+ # Requires uv >= 0.9.17 for the relative-duration ("7 days") form.
279
+ exclude-newer = "7 days"
280
+ # cryptography bundles OpenSSL directly in its wheels, so OpenSSL security
281
+ # fixes are delivered as new cryptography releases. Exempt it from the
282
+ # supply-chain cooldown above so those patches are not held back by the
283
+ # rolling window.
284
+ #
285
+ # Added 2026-06-15 to unblock GHSA-537c-gmf6-5ccf ("Vulnerable OpenSSL
286
+ # included in cryptography wheels"; no CVE assigned), fixed in 48.0.1,
287
+ # which the 7-day cooldown was excluding. References:
288
+ # https://github.com/advisories/GHSA-537c-gmf6-5ccf
289
+ # https://openssl-library.org/news/secadv/20260609.txt
290
+ exclude-newer-package = { cryptography = "0 days" }
271
291
  # uv will manage installation based on this pyproject and lockfile.
272
292
  # No extra settings are required here; this stanza reserves the
273
293
  # namespace for future use if needed.
@@ -410,9 +410,13 @@ class Orchestrator:
410
410
 
411
411
  # Check if client has authentication
412
412
  if not client.is_authenticated:
413
+ from .gerrit_rest import warn_gerrit_credentials_unavailable
414
+
415
+ warn_gerrit_credentials_unavailable()
413
416
  log.debug(
414
- "Cannot update Gerrit change metadata: "
415
- "No credentials found (check .netrc or environment)"
417
+ "Cannot update Gerrit change metadata for %s: "
418
+ "no Gerrit REST credentials available",
419
+ change_id,
416
420
  )
417
421
  return False
418
422
 
@@ -965,6 +969,7 @@ class Orchestrator:
965
969
  gerrit.project,
966
970
  branch=gh.base_ref,
967
971
  max_results=200,
972
+ github_repository=gh.repository,
968
973
  )
969
974
 
970
975
  # Collect all matching changes, then select the
@@ -2174,6 +2179,7 @@ class Orchestrator:
2174
2179
  exclude_change_ids=prep.change_ids,
2175
2180
  dry_run=False,
2176
2181
  target_branch=self._resolve_target_branch(),
2182
+ github_repository=gh.repository,
2177
2183
  )
2178
2184
  except Exception as exc:
2179
2185
  log.debug("Post-push supersession sweep skipped: %s", exc)
@@ -380,12 +380,26 @@ def external_api_call(
380
380
  reason = (
381
381
  "final attempt" if is_final_attempt else "non-retryable"
382
382
  )
383
- log_exception_conditionally(
384
- log,
383
+ failure_msg = (
385
384
  f"[{api_type.value}] {operation} failed ({reason}) "
386
385
  f"after {attempt} attempt(s) in {duration:.2f}s: "
387
- f"{target}",
386
+ f"{target}"
388
387
  )
388
+ # Authentication/authorization failures (Gerrit REST
389
+ # 401/403) are surfaced once, closer to the request, as a
390
+ # concise warning. Avoid emitting a duplicate error/
391
+ # traceback for them here. Scope this strictly to Gerrit
392
+ # REST: other API types (e.g. GitHub) may also expose a
393
+ # ``.status`` attribute, and their auth/permission
394
+ # failures must retain error-level visibility.
395
+ is_gerrit_auth_failure = (
396
+ api_type == ApiType.GERRIT_REST
397
+ and getattr(exc, "status", None) in (401, 403)
398
+ )
399
+ if is_gerrit_auth_failure:
400
+ log.debug(failure_msg)
401
+ else:
402
+ log_exception_conditionally(log, failure_msg)
389
403
  _update_metrics(api_type, context, success=False, exc=exc)
390
404
  raise
391
405
  else:
@@ -1754,6 +1754,19 @@ def _abandon_gerrit_change(
1754
1754
  abandon_data = {"message": message}
1755
1755
  client.post(abandon_path, data=abandon_data)
1756
1756
  log.debug("Successfully abandoned Gerrit change %s", change_number)
1757
+ except GerritRestError as exc:
1758
+ if exc.is_auth_error:
1759
+ # Expected when no Gerrit REST credentials are available; the
1760
+ # REST layer already surfaced this once. Avoid a duplicate
1761
+ # error-level traceback for an authentication failure.
1762
+ log.debug(
1763
+ "REST abandon for Gerrit change %s failed (HTTP %s)",
1764
+ change_number,
1765
+ exc.status,
1766
+ )
1767
+ else:
1768
+ log.exception("Failed to abandon Gerrit change %s", change_number)
1769
+ raise
1757
1770
  except Exception:
1758
1771
  log.exception("Failed to abandon Gerrit change %s", change_number)
1759
1772
  raise
@@ -1767,6 +1780,7 @@ def abandon_superseded_dependency_changes(
1767
1780
  *,
1768
1781
  dry_run: bool = False,
1769
1782
  target_branch: str | None = None,
1783
+ github_repository: str | None = None,
1770
1784
  ) -> list[str]:
1771
1785
  """Abandon open Gerrit changes superseded by a newer dependency update.
1772
1786
 
@@ -1788,6 +1802,10 @@ def abandon_superseded_dependency_changes(
1788
1802
  dry_run: If True, log but do not actually abandon.
1789
1803
  target_branch: Optional Gerrit branch name. When provided,
1790
1804
  only changes targeting this branch are considered.
1805
+ github_repository: Current GitHub repository in ``owner/repo``
1806
+ form. Enables the anonymous supersession fallback when no
1807
+ Gerrit REST credentials are available (the discovery query is
1808
+ then scoped to this repository via the ``GitHub-PR`` trailer).
1791
1809
 
1792
1810
  Returns:
1793
1811
  List of Gerrit change numbers that were abandoned
@@ -1817,7 +1835,11 @@ def abandon_superseded_dependency_changes(
1817
1835
  try:
1818
1836
  client = build_client_for_host(gerrit_server)
1819
1837
  open_changes = query_open_changes_by_project(
1820
- client, gerrit_project, branch=target_branch, max_results=200
1838
+ client,
1839
+ gerrit_project,
1840
+ branch=target_branch,
1841
+ max_results=200,
1842
+ github_repository=github_repository,
1821
1843
  )
1822
1844
 
1823
1845
  url_builder = create_gerrit_url_builder(gerrit_server)