Skip to content

Commit 28ef586

Browse files
dralleymdellweg
authored andcommitted
Add --version option to rpm and file distribution commands
Allow setting repository_version on distributions for publication-based plugins (rpm, file), matching the capability added in pulpcore 3.106.0. Assisted-By: Claude Opus 4.6
1 parent 3d24c23 commit 28ef586

7 files changed

Lines changed: 167 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added the `--version` option to rpm and file distribution create and update commands to pin a specific repository version for distribution.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added support for setting `repository_version` on rpm and file distributions via a `version` parameter.

pulp-glue/src/pulp_glue/file/context.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
PulpRepositoryVersionContext,
1313
api_spec_quirk,
1414
)
15+
from pulp_glue.common.exceptions import PulpException
1516
from pulp_glue.common.i18n import get_translation
1617

1718
translation = get_translation(__package__)
@@ -87,6 +88,23 @@ def preprocess_entity(self, body: EntityDefinition, partial: bool = False) -> En
8788
body["publication"] = None
8889
if "repository" not in body and "publication" in body:
8990
body["repository"] = None
91+
92+
version = body.pop("version", None)
93+
if version is not None:
94+
self.pulp_ctx.needs_plugin(PluginRequirement("core", specifier=">=3.106.0"))
95+
repository_href = body.pop("repository", None)
96+
if repository_href is None and partial:
97+
repository_href = self.entity.get("repository")
98+
if repository_href is None:
99+
raise PulpException(_("--repository must be provided"))
100+
body["repository_version"] = f"{repository_href}versions/{version}/"
101+
body["repository"] = None
102+
body["publication"] = None
103+
elif "repository" in body and self.pulp_ctx.has_plugin(
104+
PluginRequirement("core", specifier=">=3.106.0")
105+
):
106+
body["repository_version"] = None
107+
90108
return body
91109

92110

pulp-glue/src/pulp_glue/rpm/context.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
PulpViewSetContext,
1616
api_spec_quirk,
1717
)
18+
from pulp_glue.common.exceptions import PulpException
1819
from pulp_glue.common.i18n import get_translation
1920

