github2gerrit 1.2.0__py3-none-any.whl → 1.2.2__py3-none-any.whl
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/cli.py +25 -12
- github2gerrit/commit_normalization.py +3 -2
- github2gerrit/core.py +243 -26
- github2gerrit/gerrit_pr_closer.py +231 -28
- github2gerrit/gerrit_query.py +67 -2
- github2gerrit/netrc.py +4 -6
- github2gerrit/pr_content_filter.py +1 -1
- github2gerrit/similarity.py +21 -6
- {github2gerrit-1.2.0.dist-info → github2gerrit-1.2.2.dist-info}/METADATA +2 -1
- {github2gerrit-1.2.0.dist-info → github2gerrit-1.2.2.dist-info}/RECORD +13 -13
- {github2gerrit-1.2.0.dist-info → github2gerrit-1.2.2.dist-info}/WHEEL +0 -0
- {github2gerrit-1.2.0.dist-info → github2gerrit-1.2.2.dist-info}/entry_points.txt +0 -0
- {github2gerrit-1.2.0.dist-info → github2gerrit-1.2.2.dist-info}/licenses/LICENSE +0 -0
github2gerrit/cli.py
CHANGED
|
@@ -2226,7 +2226,7 @@ def _process() -> None:
|
|
|
2226
2226
|
):
|
|
2227
2227
|
try:
|
|
2228
2228
|
log.debug(
|
|
2229
|
-
"
|
|
2229
|
+
"Checking for Gerrit change to abandon for PR #%s",
|
|
2230
2230
|
gh.pr_number,
|
|
2231
2231
|
)
|
|
2232
2232
|
change_number = abandon_gerrit_change_for_closed_pr(
|
|
@@ -2238,16 +2238,29 @@ def _process() -> None:
|
|
|
2238
2238
|
progress_tracker=None,
|
|
2239
2239
|
)
|
|
2240
2240
|
if change_number:
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
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
|
+
)
|
|
2251
2264
|
# Console output already done by
|
|
2252
2265
|
# abandon_gerrit_change_for_closed_pr
|
|
2253
2266
|
else:
|
|
@@ -2295,7 +2308,7 @@ def _process() -> None:
|
|
|
2295
2308
|
log.warning("Gerrit cleanup failed: %s", exc)
|
|
2296
2309
|
|
|
2297
2310
|
log.debug(
|
|
2298
|
-
"
|
|
2311
|
+
"Cleanup operations completed for closed PR #%s",
|
|
2299
2312
|
gh.pr_number or "unknown",
|
|
2300
2313
|
)
|
|
2301
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:
|
github2gerrit/core.py
CHANGED
|
@@ -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:
|
|
@@ -54,6 +54,32 @@ FORCE_ABANDONED_CLEANUP = _env_bool("CLEANUP_ABANDONED", True)
|
|
|
54
54
|
FORCE_GERRIT_CLEANUP = _env_bool("CLEANUP_GERRIT", True)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
def _build_gerrit_change_url(
|
|
58
|
+
gerrit_server: str,
|
|
59
|
+
gerrit_project: str,
|
|
60
|
+
change_number: str,
|
|
61
|
+
) -> str | None:
|
|
62
|
+
"""Build a Gerrit change URL from a change number string.
|
|
63
|
+
|
|
64
|
+
Returns the URL string, or ``None`` when URL generation fails.
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
from .gerrit_urls import create_gerrit_url_builder
|
|
68
|
+
|
|
69
|
+
url_builder = create_gerrit_url_builder(gerrit_server)
|
|
70
|
+
return url_builder.change_url(gerrit_project, int(change_number))
|
|
71
|
+
except Exception:
|
|
72
|
+
log.debug(
|
|
73
|
+
"Could not build Gerrit change URL for server=%r, project=%r, "
|
|
74
|
+
"change_number=%r; skipping URL generation",
|
|
75
|
+
gerrit_server,
|
|
76
|
+
gerrit_project,
|
|
77
|
+
change_number,
|
|
78
|
+
exc_info=True,
|
|
79
|
+
)
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
|
|
57
83
|
def extract_change_number_from_url(
|
|
58
84
|
gerrit_change_url: str,
|
|
59
85
|
) -> tuple[str, str] | None:
|
|
@@ -1078,7 +1104,7 @@ def abandon_gerrit_change_for_closed_pr(
|
|
|
1078
1104
|
(or would be in dry-run), None otherwise
|
|
1079
1105
|
"""
|
|
1080
1106
|
log.debug(
|
|
1081
|
-
"
|
|
1107
|
+
"Looking for Gerrit change associated with PR #%d",
|
|
1082
1108
|
pr_number,
|
|
1083
1109
|
)
|
|
1084
1110
|
|
|
@@ -1232,24 +1258,35 @@ def abandon_gerrit_change_for_closed_pr(
|
|
|
1232
1258
|
|
|
1233
1259
|
# Abandon the Gerrit change
|
|
1234
1260
|
if not dry_run:
|
|
1235
|
-
gerrit_change_url = (
|
|
1236
|
-
|
|
1237
|
-
f"{gerrit_project}/+/{change_number}"
|
|
1261
|
+
gerrit_change_url = _build_gerrit_change_url(
|
|
1262
|
+
gerrit_server, gerrit_project, change_number
|
|
1238
1263
|
)
|
|
1239
1264
|
_abandon_gerrit_change(
|
|
1240
1265
|
gerrit_client,
|
|
1241
1266
|
change_number,
|
|
1242
1267
|
abandon_message,
|
|
1243
1268
|
)
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1269
|
+
if gerrit_change_url:
|
|
1270
|
+
log.debug(
|
|
1271
|
+
"Abandoned Gerrit change %s: %s",
|
|
1272
|
+
change_number,
|
|
1273
|
+
gerrit_change_url,
|
|
1274
|
+
)
|
|
1275
|
+
safe_console_print(
|
|
1276
|
+
f"✅ Abandoned Gerrit change "
|
|
1277
|
+
f"{gerrit_change_url} "
|
|
1278
|
+
f"for pull request #{pr_number}"
|
|
1279
|
+
)
|
|
1280
|
+
else:
|
|
1281
|
+
log.debug(
|
|
1282
|
+
"Abandoned Gerrit change %s",
|
|
1283
|
+
change_number,
|
|
1284
|
+
)
|
|
1285
|
+
safe_console_print(
|
|
1286
|
+
f"✅ Abandoned Gerrit change "
|
|
1287
|
+
f"{change_number} "
|
|
1288
|
+
f"for pull request #{pr_number}"
|
|
1289
|
+
)
|
|
1253
1290
|
else:
|
|
1254
1291
|
log.debug(
|
|
1255
1292
|
"DRY-RUN: Would abandon Gerrit change %s",
|
|
@@ -1274,9 +1311,8 @@ def abandon_gerrit_change_for_closed_pr(
|
|
|
1274
1311
|
)
|
|
1275
1312
|
|
|
1276
1313
|
if not dry_run:
|
|
1277
|
-
gerrit_change_url = (
|
|
1278
|
-
|
|
1279
|
-
f"{gerrit_project}/+/{change_number}"
|
|
1314
|
+
gerrit_change_url = _build_gerrit_change_url(
|
|
1315
|
+
gerrit_server, gerrit_project, change_number
|
|
1280
1316
|
)
|
|
1281
1317
|
_abandon_gerrit_change(
|
|
1282
1318
|
gerrit_client,
|
|
@@ -1284,10 +1320,18 @@ def abandon_gerrit_change_for_closed_pr(
|
|
|
1284
1320
|
simple_message,
|
|
1285
1321
|
)
|
|
1286
1322
|
log.debug("Abandoned Gerrit change %s", change_number)
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1323
|
+
if gerrit_change_url:
|
|
1324
|
+
safe_console_print(
|
|
1325
|
+
f"✅ Abandoned Gerrit change "
|
|
1326
|
+
f"{gerrit_change_url} "
|
|
1327
|
+
f"for pull request #{pr_number}"
|
|
1328
|
+
)
|
|
1329
|
+
else:
|
|
1330
|
+
safe_console_print(
|
|
1331
|
+
f"✅ Abandoned Gerrit change "
|
|
1332
|
+
f"{change_number} "
|
|
1333
|
+
f"for pull request #{pr_number}"
|
|
1334
|
+
)
|
|
1291
1335
|
else:
|
|
1292
1336
|
log.debug(
|
|
1293
1337
|
"DRY-RUN: Would abandon Gerrit change %s",
|
|
@@ -1454,20 +1498,25 @@ def cleanup_closed_github_prs(
|
|
|
1454
1498
|
|
|
1455
1499
|
# Abandon the Gerrit change
|
|
1456
1500
|
if not dry_run:
|
|
1457
|
-
gerrit_change_url = (
|
|
1458
|
-
|
|
1459
|
-
f"{gerrit_project}/+/{change_number}"
|
|
1501
|
+
gerrit_change_url = _build_gerrit_change_url(
|
|
1502
|
+
gerrit_server, gerrit_project, change_number
|
|
1460
1503
|
)
|
|
1461
1504
|
_abandon_gerrit_change(
|
|
1462
1505
|
gerrit_client,
|
|
1463
1506
|
change_number,
|
|
1464
1507
|
abandon_message,
|
|
1465
1508
|
)
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1509
|
+
if gerrit_change_url:
|
|
1510
|
+
log.info(
|
|
1511
|
+
"Abandoned Gerrit change %s: %s",
|
|
1512
|
+
change_number,
|
|
1513
|
+
gerrit_change_url,
|
|
1514
|
+
)
|
|
1515
|
+
else:
|
|
1516
|
+
log.info(
|
|
1517
|
+
"Abandoned Gerrit change %s",
|
|
1518
|
+
change_number,
|
|
1519
|
+
)
|
|
1471
1520
|
else:
|
|
1472
1521
|
log.info(
|
|
1473
1522
|
"DRY-RUN: Would abandon Gerrit change %s",
|
|
@@ -1652,3 +1701,157 @@ def _abandon_gerrit_change(
|
|
|
1652
1701
|
except Exception:
|
|
1653
1702
|
log.exception("Failed to abandon Gerrit change %s", change_number)
|
|
1654
1703
|
raise
|
|
1704
|
+
|
|
1705
|
+
|
|
1706
|
+
def abandon_superseded_dependency_changes(
|
|
1707
|
+
gerrit_server: str,
|
|
1708
|
+
gerrit_project: str,
|
|
1709
|
+
current_subject: str,
|
|
1710
|
+
exclude_change_ids: list[str],
|
|
1711
|
+
*,
|
|
1712
|
+
dry_run: bool = False,
|
|
1713
|
+
target_branch: str | None = None,
|
|
1714
|
+
) -> list[str]:
|
|
1715
|
+
"""Abandon open Gerrit changes superseded by a newer dependency update.
|
|
1716
|
+
|
|
1717
|
+
After pushing a new dependency-update change, this function
|
|
1718
|
+
queries Gerrit for other open changes in the same project that
|
|
1719
|
+
bump the **same** dependency package. Matches are abandoned
|
|
1720
|
+
with an explanatory message.
|
|
1721
|
+
|
|
1722
|
+
This serves as the "Option A fallback" when the primary
|
|
1723
|
+
update-in-place strategy (Change-Id reuse) could not be
|
|
1724
|
+
applied.
|
|
1725
|
+
|
|
1726
|
+
Args:
|
|
1727
|
+
gerrit_server: Gerrit server hostname.
|
|
1728
|
+
gerrit_project: Gerrit project name.
|
|
1729
|
+
current_subject: Subject of the change that was just pushed.
|
|
1730
|
+
exclude_change_ids: Change-IDs to exclude (the change we
|
|
1731
|
+
just pushed, so we don't abandon ourselves).
|
|
1732
|
+
dry_run: If True, log but do not actually abandon.
|
|
1733
|
+
target_branch: Optional Gerrit branch name. When provided,
|
|
1734
|
+
only changes targeting this branch are considered.
|
|
1735
|
+
|
|
1736
|
+
Returns:
|
|
1737
|
+
List of Gerrit change numbers that were abandoned
|
|
1738
|
+
(or would be in dry-run mode).
|
|
1739
|
+
"""
|
|
1740
|
+
from .gerrit_query import query_open_changes_by_project
|
|
1741
|
+
from .gerrit_rest import build_client_for_host
|
|
1742
|
+
from .gerrit_urls import create_gerrit_url_builder
|
|
1743
|
+
from .similarity import extract_dependency_package_from_subject
|
|
1744
|
+
from .trailers import GITHUB_PR_TRAILER
|
|
1745
|
+
from .trailers import parse_trailers
|
|
1746
|
+
|
|
1747
|
+
current_pkg = extract_dependency_package_from_subject(current_subject)
|
|
1748
|
+
if not current_pkg:
|
|
1749
|
+
log.debug(
|
|
1750
|
+
"Cannot extract dependency package from subject: %s",
|
|
1751
|
+
current_subject,
|
|
1752
|
+
)
|
|
1753
|
+
return []
|
|
1754
|
+
|
|
1755
|
+
log.info(
|
|
1756
|
+
"Checking for superseded dependency changes (package: %s)",
|
|
1757
|
+
current_pkg,
|
|
1758
|
+
)
|
|
1759
|
+
|
|
1760
|
+
abandoned: list[str] = []
|
|
1761
|
+
try:
|
|
1762
|
+
client = build_client_for_host(gerrit_server)
|
|
1763
|
+
open_changes = query_open_changes_by_project(
|
|
1764
|
+
client, gerrit_project, branch=target_branch, max_results=200
|
|
1765
|
+
)
|
|
1766
|
+
|
|
1767
|
+
url_builder = create_gerrit_url_builder(gerrit_server)
|
|
1768
|
+
|
|
1769
|
+
for change in open_changes:
|
|
1770
|
+
# Skip the change(s) we just pushed
|
|
1771
|
+
if change.change_id in exclude_change_ids:
|
|
1772
|
+
continue
|
|
1773
|
+
|
|
1774
|
+
# Only target changes created by GitHub2Gerrit
|
|
1775
|
+
commit_msg = change.commit_message or ""
|
|
1776
|
+
trailers = parse_trailers(commit_msg)
|
|
1777
|
+
if GITHUB_PR_TRAILER not in trailers:
|
|
1778
|
+
log.debug(
|
|
1779
|
+
"Skipping change %s: no GitHub2Gerrit "
|
|
1780
|
+
"metadata (missing %s trailer)",
|
|
1781
|
+
change.number,
|
|
1782
|
+
GITHUB_PR_TRAILER,
|
|
1783
|
+
)
|
|
1784
|
+
continue
|
|
1785
|
+
|
|
1786
|
+
candidate_pkg = extract_dependency_package_from_subject(
|
|
1787
|
+
change.subject
|
|
1788
|
+
)
|
|
1789
|
+
if not candidate_pkg or candidate_pkg != current_pkg:
|
|
1790
|
+
continue
|
|
1791
|
+
|
|
1792
|
+
# Parse change number for URL construction
|
|
1793
|
+
try:
|
|
1794
|
+
candidate_num = int(change.number)
|
|
1795
|
+
except (TypeError, ValueError):
|
|
1796
|
+
log.debug(
|
|
1797
|
+
"Skipping change with unparsable number: %r",
|
|
1798
|
+
change.number,
|
|
1799
|
+
)
|
|
1800
|
+
continue
|
|
1801
|
+
|
|
1802
|
+
change_url = url_builder.change_url(gerrit_project, candidate_num)
|
|
1803
|
+
log.info(
|
|
1804
|
+
"Found superseded change %s (%s)",
|
|
1805
|
+
change.number,
|
|
1806
|
+
change.subject,
|
|
1807
|
+
)
|
|
1808
|
+
|
|
1809
|
+
if dry_run:
|
|
1810
|
+
log.info(
|
|
1811
|
+
"DRY-RUN: Would abandon superseded change %s",
|
|
1812
|
+
change_url,
|
|
1813
|
+
)
|
|
1814
|
+
abandoned.append(str(change.number))
|
|
1815
|
+
continue
|
|
1816
|
+
|
|
1817
|
+
superseding_info = f"New change subject: {current_subject}"
|
|
1818
|
+
if exclude_change_ids:
|
|
1819
|
+
superseding_info += "\nChange-Id(s): " + ", ".join(
|
|
1820
|
+
str(cid) for cid in exclude_change_ids
|
|
1821
|
+
)
|
|
1822
|
+
abandon_msg = (
|
|
1823
|
+
f"Superseded by a newer update for {current_pkg}\n\n"
|
|
1824
|
+
f"{superseding_info}\n\n"
|
|
1825
|
+
"This change was automatically abandoned by "
|
|
1826
|
+
"GitHub2Gerrit because a newer dependency update "
|
|
1827
|
+
"for the same package was pushed."
|
|
1828
|
+
)
|
|
1829
|
+
try:
|
|
1830
|
+
_abandon_gerrit_change(client, str(change.number), abandon_msg)
|
|
1831
|
+
log.info(
|
|
1832
|
+
"Abandoned superseded change: %s",
|
|
1833
|
+
change_url,
|
|
1834
|
+
)
|
|
1835
|
+
abandoned.append(str(change.number))
|
|
1836
|
+
except Exception as exc:
|
|
1837
|
+
log.warning(
|
|
1838
|
+
"Failed to abandon superseded change %s: %s",
|
|
1839
|
+
change_url,
|
|
1840
|
+
exc,
|
|
1841
|
+
)
|
|
1842
|
+
|
|
1843
|
+
except Exception as exc:
|
|
1844
|
+
log.warning(
|
|
1845
|
+
"Superseded dependency change sweep failed (non-fatal): %s",
|
|
1846
|
+
exc,
|
|
1847
|
+
)
|
|
1848
|
+
|
|
1849
|
+
if abandoned:
|
|
1850
|
+
log.info(
|
|
1851
|
+
"Supersession sweep complete: abandoned %d change(s)",
|
|
1852
|
+
len(abandoned),
|
|
1853
|
+
)
|
|
1854
|
+
else:
|
|
1855
|
+
log.debug("No superseded dependency changes found")
|
|
1856
|
+
|
|
1857
|
+
return abandoned
|
github2gerrit/gerrit_query.py
CHANGED
|
@@ -10,6 +10,7 @@ based on topics, with support for pagination and safe parsing.
|
|
|
10
10
|
import logging
|
|
11
11
|
from dataclasses import dataclass
|
|
12
12
|
from typing import Any
|
|
13
|
+
from urllib.parse import quote
|
|
13
14
|
|
|
14
15
|
from .gerrit_rest import GerritRestClient
|
|
15
16
|
|
|
@@ -17,6 +18,20 @@ from .gerrit_rest import GerritRestClient
|
|
|
17
18
|
log = logging.getLogger(__name__)
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
def _gerrit_quote(value: str) -> str:
|
|
22
|
+
"""Escape a value for safe use inside Gerrit query double-quotes.
|
|
23
|
+
|
|
24
|
+
Gerrit query syntax uses double-quoted strings for values
|
|
25
|
+
containing special characters. Backslashes and double-quotes
|
|
26
|
+
inside the value must be escaped to prevent query injection or
|
|
27
|
+
malformed queries (e.g. branch names containing ``"``).
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
The escaped string (without surrounding quotes).
|
|
31
|
+
"""
|
|
32
|
+
return value.replace("\\", "\\\\").replace('"', '\\"')
|
|
33
|
+
|
|
34
|
+
|
|
20
35
|
@dataclass
|
|
21
36
|
class GerritChange:
|
|
22
37
|
"""Represents a Gerrit change from query results."""
|
|
@@ -82,7 +97,7 @@ def query_changes_by_topic(
|
|
|
82
97
|
|
|
83
98
|
# Build query string
|
|
84
99
|
status_query = " OR ".join(f"status:{status}" for status in statuses)
|
|
85
|
-
query = f
|
|
100
|
+
query = f'topic:"{_gerrit_quote(topic)}" AND ({status_query})'
|
|
86
101
|
|
|
87
102
|
log.debug("Querying Gerrit for changes: %s", query)
|
|
88
103
|
|
|
@@ -105,6 +120,56 @@ def query_changes_by_topic(
|
|
|
105
120
|
return changes
|
|
106
121
|
|
|
107
122
|
|
|
123
|
+
def query_open_changes_by_project(
|
|
124
|
+
client: GerritRestClient,
|
|
125
|
+
project: str,
|
|
126
|
+
*,
|
|
127
|
+
branch: str | None = None,
|
|
128
|
+
max_results: int = 100,
|
|
129
|
+
) -> list[GerritChange]:
|
|
130
|
+
"""Query open changes owned by the current user in a Gerrit project.
|
|
131
|
+
|
|
132
|
+
Used by the supersession sweep to discover open changes that
|
|
133
|
+
may be superseded by a newer dependency update. Only returns
|
|
134
|
+
changes owned by the authenticated user (``owner:self``) to
|
|
135
|
+
avoid acting on unrelated human-authored changes.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
client: Gerrit REST client.
|
|
139
|
+
project: Gerrit project name (e.g. ``myorg/myrepo``).
|
|
140
|
+
branch: Optional Gerrit branch name to scope the query.
|
|
141
|
+
When provided, only changes targeting this branch are
|
|
142
|
+
returned.
|
|
143
|
+
max_results: Maximum number of results to return.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
List of open ``GerritChange`` objects.
|
|
147
|
+
"""
|
|
148
|
+
query = f'project:"{_gerrit_quote(project)}" status:open owner:self'
|
|
149
|
+
if branch:
|
|
150
|
+
query += f' branch:"{_gerrit_quote(branch)}"'
|
|
151
|
+
log.debug("Querying Gerrit for open changes: %s", query)
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
changes = _execute_query_with_pagination(
|
|
155
|
+
client, query, max_results=max_results
|
|
156
|
+
)
|
|
157
|
+
log.debug(
|
|
158
|
+
"Found %d open changes in project '%s'",
|
|
159
|
+
len(changes),
|
|
160
|
+
project,
|
|
161
|
+
)
|
|
162
|
+
except Exception as exc:
|
|
163
|
+
log.warning(
|
|
164
|
+
"Failed to query open Gerrit changes for project '%s': %s",
|
|
165
|
+
project,
|
|
166
|
+
exc,
|
|
167
|
+
)
|
|
168
|
+
return []
|
|
169
|
+
else:
|
|
170
|
+
return changes
|
|
171
|
+
|
|
172
|
+
|
|
108
173
|
def _execute_query_with_pagination(
|
|
109
174
|
client: GerritRestClient,
|
|
110
175
|
query: str,
|
|
@@ -135,7 +200,7 @@ def _execute_query_with_pagination(
|
|
|
135
200
|
# Build query URL with parameters
|
|
136
201
|
# Gerrit REST API: /changes/?q=query&n=limit&S=skip&o=options
|
|
137
202
|
query_params = [
|
|
138
|
-
f"q={query}",
|
|
203
|
+
f"q={quote(query, safe='')}",
|
|
139
204
|
f"n={current_limit}",
|
|
140
205
|
f"S={start}",
|
|
141
206
|
"o=CURRENT_REVISION",
|
github2gerrit/netrc.py
CHANGED
|
@@ -769,9 +769,8 @@ def resolve_gerrit_credentials(
|
|
|
769
769
|
|
|
770
770
|
if env_user and env_pass:
|
|
771
771
|
log.debug(
|
|
772
|
-
"Using credentials from environment
|
|
772
|
+
"Using credentials from environment variable %s",
|
|
773
773
|
env_username_var,
|
|
774
|
-
env_password_var,
|
|
775
774
|
)
|
|
776
775
|
return GerritCredentials(
|
|
777
776
|
username=env_user,
|
|
@@ -787,9 +786,8 @@ def resolve_gerrit_credentials(
|
|
|
787
786
|
|
|
788
787
|
if fallback_user and fallback_pass:
|
|
789
788
|
log.debug(
|
|
790
|
-
"Using credentials from fallback environment
|
|
789
|
+
"Using credentials from fallback environment variable %s",
|
|
791
790
|
fallback_env_username_var,
|
|
792
|
-
fallback_env_password_var,
|
|
793
791
|
)
|
|
794
792
|
return GerritCredentials(
|
|
795
793
|
username=fallback_user,
|
|
@@ -803,9 +801,9 @@ def resolve_gerrit_credentials(
|
|
|
803
801
|
fallback_user = os.getenv(fallback_env_username_var, "").strip()
|
|
804
802
|
if fallback_user and env_pass:
|
|
805
803
|
log.debug(
|
|
806
|
-
"Using credentials from mixed environment variables
|
|
804
|
+
"Using credentials from mixed environment variables"
|
|
805
|
+
" (%s + primary password)",
|
|
807
806
|
fallback_env_username_var,
|
|
808
|
-
env_password_var,
|
|
809
807
|
)
|
|
810
808
|
return GerritCredentials(
|
|
811
809
|
username=fallback_user,
|
|
@@ -112,7 +112,7 @@ class DependabotRule(FilterRule):
|
|
|
112
112
|
"Bumps " in title and " from " in title and " to " in title,
|
|
113
113
|
"Dependabot will resolve any conflicts" in body,
|
|
114
114
|
"<details>" in body and "<summary>" in body,
|
|
115
|
-
"camo.githubusercontent.com" in body,
|
|
115
|
+
"https://camo.githubusercontent.com/" in body,
|
|
116
116
|
]
|
|
117
117
|
|
|
118
118
|
# Require multiple indicators for confidence
|
github2gerrit/similarity.py
CHANGED
|
@@ -33,6 +33,7 @@ from difflib import SequenceMatcher
|
|
|
33
33
|
|
|
34
34
|
# Public API surface
|
|
35
35
|
__all__ = [
|
|
36
|
+
"CC_PREFIX_RE",
|
|
36
37
|
"ScoreResult",
|
|
37
38
|
"ScoringConfig",
|
|
38
39
|
"aggregate_scores",
|
|
@@ -48,6 +49,14 @@ __all__ = [
|
|
|
48
49
|
"sequence_ratio",
|
|
49
50
|
]
|
|
50
51
|
|
|
52
|
+
# Compiled conventional-commit prefix regex, shared across modules.
|
|
53
|
+
# Matches types like "feat:", "Fix(scope):", "Build(deps)!:" etc.
|
|
54
|
+
CC_PREFIX_RE = re.compile(
|
|
55
|
+
r"^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)"
|
|
56
|
+
r"(?:\([^)]*\))?\s*!?\s*:\s*",
|
|
57
|
+
re.IGNORECASE,
|
|
58
|
+
)
|
|
59
|
+
|
|
51
60
|
|
|
52
61
|
@dataclass(frozen=True)
|
|
53
62
|
class ScoringConfig:
|
|
@@ -197,6 +206,7 @@ def extract_dependency_package_from_subject(subject: str) -> str:
|
|
|
197
206
|
Examples to consider (to be implemented):
|
|
198
207
|
- "Bump requests from 2.31.0 to 2.32.0" -> "requests"
|
|
199
208
|
- "chore: update org/tool from v1.2.3 to v1.2.4" -> "org/tool"
|
|
209
|
+
- "Build(deps): Bump org/tool from 1.0 to 2.0" -> "org/tool"
|
|
200
210
|
|
|
201
211
|
Args:
|
|
202
212
|
subject: The (possibly unnormalized) subject line.
|
|
@@ -205,15 +215,20 @@ def extract_dependency_package_from_subject(subject: str) -> str:
|
|
|
205
215
|
Package identifier, or empty string if none could be extracted.
|
|
206
216
|
"""
|
|
207
217
|
s = (subject or "").lower()
|
|
218
|
+
# Strip any conventional-commit prefix before matching
|
|
219
|
+
cc_match = CC_PREFIX_RE.match(s)
|
|
220
|
+
if cc_match:
|
|
221
|
+
s = s[cc_match.end() :]
|
|
222
|
+
|
|
208
223
|
patterns = [
|
|
209
224
|
# Full version with "from" clause
|
|
210
|
-
r"
|
|
211
|
-
r"
|
|
212
|
-
r"
|
|
225
|
+
r"bump\s+([^\s]+)\s+from\s+",
|
|
226
|
+
r"update\s+([^\s]+)\s+from\s+",
|
|
227
|
+
r"upgrade\s+([^\s]+)\s+from\s+",
|
|
213
228
|
# Truncated version without "from" clause (for Gerrit subjects)
|
|
214
|
-
r"
|
|
215
|
-
r"
|
|
216
|
-
r"
|
|
229
|
+
r"bump\s+([^\s]+)\s*$",
|
|
230
|
+
r"update\s+([^\s]+)\s*$",
|
|
231
|
+
r"upgrade\s+([^\s]+)\s*$",
|
|
217
232
|
]
|
|
218
233
|
for pat in patterns:
|
|
219
234
|
m = re.search(pat, s)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github2gerrit
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
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
|
|
@@ -22,6 +22,7 @@ Classifier: Topic :: Software Development :: Build Tools
|
|
|
22
22
|
Classifier: Topic :: Software Development :: Version Control
|
|
23
23
|
Classifier: Typing :: Typed
|
|
24
24
|
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: click>=8.1.7
|
|
25
26
|
Requires-Dist: cryptography>=46.0.5
|
|
26
27
|
Requires-Dist: git-review>=2.5.0
|
|
27
28
|
Requires-Dist: pygerrit2>=2.0.15
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
github2gerrit/__init__.py,sha256=N1Vj1HJ28LKCJLAynQdm5jFGQQAz9YSMzZhEfvbBgow,886
|
|
2
|
-
github2gerrit/cli.py,sha256=
|
|
3
|
-
github2gerrit/commit_normalization.py,sha256=
|
|
2
|
+
github2gerrit/cli.py,sha256=osLCG6Xshrcp6QBFeA3OoWoH3NywRy6HSo_jYUkMDHU,119341
|
|
3
|
+
github2gerrit/commit_normalization.py,sha256=6FOkNfYvR_wicBAhbd4gcwVlkgnqdVFWvUFQ2cwefYA,17131
|
|
4
4
|
github2gerrit/commit_rules.py,sha256=GMlCYwuZb-RcEP1SwMfI3r4i_Dy2yqK6f0RwnYFMGH4,16297
|
|
5
5
|
github2gerrit/config.py,sha256=nvSc9e_wJM2GS5whFE5R3UwmZjYPZgucaS8s6rrEbjY,26469
|
|
6
6
|
github2gerrit/constants.py,sha256=uCAx-lZiyxlVSHaGmDx6TFbCajkK3VO2ggCOej0EeXk,1306
|
|
7
|
-
github2gerrit/core.py,sha256=
|
|
7
|
+
github2gerrit/core.py,sha256=W08u18H9Uhsy1_0cm7hiXZmuXljr6AwGI03ybNcUO-Y,254613
|
|
8
8
|
github2gerrit/duplicate_detection.py,sha256=CTrzD3YLc-AyPmaR5bSC5b22LpBDpCcNbVxMv2X0o7A,32127
|
|
9
9
|
github2gerrit/error_codes.py,sha256=MclL3tiOSR4R61c3J642CYDyJJ10tzEQmI2YW7Wkqjw,19098
|
|
10
10
|
github2gerrit/external_api.py,sha256=9483kkgIs1ECOl_f0lcGb8GrJQF9IfYmWfBQwUJT9hk,18480
|
|
11
|
-
github2gerrit/gerrit_pr_closer.py,sha256=
|
|
12
|
-
github2gerrit/gerrit_query.py,sha256=
|
|
11
|
+
github2gerrit/gerrit_pr_closer.py,sha256=irsAI4eNJWi66P39q1gRibpV5SdhiVKv11u-G8yD9a0,61591
|
|
12
|
+
github2gerrit/gerrit_query.py,sha256=pn44IN46tgKPq-s48XnGXpS6kfeQyiECQP75CnJSOr8,10472
|
|
13
13
|
github2gerrit/gerrit_rest.py,sha256=FyQAMCSuZmJ7ClLuPRgkwapXbJnhW41TYdgkB6UGKQY,12612
|
|
14
14
|
github2gerrit/gerrit_urls.py,sha256=aoPuUOCysHffVr9qkBRIPCZEUoSBMHmfviydy1U1uSQ,13843
|
|
15
15
|
github2gerrit/github_api.py,sha256=wYKWRMQsYu7zbxyRQRO7N4cTqBvE6vYf8SdN_3C6XTo,11486
|
|
@@ -17,13 +17,13 @@ github2gerrit/gitreview.py,sha256=I1FPsYjkPttj6DdjqHI0HkWOwsnInhmijLeGjnD658I,23
|
|
|
17
17
|
github2gerrit/gitutils.py,sha256=J-pgSeeOslspYSwkzkxHAjej4h7t65s2w15ijDpEHOY,26462
|
|
18
18
|
github2gerrit/mapping_comment.py,sha256=3WAL3KQgjfPnUMZS_-aqNL7qtD0jNXr0VTK_GPEcW3k,9746
|
|
19
19
|
github2gerrit/models.py,sha256=beZ1C3S8v-qA4tD3Niq6trpoHofxLx6q3VQgvOz0WyI,4304
|
|
20
|
-
github2gerrit/netrc.py,sha256=
|
|
20
|
+
github2gerrit/netrc.py,sha256=x8l53j-t1kl-v1YZEPaDT2Mmf5qUfgP5J0YZzE7fkIY,27534
|
|
21
21
|
github2gerrit/pr_commands.py,sha256=VZNEvzK1MHnXul3SbkjjoY_HW34M9QFyt74RxcRZpQU,12440
|
|
22
|
-
github2gerrit/pr_content_filter.py,sha256=
|
|
22
|
+
github2gerrit/pr_content_filter.py,sha256=WUeYiTTYbIgz4Kb0u8M5e3vEoNU5L2WQ9h27xV1iNwE,19278
|
|
23
23
|
github2gerrit/reconcile_matcher.py,sha256=s7v3LJdv5CZmi7LvhENFoXlQ-GCxFS-CtkuOCUsD5ow,20466
|
|
24
24
|
github2gerrit/rich_display.py,sha256=Y2rtJNLP_EG9zdR29NiSqM3j_o_51gpQVeTNBqZch5o,16038
|
|
25
25
|
github2gerrit/rich_logging.py,sha256=D8yyV4NrsLf74fZH5-AFr1c-Im7MyNyx3LbJTLqepHs,10991
|
|
26
|
-
github2gerrit/similarity.py,sha256=
|
|
26
|
+
github2gerrit/similarity.py,sha256=UHJI_qQB8wGEdWroyHcH5Nb1f3P-fgUU4XnBr9EzIUU,16582
|
|
27
27
|
github2gerrit/ssh_agent_setup.py,sha256=5nccjdjJuTxbDR9nxnP1ITkSGItbCFD4Owxsrnt8IYc,22613
|
|
28
28
|
github2gerrit/ssh_common.py,sha256=OC20SQsX_AP3yTUJ_tqz6XAN30O5GbgtZwzwUzyX4DQ,8216
|
|
29
29
|
github2gerrit/ssh_config_parser.py,sha256=Yd6yoh95lSfUXHRHw2mrgNlY0yizz1A7GmQod30dCT8,15523
|
|
@@ -32,8 +32,8 @@ github2gerrit/trailers.py,sha256=9w0vIxPNBNQp56sIy-MF62d22Rm6vY-msh9ao1lX0rQ,838
|
|
|
32
32
|
github2gerrit/utils.py,sha256=zNH1qcxyOYdhIn4Ku0o5p9pbwYWdQfboLNqzADDts8A,3804
|
|
33
33
|
github2gerrit/orchestrator/__init__.py,sha256=HAEcdCAHOFr8LsdIwAdcIcFZn_ayMbX9rdVUULp8410,864
|
|
34
34
|
github2gerrit/orchestrator/reconciliation.py,sha256=-JQre_PUx6aZeX8Qs6uHqzujKAXXCj9wNv7Cy-543Z8,19391
|
|
35
|
-
github2gerrit-1.2.
|
|
36
|
-
github2gerrit-1.2.
|
|
37
|
-
github2gerrit-1.2.
|
|
38
|
-
github2gerrit-1.2.
|
|
39
|
-
github2gerrit-1.2.
|
|
35
|
+
github2gerrit-1.2.2.dist-info/METADATA,sha256=JUrvlCF0qFREPgEa7h_tgro6fp9VT0FO3zUF5c0Jjjs,70210
|
|
36
|
+
github2gerrit-1.2.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
37
|
+
github2gerrit-1.2.2.dist-info/entry_points.txt,sha256=MxN2_liIKo3-xJwtAulAeS5GcOS6JS96nvwOQIkP3W8,56
|
|
38
|
+
github2gerrit-1.2.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
39
|
+
github2gerrit-1.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|