Skip to content

Commit 0a48c6b

Browse files
committed
refactor copr repo provider to facilitate listing packages
1 parent a6c2c6e commit 0a48c6b

File tree

3 files changed

+64
-8
lines changed

3 files changed

+64
-8
lines changed

tests/unit/artifact/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from tmt.steps.prepare.artifact import get_artifact_provider
77
from tmt.steps.prepare.artifact.providers import koji as koji_module
88
from tmt.steps.prepare.artifact.providers.brew import BrewArtifactProvider
9+
from tmt.steps.prepare.artifact.providers.copr_build import CoprBuildArtifactProvider
910
from tmt.steps.prepare.artifact.providers.koji import KojiArtifactProvider
1011

1112

@@ -50,6 +51,13 @@ def mock_initialize_session(self, api_url=None, top_url=None):
5051
yield mock_koji
5152

5253

54+
@pytest.fixture
55+
def mock_copr():
56+
mock_session = MagicMock()
57+
with patch.object(CoprBuildArtifactProvider, '_initialize_session', return_value=mock_session):
58+
yield mock_session
59+
60+
5361
@pytest.fixture
5462
def artifact_provider(root_logger, _load_plugins):
5563
def get_provider(provider_id: str, repository_priority: int = 50):

tests/unit/artifact/test_copr_repository.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from unittest.mock import MagicMock
1+
from unittest.mock import MagicMock, patch
22

33
import pytest
44

@@ -28,13 +28,23 @@ def test_invalid_repository_id(raw_id, error, artifact_provider):
2828
artifact_provider(raw_id)
2929

3030

31-
def test_fetch_contents_enables_repository(mock_package_manager, artifact_provider, tmppath):
31+
def test_fetch_contents_enables_repository(
32+
mock_copr, mock_package_manager, artifact_provider, tmppath
33+
):
34+
mock_repo = MagicMock()
3235
mock_guest = MagicMock()
3336
mock_guest.package_manager = mock_package_manager
3437

3538
provider = artifact_provider("copr.repository:@teemtee/stable")
3639

37-
result = provider.fetch_contents(mock_guest, tmppath / "artifacts")
40+
with patch(
41+
'tmt.steps.prepare.artifact.providers.copr_repository.Repository.from_file_path',
42+
return_value=mock_repo,
43+
) as mock_from_file:
44+
result = provider.fetch_contents(mock_guest, tmppath / "artifacts")
3845

3946
mock_package_manager.enable_copr.assert_called_once_with('@teemtee/stable')
47+
mock_guest.pull.assert_called_once()
48+
mock_from_file.assert_called_once()
4049
assert result == []
50+
assert provider.repository is mock_repo

tmt/steps/prepare/artifact/providers/copr_repository.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,44 @@
99
from typing import Optional
1010

1111
import tmt.log
12+
import tmt.utils
13+
from tmt.container import container
1214
from tmt.guest import Guest
1315
from tmt.steps.prepare.artifact.providers import (
1416
ArtifactInfo,
1517
ArtifactProvider,
1618
ArtifactProviderId,
19+
Repository,
1720
UnsupportedOperationError,
1821
provides_artifact_provider,
1922
)
2023
from tmt.utils import Path
2124

25+
COPR_REPO_PATTERN = re.compile(r'^(?P<group>@)?(?P<name>[^/]+)/(?P<project>[^/]+)$')
2226
COPR_REPOSITORY_PATTERN = re.compile(r'^(?:@[^/]+/[^/]+|[^@/]+/[^/]+)$')
2327

2428

29+
@container(frozen=True)
30+
class CoprRepo:
31+
is_group: bool
32+
name: str
33+
project: str
34+
35+
36+
def parse_copr_repo(copr_repo: str) -> CoprRepo:
37+
"""
38+
Parse a COPR repository identifier into its components.
39+
"""
40+
matched = COPR_REPO_PATTERN.match(copr_repo)
41+
if not matched:
42+
raise tmt.utils.PrepareError(f"Invalid copr repository '{copr_repo}'.")
43+
return CoprRepo(
44+
is_group=bool(matched.group('group')),
45+
name=matched.group('name'),
46+
project=matched.group('project'),
47+
)
48+
49+
2550
@provides_artifact_provider('copr.repository')
2651
class CoprRepositoryProvider(ArtifactProvider):
2752
"""
@@ -41,10 +66,12 @@ class CoprRepositoryProvider(ArtifactProvider):
4166
"""
4267

4368
copr_repo: str # Parsed Copr repository name (e.g. 'packit/packit-dev')
69+
repository: Optional[Repository]
4470

4571
def __init__(self, raw_id: str, repository_priority: int, logger: tmt.log.Logger):
4672
super().__init__(raw_id, repository_priority, logger)
4773
self.copr_repo = self.id
74+
self.repository = None
4875

4976
@classmethod
5077
def _extract_provider_id(cls, raw_id: str) -> ArtifactProviderId:
@@ -83,11 +110,22 @@ def fetch_contents(
83110
exclude_patterns: Optional[list[Pattern[str]]] = None,
84111
) -> list[Path]:
85112
"""
86-
Enable the Copr repository on the guest.
87-
88-
:return: Empty list as no files are downloaded.
89-
:raises tmt.utils.PrepareError: If the package manager does not support
90-
enabling Copr repositories.
113+
Enable the Copr repository on the guest and retrieve the resulting
114+
``.repo`` file content.
91115
"""
92116
guest.package_manager.enable_copr(self.copr_repo)
117+
118+
repo = parse_copr_repo(self.copr_repo)
119+
owner = f'group_{repo.name}' if repo.is_group else repo.name
120+
repo_filename = f"_copr:copr.fedorainfracloud.org:{owner}:{repo.project}.repo"
121+
122+
# pull from guest to build Repository object with populated repo_ids
123+
guest.pull(
124+
source=Path(f"/etc/yum.repos.d/{repo_filename}"),
125+
destination=download_path,
126+
)
127+
128+
self.repository = Repository.from_file_path(
129+
download_path / repo_filename, self.logger, name=self.copr_repo
130+
)
93131
return []

0 commit comments

Comments
 (0)