2021
translation = get_translation(__package__)
@@ -86,6 +87,23 @@ def preprocess_entity(self, body: EntityDefinition, partial: bool = False) -> En
8687
body["publication"] = None
8788
if "repository" not in body and "publication" in body:
8889
body["repository"] = None
90+
91+
version = body.pop("version", None)
92+
if version is not None:
93+
self.pulp_ctx.needs_plugin(PluginRequirement("core", specifier=">=3.106.0"))
94+
repository_href = body.pop("repository", None)
95+
if repository_href is None and partial:
96+
repository_href = self.entity.get("repository")
97+
if repository_href is None:
98+
raise PulpException(_("--repository must be provided"))
99+
body["repository_version"] = f"{repository_href}versions/{version}/"
100+
body["repository"] = None
101+
body["publication"] = None
102+
elif "repository" in body and self.pulp_ctx.has_plugin(
103+
PluginRequirement("core", specifier=">=3.106.0")
104+
):
105+
body["repository_version"] = None
106+
89107
if body.get("generate_repo_config") is False:
90108
self.pulp_ctx.needs_plugin(
91109
PluginRequirement(
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import json
2+
import typing as t
3+
4+
import pytest
5+
6+
from pulp_glue.common import context as context_module
7+
from pulp_glue.common.context import PulpContext, PulpDistributionContext
8+
from pulp_glue.common.exceptions import PulpException
9+
from pulp_glue.common.openapi import OpenAPI
10+
from pulp_glue.file.context import PulpFileDistributionContext
11+
from pulp_glue.rpm.context import PulpRpmDistributionContext
12+
13+
pytestmark = pytest.mark.glue
14+
15+
REPO_HREF = "/pulp/api/v3/repositories/rpm/rpm/01234567-0123-0123-0123-0123456789ab/"
16+
REPO_VERSION_HREF = REPO_HREF + "versions/5/"
17+
PUBLICATION_HREF = "/pulp/api/v3/publications/rpm/rpm/01234567-0123-0123-0123-0123456789ab/"
18+
19+
20+
@pytest.fixture
21+
def modern_pulp_ctx(monkeypatch: pytest.MonkeyPatch) -> PulpContext:
22+
spec = json.dumps(
23+
{
24+
"openapi": "3.0.3",
25+
"info": {
26+
"title": "test",
27+
"version": "0.0.0",
28+
"x-pulp-app-versions": {"core": "3.106.0", "rpm": "3.30.0", "file": "3.75.0"},
29+
},
30+
"paths": {},
31+
}
32+
)
33+
monkeypatch.setattr(context_module, "_patch_api_hook", lambda spec: spec)
34+
monkeypatch.setattr(OpenAPI, "load_api", lambda self, refresh_cache: self._parse_api(spec))
35+
monkeypatch.setattr(
36+
OpenAPI,
37+
"_send_request",
38+
lambda *args, **kwargs: pytest.fail("No API calls allowed in unit tests."),
39+
)
40+
settings: dict[str, t.Any] = {"base_url": "nowhere"}
41+
return PulpContext.from_config(settings)
42+
43+
44+
@pytest.mark.parametrize(
45+
"ctx_cls",
46+
[PulpRpmDistributionContext, PulpFileDistributionContext],
47+
ids=["rpm", "file"],
48+
)
49+
class TestDistributionPreprocess:
50+
def test_version_with_repository(
51+
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
52+
) -> None:
53+
ctx = ctx_cls(modern_pulp_ctx)
54+
body = ctx.preprocess_entity({"version": 5, "repository": REPO_HREF}, partial=False)
55+
assert body["repository_version"] == REPO_VERSION_HREF
56+
assert body["repository"] is None
57+
assert body["publication"] is None
58+
assert "version" not in body
59+
60+
def test_repository_without_version(
61+
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
62+
) -> None:
63+
ctx = ctx_cls(modern_pulp_ctx)
64+
body = ctx.preprocess_entity({"repository": REPO_HREF}, partial=False)
65+
assert body["repository"] == REPO_HREF
66+
assert body["repository_version"] is None
67+
assert body["publication"] is None
68+
69+
def test_publication_without_repository(
70+
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
71+
) -> None:
72+
ctx = ctx_cls(modern_pulp_ctx)
73+
body = ctx.preprocess_entity({"publication": PUBLICATION_HREF}, partial=False)
74+
assert body["publication"] == PUBLICATION_HREF
75+
assert body["repository"] is None
76+
assert body["repository_version"] is None
77+
78+
def test_version_without_repository_raises(
79+
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
80+
) -> None:
81+
ctx = ctx_cls(modern_pulp_ctx)
82+
with pytest.raises(PulpException, match="--repository"):
83+
ctx.preprocess_entity({"version": 5}, partial=False)
84+
85+
def test_version_without_repository_partial_raises(
86+
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
87+
) -> None:
88+
ctx = ctx_cls(modern_pulp_ctx)
89+
ctx._entity = {"repository": None, "repository_version": None}
90+
with pytest.raises(PulpException, match="--repository"):
91+
ctx.preprocess_entity({"version": 5}, partial=True)
92+
93+
def test_partial_version_infers_repository(
94+
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
95+
) -> None:
96+
ctx = ctx_cls(modern_pulp_ctx)
97+
ctx._entity = {"repository": REPO_HREF, "repository_version": None}
98+
body = ctx.preprocess_entity({"version": 3}, partial=True)
99+
assert body["repository_version"] == REPO_HREF + "versions/3/"
100+
assert body["repository"] is None
101+
assert body["publication"] is None
102+
103+
def test_unrelated_field_no_nullification(
104+
self, modern_pulp_ctx: PulpContext, ctx_cls: type[PulpDistributionContext]
105+
) -> None:
106+
ctx = ctx_cls(modern_pulp_ctx)
107+
body = ctx.preprocess_entity({"base_path": "/new/path"}, partial=True)
108+
assert body.get("base_path") == "/new/path"
109+
assert "repository" not in body
110+
assert "repository_version" not in body
111+
assert "publication" not in body

src/pulpcore/cli/file/distribution.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ def distribution() -> None:
6363
),
6464
),
6565
repository_option,
66+
pulp_option(
67+
"--version",
68+
type=int,
69+
help=_(
70+
"The repository version number to distribute."
71+
" When unset, the latest version of the repository will be auto-distributed."
72+
),
73+
needs_plugins=[PluginRequirement("core", specifier=">=3.106.0")],
74+
),
6675
content_guard_option,
6776
pulp_labels_option,
6877
pulp_option(

src/pulpcore/cli/rpm/distribution.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ def distribution() -> None:
6464
help=_("Option specifying whether ``*.repo`` files will be generated and served."),
6565
),
6666
repository_option,
67+
pulp_option(
68+
"--version",
69+
type=int,
70+
help=_(
71+
"The repository version number to distribute."
72+
" When unset, the latest version of the repository will be auto-distributed."
73+
),
74+
needs_plugins=[PluginRequirement("core", specifier=">=3.106.0")],
75+
),
6776
content_guard_option,
6877
pulp_labels_option,
6978
pulp_option(

0 commit comments

Comments
 (0)