github2gerrit 2.3.0__tar.gz → 2.4.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 (155) hide show
  1. github2gerrit-2.4.1/.pre-commit-config.yaml +241 -0
  2. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/PKG-INFO +12 -2
  3. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/README.md +11 -1
  4. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/action.yaml +20 -4
  5. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/docs/features.md +44 -17
  6. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/models.py +5 -2
  7. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_bulk_fan_out.py +15 -258
  8. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_precommit_mypy_pins.py +7 -0
  9. github2gerrit-2.4.1/tests/test_scheduled_sweep.py +410 -0
  10. github2gerrit-2.4.1/tests/workflow_harness.py +312 -0
  11. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/uv.lock +25 -25
  12. github2gerrit-2.3.0/.pre-commit-config.yaml +0 -251
  13. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/.aislop/config.yml +0 -0
  14. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/.editorconfig +0 -0
  15. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/.gitignore +0 -0
  16. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/.gitlint +0 -0
  17. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/.markdownlint.yaml +0 -0
  18. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/.yamllint +0 -0
  19. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/LICENSE +0 -0
  20. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/LICENSES/Apache-2.0.txt +0 -0
  21. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/REUSE.toml +0 -0
  22. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/SECURITY.md +0 -0
  23. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/docs/COMMIT_RULES.md +0 -0
  24. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/docs/cli.md +0 -0
  25. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/docs/development.md +0 -0
  26. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/docs/github2gerrit_token_permissions_classic.png +0 -0
  27. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/pyproject.toml +0 -0
  28. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/sitecustomize.py +0 -0
  29. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/__init__.py +0 -0
  30. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/approvers.py +0 -0
  31. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/cli.py +0 -0
  32. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/commit_normalization.py +0 -0
  33. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/commit_rules.py +0 -0
  34. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/config.py +0 -0
  35. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/config_ini.py +0 -0
  36. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/constants.py +0 -0
  37. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/core.py +0 -0
  38. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/duplicate_detection.py +0 -0
  39. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/error_codes.py +0 -0
  40. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/external_api.py +0 -0
  41. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/gerrit_pr_closer.py +0 -0
  42. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/gerrit_query.py +0 -0
  43. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/gerrit_rest.py +0 -0
  44. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/gerrit_ssh.py +0 -0
  45. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/gerrit_urls.py +0 -0
  46. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/github_api.py +0 -0
  47. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/gitreview.py +0 -0
  48. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/gitutils.py +0 -0
  49. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/mapping_comment.py +0 -0
  50. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/netrc.py +0 -0
  51. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/orchestrator/__init__.py +0 -0
  52. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/orchestrator/reconciliation.py +0 -0
  53. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/pr_approval.py +0 -0
  54. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/pr_commands.py +0 -0
  55. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/pr_content_filter.py +0 -0
  56. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/pr_directives.py +0 -0
  57. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/project_names.py +0 -0
  58. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/reconcile_matcher.py +0 -0
  59. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/rich_display.py +0 -0
  60. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/rich_logging.py +0 -0
  61. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/similarity.py +0 -0
  62. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/ssh_agent_setup.py +0 -0
  63. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/ssh_common.py +0 -0
  64. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/ssh_config_parser.py +0 -0
  65. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/ssh_discovery.py +0 -0
  66. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/trailers.py +0 -0
  67. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/trust.py +0 -0
  68. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/src/github2gerrit/utils.py +0 -0
  69. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/conftest.py +0 -0
  70. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/fixtures/__init__.py +0 -0
  71. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/fixtures/make_repo.py +0 -0
  72. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/fixtures/ssh_config_samples.py +0 -0
  73. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_action_environment_mapping.py +0 -0
  74. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_action_outputs.py +0 -0
  75. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_action_pr_number_handling.py +0 -0
  76. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_action_step_validation.py +0 -0
  77. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_approver_sources.py +0 -0
  78. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_automation_only.py +0 -0
  79. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_cache_isolation.py +0 -0
  80. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_change_id_deduplication.py +0 -0
  81. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_clean_squash_title.py +0 -0
  82. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_cli.py +0 -0
  83. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_cli_helpers.py +0 -0
  84. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_cli_netrc_options.py +0 -0
  85. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_cli_outputs_file.py +0 -0
  86. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_cli_url_and_dryrun.py +0 -0
  87. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_commit_normalization.py +0 -0
  88. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_commit_rules.py +0 -0
  89. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_composite_action_coverage.py +0 -0
  90. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_config_and_reviewers.py +0 -0
  91. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_config_helpers.py +0 -0
  92. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_close_pr_policy.py +0 -0
  93. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_config_and_errors.py +0 -0
  94. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_gerrit_backref_comment.py +0 -0
  95. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_gerrit_push_errors.py +0 -0
  96. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_gerrit_rest_results.py +0 -0
  97. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_integration_fixture_repo.py +0 -0
  98. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_prepare_commits.py +0 -0
  99. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_shallow_clone.py +0 -0
  100. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_ssh_setup.py +0 -0
  101. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_core_ssrf_protection.py +0 -0
  102. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_dependency_supersession.py +0 -0
  103. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_dns_validation_and_no_gerrit.py +0 -0
  104. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_duplicate_detection.py +0 -0
  105. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_email_case_normalization.py +0 -0
  106. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_error_codes.py +0 -0
  107. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_external_api_framework.py +0 -0
  108. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_final_state_reconciliation.py +0 -0
  109. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_force_flag_cli.py +0 -0
  110. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_fork_approval_gate.py +0 -0
  111. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_fork_pr_hardening.py +0 -0
  112. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_fork_transfer_record.py +0 -0
  113. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_gerrit_change_id_footer.py +0 -0
  114. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_gerrit_change_status_checks.py +0 -0
  115. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_gerrit_pr_closer.py +0 -0
  116. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_gerrit_rest_client.py +0 -0
  117. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_gerrit_ssh_abandon.py +0 -0
  118. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_gerrit_urls.py +0 -0
  119. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_gerrit_urls_more.py +0 -0
  120. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_ghe_and_gitreview_args.py +0 -0
  121. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_github_api_error_handling.py +0 -0
  122. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_github_api_helpers.py +0 -0
  123. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_github_api_retry_and_helpers.py +0 -0
  124. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_gitreview.py +0 -0
  125. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_gitutils_helpers.py +0 -0
  126. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_issue_157_regressions.py +0 -0
  127. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_mapping_comment_additional.py +0 -0
  128. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_mapping_comment_digest_and_backref.py +0 -0
  129. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_metadata_and_reconciliation.py +0 -0
  130. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_metadata_trailer_separation_bug.py +0 -0
  131. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_misc_small_coverage.py +0 -0
  132. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_netrc.py +0 -0
  133. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_orphan_rest_side_effects.py +0 -0
  134. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_pr_closed_handling.py +0 -0
  135. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_pr_command_authorisation.py +0 -0
  136. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_pr_commands.py +0 -0
  137. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_pr_content_filter.py +0 -0
  138. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_pr_content_filter_integration.py +0 -0
  139. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_pr_update_detection.py +0 -0
  140. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_project_names.py +0 -0
  141. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_reconciliation_extracted_module.py +0 -0
  142. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_reconciliation_plan_and_orphans.py +0 -0
  143. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_reconciliation_scenarios.py +0 -0
  144. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_ssh_agent.py +0 -0
  145. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_ssh_agent_ownership.py +0 -0
  146. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_ssh_artifact_prevention.py +0 -0
  147. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_ssh_common.py +0 -0
  148. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_ssh_discovery.py +0 -0
  149. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_ssh_discovery_dry_run.py +0 -0
  150. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_sync_mypy_pins.py +0 -0
  151. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_trailers_additional.py +0 -0
  152. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_url_parser.py +0 -0
  153. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/test_utils.py +0 -0
  154. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/unit/test_config_integration.py +0 -0
  155. {github2gerrit-2.3.0 → github2gerrit-2.4.1}/tests/unit/test_ssh_config_parser.py +0 -0
