Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(rust_plugin): fix dependency validation logic #903

Merged
merged 4 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions craft_parts/plugins/rust_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,14 @@ def validate_environment(

:param part_dependencies: A list of the parts this part depends on.
"""
if "rust-deps" in (part_dependencies or []):
options = cast(RustPluginProperties, self._options)
if options.rust_channel and options.rust_channel != "none":
options = cast(RustPluginProperties, self._options)
has_rust_deps = "rust-deps" in (part_dependencies or [])
if has_rust_deps or options.rust_channel == "none":
lengau marked this conversation as resolved.
Show resolved Hide resolved
if (
has_rust_deps
and options.rust_channel
and options.rust_channel != "none"
):
raise validator.errors.PluginEnvironmentValidationError(
part_name=self._part_name,
reason="rust-deps can not be used"
Expand All @@ -99,6 +104,14 @@ def validate_environment(
plugin_name="rust",
part_dependencies=part_dependencies,
)
return
# Check if rustup is properly installed
self.validate_dependency(
dependency="rustup",
argument="dump-testament",
plugin_name="rust",
part_dependencies=part_dependencies,
)


class RustPlugin(Plugin):
Expand Down Expand Up @@ -172,6 +185,8 @@ def get_build_snaps(self) -> set[str]:
if not options.rust_channel and self._check_system_rust():
logger.info("Rust is installed on the system, skipping rustup")
return set()
if options.rust_channel == "none" or "rust-deps" in (options.after or []):
return set()
return {"rustup"}

@override
Expand Down
62 changes: 62 additions & 0 deletions tests/unit/plugins/test_rust_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from craft_parts.infos import PartInfo, ProjectInfo
from craft_parts.parts import Part
from craft_parts.plugins.rust_plugin import RustPlugin, RustPluginProperties
from craft_parts.plugins.validator import PluginEnvironmentValidator
from pydantic import ValidationError


Expand All @@ -32,6 +33,17 @@ def part_info(new_dir):
)


@pytest.fixture
def mock_validator(monkeypatch):
def fake_execute(self, cmd: str):
return subprocess.check_output( # noqa: S602
cmd,
shell=True,
)

monkeypatch.setattr(PluginEnvironmentValidator, "_execute", fake_execute)


@pytest.mark.parametrize(
"rust_channel",
[
Expand Down Expand Up @@ -262,6 +274,56 @@ def callback_fail():
]


@pytest.mark.parametrize("after", [["something-else"], []])
def test_validate_environment_should_have_rustup(
after, fake_process: pytest_subprocess.FakeProcess, mock_validator
):
def exec_fail(x):
raise subprocess.CalledProcessError(127, x)

fake_process.register(
["cargo", "--version"], stdout="cargo 1.84.0-nightly (69e595908 2024-11-16)"
)
fake_process.register(
["rustc", "--version"], stdout="rustc 1.84.0-nightly (5ec7d6eee 2024-11-17)"
)
fake_process.register(["rustup", "dump-testament"], callback=exec_fail)

properties = RustPlugin.properties_class.unmarshal({"source": ".", "after": after})
validator = RustPlugin.validator_class(
part_name="my-part", properties=properties, env=""
)
# positive test
validator.validate_environment(part_dependencies=["rust-deps"])
# negative test
with pytest.raises(PluginEnvironmentValidationError):
validator.validate_environment(part_dependencies=after)


def test_validate_environment_should_not_have_rustup(
fake_process: pytest_subprocess.FakeProcess, mock_validator
):
def exec_fail(x):
raise subprocess.CalledProcessError(127, x)

fake_process.register(["cargo", "--version"], callback=exec_fail)
fake_process.register(["rustc", "--version"], callback=exec_fail)
fake_process.register(
["rustup", "dump-testament"],
stdout="Rustup version renders as: 1.27.1 (2024-05-07)",
)

properties = RustPlugin.properties_class.unmarshal(
{"source": ".", "after": ["rust-deps"]}
)
validator = RustPlugin.validator_class(
part_name="my-part", properties=properties, env=""
)
# positive test
validator.validate_environment(part_dependencies=[])
validator.validate_environment(part_dependencies=["rust-deps"])


def test_get_out_of_source_build(part_info):
properties = RustPlugin.properties_class.unmarshal({"source": "."})
plugin = RustPlugin(properties=properties, part_info=part_info)
Expand Down