github2gerrit 0.1.7__tar.gz → 0.1.8__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.
Files changed (89) hide show
  1. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/PKG-INFO +1 -1
  2. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/cli.py +8 -13
  3. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/core.py +3 -10
  4. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/duplicate_detection.py +0 -2
  5. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/external_api.py +2 -3
  6. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/gitutils.py +4 -12
  7. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit.egg-info/PKG-INFO +1 -1
  8. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_duplicate_detection.py +1 -4
  9. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_external_api_framework.py +1 -2
  10. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_gerrit_rest_client.py +1 -4
  11. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_gitutils_helpers.py +1 -2
  12. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_url_parser.py +1 -1
  13. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.editorconfig +0 -0
  14. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/actionlint.yaml +0 -0
  15. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/dependabot.yml +0 -0
  16. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/release-drafter.yml +0 -0
  17. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/workflows/build-test-release.yaml +0 -0
  18. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/workflows/build-test.yaml +0 -0
  19. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/workflows/dependencies.yaml +0 -0
  20. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/workflows/github2gerrit.yaml +0 -0
  21. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/workflows/release-drafter.yaml +0 -0
  22. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/workflows/semantic-pull-request.yaml +0 -0
  23. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/workflows/sha-pinned-actions.yaml +0 -0
  24. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.github/workflows/testing.yaml +0 -0
  25. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.gitignore +0 -0
  26. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.gitlint +0 -0
  27. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.markdownlint.yaml +0 -0
  28. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.pre-commit-config.yaml +0 -0
  29. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.readthedocs.yml +0 -0
  30. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/.yamllint +0 -0
  31. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/LICENSE +0 -0
  32. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/LICENSES/Apache-2.0.txt +0 -0
  33. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/README.md +0 -0
  34. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/REUSE.toml +0 -0
  35. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/action.yaml +0 -0
  36. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/pyproject.toml +0 -0
  37. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/setup.cfg +0 -0
  38. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/sitecustomize.py +0 -0
  39. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/__init__.py +0 -0
  40. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/commit_normalization.py +0 -0
  41. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/config.py +0 -0
  42. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/gerrit_rest.py +0 -0
  43. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/gerrit_urls.py +0 -0
  44. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/github_api.py +0 -0
  45. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/models.py +0 -0
  46. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/pr_content_filter.py +0 -0
  47. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/similarity.py +0 -0
  48. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/ssh_agent_setup.py +0 -0
  49. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/ssh_common.py +0 -0
  50. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/ssh_discovery.py +0 -0
  51. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit/utils.py +0 -0
  52. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit.egg-info/SOURCES.txt +0 -0
  53. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit.egg-info/dependency_links.txt +0 -0
  54. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit.egg-info/entry_points.txt +0 -0
  55. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit.egg-info/requires.txt +0 -0
  56. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/src/github2gerrit.egg-info/top_level.txt +0 -0
  57. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/conftest.py +0 -0
  58. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/fixtures/__init__.py +0 -0
  59. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/fixtures/make_repo.py +0 -0
  60. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_change_id_deduplication.py +0 -0
  61. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_cli.py +0 -0
  62. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_cli_helpers.py +0 -0
  63. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_cli_outputs_file.py +0 -0
  64. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_cli_url_and_dryrun.py +0 -0
  65. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_commit_normalization.py +0 -0
  66. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_config_and_reviewers.py +0 -0
  67. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_config_helpers.py +0 -0
  68. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_core_close_pr_policy.py +0 -0
  69. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_core_config_and_errors.py +0 -0
  70. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_core_gerrit_backref_comment.py +0 -0
  71. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_core_gerrit_push_errors.py +0 -0
  72. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_core_gerrit_rest_results.py +0 -0
  73. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_core_integration_fixture_repo.py +0 -0
  74. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_core_prepare_commits.py +0 -0
  75. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_core_ssh_setup.py +0 -0
  76. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_gerrit_change_id_footer.py +0 -0
  77. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_gerrit_urls.py +0 -0
  78. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_gerrit_urls_more.py +0 -0
  79. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_ghe_and_gitreview_args.py +0 -0
  80. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_github_api_helpers.py +0 -0
  81. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_github_api_retry_and_helpers.py +0 -0
  82. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_misc_small_coverage.py +0 -0
  83. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_pr_content_filter.py +0 -0
  84. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_pr_content_filter_integration.py +0 -0
  85. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_ssh_agent.py +0 -0
  86. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_ssh_common.py +0 -0
  87. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_ssh_discovery.py +0 -0
  88. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/tests/test_utils.py +0 -0
  89. {github2gerrit-0.1.7 → github2gerrit-0.1.8}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github2gerrit