@@ -0,0 +1,241 @@
1
+ ---
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ # SPDX-FileCopyrightText: 2025 The Linux Foundation
4
+
5
+ ci:
6
+ autofix_prs: false
7
+ skip:
8
+ - pytest
9
+ - gha-workflow-linter
10
+ - aislop
11
+ - eslint
12
+ - go-fmt
13
+ - go-vet
14
+ - go-mod-tidy
15
+ - golangci-lint
16
+ autofix_commit_msg: |
17
+ Chore: Pre-commit autofixes
18
+
19
+ Signed-off-by: pre-commit-ci[bot] <pre-commit-ci@users.noreply.github.com>
20
+ autoupdate_commit_msg: |
21
+ Chore: Pre-commit autoupdate
22
+
23
+ Signed-off-by: pre-commit-ci[bot] <pre-commit-ci@users.noreply.github.com>
24
+
25
+ default_install_hook_types: [pre-commit, commit-msg]
26
+ default_stages: [pre-commit]
27
+
28
+ exclude: |
29
+ (?x)^(docs/conf.py|
30
+ \.mypy_cache/|
31
+ \.pytest_cache/|
32
+ \.ruff_cache/|
33
+ dist/|
34
+ build/|
35
+ coverage_html_report/|
36
+ \.venv/)
37
+
38
+ repos:
39
+ - repo: local
40
+ hooks:
41
+ - id: frozen-rev-is-a-release
42
+ name: frozen revs name a release
43
+ language: pygrep
44
+ # yamllint disable-line rule:line-length
45
+ entry: '^[ \t]*rev:[ \t]*\S+[ \t]+#[ \t]*frozen:(?![ \t]+v?[0-9]+(\.[0-9]+)*(-[0-9]+)?[ \t]*$)'
46
+ files: '^\.pre-commit-config\.yaml$'
47
+
48
+ - repo: https://github.com/pre-commit/pre-commit-hooks
49
+ rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
50
+ hooks:
51
+ - id: trailing-whitespace
52
+ - id: check-added-large-files
53
+ - id: check-ast
54
+ - id: check-json
55
+ - id: check-merge-conflict
56
+ - id: check-xml
57
+ - id: check-yaml
58
+ - id: debug-statements
59
+ - id: end-of-file-fixer
60
+ - id: requirements-txt-fixer
61
+ - id: mixed-line-ending
62
+ args: ["--fix=lf"]
63
+ - id: no-commit-to-branch
64
+ args:
65
+ - --branch=dev
66
+ - --branch=master
67
+ - --branch=main
68
+ - --branch=rc
69
+ - --branch=production
70
+
71
+ - repo: https://github.com/gitleaks/gitleaks
72
+ rev: 2ca41cc1372d1e939a6a879f18cdc19fc1cac1ce # frozen: v8.30.0
73
+ hooks:
74
+ - id: gitleaks
75
+ always_run: true
76
+
77
+ - repo: https://github.com/jorisroovers/gitlint
78
+ rev: acc9d9de6369b76d22cb4167029d2035e8730b98 # frozen: v0.19.1
79
+ hooks:
80
+ - id: gitlint
81
+
82
+ - repo: https://github.com/adrienverge/yamllint.git
83
+ rev: cba56bcde1fdd01c1deb3f945e69764c291a6530 # frozen: v1.38.0
84
+ hooks:
85
+ - id: yamllint
86
+ types: [yaml]
87
+
88
+ - repo: https://github.com/astral-sh/ruff-pre-commit
89
+ rev: a56c0b927e6465d37cae3e97d35d4d18ab2b96cd # frozen: v0.16.9
90
+ hooks:
91
+ - id: ruff
92
+ files: ^(src|scripts|tests)/.+\.py$|^sitecustomize\.py$
93
+ args: [--fix, --exit-non-zero-on-fix]
94
+ - id: ruff-format
95
+ files: ^(src|scripts|tests)/.+\.py$|^sitecustomize\.py$
96
+
97
+ - repo: local
98
+ hooks:
99
+ - id: sync-mypy-pins
100
+ name: sync mypy hook pins with uv.lock
101
+ entry: python3 scripts/sync_mypy_pins.py
102
+ language: system
103
+ files: ^(\.pre-commit-config\.yaml|uv\.lock)$
104
+ pass_filenames: false
105
+
106
+ - repo: https://github.com/pre-commit/mirrors-mypy
107
+ rev: 7ff8d35ae36a7d2b968f2f90b4c723e292e594ee # frozen: v2.3.1
108
+ hooks:
109
+ - id: mypy
110
+ files: ^(src|scripts|tests)/.+\.py$|^sitecustomize\.py$
111
+ additional_dependencies:
112
+ - mypy==2.3.1
113
+ - click==8.5.0
114
+ - pytest==9.1.1
115
+ - responses==0.26.3
116
+ - rich==15.0.0
117
+ - typer==0.27.2
118
+ - types-PyYAML==6.0.12.20260906
119
+ - types-requests==2.33.0.20260906
120
+
121
+ - repo: https://github.com/btford/write-good
122
+ rev: ab66ce10136dfad5146e69e70f82a3efac8842c1 # frozen: v1.0.8
123
+ hooks:
124
+ - id: write-good
125
+ files: "\\.(rst|md|markdown|mdown|mkdn)$"
126
+ args: ["--no-adverb"]
127
+
128
+ - repo: https://github.com/shellcheck-py/shellcheck-py
129
+ rev: a6564f5d57444e123433a5c9af733e3975c77229 # frozen: v0.11.0.1-1
130
+ hooks:
131
+ - id: shellcheck
132
+
133
+ - repo: https://github.com/igorshubovych/markdownlint-cli
134
+ rev: 5b5dddc4fb0f83c3ea1fc5616fa63e115dce83e0 # frozen: v0.49.1
135
+ hooks:
136
+ - id: markdownlint
137
+ args: ["--fix", "--config", ".markdownlint.yaml"]
138
+ exclude: "GERRIT_URL_CENTRALIZATION.md"
139
+
140
+ - repo: https://github.com/fsfe/reuse-tool
141
+ rev: 9fabf9eb815a57aac116b435e8346c200cdb8604 # frozen: v6.2.0
142
+ hooks:
143
+ - id: reuse
144
+
145
+ - repo: https://github.com/kjanat/actionlint
146
+ rev: 2da2ceb4af2c7ecc48f5b9311d85080c3971b5f0 # frozen: v1.17.0
147
+ hooks:
148
+ - id: actionlint-shellcheck
149
+
150
+ - repo: https://github.com/codespell-project/codespell
151
+ rev: 57b21406f092110c18776e39b0bda50d37c945c8 # frozen: v2.4.3
152
+ hooks:
153
+ - id: codespell
154
+
155
+ - repo: https://github.com/DetachHead/basedpyright-prek-mirror
156
+ rev: 706e6acebe9e88c2c28d1527d3085a14b11794c4 # frozen: 1.40.1
157
+ hooks:
158
+ - id: basedpyright
159
+ files: ^(src|scripts|tests)/.+\.py$|^sitecustomize\.py$
160
+
161
+ - repo: https://github.com/lfreleng-actions/gha-workflow-linter
162
+ rev: 0c582e431022f1c7004196fb4ea32744d1fe4843 # frozen: v1.7.0
163
+ hooks:
164
+ - id: gha-workflow-linter
165
+
166
+ - repo: local
167
+ hooks:
168
+ - id: aislop
169
+ name: aislop
170
+ entry: aislop ci --human
171
+ language: node
172
+ additional_dependencies: ["aislop@0.14.1"]
173
+ pass_filenames: false
174
+ always_run: true
175
+
176
+ - repo: local
177
+ hooks:
178
+ - id: eslint
179
+ name: eslint
180
+ # yamllint disable-line rule:line-length
181
+ entry: bash -c 'export GUARD_EXCLUDE="(^|/)(dist|build|coverage|node_modules)/|(^|/)eslint[.]config[.](m|c)?js$"; sh .github/hooks/manifest-guard.sh package.json "*.js" "*.jsx" "*.mjs" "*.cjs" "*.ts" "*.tsx" "*.mts" "*.cts"; rc=$?; case "$rc" in 3|4) ;; *) exit "$rc";; esac; v=$(npx --no-install eslint --version 2>/dev/null | sed "s/^v//; s/[.].*$//"); if [ -z "$v" ]; then echo "eslint not installed - add eslint >=9 to devDependencies" >&2; exit 1; fi; if [ "$v" -lt 9 ]; then echo "eslint $v cannot load a flat config reliably - upgrade to >=9" >&2; exit 1; fi; if ! git ls-files -- eslint.config.mjs | grep -q .; then echo "eslint.config.mjs is not tracked - copy it from actions-template, or ESLint would lint against a config outside this repository" >&2; exit 1; fi; { sh .github/hooks/manifest-guard.sh --list package.json "*.js" "*.jsx" "*.mjs" "*.cjs" "*.ts" "*.tsx" "*.mts" "*.cts"; printf "eslint.config.mjs\0"; } | xargs -0 env ESLINT_USE_FLAT_CONFIG=true npx --no-install eslint --no-config-lookup --config eslint.config.mjs --fix'
182
+ language: system
183
+ always_run: true
184
+ pass_filenames: false
185
+
186
+ - repo: local
187
+ hooks:
188
+ - id: go-fmt
189
+ name: go format (gofmt + goimports)
190
+ # yamllint disable-line rule:line-length
191
+ entry: bash -c 'sh .github/hooks/manifest-guard.sh go.mod "*.go"; rc=$?; case "$rc" in 3) ;; 4) exit 0;; *) exit "$rc";; esac; command -v golangci-lint >/dev/null 2>&1 || { echo "golangci-lint not installed - see https://golangci-lint.run/welcome/install/" >&2; exit 1; }; sh .github/hooks/manifest-guard.sh --list go.mod "*.go" | xargs -0 golangci-lint fmt'
192
+ language: system
193
+ always_run: true
194
+ pass_filenames: false
195
+ - id: go-mod-tidy
196
+ name: go mod tidy
197
+ # yamllint disable-line rule:line-length
198
+ entry: bash -c 'sh .github/hooks/manifest-guard.sh go.mod "*.go"; rc=$?; case "$rc" in 3|4) ;; *) exit "$rc";; esac; go mod tidy'
199
+ language: system
200
+ always_run: true
201
+ pass_filenames: false
202
+ - id: go-vet
203
+ name: go vet
204
+ # yamllint disable-line rule:line-length
205
+ entry: bash -c 'sh .github/hooks/manifest-guard.sh go.mod "*.go"; rc=$?; case "$rc" in 3) ;; 4) exit 0;; *) exit "$rc";; esac; go vet ./...'
206
+ language: system
207
+ always_run: true
208
+ pass_filenames: false
209
+ - id: golangci-lint
210
+ name: golangci-lint
211
+ # yamllint disable-line rule:line-length
212
+ entry: bash -c 'sh .github/hooks/manifest-guard.sh go.mod "*.go"; rc=$?; case "$rc" in 3) ;; 4) exit 0;; *) exit "$rc";; esac; command -v golangci-lint >/dev/null 2>&1 || { echo "golangci-lint not installed - see https://golangci-lint.run/welcome/install/" >&2; exit 1; }; golangci-lint run --timeout=5m'
213
+ language: system
214
+ always_run: true
215
+ pass_filenames: false
216
+
217
+ - repo: https://github.com/python-jsonschema/check-jsonschema
218
+ rev: 4f85d46a92dc17713078e5de73f8a13190edf6fc # frozen: 0.38.2
219
+ hooks:
220
+ - id: check-github-actions
221
+ - id: check-github-workflows
222
+ - id: check-jsonschema
223
+ name: Check GitHub Workflows set timeout-minutes
224
+ args:
225
+ - --builtin-schema
226
+ - github-workflows-require-timeout
227
+ files: ^\.github/workflows/[^/]+$
228
+ types:
229
+ - yaml
230
+ - id: check-readthedocs
231
+
232
+ - repo: local
233
+ hooks:
234
+ - id: pytest
235
+ name: pytest
236
+ entry: uv
237
+ args: [run, pytest, --tb=short, -q, -v, --strict-markers, --maxfail=5]
238
+ language: system
239
+ pass_filenames: false
240
+ always_run: true
241
+ require_serial: true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: github2gerrit
3
- Version: 2.3.0
3
+ Version: 2.4.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
@@ -145,6 +145,9 @@ on:
145
145
  # pull_request_review runs on fork PRs.
146
146
  issue_comment:
147
147
  types: [created]
148
+ # Optional: transfer approved fork PRs without anyone commenting.
149
+ # schedule:
150
+ # - cron: "17 * * * *"
148
151
  push:
149
152
  branches: [main, master]
150
153
  workflow_dispatch:
@@ -189,6 +192,12 @@ which closes a human-authored pull request before the approval gate
189
192
  sees it, so leaving it set would make the fork path unreachable. Drop
190
193
  both if the repository only ever receives automation PRs.
191
194
 
195
+ The optional `schedule` trigger does the comment's job unattended: each
196
+ run transfers fork pull requests whose current commit a maintainer has
197
+ approved, one job per pull request, and leaves every other pull request
198
+ alone. See
199
+ [scheduled sweeps](docs/features.md#which-triggers-can-lift-the-gate).
200
+
192
201
  Repositories using the Gerrit-side dispatch integration should also
193
202
  declare `GERRIT_CHANGE_URL`, `GERRIT_EVENT_TYPE`, and `GERRIT_BRANCH`
194
203
  as dispatch inputs and forward them the same way.
@@ -263,7 +272,8 @@ A composite action cannot start jobs, so here a bulk `workflow_dispatch`
263
272
  (`PR_NUMBER` of `0`) still processes every open pull request in one job. That
264
273
  job does not serialise against the per-pull-request runs events start, so
265
274
  avoid triggering one while pull request activity is in flight, or use the
266
- reusable workflow, which fans the sweep out.
275
+ reusable workflow, which fans the sweep out. For the same reason a `schedule`
276
+ trigger works only through the reusable workflow.
267
277
 
268
278
  ### Option C: command-line tool
269
279
 
@@ -98,6 +98,9 @@ on:
98
98
  # pull_request_review runs on fork PRs.
99
99
  issue_comment:
100
100
  types: [created]
101
+ # Optional: transfer approved fork PRs without anyone commenting.
102
+ # schedule:
103
+ # - cron: "17 * * * *"
101
104
  push:
102
105
  branches: [main, master]
103
106
  workflow_dispatch:
@@ -142,6 +145,12 @@ which closes a human-authored pull request before the approval gate
142
145
  sees it, so leaving it set would make the fork path unreachable. Drop
143
146
  both if the repository only ever receives automation PRs.
144
147
 
148
+ The optional `schedule` trigger does the comment's job unattended: each
149
+ run transfers fork pull requests whose current commit a maintainer has
150
+ approved, one job per pull request, and leaves every other pull request
151
+ alone. See
152
+ [scheduled sweeps](docs/features.md#which-triggers-can-lift-the-gate).
153
+
145
154
  Repositories using the Gerrit-side dispatch integration should also
146
155
  declare `GERRIT_CHANGE_URL`, `GERRIT_EVENT_TYPE`, and `GERRIT_BRANCH`
147
156
  as dispatch inputs and forward them the same way.
@@ -216,7 +225,8 @@ A composite action cannot start jobs, so here a bulk `workflow_dispatch`
216
225
  (`PR_NUMBER` of `0`) still processes every open pull request in one job. That
217
226
  job does not serialise against the per-pull-request runs events start, so
218
227
  avoid triggering one while pull request activity is in flight, or use the
219
- reusable workflow, which fans the sweep out.
228
+ reusable workflow, which fans the sweep out. For the same reason a `schedule`
229
+ trigger works only through the reusable workflow.
220
230
 
221
231
  ### Option C: command-line tool
222
232
 
@@ -293,17 +293,22 @@ runs:
293
293
  fi
294
294
 
295
295
  - name: "Validate PR_NUMBER usage"
296
+ # A scheduled sweep names its pull request the same way a dispatch
297
+ # does: the reusable workflow fans the schedule out to one job per
298
+ # pull request, each passing that pull request's number.
296
299
  # yamllint disable-line rule:line-length
297
- if: ${{ steps.disabled-check.outputs.disabled != 'true' && github.event_name != 'workflow_dispatch' && inputs.PR_NUMBER != '' && inputs.PR_NUMBER != '0' }}
300
+ if: ${{ steps.disabled-check.outputs.disabled != 'true' && github.event_name != 'workflow_dispatch' && github.event_name != 'schedule' && inputs.PR_NUMBER != '' && inputs.PR_NUMBER != '0' }}
298
301
  shell: bash
299
302
  run: |
300
303
  # Validate PR_NUMBER usage
301
304
  set -euo pipefail
302
- echo "Error: PR_NUMBER only valid during workflow_dispatch events."
305
+ echo "Error: PR_NUMBER only valid during workflow_dispatch events," \
306
+ "or on schedule for one job of a scheduled sweep."
303
307
  exit 2
304
308
 
305
309
  - name: "Normalize PR_NUMBER"
306
- if: ${{ steps.disabled-check.outputs.disabled != 'true' && github.event_name == 'workflow_dispatch' }}
310
+ # yamllint disable-line rule:line-length
311
+ if: ${{ steps.disabled-check.outputs.disabled != 'true' && (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') }}
307
312
  id: normalize
308
313
  shell: bash
309
314
  env:
@@ -347,7 +352,18 @@ runs:
347
352
  fi
348
353
 
349
354
  # Honor PR_NUMBER or sync-all from workflow_dispatch normalization
350
- if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
355
+ if [[ "${GITHUB_EVENT_NAME}" == "schedule" ]]; then
356
+ # One job of a scheduled sweep, which names its pull request.
357
+ # A schedule sweeping every pull request in one job would race
358
+ # the runs events start for them (#421), so it is refused.
359
+ if [[ -n "${DISPATCH_SYNC_ALL:-}" || -z "${DISPATCH_PR_NUMBER:-}" ]]; then
360
+ echo "Error: a scheduled run needs PR_NUMBER naming one pull request."
361
+ echo "The reusable workflow fans a schedule out to one job per pull"
362
+ echo "request; a composite action cannot start jobs of its own."
363
+ exit 2
364
+ fi
365
+ echo "pr_number=${DISPATCH_PR_NUMBER}" >> "$GITHUB_OUTPUT"
366
+ elif [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
351
367
  if [[ -n "${DISPATCH_SYNC_ALL:-}" ]]; then
352
368
  echo "Processing all open pull requests via workflow_dispatch."
353
369
  echo "sync_all=true" >> "$GITHUB_OUTPUT"
@@ -505,7 +505,8 @@ decides *whether* to proceed, and the tool always re-reads it from the pull
505
505
  request's reviews. So no trigger, and nobody who fires one, can grant
506
506
  anything.
507
507
 
508
- Of the privileged triggers, this mechanism uses `issue_comment`.
508
+ Of the privileged triggers, this mechanism uses `issue_comment` and, through
509
+ the reusable workflow, `schedule`.
509
510
 
510
511
  **A comment** is the privileged re-check. Subscribe to `issue_comment`:
511
512
 
@@ -531,13 +532,38 @@ This trigger needs `AUTOMATION_ONLY: false`. It defaults to `true` and closes
531
532
  a human-authored pull request before the gate sees it, which would leave
532
533
  nothing to unblock.
533
534
 
534
- A zero-touch variant — a periodic sweep that notices the approval without
535
- anyone commenting — remains open as
536
- [#421](https://github.com/lfreleng-actions/github2gerrit-action/issues/421).
537
- It can build on two pieces that bulk dispatch now uses: a job per pull
538
- request, so each transfer takes that pull request's own lock, and the
539
- transfer record described below, so an already-transferred head gets no
540
- second visit.
535
+ **A schedule** removes the comment. A maintainer approves in the ordinary way
536
+ and the next sweep transfers the change. Add a `schedule` trigger to a caller
537
+ of the reusable workflow:
538
+
539
+ ```yaml
540
+ on:
541
+ schedule:
542
+ - cron: "17 * * * *" # hourly, off the top of the hour
543
+ ```
544
+
545
+ A scheduled run lists the open pull requests and starts one job for each that
546
+ the gate could be holding back: a head not known to be in this repository,
547
+ with an approving review of its current commit. Same-repository pull requests
548
+ never enter a sweep. That also covers every automation pull request, which
549
+ the events already handle. The sweep still visits a head whose repository no
550
+ longer exists, since the gate applies to it. The approval check only narrows
551
+ the list; the job's own gate still decides whether that approval counts.
552
+
553
+ Each job runs in its pull request's concurrency group, so it queues behind a
554
+ comment re-check or a push for the same pull request rather than racing it.
555
+ Once a pull request has transferred, the transfer record described below has
556
+ later sweeps pass it over until its head moves. A pull request approved but
557
+ not yet transferred gets a job on every run until it transfers, so pick an
558
+ interval to match: hourly suits most projects.
559
+
560
+ The same caveats apply as for comments: the trigger needs
561
+ `AUTOMATION_ONLY: false`, and a scheduled workflow runs from the default
562
+ branch. A composite action cannot start jobs of its own, so a scheduled run
563
+ calling it directly fails with an error saying so, rather than sweeping every
564
+ pull request in one job and racing the runs events start for them. GitHub
565
+ suspends schedules in a public repository after 60 days without activity; the
566
+ comment re-check keeps working regardless.
541
567
 
542
568
  `pull_request_review` should be **removed** from any workflow that still
543
569
  carries it. It can no longer transfer anything: on a fork pull request it runs
@@ -598,15 +624,16 @@ transferred pull request does not keep displaying a stale block. After the
598
624
  transfer succeeds it edits the comment once more to say so, recording the
599
625
  transferred commit in a hidden marker.
600
626
 
601
- A bulk sweep (`PR_NUMBER` of `0`) reads that marker and passes over a pull
602
- request whose current head it names, rather than submitting the same commit
603
- again. Through the reusable workflow each pull request in a sweep runs as a
604
- job of its own, and the marker behaves the same there. Anything less means the sweep processes the pull request as before:
605
- no comment, no marker, a comment it cannot read, or a head that has moved
606
- since. Only a run that pushes records anything: a dry run does not, nor does
607
- one that finds the pull request's changes already merged or abandoned and
608
- acts on GitHub instead. Neither does a pull request approved before its
609
- first run, which never had a comment to edit. Only sweeps consult
627
+ A sweep reads that marker and passes over a pull request whose current head it
628
+ names, rather than submitting the same commit again. Sweeps are the bulk
629
+ dispatch (`PR_NUMBER` of `0`) and the scheduled run; through the reusable
630
+ workflow each pull request in either runs as a job of its own, and the marker
631
+ behaves the same there. Anything less means the sweep processes the pull
632
+ request as before: no comment, no marker, a comment it cannot read, or a head
633
+ that has moved since. Only a run that pushes records anything: a dry run does
634
+ not, nor does one that finds the pull request's changes already merged or
635
+ abandoned and acts on GitHub instead. Neither does a pull request approved
636
+ before its first run, which never had a comment to edit. Only sweeps consult
610
637
  the marker. A dispatch naming the pull request, a push and a
611
638
  `@github2gerrit check` comment transfer whatever it says.
612
639
 
@@ -27,18 +27,21 @@ RECHECK_EVENTS: frozenset[str] = frozenset(
27
27
  {
28
28
  "issue_comment",
29
29
  "pull_request_review",
30
+ "schedule",
30
31
  }
31
32
  )
32
33
  """Events that ask the tool to look at a pull request again.
33
34
 
34
- Neither changes the pull request's code, so each maps to UPDATE: an
35
+ None changes the pull request's code, so each maps to UPDATE: an
35
36
  existing Gerrit change should gain a patchset rather than a sibling.
36
37
 
37
38
  They exist because the fork approval gate needs a **privileged** run to
38
39
  notice that a maintainer has approved. GitHub withholds secrets from
39
40
  ``pull_request_review`` on a fork pull request, so the approval itself
40
41
  cannot be the run that acts on it (see
41
- :func:`github2gerrit.cli._skip_unprivileged_fork_run`).
42
+ :func:`github2gerrit.cli._skip_unprivileged_fork_run`). A comment
43
+ asks for the re-check; a schedule makes it without anyone asking, one
44
+ pull request per job of the reusable workflow's sweep (#421).
42
45
 
43
46
  The important property is that these are *triggers only*. They decide
44
47
  when to re-evaluate, never whether to proceed: authorisation is always