github2gerrit 1.1.0__tar.gz → 1.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/.pre-commit-config.yaml +17 -12
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/PKG-INFO +1 -1
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/action.yaml +40 -3
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/pyproject.toml +1 -13
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/cli.py +33 -12
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/commit_normalization.py +3 -2
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/core.py +243 -26
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/gerrit_pr_closer.py +231 -28
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/gerrit_query.py +67 -2
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/netrc.py +4 -6
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/pr_content_filter.py +1 -1
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/similarity.py +21 -6
- github2gerrit-1.2.1/tests/test_clean_squash_title.py +298 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_cli.py +49 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_commit_normalization.py +1 -1
- github2gerrit-1.2.1/tests/test_dependency_supersession.py +776 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/uv.lock +204 -183
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/.editorconfig +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/.gitignore +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/.gitlint +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/.markdownlint.yaml +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/.readthedocs.yml +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/.yamllint +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/LICENSE +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/LICENSES/Apache-2.0.txt +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/README.md +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/REUSE.toml +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/docs/COMMIT_RULES.md +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/docs/COMPOSITE_ACTION_TESTING.md +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/docs/PR_UPDATE_IMPLEMENTATION.md +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/docs/RELEASE-v0.2.0.md +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/docs/github2gerrit_token_permissions_classic.png +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/sitecustomize.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/__init__.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/commit_rules.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/config.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/constants.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/duplicate_detection.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/error_codes.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/external_api.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/gerrit_rest.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/gerrit_urls.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/github_api.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/gitreview.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/gitutils.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/mapping_comment.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/models.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/orchestrator/__init__.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/orchestrator/reconciliation.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/pr_commands.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/reconcile_matcher.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/rich_display.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/rich_logging.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/ssh_agent_setup.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/ssh_common.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/ssh_config_parser.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/ssh_discovery.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/trailers.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/src/github2gerrit/utils.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/conftest.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/fixtures/__init__.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/fixtures/make_repo.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/fixtures/ssh_config_samples.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_action_environment_mapping.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_action_outputs.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_action_pr_number_handling.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_action_step_validation.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_automation_only.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_change_id_deduplication.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_cli_helpers.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_cli_netrc_options.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_cli_outputs_file.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_cli_url_and_dryrun.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_commit_rules.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_composite_action_coverage.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_config_and_reviewers.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_config_helpers.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_close_pr_policy.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_config_and_errors.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_gerrit_backref_comment.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_gerrit_push_errors.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_gerrit_rest_results.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_integration_fixture_repo.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_prepare_commits.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_shallow_clone.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_ssh_setup.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_core_ssrf_protection.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_dns_validation_and_no_gerrit.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_duplicate_detection.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_email_case_normalization.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_error_codes.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_external_api_framework.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_force_flag_cli.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_gerrit_change_id_footer.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_gerrit_change_status_checks.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_gerrit_pr_closer.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_gerrit_rest_client.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_gerrit_urls.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_gerrit_urls_more.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_ghe_and_gitreview_args.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_github_api_error_handling.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_github_api_helpers.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_github_api_retry_and_helpers.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_gitreview.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_gitutils_helpers.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_issue_157_regressions.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_mapping_comment_additional.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_mapping_comment_digest_and_backref.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_metadata_and_reconciliation.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_metadata_trailer_separation_bug.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_misc_small_coverage.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_netrc.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_orphan_rest_side_effects.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_pr_commands.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_pr_content_filter.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_pr_content_filter_integration.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_pr_update_detection.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_reconciliation_extracted_module.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_reconciliation_plan_and_orphans.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_reconciliation_scenarios.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_ssh_agent.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_ssh_agent_ownership.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_ssh_artifact_prevention.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_ssh_common.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_ssh_discovery.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_ssh_discovery_dry_run.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_trailers_additional.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_url_parser.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/test_utils.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/unit/test_config_integration.py +0 -0
- {github2gerrit-1.1.0 → github2gerrit-1.2.1}/tests/unit/test_ssh_config_parser.py +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# SPDX-FileCopyrightText: 2025 The Linux Foundation
|
|
4
4
|
|
|
5
5
|
ci:
|
|
6
|
-
skip: [pytest]
|
|
6
|
+
skip: [pytest, gha-workflow-linter]
|
|
7
7
|
autofix_commit_msg: |
|
|
8
8
|
Chore: pre-commit autofixes
|
|
9
9
|
|
|
@@ -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: b831c3dc5d27d9da294ae4e915773b99aa24a7c5 # frozen: v0.15.10
|
|
63
63
|
hooks:
|
|
64
64
|
- id: ruff
|
|
65
65
|
files: ^(src|scripts|tests)/.+\.py$
|
|
@@ -68,7 +68,7 @@ repos:
|
|
|
68
68
|
files: ^(src|scripts|tests)/.+\.py$
|
|
69
69
|
|
|
70
70
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
71
|
-
rev:
|
|
71
|
+
rev: 0f369d245750787ce34997d464ed9605391a5283 # frozen: v1.20.1
|
|
72
72
|
hooks:
|
|
73
73
|
- id: mypy
|
|
74
74
|
files: ^src/.+\.py$
|
|
@@ -76,13 +76,6 @@ repos:
|
|
|
76
76
|
- types-PyYAML
|
|
77
77
|
- types-requests
|
|
78
78
|
|
|
79
|
-
# Doesn't seem to run reliably inside pre-commit.ci
|
|
80
|
-
# - repo: https://github.com/RobertCraigie/pyright-python
|
|
81
|
-
# rev: d393df1703a808473b84bd14a2702f4793014031 # frozen: v1.1.404
|
|
82
|
-
# hooks:
|
|
83
|
-
# - id: pyright
|
|
84
|
-
# files: ^(src|scripts|tests)/.+\.py$
|
|
85
|
-
|
|
86
79
|
- repo: https://github.com/btford/write-good
|
|
87
80
|
rev: ab66ce10136dfad5146e69e70f82a3efac8842c1 # frozen: v1.0.8
|
|
88
81
|
hooks:
|
|
@@ -110,7 +103,7 @@ repos:
|
|
|
110
103
|
# Replaces: https://github.com/rhysd/actionlint
|
|
111
104
|
# Permits actionlint to run both locally and with precommit.ci/GitHub
|
|
112
105
|
- repo: https://github.com/Mateusz-Grzelinski/actionlint-py
|
|
113
|
-
rev:
|
|
106
|
+
rev: c04ed26e40637cab1aa9879c693832a9c120fb20 # frozen: v1.7.12.24
|
|
114
107
|
hooks:
|
|
115
108
|
- id: actionlint
|
|
116
109
|
|
|
@@ -120,8 +113,20 @@ repos:
|
|
|
120
113
|
hooks:
|
|
121
114
|
- id: codespell
|
|
122
115
|
|
|
116
|
+
# Requires a mirror, primary repo lacks .pre-commit-hooks.yaml
|
|
117
|
+
- repo: https://github.com/DetachHead/basedpyright-prek-mirror
|
|
118
|
+
rev: 7664ed7e31234c8369d85ee9a13a1ca3361c0aa1 # frozen: 1.39.0
|
|
119
|
+
hooks:
|
|
120
|
+
- id: basedpyright
|
|
121
|
+
files: ^src/.+\.py$
|
|
122
|
+
|
|
123
|
+
- repo: https://github.com/lfreleng-actions/gha-workflow-linter
|
|
124
|
+
rev: a7caf8f3a1a05688d1cee46615ff94def617e5a3 # frozen: v1.0.2
|
|
125
|
+
hooks:
|
|
126
|
+
- id: gha-workflow-linter
|
|
127
|
+
|
|
123
128
|
- repo: https://github.com/python-jsonschema/check-jsonschema
|
|
124
|
-
rev:
|
|
129
|
+
rev: ed81924a8b1cecdaa570b072528fa80c9c4d6ccd # frozen: 0.37.1
|
|
125
130
|
hooks:
|
|
126
131
|
- id: check-github-actions
|
|
127
132
|
- id: check-github-workflows
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github2gerrit
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Submit a GitHub pull request to a Gerrit repository.
|
|
5
5
|
Project-URL: Homepage, https://github.com/lfreleng-actions/github2gerrit-action
|
|
6
6
|
Project-URL: Repository, https://github.com/lfreleng-actions/github2gerrit-action
|
|
@@ -157,6 +157,10 @@ inputs:
|
|
|
157
157
|
description: "Create a Gerrit change when an UPDATE operation cannot find an existing one"
|
|
158
158
|
required: false
|
|
159
159
|
default: "false"
|
|
160
|
+
G2G_DISABLED:
|
|
161
|
+
description: "Set to 'true' to disable the action entirely (exits successfully with a message)"
|
|
162
|
+
required: false
|
|
163
|
+
default: ""
|
|
160
164
|
|
|
161
165
|
outputs:
|
|
162
166
|
gerrit_change_request_url:
|
|
@@ -172,7 +176,34 @@ outputs:
|
|
|
172
176
|
runs:
|
|
173
177
|
using: "composite"
|
|
174
178
|
steps:
|
|
179
|
+
- name: "Check if GitHub2Gerrit is disabled"
|
|
180
|
+
id: disabled-check
|
|
181
|
+
shell: bash
|
|
182
|
+
run: |
|
|
183
|
+
# Check if GitHub2Gerrit is disabled
|
|
184
|
+
set -euo pipefail
|
|
185
|
+
DISABLED_ENV="${G2G_DISABLED:-}"
|
|
186
|
+
DISABLED_INPUT="${{ inputs.G2G_DISABLED }}"
|
|
187
|
+
# Normalize: accept the same truthy set as env_bool()
|
|
188
|
+
# (1/true/yes/on, case-insensitive, trimmed)
|
|
189
|
+
_normalize() {
|
|
190
|
+
printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | xargs
|
|
191
|
+
}
|
|
192
|
+
_is_truthy() {
|
|
193
|
+
case "$(_normalize "$1")" in
|
|
194
|
+
1|true|yes|on) return 0 ;;
|
|
195
|
+
*) return 1 ;;
|
|
196
|
+
esac
|
|
197
|
+
}
|
|
198
|
+
if _is_truthy "${DISABLED_ENV}" || _is_truthy "${DISABLED_INPUT}"; then
|
|
199
|
+
echo "🛑 GitHub2Gerrit is disabled by check of G2G_DISABLED variable or input"
|
|
200
|
+
echo "disabled=true" >> "$GITHUB_OUTPUT"
|
|
201
|
+
else
|
|
202
|
+
echo "disabled=false" >> "$GITHUB_OUTPUT"
|
|
203
|
+
fi
|
|
204
|
+
|
|
175
205
|
- name: "Checkout repository"
|
|
206
|
+
if: steps.disabled-check.outputs.disabled != 'true'
|
|
176
207
|
# yamllint disable-line rule:line-length
|
|
177
208
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
178
209
|
with:
|
|
@@ -181,18 +212,21 @@ runs:
|
|
|
181
212
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
182
213
|
|
|
183
214
|
- name: "Setup Python"
|
|
215
|
+
if: steps.disabled-check.outputs.disabled != 'true'
|
|
184
216
|
# yamllint disable-line rule:line-length
|
|
185
217
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
186
218
|
with:
|
|
187
219
|
python-version-file: '${{ github.action_path }}/pyproject.toml'
|
|
188
220
|
|
|
189
221
|
- name: "Setup uv"
|
|
222
|
+
if: steps.disabled-check.outputs.disabled != 'true'
|
|
190
223
|
# yamllint disable-line rule:line-length
|
|
191
|
-
uses: astral-sh/setup-uv@
|
|
224
|
+
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
|
192
225
|
with:
|
|
193
226
|
enable-cache: false
|
|
194
227
|
|
|
195
228
|
- name: "Setup github2gerrit"
|
|
229
|
+
if: steps.disabled-check.outputs.disabled != 'true'
|
|
196
230
|
shell: bash
|
|
197
231
|
env:
|
|
198
232
|
# Provide version for hatch-vcs (via setuptools-scm)
|
|
@@ -214,7 +248,7 @@ runs:
|
|
|
214
248
|
|
|
215
249
|
- name: "Validate PR_NUMBER usage"
|
|
216
250
|
# yamllint disable-line rule:line-length
|
|
217
|
-
if: ${{ github.event_name != 'workflow_dispatch' && inputs.PR_NUMBER != '' && inputs.PR_NUMBER != '0' }}
|
|
251
|
+
if: ${{ steps.disabled-check.outputs.disabled != 'true' && github.event_name != 'workflow_dispatch' && inputs.PR_NUMBER != '' && inputs.PR_NUMBER != '0' }}
|
|
218
252
|
shell: bash
|
|
219
253
|
run: |
|
|
220
254
|
# Validate PR_NUMBER usage
|
|
@@ -223,7 +257,7 @@ runs:
|
|
|
223
257
|
exit 2
|
|
224
258
|
|
|
225
259
|
- name: "Normalize PR_NUMBER"
|
|
226
|
-
if: ${{ github.event_name == 'workflow_dispatch' }}
|
|
260
|
+
if: ${{ steps.disabled-check.outputs.disabled != 'true' && github.event_name == 'workflow_dispatch' }}
|
|
227
261
|
shell: bash
|
|
228
262
|
run: |
|
|
229
263
|
# Normalize PR_NUMBER
|
|
@@ -243,6 +277,7 @@ runs:
|
|
|
243
277
|
fi
|
|
244
278
|
|
|
245
279
|
- name: "Extract PR number, validate context"
|
|
280
|
+
if: steps.disabled-check.outputs.disabled != 'true'
|
|
246
281
|
shell: bash
|
|
247
282
|
run: |
|
|
248
283
|
# Extract PR number, validate context
|
|
@@ -284,6 +319,7 @@ runs:
|
|
|
284
319
|
fi
|
|
285
320
|
|
|
286
321
|
- name: "Run github2gerrit Python CLI"
|
|
322
|
+
if: steps.disabled-check.outputs.disabled != 'true'
|
|
287
323
|
id: run-cli
|
|
288
324
|
shell: bash
|
|
289
325
|
env:
|
|
@@ -357,6 +393,7 @@ runs:
|
|
|
357
393
|
fi
|
|
358
394
|
|
|
359
395
|
- name: "Capture outputs"
|
|
396
|
+
if: steps.disabled-check.outputs.disabled != 'true'
|
|
360
397
|
id: capture-outputs
|
|
361
398
|
shell: bash
|
|
362
399
|
# yamllint disable rule:line-length
|
|
@@ -235,19 +235,7 @@ markers = [
|
|
|
235
235
|
|
|
236
236
|
[tool.pyright]
|
|
237
237
|
pythonVersion = "3.11"
|
|
238
|
-
include = ["src"
|
|
239
|
-
exclude = [
|
|
240
|
-
"build/",
|
|
241
|
-
"dist/",
|
|
242
|
-
"docs/",
|
|
243
|
-
"scripts/",
|
|
244
|
-
"tests/fixtures/",
|
|
245
|
-
".venv/",
|
|
246
|
-
".mypy_cache/",
|
|
247
|
-
".pytest_cache/",
|
|
248
|
-
".ruff_cache/",
|
|
249
|
-
"coverage_html_report/",
|
|
250
|
-
]
|
|
238
|
+
include = ["src"]
|
|
251
239
|
typeCheckingMode = "strict"
|
|
252
240
|
reportMissingImports = "none"
|
|
253
241
|
reportMissingTypeStubs = "none"
|
|
@@ -845,6 +845,14 @@ def main(
|
|
|
845
845
|
typer.echo("Version information not available")
|
|
846
846
|
sys.exit(int(ExitCode.SUCCESS))
|
|
847
847
|
|
|
848
|
+
# Check if GitHub2Gerrit is disabled via environment variable
|
|
849
|
+
if env_bool("G2G_DISABLED"):
|
|
850
|
+
typer.echo(
|
|
851
|
+
"\U0001f6d1 GitHub2Gerrit is disabled by check of "
|
|
852
|
+
"G2G_DISABLED variable"
|
|
853
|
+
)
|
|
854
|
+
sys.exit(int(ExitCode.SUCCESS))
|
|
855
|
+
|
|
848
856
|
# Override boolean parameters with properly parsed environment variables.
|
|
849
857
|
# This ensures that string "false" from GitHub Actions is handled
|
|
850
858
|
# correctly (Typer/Click treats any non-empty string as truthy).
|
|
@@ -2218,7 +2226,7 @@ def _process() -> None:
|
|
|
2218
2226
|
):
|
|
2219
2227
|
try:
|
|
2220
2228
|
log.debug(
|
|
2221
|
-
"
|
|
2229
|
+
"Checking for Gerrit change to abandon for PR #%s",
|
|
2222
2230
|
gh.pr_number,
|
|
2223
2231
|
)
|
|
2224
2232
|
change_number = abandon_gerrit_change_for_closed_pr(
|
|
@@ -2230,16 +2238,29 @@ def _process() -> None:
|
|
|
2230
2238
|
progress_tracker=None,
|
|
2231
2239
|
)
|
|
2232
2240
|
if change_number:
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2241
|
+
try:
|
|
2242
|
+
from .gerrit_urls import create_gerrit_url_builder
|
|
2243
|
+
|
|
2244
|
+
_url_builder = create_gerrit_url_builder(
|
|
2245
|
+
data.gerrit_server
|
|
2246
|
+
)
|
|
2247
|
+
gerrit_change_url = _url_builder.change_url(
|
|
2248
|
+
data.gerrit_project,
|
|
2249
|
+
int(change_number),
|
|
2250
|
+
)
|
|
2251
|
+
log.debug(
|
|
2252
|
+
"Successfully abandoned Gerrit "
|
|
2253
|
+
"change %s for pull request #%s",
|
|
2254
|
+
gerrit_change_url,
|
|
2255
|
+
gh.pr_number,
|
|
2256
|
+
)
|
|
2257
|
+
except Exception:
|
|
2258
|
+
log.debug(
|
|
2259
|
+
"Successfully abandoned Gerrit "
|
|
2260
|
+
"change %s for pull request #%s",
|
|
2261
|
+
change_number,
|
|
2262
|
+
gh.pr_number,
|
|
2263
|
+
)
|
|
2243
2264
|
# Console output already done by
|
|
2244
2265
|
# abandon_gerrit_change_for_closed_pr
|
|
2245
2266
|
else:
|
|
@@ -2287,7 +2308,7 @@ def _process() -> None:
|
|
|
2287
2308
|
log.warning("Gerrit cleanup failed: %s", exc)
|
|
2288
2309
|
|
|
2289
2310
|
log.debug(
|
|
2290
|
-
"
|
|
2311
|
+
"Cleanup operations completed for closed PR #%s",
|
|
2291
2312
|
gh.pr_number or "unknown",
|
|
2292
2313
|
)
|
|
2293
2314
|
return
|
|
@@ -401,8 +401,9 @@ class CommitNormalizer:
|
|
|
401
401
|
# Remove trailing ellipsis
|
|
402
402
|
title = re.sub(r"\s*[.]{3,}.*$", "", title)
|
|
403
403
|
|
|
404
|
-
# Remove markdown formatting
|
|
405
|
-
|
|
404
|
+
# Remove markdown bold/code formatting but preserve underscores
|
|
405
|
+
# (which appear in package names and filesystem paths).
|
|
406
|
+
title = re.sub(r"[*`]", "", title)
|
|
406
407
|
|
|
407
408
|
# For dependabot titles, extract the essential information
|
|
408
409
|
for pattern in DEPENDABOT_PATTERNS:
|
|
@@ -154,6 +154,93 @@ def _clean_ellipses_from_message(message: str) -> str:
|
|
|
154
154
|
return "\n".join(cleaned_lines)
|
|
155
155
|
|
|
156
156
|
|
|
157
|
+
def _clean_squash_title_line(title_line: str | None) -> str:
|
|
158
|
+
"""Clean and truncate a squashed commit title line.
|
|
159
|
+
|
|
160
|
+
Handles markdown removal, separator splitting, and length
|
|
161
|
+
truncation while preserving conventional commit prefixes
|
|
162
|
+
and underscores in package/path names.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
title_line: Raw title line from git log output.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Cleaned title line, safe for use as a commit subject.
|
|
169
|
+
"""
|
|
170
|
+
from .similarity import CC_PREFIX_RE
|
|
171
|
+
|
|
172
|
+
if not title_line:
|
|
173
|
+
return ""
|
|
174
|
+
|
|
175
|
+
# Remove markdown links
|
|
176
|
+
title_line = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", title_line)
|
|
177
|
+
# Remove trailing ellipsis/truncation
|
|
178
|
+
title_line = re.sub(r"\s*[.]{3,}.*$", "", title_line)
|
|
179
|
+
# Split on common separators to avoid leaking body content
|
|
180
|
+
for separator in [". Bumps ", " Bumps ", ". - ", " - "]:
|
|
181
|
+
if separator in title_line:
|
|
182
|
+
title_line = title_line.split(separator)[0].strip()
|
|
183
|
+
break
|
|
184
|
+
# Remove markdown bold/code formatting but preserve underscores
|
|
185
|
+
# (which appear in package names and filesystem paths).
|
|
186
|
+
title_line = re.sub(r"[*`]", "", title_line).strip()
|
|
187
|
+
|
|
188
|
+
if len(title_line) > 100:
|
|
189
|
+
# Detect conventional commit prefix length so that the
|
|
190
|
+
# ": " break-point does not split on the prefix separator
|
|
191
|
+
# (e.g. "Build(deps): " should not be treated as a sentence
|
|
192
|
+
# break).
|
|
193
|
+
cc_match = CC_PREFIX_RE.match(title_line)
|
|
194
|
+
cc_prefix_len = cc_match.end() if cc_match else 0
|
|
195
|
+
|
|
196
|
+
break_points = [". ", "! ", "? ", " - ", ": "]
|
|
197
|
+
max_bp_len = max(len(bp) for bp in break_points)
|
|
198
|
+
truncated = False
|
|
199
|
+
for bp in break_points:
|
|
200
|
+
# For the ": " break-point, start searching after the
|
|
201
|
+
# conventional commit prefix to avoid splitting there.
|
|
202
|
+
search_start = cc_prefix_len if bp == ": " else 0
|
|
203
|
+
# Extend the slice by (max_bp_len - 1) so that a
|
|
204
|
+
# break-point starting just before position 100 is
|
|
205
|
+
# still detected even if it spans across the boundary.
|
|
206
|
+
candidate_end = min(len(title_line), 100 + max_bp_len - 1)
|
|
207
|
+
candidate = title_line[search_start:candidate_end]
|
|
208
|
+
bp_offset = candidate.find(bp)
|
|
209
|
+
if bp_offset != -1:
|
|
210
|
+
bp_idx = search_start + bp_offset
|
|
211
|
+
# Only use this break-point if it starts within
|
|
212
|
+
# the 100-char limit.
|
|
213
|
+
if bp_idx >= 100:
|
|
214
|
+
continue
|
|
215
|
+
# Punctuation break-points (". ", ": ") — include
|
|
216
|
+
# the punctuation mark. Separator break-points
|
|
217
|
+
# (" - ") — truncate before the separator.
|
|
218
|
+
if bp[0].isspace():
|
|
219
|
+
title_line = title_line[:bp_idx].rstrip()
|
|
220
|
+
else:
|
|
221
|
+
title_line = title_line[
|
|
222
|
+
: bp_idx + len(bp.rstrip())
|
|
223
|
+
].rstrip()
|
|
224
|
+
truncated = True
|
|
225
|
+
break
|
|
226
|
+
|
|
227
|
+
if not truncated and cc_prefix_len == 0:
|
|
228
|
+
# Non-CC title with no break-point found: fall back
|
|
229
|
+
# to word-boundary truncation at 100 characters.
|
|
230
|
+
words = title_line[:100].split()
|
|
231
|
+
title_line = (
|
|
232
|
+
" ".join(words[:-1])
|
|
233
|
+
if len(words) > 1
|
|
234
|
+
else title_line[:100].rstrip()
|
|
235
|
+
)
|
|
236
|
+
# For CC titles with no break-point: pass through the
|
|
237
|
+
# full title. The length is inherent to the structured
|
|
238
|
+
# subject (e.g. long dependency paths), not body-content
|
|
239
|
+
# leakage.
|
|
240
|
+
|
|
241
|
+
return title_line
|
|
242
|
+
|
|
243
|
+
|
|
157
244
|
# ---------------------
|
|
158
245
|
# Utility functions
|
|
159
246
|
# ---------------------
|
|
@@ -678,6 +765,9 @@ class Orchestrator:
|
|
|
678
765
|
2. GitHub-Hash trailer matching
|
|
679
766
|
3. GitHub-PR trailer URL matching
|
|
680
767
|
4. Mapping comment parsing from PR comments
|
|
768
|
+
5. Dependency package match — find an open change that
|
|
769
|
+
bumps the same dependency (for Dependabot / Renovate
|
|
770
|
+
supersession).
|
|
681
771
|
|
|
682
772
|
Args:
|
|
683
773
|
gh: GitHub context containing PR information
|
|
@@ -793,6 +883,10 @@ class Orchestrator:
|
|
|
793
883
|
except Exception as exc:
|
|
794
884
|
log.debug("GitHub-Hash trailer query failed: %s", exc)
|
|
795
885
|
|
|
886
|
+
# Cache the PR title for reuse across strategies 4 and 5
|
|
887
|
+
# so we don't duplicate GitHub API requests.
|
|
888
|
+
cached_pr_title: str = ""
|
|
889
|
+
|
|
796
890
|
# Strategy 4: Parse mapping comments from PR
|
|
797
891
|
try:
|
|
798
892
|
from .mapping_comment import parse_mapping_comments
|
|
@@ -800,6 +894,7 @@ class Orchestrator:
|
|
|
800
894
|
client_gh = build_client()
|
|
801
895
|
repo = get_repo_from_env(client_gh)
|
|
802
896
|
pr_obj = get_pull(repo, int(gh.pr_number))
|
|
897
|
+
cached_pr_title = getattr(pr_obj, "title", "") or ""
|
|
803
898
|
|
|
804
899
|
issue = pr_obj.as_issue()
|
|
805
900
|
comments = list(issue.get_comments())
|
|
@@ -830,6 +925,110 @@ class Orchestrator:
|
|
|
830
925
|
except Exception as exc:
|
|
831
926
|
log.debug("Mapping comment parsing failed: %s", exc)
|
|
832
927
|
|
|
928
|
+
# Strategy 5: Dependency package match (supersession)
|
|
929
|
+
# When a new Dependabot/Renovate PR bumps the same dependency
|
|
930
|
+
# as an existing open Gerrit change, reuse that Change-Id so
|
|
931
|
+
# the push creates a new patchset instead of a duplicate change.
|
|
932
|
+
try:
|
|
933
|
+
from .gerrit_query import GerritChange
|
|
934
|
+
from .gerrit_query import query_open_changes_by_project
|
|
935
|
+
from .gerrit_rest import build_client_for_host
|
|
936
|
+
from .similarity import extract_dependency_package_from_subject
|
|
937
|
+
from .trailers import GITHUB_PR_TRAILER
|
|
938
|
+
from .trailers import parse_trailers
|
|
939
|
+
|
|
940
|
+
# Reuse PR title cached by Strategy 4 to avoid a
|
|
941
|
+
# duplicate GitHub API request.
|
|
942
|
+
pr_title = cached_pr_title
|
|
943
|
+
if not pr_title:
|
|
944
|
+
log.debug(
|
|
945
|
+
"Strategy 5: PR title cache miss, fetching from GitHub API",
|
|
946
|
+
)
|
|
947
|
+
try:
|
|
948
|
+
gh_client = build_client()
|
|
949
|
+
gh_repo = get_repo_from_env(gh_client)
|
|
950
|
+
pr_obj = get_pull(gh_repo, int(gh.pr_number))
|
|
951
|
+
pr_title = getattr(pr_obj, "title", "") or ""
|
|
952
|
+
except Exception:
|
|
953
|
+
pr_title = ""
|
|
954
|
+
|
|
955
|
+
current_pkg = extract_dependency_package_from_subject(pr_title)
|
|
956
|
+
if current_pkg:
|
|
957
|
+
log.debug(
|
|
958
|
+
"Strategy 5: searching for open changes that "
|
|
959
|
+
"bump dependency '%s'",
|
|
960
|
+
current_pkg,
|
|
961
|
+
)
|
|
962
|
+
dep_client = build_client_for_host(gerrit.host)
|
|
963
|
+
open_changes = query_open_changes_by_project(
|
|
964
|
+
dep_client,
|
|
965
|
+
gerrit.project,
|
|
966
|
+
branch=gh.base_ref,
|
|
967
|
+
max_results=200,
|
|
968
|
+
)
|
|
969
|
+
|
|
970
|
+
# Collect all matching changes, then select the
|
|
971
|
+
# oldest one (lowest change number) to avoid
|
|
972
|
+
# "downgrading" a newer change by uploading an
|
|
973
|
+
# older patchset to it.
|
|
974
|
+
candidates: list[tuple[int, GerritChange]] = []
|
|
975
|
+
for change in open_changes:
|
|
976
|
+
candidate_pkg = extract_dependency_package_from_subject(
|
|
977
|
+
change.subject
|
|
978
|
+
)
|
|
979
|
+
if candidate_pkg and candidate_pkg == current_pkg:
|
|
980
|
+
# Verify this is a GitHub2Gerrit change
|
|
981
|
+
commit_msg = change.commit_message or ""
|
|
982
|
+
trailers = parse_trailers(commit_msg)
|
|
983
|
+
if GITHUB_PR_TRAILER not in trailers:
|
|
984
|
+
log.debug(
|
|
985
|
+
"Strategy 5: skipping change %s "
|
|
986
|
+
"(no GitHub2Gerrit metadata)",
|
|
987
|
+
change.number,
|
|
988
|
+
)
|
|
989
|
+
continue
|
|
990
|
+
try:
|
|
991
|
+
change_num = int(change.number)
|
|
992
|
+
except (TypeError, ValueError):
|
|
993
|
+
log.debug(
|
|
994
|
+
"Strategy 5: skipping change with "
|
|
995
|
+
"invalid number %r for subject %r",
|
|
996
|
+
change.number,
|
|
997
|
+
change.subject,
|
|
998
|
+
)
|
|
999
|
+
continue
|
|
1000
|
+
candidates.append((change_num, change))
|
|
1001
|
+
|
|
1002
|
+
if candidates:
|
|
1003
|
+
# Prefer the oldest open change so the newest
|
|
1004
|
+
# PR always updates the original change and
|
|
1005
|
+
# the post-push sweep abandons the rest.
|
|
1006
|
+
candidates.sort(key=lambda t: t[0])
|
|
1007
|
+
_, oldest = candidates[0]
|
|
1008
|
+
change_ids = [oldest.change_id]
|
|
1009
|
+
log.info(
|
|
1010
|
+
"Found superseding target by dependency "
|
|
1011
|
+
"package '%s': change %s (%s) "
|
|
1012
|
+
"(oldest of %d candidate(s))",
|
|
1013
|
+
current_pkg,
|
|
1014
|
+
oldest.number,
|
|
1015
|
+
oldest.subject,
|
|
1016
|
+
len(candidates),
|
|
1017
|
+
)
|
|
1018
|
+
return change_ids
|
|
1019
|
+
|
|
1020
|
+
log.debug(
|
|
1021
|
+
"No open changes found for dependency '%s'",
|
|
1022
|
+
current_pkg,
|
|
1023
|
+
)
|
|
1024
|
+
else:
|
|
1025
|
+
log.debug(
|
|
1026
|
+
"Strategy 5 skipped: could not extract dependency "
|
|
1027
|
+
"package from PR title"
|
|
1028
|
+
)
|
|
1029
|
+
except Exception as exc:
|
|
1030
|
+
log.debug("Dependency package strategy failed: %s", exc)
|
|
1031
|
+
|
|
833
1032
|
log.warning(
|
|
834
1033
|
"⚠️ No existing Gerrit changes found for PR #%s",
|
|
835
1034
|
gh.pr_number,
|
|
@@ -1936,6 +2135,49 @@ class Orchestrator:
|
|
|
1936
2135
|
# Validate that no unexpected files were committed
|
|
1937
2136
|
self._validate_committed_files(gh, result)
|
|
1938
2137
|
|
|
2138
|
+
# Post-push supersession sweep (Option A fallback).
|
|
2139
|
+
# After a successful push, check whether other open Gerrit
|
|
2140
|
+
# changes in the same project bump the same dependency
|
|
2141
|
+
# package. If Strategy 5 already reused the old Change-Id
|
|
2142
|
+
# (update-in-place), no duplicates should exist. If that
|
|
2143
|
+
# path was skipped (e.g. non-dependency PR, or the query
|
|
2144
|
+
# failed), this sweep catches and abandons stale changes.
|
|
2145
|
+
if not inputs.dry_run and gerrit and prep.change_ids:
|
|
2146
|
+
try:
|
|
2147
|
+
from .gerrit_pr_closer import (
|
|
2148
|
+
abandon_superseded_dependency_changes,
|
|
2149
|
+
)
|
|
2150
|
+
|
|
2151
|
+
# Derive the subject from the pushed commit,
|
|
2152
|
+
# regardless of whether change URL lookup
|
|
2153
|
+
# succeeded.
|
|
2154
|
+
push_subject = ""
|
|
2155
|
+
try:
|
|
2156
|
+
push_subject = run_cmd(
|
|
2157
|
+
[
|
|
2158
|
+
"git",
|
|
2159
|
+
"show",
|
|
2160
|
+
"-s",
|
|
2161
|
+
"--pretty=format:%s",
|
|
2162
|
+
"HEAD",
|
|
2163
|
+
],
|
|
2164
|
+
cwd=self.workspace,
|
|
2165
|
+
).stdout.strip()
|
|
2166
|
+
except Exception:
|
|
2167
|
+
push_subject = ""
|
|
2168
|
+
|
|
2169
|
+
if push_subject:
|
|
2170
|
+
abandon_superseded_dependency_changes(
|
|
2171
|
+
gerrit_server=gerrit.host,
|
|
2172
|
+
gerrit_project=gerrit.project,
|
|
2173
|
+
current_subject=push_subject,
|
|
2174
|
+
exclude_change_ids=prep.change_ids,
|
|
2175
|
+
dry_run=False,
|
|
2176
|
+
target_branch=self._resolve_target_branch(),
|
|
2177
|
+
)
|
|
2178
|
+
except Exception as exc:
|
|
2179
|
+
log.debug("Post-push supersession sweep skipped: %s", exc)
|
|
2180
|
+
|
|
1939
2181
|
self._close_pull_request_if_required(gh)
|
|
1940
2182
|
|
|
1941
2183
|
log.debug("Pipeline complete: %s", result)
|
|
@@ -3564,32 +3806,7 @@ class Orchestrator:
|
|
|
3564
3806
|
return message_lines, signed_off, change_ids
|
|
3565
3807
|
|
|
3566
3808
|
def _clean_title_line(title_line: str) -> str:
|
|
3567
|
-
|
|
3568
|
-
title_line = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", title_line)
|
|
3569
|
-
# Remove trailing ellipsis/truncation
|
|
3570
|
-
title_line = re.sub(r"\s*[.]{3,}.*$", "", title_line)
|
|
3571
|
-
# Split on common separators to avoid leaking body content
|
|
3572
|
-
for separator in [". Bumps ", " Bumps ", ". - ", " - "]:
|
|
3573
|
-
if separator in title_line:
|
|
3574
|
-
title_line = title_line.split(separator)[0].strip()
|
|
3575
|
-
break
|
|
3576
|
-
# Remove simple markdown/formatting artifacts
|
|
3577
|
-
title_line = re.sub(r"[*_`]", "", title_line).strip()
|
|
3578
|
-
if len(title_line) > 100:
|
|
3579
|
-
break_points = [". ", "! ", "? ", " - ", ": "]
|
|
3580
|
-
for bp in break_points:
|
|
3581
|
-
if bp in title_line[:100]:
|
|
3582
|
-
title_line = title_line[
|
|
3583
|
-
: title_line.index(bp) + len(bp.strip())
|
|
3584
|
-
]
|
|
3585
|
-
break
|
|
3586
|
-
else:
|
|
3587
|
-
words = title_line[:100].split()
|
|
3588
|
-
title_line = (
|
|
3589
|
-
" ".join(words[:-1])
|
|
3590
|
-
if len(words) > 1
|
|
3591
|
-
else title_line[:100].rstrip()
|
|
3592
|
-
)
|
|
3809
|
+
title_line = _clean_squash_title_line(title_line)
|
|
3593
3810
|
|
|
3594
3811
|
# Apply conventional commit normalization if enabled
|
|
3595
3812
|
if inputs.normalise_commit and gh.pr_number:
|