github2gerrit 0.1.4__py3-none-any.whl → 0.1.6__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 +196 -108
- github2gerrit/config.py +207 -5
- github2gerrit/core.py +542 -398
- github2gerrit/duplicate_detection.py +375 -193
- github2gerrit/gerrit_urls.py +256 -0
- github2gerrit/github_api.py +15 -20
- github2gerrit/gitutils.py +49 -13
- github2gerrit/models.py +1 -0
- github2gerrit/similarity.py +458 -0
- github2gerrit/ssh_discovery.py +365 -0
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/METADATA +24 -25
- github2gerrit-0.1.6.dist-info/RECORD +17 -0
- github2gerrit-0.1.4.dist-info/RECORD +0 -14
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/WHEEL +0 -0
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/entry_points.txt +0 -0
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/licenses/LICENSE +0 -0
- {github2gerrit-0.1.4.dist-info → github2gerrit-0.1.6.dist-info}/top_level.txt +0 -0
github2gerrit/config.py
CHANGED
@@ -80,6 +80,8 @@ KNOWN_KEYS: set[str] = {
|
|
80
80
|
"ISSUE_ID",
|
81
81
|
"G2G_VERBOSE",
|
82
82
|
"G2G_SKIP_GERRIT_COMMENTS",
|
83
|
+
"G2G_ENABLE_DERIVATION",
|
84
|
+
"G2G_AUTO_SAVE_CONFIG",
|
83
85
|
"GITHUB_TOKEN",
|
84
86
|
# Optional inputs (reusable workflow compatibility)
|
85
87
|
"GERRIT_SERVER",
|
@@ -128,11 +130,7 @@ def _coerce_value(raw: str) -> str:
|
|
128
130
|
# Normalize escaped newline sequences into real newlines so that values
|
129
131
|
# like SSH keys or known_hosts entries can be specified inline using
|
130
132
|
# '\n' or '\r\n' in configuration files.
|
131
|
-
normalized_newlines = (
|
132
|
-
unquoted.replace("\\r\\n", "\n")
|
133
|
-
.replace("\\n", "\n")
|
134
|
-
.replace("\r\n", "\n")
|
135
|
-
)
|
133
|
+
normalized_newlines = unquoted.replace("\\r\\n", "\n").replace("\\n", "\n").replace("\r\n", "\n")
|
136
134
|
b = _normalize_bool_like(normalized_newlines)
|
137
135
|
return b if b is not None else normalized_newlines
|
138
136
|
|
@@ -313,6 +311,210 @@ def filter_known(
|
|
313
311
|
return {k: v for k, v in cfg.items() if k in KNOWN_KEYS}
|
314
312
|
|
315
313
|
|
314
|
+
def _is_github_actions_context() -> bool:
|
315
|
+
"""Detect if running in GitHub Actions environment."""
|
316
|
+
return os.getenv("GITHUB_ACTIONS") == "true" or os.getenv("GITHUB_EVENT_NAME", "").strip() != ""
|
317
|
+
|
318
|
+
|
319
|
+
def _is_local_cli_context() -> bool:
|
320
|
+
"""Detect if running as local CLI tool."""
|
321
|
+
return not _is_github_actions_context()
|
322
|
+
|
323
|
+
|
324
|
+
def derive_gerrit_parameters(organization: str | None) -> dict[str, str]:
|
325
|
+
"""Derive Gerrit parameters from GitHub organization name.
|
326
|
+
|
327
|
+
Args:
|
328
|
+
organization: GitHub organization name
|
329
|
+
|
330
|
+
Returns:
|
331
|
+
Dict with derived parameter values:
|
332
|
+
- GERRIT_SSH_USER_G2G: [org].gh2gerrit
|
333
|
+
- GERRIT_SSH_USER_G2G_EMAIL: releng+[org]-gh2gerrit@linuxfoundation.org
|
334
|
+
- GERRIT_SERVER: gerrit.[org].org
|
335
|
+
"""
|
336
|
+
if not organization:
|
337
|
+
return {}
|
338
|
+
|
339
|
+
org = organization.strip().lower()
|
340
|
+
return {
|
341
|
+
"GERRIT_SSH_USER_G2G": f"{org}.gh2gerrit",
|
342
|
+
"GERRIT_SSH_USER_G2G_EMAIL": (f"releng+{org}-gh2gerrit@linuxfoundation.org"),
|
343
|
+
"GERRIT_SERVER": f"gerrit.{org}.org",
|
344
|
+
}
|
345
|
+
|
346
|
+
|
347
|
+
def apply_parameter_derivation(
|
348
|
+
cfg: dict[str, str],
|
349
|
+
organization: str | None = None,
|
350
|
+
save_to_config: bool = True,
|
351
|
+
) -> dict[str, str]:
|
352
|
+
"""Apply dynamic parameter derivation for missing Gerrit parameters.
|
353
|
+
|
354
|
+
This function derives standard Gerrit parameters when they are not
|
355
|
+
explicitly configured. The derivation is based on the GitHub organization:
|
356
|
+
|
357
|
+
- gerrit_ssh_user_g2g: [org].gh2gerrit
|
358
|
+
- gerrit_ssh_user_g2g_email: releng+[org]-gh2gerrit@linuxfoundation.org
|
359
|
+
- gerrit_server: gerrit.[org].org
|
360
|
+
|
361
|
+
Derivation behavior depends on execution context:
|
362
|
+
- GitHub Actions: Automatic derivation when organization is available
|
363
|
+
- Local CLI: Requires G2G_ENABLE_DERIVATION=true for automatic
|
364
|
+
derivation
|
365
|
+
|
366
|
+
Args:
|
367
|
+
cfg: Configuration dictionary to augment
|
368
|
+
organization: GitHub organization name for derivation
|
369
|
+
save_to_config: Whether to save derived parameters to config file
|
370
|
+
|
371
|
+
Returns:
|
372
|
+
Configuration dictionary with derived values for missing parameters
|
373
|
+
"""
|
374
|
+
if not organization:
|
375
|
+
return cfg
|
376
|
+
|
377
|
+
# Check execution context to determine derivation strategy
|
378
|
+
is_github_actions = _is_github_actions_context()
|
379
|
+
enable_derivation = is_github_actions or os.getenv("G2G_ENABLE_DERIVATION", "").strip().lower() in (
|
380
|
+
"1",
|
381
|
+
"true",
|
382
|
+
"yes",
|
383
|
+
"on",
|
384
|
+
)
|
385
|
+
|
386
|
+
if not enable_derivation:
|
387
|
+
log.debug(
|
388
|
+
"Parameter derivation disabled for local CLI usage. "
|
389
|
+
"Set G2G_ENABLE_DERIVATION=true to enable automatic derivation."
|
390
|
+
)
|
391
|
+
return cfg
|
392
|
+
|
393
|
+
# Only derive parameters that are missing or empty
|
394
|
+
derived = derive_gerrit_parameters(organization)
|
395
|
+
result = dict(cfg)
|
396
|
+
newly_derived = {}
|
397
|
+
|
398
|
+
for key, value in derived.items():
|
399
|
+
if key not in result or not result[key].strip():
|
400
|
+
log.debug(
|
401
|
+
"Deriving %s from organization '%s': %s (context: %s)",
|
402
|
+
key,
|
403
|
+
organization,
|
404
|
+
value,
|
405
|
+
"GitHub Actions" if is_github_actions else "Local CLI",
|
406
|
+
)
|
407
|
+
result[key] = value
|
408
|
+
newly_derived[key] = value
|
409
|
+
|
410
|
+
if newly_derived:
|
411
|
+
log.info(
|
412
|
+
"Derived parameters applied for organization '%s' (%s): %s",
|
413
|
+
organization,
|
414
|
+
"GitHub Actions" if is_github_actions else "Local CLI",
|
415
|
+
", ".join(f"{k}={v}" for k, v in newly_derived.items()),
|
416
|
+
)
|
417
|
+
# Save newly derived parameters to configuration file for future use
|
418
|
+
# Default to true for local CLI, false for GitHub Actions
|
419
|
+
default_auto_save = "false" if _is_github_actions_context() else "true"
|
420
|
+
auto_save_enabled = os.getenv("G2G_AUTO_SAVE_CONFIG", default_auto_save).strip().lower() in (
|
421
|
+
"1",
|
422
|
+
"true",
|
423
|
+
"yes",
|
424
|
+
"on",
|
425
|
+
)
|
426
|
+
if save_to_config and newly_derived and auto_save_enabled:
|
427
|
+
# Save to config in local CLI mode to create persistent configuration
|
428
|
+
try:
|
429
|
+
save_derived_parameters_to_config(organization, newly_derived)
|
430
|
+
log.info(
|
431
|
+
"Automatically saved derived parameters to configuration "
|
432
|
+
"file for organization '%s'. "
|
433
|
+
"This creates a persistent configuration that you can "
|
434
|
+
"customize if needed.",
|
435
|
+
organization,
|
436
|
+
)
|
437
|
+
except Exception as exc:
|
438
|
+
log.warning("Failed to save derived parameters to config: %s", exc)
|
439
|
+
|
440
|
+
return result
|
441
|
+
|
442
|
+
|
443
|
+
def save_derived_parameters_to_config(
|
444
|
+
organization: str,
|
445
|
+
derived_params: dict[str, str],
|
446
|
+
config_path: str | None = None,
|
447
|
+
) -> None:
|
448
|
+
"""Save derived parameters to the organization's configuration file.
|
449
|
+
|
450
|
+
This function updates the configuration file to include any derived
|
451
|
+
parameters that are not already present in the organization section.
|
452
|
+
This creates a persistent configuration that users can modify if needed.
|
453
|
+
|
454
|
+
Args:
|
455
|
+
organization: GitHub organization name for config section
|
456
|
+
derived_params: Dictionary of derived parameter key-value pairs
|
457
|
+
config_path: Path to config file (optional, uses default if not
|
458
|
+
provided)
|
459
|
+
|
460
|
+
Raises:
|
461
|
+
Exception: If saving fails
|
462
|
+
"""
|
463
|
+
if not organization or not derived_params:
|
464
|
+
return
|
465
|
+
|
466
|
+
if config_path is None:
|
467
|
+
config_path = os.getenv("G2G_CONFIG_PATH", "").strip() or DEFAULT_CONFIG_PATH
|
468
|
+
|
469
|
+
config_file = Path(config_path).expanduser()
|
470
|
+
|
471
|
+
try:
|
472
|
+
# Only update when a configuration file already exists
|
473
|
+
if not config_file.exists():
|
474
|
+
log.debug(
|
475
|
+
"Configuration file does not exist; skipping auto-save of derived parameters: %s",
|
476
|
+
config_file,
|
477
|
+
)
|
478
|
+
return
|
479
|
+
|
480
|
+
# Parse existing content using configparser
|
481
|
+
cp = _load_ini(config_file)
|
482
|
+
|
483
|
+
# Find or create the organization section
|
484
|
+
org_section = _select_section(cp, organization)
|
485
|
+
if org_section is None:
|
486
|
+
# Section doesn't exist, we'll need to add it
|
487
|
+
cp.add_section(organization)
|
488
|
+
org_section = organization
|
489
|
+
|
490
|
+
# Add derived parameters that don't already exist
|
491
|
+
params_added = []
|
492
|
+
for key, value in derived_params.items():
|
493
|
+
if not cp.has_option(org_section, key):
|
494
|
+
cp.set(org_section, key, f'"{value}"')
|
495
|
+
params_added.append(key)
|
496
|
+
|
497
|
+
# Only write if we added parameters
|
498
|
+
if params_added:
|
499
|
+
# Write the updated configuration
|
500
|
+
with config_file.open("w", encoding="utf-8") as f:
|
501
|
+
cp.write(f)
|
502
|
+
|
503
|
+
log.debug(
|
504
|
+
"Saved derived parameters to configuration file %s [%s]: %s",
|
505
|
+
config_file,
|
506
|
+
organization,
|
507
|
+
", ".join(params_added),
|
508
|
+
)
|
509
|
+
|
510
|
+
except Exception as exc:
|
511
|
+
log.warning(
|
512
|
+
"Failed to save derived parameters to configuration file %s: %s",
|
513
|
+
config_file,
|
514
|
+
exc,
|
515
|
+
)
|
516
|
+
|
517
|
+
|
316
518
|
def overlay_missing(
|
317
519
|
primary: dict[str, str],
|
318
520
|
fallback: dict[str, str],
|