github2gerrit 0.1.7__py3-none-any.whl → 0.1.9__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 CHANGED
@@ -359,15 +359,21 @@ def main(
359
359
  # URL mode handling
360
360
  if target_url:
361
361
  org, repo, pr = _parse_github_target(target_url)
362
+ log.debug("Parsed GitHub URL: org=%s, repo=%s, pr=%s", org, repo, pr)
362
363
  if org:
363
364
  os.environ["ORGANIZATION"] = org
365
+ log.debug("Set ORGANIZATION=%s", org)
364
366
  if org and repo:
365
- os.environ["GITHUB_REPOSITORY"] = f"{org}/{repo}"
367
+ github_repo = f"{org}/{repo}"
368
+ os.environ["GITHUB_REPOSITORY"] = github_repo
369
+ log.debug("Set GITHUB_REPOSITORY=%s", github_repo)
366
370
  if pr:
367
371
  os.environ["PR_NUMBER"] = str(pr)
368
372
  os.environ["SYNC_ALL_OPEN_PRS"] = "false"
373
+ log.debug("Set PR_NUMBER=%s", pr)
369
374
  else:
370
375
  os.environ["SYNC_ALL_OPEN_PRS"] = "true"
376
+ log.debug("Set SYNC_ALL_OPEN_PRS=true")
371
377
  os.environ["G2G_TARGET_URL"] = "1"
372
378
  # Debug: Show environment at CLI startup
373
379
  log.debug("CLI startup environment check:")
github2gerrit/config.py CHANGED
@@ -131,6 +131,22 @@ def _coerce_value(raw: str) -> str:
131
131
  # like SSH keys or known_hosts entries can be specified inline using
132
132
  # '\n' or '\r\n' in configuration files.
133
133
  normalized_newlines = unquoted.replace("\\r\\n", "\n").replace("\\n", "\n").replace("\r\n", "\n")
134
+
135
+ # Additional sanitization for SSH private keys
136
+ if ("-----BEGIN" in normalized_newlines and "PRIVATE KEY-----" in normalized_newlines) or (
137
+ "ssh-" in normalized_newlines.lower() and "key" in normalized_newlines.lower()
138
+ ):
139
+ # Clean up SSH key formatting: remove extra whitespace, normalize line endings
140
+ lines = normalized_newlines.split("\n")
141
+ sanitized_lines = []
142
+ for line in lines:
143
+ cleaned = line.strip()
144
+ if cleaned:
145
+ # Remove any stray quotes that might have been embedded in the key content
146
+ cleaned = cleaned.replace('"', "").replace("'", "")
147
+ sanitized_lines.append(cleaned)
148
+ normalized_newlines = "\n".join(sanitized_lines)
149
+
134
150
  b = _normalize_bool_like(normalized_newlines)
135
151
  return b if b is not None else normalized_newlines
136
152
 
@@ -149,7 +165,7 @@ def _select_section(
149
165
 
150
166
  def _load_ini(path: Path) -> configparser.RawConfigParser:
151
167
  cp = configparser.RawConfigParser()
152
- # Preserve option case; mypy requires a cast for attribute assignment
168
+ # Preserve option case; mypy requires a cast for attribute requirement
153
169
  cast(Any, cp).optionxform = str
154
170
  try:
155
171
  with path.open("r", encoding="utf-8") as fh:
@@ -162,6 +178,9 @@ def _load_ini(path: Path) -> configparser.RawConfigParser:
162
178
  # We collapse these into a single line with '\n' escapes so that
163
179
  # configparser can ingest them reliably; later, _coerce_value()
164
180
  # converts the escapes back to real newlines.
181
+ #
182
+ # We also handle SSH private keys and other multi-line values that
183
+ # might have formatting inconsistencies by sanitizing them.
165
184
  lines = raw_text.splitlines()
166
185
  out_lines: list[str] = []
167
186
  i = 0
@@ -171,6 +190,8 @@ def _load_ini(path: Path) -> configparser.RawConfigParser:
171
190
  if eq_idx != -1:
172
191
  left = line[: eq_idx + 1]
173
192
  rhs = line[eq_idx + 1 :].strip()
193
+
194
+ # Handle standard multi-line quoted values: key = "
174
195
  if rhs == '"':
175
196
  i += 1
176
197
  block: list[str] = []
@@ -187,10 +208,64 @@ def _load_ini(path: Path) -> configparser.RawConfigParser:
187
208
  else:
188
209
  # No closing quote found; fall through
189
210
  # and keep original line
211
+ log.debug("Multi-line quote not properly closed for line: %s", line[:50])
190
212
  out_lines.append(line)
191
213
  continue
214
+
215
+ # Handle SSH private keys and other values that start with a quote
216
+ # but contain embedded content that might confuse configparser
217
+ elif rhs.startswith('"') and not rhs.endswith('"'):
218
+ # This looks like a multi-line value that starts on the same line
219
+ # Collect all content until we find a line ending with a quote
220
+ content_lines = [rhs[1:]] # Remove opening quote
221
+ i += 1
222
+
223
+ while i < len(lines):
224
+ current_line = lines[i]
225
+ if current_line.strip().endswith('"') and not current_line.strip().endswith('\\"'):
226
+ # Found closing quote - remove it and add final line
227
+ final_content = current_line.rstrip()
228
+ if final_content.endswith('"'):
229
+ final_content = final_content[:-1]
230
+ if final_content: # Only add if there's content after removing quote
231
+ content_lines.append(final_content)
232
+ break
233
+ else:
234
+ content_lines.append(current_line)
235
+ i += 1
236
+
237
+ # Join all content and sanitize for SSH keys
238
+ full_content = "\\n".join(content_lines)
239
+
240
+ # Special handling for SSH private keys - remove extra whitespace and line breaks
241
+ key_name = left.split("=")[0].strip().upper()
242
+ if "SSH" in key_name and "KEY" in key_name:
243
+ # For SSH keys, clean up base64 content by removing whitespace within lines
244
+ sanitized_lines = []
245
+ for content_line in content_lines:
246
+ cleaned = content_line.strip()
247
+ # Preserve SSH key headers/footers but clean base64 content
248
+ if cleaned.startswith("-----") or not cleaned:
249
+ sanitized_lines.append(cleaned)
250
+ else:
251
+ # Remove any embedded quotes and whitespace from base64 content
252
+ cleaned = cleaned.replace('"', "").replace("'", "").strip()
253
+ if cleaned:
254
+ sanitized_lines.append(cleaned)
255
+ full_content = "\\n".join(sanitized_lines)
256
+
257
+ log.debug(
258
+ "Processed multi-line value for key %s (length: %d)",
259
+ left.split("=")[0].strip(),
260
+ len(full_content),
261
+ )
262
+ out_lines.append(f'{left} "{full_content}"')
263
+ i += 1
264
+ continue
265
+
192
266
  out_lines.append(line)
193
267
  i += 1
268
+
194
269
  preprocessed = "\n".join(out_lines) + ("\n" if out_lines else "")
195
270
  cp.read_string(preprocessed)
196
271
  except FileNotFoundError:
@@ -272,6 +347,8 @@ def load_org_config(
272
347
 
273
348
  # Report unknown configuration keys to help users catch typos
274
349
  unknown_keys = set(normalized.keys()) - KNOWN_KEYS
350
+ log.debug("All parsed keys from config: %s", sorted(normalized.keys()))
351
+ log.debug("Known keys: %s", sorted(KNOWN_KEYS))
275
352
  if unknown_keys:
276
353
  log.warning(
277
354
  "Unknown configuration keys found in [%s]: %s. "
@@ -171,7 +171,9 @@ def build_client(token: str | None = None) -> GhClient:
171
171
  def get_repo_from_env(client: GhClient) -> GhRepository:
172
172
  """Return the repository object based on GITHUB_REPOSITORY."""
173
173
  full = _getenv_str("GITHUB_REPOSITORY")
174
+ log.debug("GITHUB_REPOSITORY environment variable: '%s'", full)
174
175
  if not full or "/" not in full:
176
+ log.error("Invalid GITHUB_REPOSITORY: '%s' (expected format: 'owner/repo')", full)
175
177
  raise ValueError(_MSG_BAD_GITHUB_REPOSITORY)
176
178
  repo = client.get_repo(full)
177
179
  return repo
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github2gerrit
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Submit a GitHub pull request to a Gerrit repository.
5
5
  Author-email: Matthew Watkins <mwatkins@linuxfoundation.org>
6
6
  License-Expression: Apache-2.0
@@ -297,7 +297,7 @@ jobs:
297
297
  steps:
298
298
  - name: Submit PR to Gerrit
299
299
  id: g2g
300
- uses: lfit/github2gerrit@main
300
+ uses: lfreleng-actions/github2gerrit-action@main
301
301
  with:
302
302
  SUBMIT_SINGLE_COMMITS: "false"
303
303
  USE_PR_AS_COMMIT: "false"
@@ -343,7 +343,7 @@ uv run github2gerrit
343
343
  uvx github2gerrit https://github.com/owner/repo/pull/123
344
344
 
345
345
  # Install from specific version/source
346
- uvx --from git+https://github.com/lfit/github2gerrit@main github2gerrit https://github.com/owner/repo/pull/123
346
+ uvx --from git+https://github.com/lfreleng-actions/github2gerrit-action@main github2gerrit https://github.com/owner/repo/pull/123
347
347
  ```
348
348
 
349
349
  ### Available Options
@@ -524,7 +524,7 @@ jobs:
524
524
 
525
525
  - name: Submit PR to Gerrit (with explicit overrides)
526
526
  id: g2g
527
- uses: lfit/github2gerrit@main
527
+ uses: lfreleng-actions/github2gerrit-action@main
528
528
  with:
529
529
  # Behavior
530
530
  SUBMIT_SINGLE_COMMITS: "false"
@@ -777,7 +777,7 @@ For CI/CD pipelines (like GitHub Actions), use `uvx` to install and run without
777
777
  uvx github2gerrit <PR_URL> --dry-run
778
778
 
779
779
  # Install from a specific version or source
780
- uvx --from git+https://github.com/lfit/github2gerrit@main github2gerrit <PR_URL>
780
+ uvx --from git+https://github.com/lfreleng-actions/github2gerrit-action@main github2gerrit <PR_URL>
781
781
 
782
782
  # Run with specific Python version
783
783
  uvx --python 3.11 github2gerrit <PR_URL>
@@ -1,13 +1,13 @@
1
1
  github2gerrit/__init__.py,sha256=N1Vj1HJ28LKCJLAynQdm5jFGQQAz9YSMzZhEfvbBgow,886
2
- github2gerrit/cli.py,sha256=heW3rwfUwPiBVIPyxDq49kF7fdtKunJAUer9z31mg_o,45538
2
+ github2gerrit/cli.py,sha256=ClwCOtMl9eecobeN6Bb1GFQ_E-kbILdxKFE89Vtvai4,45865
3
3
  github2gerrit/commit_normalization.py,sha256=u4AZigz3qOpz5XYpUOq3WUqsY-o08YrkgaT160eyIIs,16594
4
- github2gerrit/config.py,sha256=sTiujlOjCsJbaA-Ftr5XR--9nxs7XH8uEWD-IbGqLYk,17370
4
+ github2gerrit/config.py,sha256=uRVVcofzojTayA8AyM_3rgSFsV0fWGjixWmK48aZsRA,21471
5
5
  github2gerrit/core.py,sha256=Mjo_TNocFE5HDmRTxh54LEHAEw5y1u6lc1qItEZ76GA,105180
6
6
  github2gerrit/duplicate_detection.py,sha256=5j6EDz2P3GnnT2JkR1tGbzWCjA6TV0l5VjsEMs-tfCM,26544
7
7
  github2gerrit/external_api.py,sha256=EVHh__v6lRq_ojpBI_nkGZ31AQSZ9NG8tDqVUid23XY,17638
8
8
  github2gerrit/gerrit_rest.py,sha256=NmC95hb2hLpnko8Uu3OwPEKktNv-k3qrmuA7A2q-H0Y,10562
9
9
  github2gerrit/gerrit_urls.py,sha256=pw4rjIQeQ-i-vbPqsOZjZpdz7FO03pnMUMWc8L9Mcic,12893
10
- github2gerrit/github_api.py,sha256=1cTZMunDx6_oLNR3Z__ya6vsw0_PFS5ww3jQla5SVFA,8229
10
+ github2gerrit/github_api.py,sha256=6S9DRt-Dza78cgxQwZ3_ujI0yPPozDGBIKnYgHan760,8388
11
11
  github2gerrit/gitutils.py,sha256=3ob1K7dlKeWYYYOPXIIJ56p2N49lm1yj8XHHg2zSlhw,25929
12
12
  github2gerrit/models.py,sha256=sRS4rPMF_wjV19HMFTzhHXFMfsLFzm9TFb1JydJsSyQ,1875
13
13
  github2gerrit/pr_content_filter.py,sha256=w4MqSZ4uLX-3Da1DL_A56zVQa7xJ4h5jHMNteOOG2AE,16896
@@ -16,9 +16,9 @@ github2gerrit/ssh_agent_setup.py,sha256=fvKuMdF9Hv5ioaSu_alzLcjeoQLGGFRLS2wb46Kd
16
16
  github2gerrit/ssh_common.py,sha256=f0QQmnj6yIW74N3ZjGkYmJryEstjEOmq41EIXOZdGTM,8102
17
17
  github2gerrit/ssh_discovery.py,sha256=zH0tX9VN1pn8DLM1J9QQquoKBhA9gk5drXHvMSHNNLg,13021
18
18
  github2gerrit/utils.py,sha256=ADE5YZe1h3PB3cgfxbsCuYU-Xs-1CMHrDHxfIdariFE,3369
19
- github2gerrit-0.1.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
20
- github2gerrit-0.1.7.dist-info/METADATA,sha256=4EZvJoxh9EhKSxcHrv-SJ1IHE7rbe5VYFH7WhKzxJD0,30930
21
- github2gerrit-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
- github2gerrit-0.1.7.dist-info/entry_points.txt,sha256=MxN2_liIKo3-xJwtAulAeS5GcOS6JS96nvwOQIkP3W8,56
23
- github2gerrit-0.1.7.dist-info/top_level.txt,sha256=bWTYXjvuu4sSU90KLT1JlnjD7xV_iXZ-vKoulpjLTy8,14
24
- github2gerrit-0.1.7.dist-info/RECORD,,
19
+ github2gerrit-0.1.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
20
+ github2gerrit-0.1.9.dist-info/METADATA,sha256=QlTEhsWuTU9Yxim1Z__-aMr6GcGWmBzpcczbqzEyBNU,31006
21
+ github2gerrit-0.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ github2gerrit-0.1.9.dist-info/entry_points.txt,sha256=MxN2_liIKo3-xJwtAulAeS5GcOS6JS96nvwOQIkP3W8,56
23
+ github2gerrit-0.1.9.dist-info/top_level.txt,sha256=bWTYXjvuu4sSU90KLT1JlnjD7xV_iXZ-vKoulpjLTy8,14
24
+ github2gerrit-0.1.9.dist-info/RECORD,,