3
- Version: 0.1.7
3
+ Version: 0.1.8
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
@@ -3,10 +3,13 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import io
6
7
  import json
7
8
  import logging
8
9
  import os
10
+ import shutil
9
11
  import tempfile
12
+ import zipfile
10
13
  from collections.abc import Callable
11
14
  from concurrent.futures import ThreadPoolExecutor
12
15
  from concurrent.futures import as_completed
@@ -17,6 +20,8 @@ from typing import Protocol
17
20
  from typing import TypeVar
18
21
  from typing import cast
19
22
  from urllib.parse import urlparse
23
+ from urllib.request import Request
24
+ from urllib.request import urlopen
20
25
 
21
26
  import click
22
27
  import typer
@@ -290,14 +295,11 @@ def main(
290
295
  """
291
296
  Tool to convert GitHub pull requests into Gerrit changes
292
297
 
293
- - Providing a URL to a pull request: converts that pull request
294
- into a Gerrit change
298
+ - Providing a URL to a pull request: converts that pull request into a Gerrit change
295
299
 
296
- - Providing a URL to a GitHub repository converts all open pull
297
- requests into Gerrit changes
300
+ - Providing a URL to a GitHub repository converts all open pull requests into Gerrit changes
298
301
 
299
- - No arguments for CI/CD environment; reads parameters from
300
- environment variables
302
+ - No arguments for CI/CD environment; reads parameters from environment variables
301
303
  """
302
304
  # Override boolean parameters with properly parsed environment variables
303
305
  # This ensures that string "false" from GitHub Actions is handled correctly
@@ -746,13 +748,6 @@ def _prepare_local_checkout(workspace: Path, gh: GitHubContext, data: Inputs) ->
746
748
 
747
749
  def _fallback_to_api_archive(workspace: Path, gh: GitHubContext, data: Inputs, pr_num_str: str) -> None:
748
750
  """Fallback to GitHub API archive download for private repos."""
749
- import io
750
- import json
751
- import shutil
752
- import zipfile
753
- from urllib.request import Request
754
- from urllib.request import urlopen
755
-
756
751
  log.info("Attempting API archive fallback for PR #%s", pr_num_str)
757
752
 
758
753
  # Get GitHub token for authenticated requests
@@ -30,7 +30,10 @@ import logging
30
30
  import os
31
31
  import re
32
32
  import shlex
33
+ import shutil
34
+ import socket
33
35
  import stat
36
+ import tempfile
34
37
  import urllib.parse
35
38
  import urllib.request
36
39
  from collections.abc import Iterable
@@ -786,8 +789,6 @@ class Orchestrator:
786
789
 
787
790
  def _strategy_open_fchmod(self, key_path: Path, key_content: str) -> None:
788
791
  """Strategy: open with os.open and specific flags, then fchmod."""
789
- import os
790
- import stat
791
792
 
792
793
  flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
793
794
  mode = stat.S_IRUSR | stat.S_IWUSR # 0o600
@@ -801,7 +802,6 @@ class Orchestrator:
801
802
 
802
803
  def _strategy_umask_open(self, key_path: Path, key_content: str) -> None:
803
804
  """Strategy: set umask, create file, restore umask."""
804
- import os
805
805
 
806
806
  original_umask = os.umask(0o077) # Only owner can read/write
807
807
  try:
@@ -813,8 +813,6 @@ class Orchestrator:
813
813
 
814
814
  def _strategy_stat_constants(self, key_path: Path, key_content: str) -> None:
815
815
  """Strategy: use stat constants for permission setting."""
816
- import os
817
- import stat
818
816
 
819
817
  with open(key_path, "w", encoding="utf-8") as f:
820
818
  f.write(key_content)
@@ -826,13 +824,9 @@ class Orchestrator:
826
824
 
827
825
  def _create_key_in_memory_fs(self, key_path: Path, key_content: str) -> bool:
828
826
  """Fallback: try to create key in memory filesystem."""
829
- import shutil
830
- import tempfile
831
-
832
827
  try:
833
828
  # Try to create in memory filesystem if available
834
829
  # Use secure temporary directories
835
- import tempfile
836
830
 
837
831
  temp_dir = tempfile.gettempdir()
838
832
  memory_dirs = [temp_dir]
@@ -2343,7 +2337,6 @@ class Orchestrator:
2343
2337
  - Verify GitHub token by fetching repository and PR metadata
2344
2338
  - Do NOT perform any write operations
2345
2339
  """
2346
- import socket
2347
2340
 
2348
2341
  log.info("Dry-run: starting preflight checks")
2349
2342
  if os.getenv("G2G_DRYRUN_DISABLE_NETWORK", "").strip().lower() in (
@@ -289,8 +289,6 @@ class DuplicateDetector:
289
289
  Returns:
290
290
  Hex-encoded SHA256 hash string (first 16 characters for readability)
291
291
  """
292
- import hashlib
293
-
294
292
  # Build hash input from stable, unique PR identifiers
295
293
  # Use server_url + repository + pr_number for global uniqueness
296
294
  hash_input = f"{gh.server_url}/{gh.repository}/pull/{gh.pr_number}"
@@ -29,12 +29,14 @@ import functools
29
29
  import logging
30
30
  import random
31
31
  import socket
32
+ import subprocess
32
33
  import time
33
34
  import urllib.error
34
35
  from collections.abc import Callable
35
36
  from dataclasses import dataclass
36
37
  from dataclasses import field
37
38
  from enum import Enum
39
+ from pathlib import Path
38
40
  from typing import Any
39
41
  from typing import NoReturn
40
42
  from typing import TypeVar
@@ -441,9 +443,6 @@ def curl_download(
441
443
  Raises:
442
444
  RuntimeError: If curl command fails after retries
443
445
  """
444
- import subprocess
445
- from pathlib import Path
446
-
447
446
  if policy is None:
448
447
  policy = RetryPolicy(max_attempts=3, timeout=timeout)
449
448
 
@@ -11,8 +11,10 @@ from __future__ import annotations
11
11
 
12
12
  import logging
13
13
  import os
14
+ import re
14
15
  import shlex
15
16
  import subprocess
17
+ import tempfile
16
18
  import time
17
19
  from collections.abc import Callable
18
20
  from collections.abc import Iterable
@@ -481,9 +483,7 @@ def git_commit_amend(
481
483
  # Write message to a temp file to avoid shell-escaping issues
482
484
  tmp_path: Path | None = None
483
485
  if message is not None:
484
- import tempfile as _tempfile
485
-
486
- with _tempfile.NamedTemporaryFile("w", delete=False, encoding="utf-8") as _tf:
486
+ with tempfile.NamedTemporaryFile("w", delete=False, encoding="utf-8") as _tf:
487
487
  _tf.write(message)
488
488
  _tf.flush()
489
489
  tmp_path = Path(_tf.name)
@@ -493,9 +493,6 @@ def git_commit_amend(
493
493
  # Determine whether to add -s; only suppress if message already has a sign-off for current committer
494
494
  effective_signoff = bool(signoff)
495
495
  try:
496
- import os
497
- import re
498
-
499
496
  # Resolve committer email (prefer repo-local; fallback to global/env)
500
497
  committer_email = os.getenv("GIT_COMMITTER_EMAIL", "").strip()
501
498
  if not committer_email:
@@ -586,9 +583,7 @@ def git_commit_new(
586
583
  # Write message to a temp file to avoid shell-escaping issues
587
584
  tmp_path: Path | None = None
588
585
  if message is not None:
589
- import tempfile as _tempfile
590
-
591
- with _tempfile.NamedTemporaryFile("w", delete=False, encoding="utf-8") as _tf:
586
+ with tempfile.NamedTemporaryFile("w", delete=False, encoding="utf-8") as _tf:
592
587
  _tf.write(message)
593
588
  _tf.flush()
594
589
  tmp_path = Path(_tf.name)
@@ -598,9 +593,6 @@ def git_commit_new(
598
593
  # Determine whether to add -s; only suppress if message already has a sign-off for current committer
599
594
  effective_signoff = bool(signoff)
600
595
  try:
601
- import os
602
- import re
603
-
604
596
  # Resolve committer email (prefer repo-local; fallback to global/env)
605
597
  committer_email = os.getenv("GIT_COMMITTER_EMAIL", "").strip()
606
598
  if not committer_email:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github2gerrit
3
- Version: 0.1.7
3
+ Version: 0.1.8
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
@@ -3,6 +3,7 @@
3
3
 
4
4
  """Tests for duplicate change detection."""
5
5
 
6
+ import json
6
7
  from datetime import UTC
7
8
  from datetime import datetime
8
9
  from datetime import timedelta
@@ -373,8 +374,6 @@ project=test/project.git
373
374
  target_pr.get_files.return_value = []
374
375
 
375
376
  # Mock Gerrit REST API response with matching subject
376
- import json
377
-
378
377
  gerrit_response = [
379
378
  {
380
379
  "_number": 12345,
@@ -420,8 +419,6 @@ project=test/project.git
420
419
  target_pr.get_files.return_value = []
421
420
 
422
421
  # Mock Gerrit REST API response with matching subject
423
- import json
424
-
425
422
  gerrit_response = [
426
423
  {
427
424
  "_number": 12345,
@@ -3,6 +3,7 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import logging
6
7
  import urllib.error
7
8
  from io import StringIO
8
9
  from pathlib import Path
@@ -209,8 +210,6 @@ def test_curl_download_failure(monkeypatch: pytest.MonkeyPatch, tmp_path: Path)
209
210
 
210
211
  def test_log_api_metrics_summary(monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture) -> None:
211
212
  """Test API metrics summary logging."""
212
- import logging
213
-
214
213
  # Set up some metrics
215
214
  reset_api_metrics()
216
215
 
@@ -7,6 +7,7 @@ import contextlib
7
7
  import io
8
8
  import os
9
9
  import stat
10
+ import urllib.error
10
11
  from collections.abc import Callable
11
12
  from email.message import Message
12
13
  from pathlib import Path
@@ -93,8 +94,6 @@ def test_gerrit_rest_retries_on_http_503_then_succeeds(
93
94
  monkeypatch.setattr("github2gerrit.gerrit_rest._PygerritRestApi", None, raising=True)
94
95
  monkeypatch.setattr("time.sleep", lambda s: None, raising=False)
95
96
 
96
- import urllib.error
97
-
98
97
  # Prepare sequence: 503 error twice, then 200 OK
99
98
  attempts: dict[str, int] = {"n": 0}
100
99
 
@@ -144,8 +143,6 @@ def test_gerrit_rest_retries_on_urlerror_timeout_then_fails(
144
143
  monkeypatch.setattr("github2gerrit.gerrit_rest._PygerritRestApi", None, raising=True)
145
144
  monkeypatch.setattr("time.sleep", lambda s: None, raising=False)
146
145
 
147
- import urllib.error
148
-
149
146
  attempts: dict[str, int] = {"n": 0}
150
147
 
151
148
  def _fake_urlopen(req: Any, timeout: float | None = None) -> _DummyResp:
@@ -8,6 +8,7 @@ from pathlib import Path
8
8
 
9
9
  import pytest
10
10
 
11
+ import github2gerrit.gitutils as gitutils
11
12
  from github2gerrit.gitutils import CommandError
12
13
  from github2gerrit.gitutils import CommandResult
13
14
  from github2gerrit.gitutils import git
@@ -203,8 +204,6 @@ def test_git_last_commit_trailers_parsing_edge_cases(
203
204
  monkeypatch: pytest.MonkeyPatch,
204
205
  ) -> None:
205
206
  # Patch git_show to return a crafted commit message body with various trailer forms.
206
- import github2gerrit.gitutils as gitutils
207
-
208
207
  body = (
209
208
  "Subject line\n\n"
210
209
  "Body content with Key: Value in the middle\n"
@@ -68,5 +68,5 @@ from github2gerrit.cli import _parse_github_target
68
68
  ("https://github.com", (None, None, None)),
69
69
  ],
70
70
  )
71
- def test_parse_github_target(url: str, expected: tuple[object, object, object]) -> None:
71
+ def test_parse_github_target(url: str, expected: tuple[str | None, str | None, int | None]) -> None:
72
72
  assert _parse_github_target(url) == expected
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes