github2gerrit 1.2.3__tar.gz → 1.2.4__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.
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/.pre-commit-config.yaml +4 -4
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/PKG-INFO +2 -2
- github2gerrit-1.2.4/SECURITY.md +77 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/action.yaml +43 -30
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/pyproject.toml +22 -2
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/core.py +6 -2
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/external_api.py +17 -3
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/gerrit_pr_closer.py +13 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/gerrit_query.py +15 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/gerrit_rest.py +98 -4
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/orchestrator/reconciliation.py +26 -2
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/pr_content_filter.py +8 -1
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/utils.py +45 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_action_environment_mapping.py +8 -7
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_action_step_validation.py +8 -3
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_dependency_supersession.py +19 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_external_api_framework.py +47 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_gerrit_rest_client.py +137 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_gerrit_urls_more.py +1 -2
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_netrc.py +8 -7
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_orphan_rest_side_effects.py +3 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_reconciliation_plan_and_orphans.py +6 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_ssh_discovery.py +9 -3
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_utils.py +81 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/uv.lock +86 -79
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/.editorconfig +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/.gitignore +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/.gitlint +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/.markdownlint.yaml +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/.readthedocs.yml +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/.yamllint +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/LICENSE +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/LICENSES/Apache-2.0.txt +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/README.md +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/REUSE.toml +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/docs/COMMIT_RULES.md +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/docs/COMPOSITE_ACTION_TESTING.md +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/docs/PR_UPDATE_IMPLEMENTATION.md +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/docs/RELEASE-v0.2.0.md +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/docs/github2gerrit_token_permissions_classic.png +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/sitecustomize.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/__init__.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/cli.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/commit_normalization.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/commit_rules.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/config.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/constants.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/duplicate_detection.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/error_codes.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/gerrit_ssh.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/gerrit_urls.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/github_api.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/gitreview.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/gitutils.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/mapping_comment.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/models.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/netrc.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/orchestrator/__init__.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/pr_commands.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/reconcile_matcher.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/rich_display.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/rich_logging.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/similarity.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/ssh_agent_setup.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/ssh_common.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/ssh_config_parser.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/ssh_discovery.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/trailers.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/conftest.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/fixtures/__init__.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/fixtures/make_repo.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/fixtures/ssh_config_samples.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_action_outputs.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_action_pr_number_handling.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_automation_only.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_change_id_deduplication.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_clean_squash_title.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_cli.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_cli_helpers.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_cli_netrc_options.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_cli_outputs_file.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_cli_url_and_dryrun.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_commit_normalization.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_commit_rules.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_composite_action_coverage.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_config_and_reviewers.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_config_helpers.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_close_pr_policy.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_config_and_errors.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_gerrit_backref_comment.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_gerrit_push_errors.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_gerrit_rest_results.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_integration_fixture_repo.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_prepare_commits.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_shallow_clone.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_ssh_setup.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_core_ssrf_protection.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_dns_validation_and_no_gerrit.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_duplicate_detection.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_email_case_normalization.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_error_codes.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_force_flag_cli.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_gerrit_change_id_footer.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_gerrit_change_status_checks.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_gerrit_pr_closer.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_gerrit_ssh_abandon.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_gerrit_urls.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_ghe_and_gitreview_args.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_github_api_error_handling.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_github_api_helpers.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_github_api_retry_and_helpers.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_gitreview.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_gitutils_helpers.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_issue_157_regressions.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_mapping_comment_additional.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_mapping_comment_digest_and_backref.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_metadata_and_reconciliation.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_metadata_trailer_separation_bug.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_misc_small_coverage.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_pr_commands.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_pr_content_filter.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_pr_content_filter_integration.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_pr_update_detection.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_reconciliation_extracted_module.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_reconciliation_scenarios.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_ssh_agent.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_ssh_agent_ownership.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_ssh_artifact_prevention.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_ssh_common.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_ssh_discovery_dry_run.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_trailers_additional.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/test_url_parser.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/tests/unit/test_config_integration.py +0 -0
- {github2gerrit-1.2.3 → github2gerrit-1.2.4}/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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
+
Version: 1.2.4
|
|
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>=
|
|
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
|
|
@@ -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="${
|
|
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@
|
|
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@
|
|
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 [[ "${
|
|
242
|
-
[[ "${
|
|
243
|
-
echo "Installing with: uv pip install --system ${
|
|
244
|
-
uv pip install --system ${
|
|
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="${
|
|
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 "
|
|
284
|
+
echo "sync_all=true" >> "$GITHUB_OUTPUT"
|
|
275
285
|
else
|
|
276
|
-
echo "
|
|
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 [[ "${
|
|
303
|
+
if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
|
|
289
304
|
echo "Push event detected - will process merged commits for PR closure"
|
|
290
|
-
#
|
|
291
|
-
echo "
|
|
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
|
|
296
|
-
if [[ "${
|
|
297
|
-
if [[ -n "${
|
|
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 "${
|
|
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 "
|
|
320
|
+
echo "pr_number=${DISPATCH_PR_NUMBER}" >> "$GITHUB_OUTPUT"
|
|
305
321
|
fi
|
|
306
322
|
else
|
|
307
|
-
|
|
308
|
-
|
|
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: ${
|
|
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: ${{
|
|
379
|
-
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 [[ "${
|
|
387
|
-
[[ "${
|
|
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:
|
|
62
|
-
|
|
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
|
-
"
|
|
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
|
|
|
@@ -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
|
-
|
|
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
|
|
@@ -13,6 +13,7 @@ from typing import Any
|
|
|
13
13
|
from urllib.parse import quote
|
|
14
14
|
|
|
15
15
|
from .gerrit_rest import GerritRestClient
|
|
16
|
+
from .gerrit_rest import warn_gerrit_credentials_unavailable
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
log = logging.getLogger(__name__)
|
|
@@ -148,6 +149,20 @@ def query_open_changes_by_project(
|
|
|
148
149
|
query = f'project:"{_gerrit_quote(project)}" status:open owner:self'
|
|
149
150
|
if branch:
|
|
150
151
|
query += f' branch:"{_gerrit_quote(branch)}"'
|
|
152
|
+
|
|
153
|
+
# The ``owner:self`` predicate requires an authenticated Gerrit
|
|
154
|
+
# session; an anonymous request is rejected with HTTP 403. Skip the
|
|
155
|
+
# query (warning once per run) instead of issuing a request that is
|
|
156
|
+
# guaranteed to fail and would otherwise emit error-level noise.
|
|
157
|
+
if not client.is_authenticated:
|
|
158
|
+
warn_gerrit_credentials_unavailable()
|
|
159
|
+
log.debug(
|
|
160
|
+
"Skipping owner:self query for project '%s': "
|
|
161
|
+
"no Gerrit REST credentials available",
|
|
162
|
+
project,
|
|
163
|
+
)
|
|
164
|
+
return []
|
|
165
|
+
|
|
151
166
|
log.debug("Querying Gerrit for open changes: %s", query)
|
|
152
167
|
|
|
153
168
|
try:
|
|
@@ -46,6 +46,7 @@ from .gerrit_urls import create_gerrit_url_builder
|
|
|
46
46
|
from .netrc import GerritCredentials
|
|
47
47
|
from .netrc import resolve_gerrit_credentials
|
|
48
48
|
from .utils import log_exception_conditionally
|
|
49
|
+
from .utils import log_warning_once
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
log = logging.getLogger("github2gerrit.gerrit_rest")
|
|
@@ -80,12 +81,79 @@ _TRANSIENT_ERR_SUBSTRINGS: Final[tuple[str, ...]] = (
|
|
|
80
81
|
"gateway timeout",
|
|
81
82
|
)
|
|
82
83
|
|
|
84
|
+
# HTTP status codes that indicate an authentication/authorization problem
|
|
85
|
+
# rather than a transient fault or a bug. These are expected when no
|
|
86
|
+
# Gerrit REST credentials are available (or they are insufficient for the
|
|
87
|
+
# requested operation), and are surfaced as concise, default-visible
|
|
88
|
+
# warnings rather than error-level tracebacks.
|
|
89
|
+
_AUTH_ERROR_STATUSES: Final[tuple[int, ...]] = (401, 403)
|
|
90
|
+
|
|
91
|
+
# Shared dedup key so the "no Gerrit REST credentials" situation is
|
|
92
|
+
# surfaced at most once per run, regardless of how many auth-gated
|
|
93
|
+
# operations are affected.
|
|
94
|
+
_CREDENTIALS_UNAVAILABLE_KEY: Final[str] = "gerrit_rest_credentials_unavailable"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _is_auth_status(status: int | None) -> bool:
|
|
98
|
+
"""Return True if the HTTP status indicates an auth/authorization error."""
|
|
99
|
+
return status in _AUTH_ERROR_STATUSES
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _extract_http_status(exc: BaseException) -> int | None:
|
|
103
|
+
"""Best-effort extraction of an HTTP status code from an exception.
|
|
104
|
+
|
|
105
|
+
Handles both the urllib path (``urllib.error.HTTPError.code``) and the
|
|
106
|
+
pygerrit2/requests path (``HTTPError.response.status_code``).
|
|
107
|
+
"""
|
|
108
|
+
code = getattr(exc, "code", None)
|
|
109
|
+
if isinstance(code, int):
|
|
110
|
+
return code
|
|
111
|
+
response = getattr(exc, "response", None)
|
|
112
|
+
status = getattr(response, "status_code", None)
|
|
113
|
+
if isinstance(status, int):
|
|
114
|
+
return status
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def warn_gerrit_credentials_unavailable() -> None:
|
|
119
|
+
"""Warn once per run that authenticated Gerrit REST is unavailable.
|
|
120
|
+
|
|
121
|
+
Call this from auth-gated code paths before skipping an operation that
|
|
122
|
+
requires Gerrit REST credentials. The warning is emitted at most once
|
|
123
|
+
per process to avoid log spam while still making the degraded behavior
|
|
124
|
+
visible at the default log level.
|
|
125
|
+
"""
|
|
126
|
+
log_warning_once(
|
|
127
|
+
log,
|
|
128
|
+
_CREDENTIALS_UNAVAILABLE_KEY,
|
|
129
|
+
"Gerrit REST credentials are not available; authenticated Gerrit "
|
|
130
|
+
"REST operations are disabled. Fallback behavior may apply and "
|
|
131
|
+
"could degrade performance. Provide GERRIT_HTTP_USER and "
|
|
132
|
+
"GERRIT_HTTP_PASSWORD (or a .netrc entry) to enable authenticated "
|
|
133
|
+
"Gerrit REST access.",
|
|
134
|
+
)
|
|
135
|
+
|
|
83
136
|
|
|
84
137
|
# Removed individual retry logic functions - now using centralized framework
|
|
85
138
|
|
|
86
139
|
|
|
87
140
|
class GerritRestError(RuntimeError):
|
|
88
|
-
"""Raised for non-retryable REST errors or exhausted retries.
|
|
141
|
+
"""Raised for non-retryable REST errors or exhausted retries.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
status: HTTP status code associated with the failure, when known.
|
|
145
|
+
Used by callers to distinguish authentication/authorization
|
|
146
|
+
problems (401/403) from transient or unexpected errors.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def __init__(self, *args: object, status: int | None = None) -> None:
|
|
150
|
+
super().__init__(*args)
|
|
151
|
+
self.status = status
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def is_auth_error(self) -> bool:
|
|
155
|
+
"""Return True if this error stems from an auth failure (401/403)."""
|
|
156
|
+
return _is_auth_status(self.status)
|
|
89
157
|
|
|
90
158
|
|
|
91
159
|
@dataclass(frozen=True)
|
|
@@ -264,13 +332,38 @@ class GerritRestClient:
|
|
|
264
332
|
except urllib.error.HTTPError as http_exc:
|
|
265
333
|
status = getattr(http_exc, "code", None)
|
|
266
334
|
msg = f"Gerrit REST {method} {url} failed with HTTP {status}"
|
|
267
|
-
|
|
268
|
-
raise GerritRestError(msg) from http_exc
|
|
335
|
+
self._log_request_failure(msg, status)
|
|
336
|
+
raise GerritRestError(msg, status=status) from http_exc
|
|
269
337
|
|
|
270
338
|
except Exception as exc:
|
|
339
|
+
status = _extract_http_status(exc)
|
|
271
340
|
msg = f"Gerrit REST {method} {url} failed: {exc}"
|
|
341
|
+
self._log_request_failure(msg, status)
|
|
342
|
+
raise GerritRestError(msg, status=status) from exc
|
|
343
|
+
|
|
344
|
+
@staticmethod
|
|
345
|
+
def _log_request_failure(msg: str, status: int | None) -> None:
|
|
346
|
+
"""Log a failed REST request at an appropriate level.
|
|
347
|
+
|
|
348
|
+
Authentication/authorization failures (401/403) are expected when
|
|
349
|
+
credentials are missing or insufficient; they are surfaced as a
|
|
350
|
+
single concise warning per run rather than an error-level traceback,
|
|
351
|
+
since they are neither transient faults nor bugs. All other failures
|
|
352
|
+
retain the existing conditional error/traceback behavior.
|
|
353
|
+
"""
|
|
354
|
+
if _is_auth_status(status):
|
|
355
|
+
log_warning_once(
|
|
356
|
+
log,
|
|
357
|
+
f"gerrit_rest_auth_{status}",
|
|
358
|
+
"Gerrit REST authentication failed (HTTP %s): the configured "
|
|
359
|
+
"credentials are missing or insufficient for this operation. "
|
|
360
|
+
"Fallback behavior may apply and could degrade performance. "
|
|
361
|
+
"Details: %s",
|
|
362
|
+
status,
|
|
363
|
+
msg,
|
|
364
|
+
)
|
|
365
|
+
else:
|
|
272
366
|
log_exception_conditionally(log, msg)
|
|
273
|
-
raise GerritRestError(msg) from exc
|
|
274
367
|
|
|
275
368
|
def __repr__(self) -> str: # pragma: no cover - convenience
|
|
276
369
|
masked = ""
|
|
@@ -385,4 +478,5 @@ __all__ = [
|
|
|
385
478
|
"GerritRestClient",
|
|
386
479
|
"GerritRestError",
|
|
387
480
|
"build_client_for_host",
|
|
481
|
+
"warn_gerrit_credentials_unavailable",
|
|
388
482
|
]
|
{github2gerrit-1.2.3 → github2gerrit-1.2.4}/src/github2gerrit/orchestrator/reconciliation.py
RENAMED
|
@@ -511,13 +511,25 @@ def _abandon_orphan_changes(
|
|
|
511
511
|
|
|
512
512
|
from github2gerrit.gerrit_rest import GerritRestError
|
|
513
513
|
from github2gerrit.gerrit_rest import build_client_for_host
|
|
514
|
+
from github2gerrit.gerrit_rest import warn_gerrit_credentials_unavailable
|
|
514
515
|
|
|
515
|
-
abandoned = []
|
|
516
|
+
abandoned: list[str] = []
|
|
516
517
|
try:
|
|
517
518
|
client = build_client_for_host(
|
|
518
519
|
gerrit.host, timeout=10.0, max_attempts=3
|
|
519
520
|
)
|
|
520
521
|
|
|
522
|
+
# Abandoning a change is a mutating REST call that requires
|
|
523
|
+
# authentication; without credentials every attempt would 403.
|
|
524
|
+
# Warn once and skip rather than emitting per-change errors.
|
|
525
|
+
if not client.is_authenticated:
|
|
526
|
+
warn_gerrit_credentials_unavailable()
|
|
527
|
+
log.debug(
|
|
528
|
+
"Skipping orphan-change abandon: "
|
|
529
|
+
"no Gerrit REST credentials available"
|
|
530
|
+
)
|
|
531
|
+
return abandoned
|
|
532
|
+
|
|
521
533
|
for change_id in orphan_ids:
|
|
522
534
|
try:
|
|
523
535
|
abandon_message = (
|
|
@@ -559,13 +571,25 @@ def _comment_orphan_changes(
|
|
|
559
571
|
|
|
560
572
|
from github2gerrit.gerrit_rest import GerritRestError
|
|
561
573
|
from github2gerrit.gerrit_rest import build_client_for_host
|
|
574
|
+
from github2gerrit.gerrit_rest import warn_gerrit_credentials_unavailable
|
|
562
575
|
|
|
563
|
-
commented = []
|
|
576
|
+
commented: list[str] = []
|
|
564
577
|
try:
|
|
565
578
|
client = build_client_for_host(
|
|
566
579
|
gerrit.host, timeout=10.0, max_attempts=3
|
|
567
580
|
)
|
|
568
581
|
|
|
582
|
+
# Posting a review comment is a mutating REST call that requires
|
|
583
|
+
# authentication; without credentials every attempt would 403.
|
|
584
|
+
# Warn once and skip rather than emitting per-change errors.
|
|
585
|
+
if not client.is_authenticated:
|
|
586
|
+
warn_gerrit_credentials_unavailable()
|
|
587
|
+
log.debug(
|
|
588
|
+
"Skipping orphan-change comment: "
|
|
589
|
+
"no Gerrit REST credentials available"
|
|
590
|
+
)
|
|
591
|
+
return commented
|
|
592
|
+
|
|
569
593
|
for change_id in orphan_ids:
|
|
570
594
|
try:
|
|
571
595
|
comment_message = (
|
|
@@ -42,6 +42,13 @@ _DANGEROUS_HTML_PATTERN = re.compile(
|
|
|
42
42
|
)
|
|
43
43
|
_MULTIPLE_NEWLINES_PATTERN = re.compile(r"\n{3,}")
|
|
44
44
|
_EMOJI_PATTERN = re.compile(r":[a-z_]+:") # GitHub emoji codes like :sparkles:
|
|
45
|
+
# Dependabot embeds compatibility-score badges proxied through GitHub's
|
|
46
|
+
# camo image host. Detected via a regex rather than a substring membership
|
|
47
|
+
# check (the latter trips CodeQL's incomplete-url-substring-sanitization
|
|
48
|
+
# heuristic and is a weaker way to match a URL).
|
|
49
|
+
_CAMO_IMAGE_URL_PATTERN = re.compile(
|
|
50
|
+
r"https://camo\.githubusercontent\.com/", re.IGNORECASE
|
|
51
|
+
)
|
|
45
52
|
|
|
46
53
|
|
|
47
54
|
@dataclass
|
|
@@ -112,7 +119,7 @@ class DependabotRule(FilterRule):
|
|
|
112
119
|
"Bumps " in title and " from " in title and " to " in title,
|
|
113
120
|
"Dependabot will resolve any conflicts" in body,
|
|
114
121
|
"<details>" in body and "<summary>" in body,
|
|
115
|
-
|
|
122
|
+
bool(_CAMO_IMAGE_URL_PATTERN.search(body)),
|
|
116
123
|
]
|
|
117
124
|
|
|
118
125
|
# Require multiple indicators for confidence
|