Skip to content
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
27 changes: 26 additions & 1 deletion craft_application/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import annotated_types
import craft_cli
import craft_platforms
import craft_providers
from platformdirs import user_cache_path

Expand Down Expand Up @@ -573,7 +574,8 @@ def register_plugins(self, plugins: dict[str, PluginType]) -> None:

def _register_default_plugins(self) -> None:
"""Register per application plugins when initializing."""
self.register_plugins(self._get_app_plugins())
with warnings.catch_warnings():
Comment thread
lengau marked this conversation as resolved.
self.register_plugins(self._get_app_plugins())

def _pre_run(self, dispatcher: craft_cli.Dispatcher) -> None:
"""Do any final setup before running the command.
Expand Down Expand Up @@ -775,7 +777,30 @@ def _setup_logging(self) -> None:
def _enable_craft_parts_features(self) -> None:
"""Enable any specific craft-parts Feature that the application will need."""

@final
def _set_plugin_group(self) -> None:
"""Set the plugin group from the lifecycle service.

If no plugin group is provided or an error occurs while determining
the build info, no plugin group is registered.
"""
try:
build_plan = self.services.get("build_plan").plan()
except (craft_cli.CraftError, craft_platforms.CraftPlatformsError):
# We can do this here because when we start the lifecycle
# we actually exit the app if there's an error.
craft_cli.emit.debug("No plugin group registered due to error.")
return
group = self.services.get_class("lifecycle").get_plugin_group(build_plan[0])

# We don't need to import this unless we have a group to set.
from craft_parts.plugins import set_plugin_group # noqa: PLC0415

if group:
set_plugin_group(group)

def _initialize_craft_parts(self) -> None:
"""Perform craft-parts-specific initialization, like features and plugins."""
self._enable_craft_parts_features()
self._register_default_plugins()
self._set_plugin_group()
2 changes: 1 addition & 1 deletion tests/spread/witchcraft/plugin-groups/task.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ execute: |

# Check that the package referencing a plugin not used on this system does not pack.
cd "invalid-${DISTRO}"
witchcraft pack --destructive-mode |& MATCH "Plugin '[a-z]+' in part 'part1' is not registered."
witchcraft pack --destructive-mode |& MATCH "plugin not registered: '[a-z]+' \(in field 'parts.part1',"


restore: |
Expand Down
58 changes: 58 additions & 0 deletions tests/unit/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,64 @@ def test_register_plugins_default(mocker, app_metadata, fake_services):
assert reg.call_count == 0


def test_set_plugin_group(mocker, app_metadata, fake_services):
"""Test that we set the plugin group early."""
mock_set = mocker.patch("craft_parts.plugins.set_plugin_group")
mock_get_plugin_group = mocker.patch.object(
fake_services.get_class("lifecycle"), "get_plugin_group"
)

app = FakeApplication(app_metadata, fake_services)
with pytest.raises(SystemExit):
app.run()

mock_set.assert_called_once_with(mock_get_plugin_group.return_value)


def test_set_plugin_group_empty(mocker, app_metadata, fake_services):
"""Test that we set the plugin group early."""
mock_set = mocker.patch("craft_parts.plugins.set_plugin_group")
mock_get_plugin_group = mocker.patch.object(
fake_services.get_class("lifecycle"), "get_plugin_group", return_value=None
)

app = FakeApplication(app_metadata, fake_services)
with pytest.raises(SystemExit):
app.run()

mock_get_plugin_group.assert_called_once()
mock_set.assert_not_called()


@pytest.mark.parametrize(
"planning_error",
[
craft_cli.CraftError("General craft error"),
craft_platforms.CraftPlatformsError("Platforms error"),
craft_application.errors.CraftValidationError("Validation!"),
],
)
def test_set_plugin_group_ignores_errors(
mocker, app_metadata, fake_services, planning_error
):
"""Test that we set the plugin group early."""
mock_set = mocker.patch("craft_parts.plugins.set_plugin_group")
mock_get_plugin_group = mocker.patch.object(
fake_services.get_class("lifecycle"), "get_plugin_group", return_value=None
)
mock_planner = mocker.patch.object(
fake_services.get("build_plan"), "plan", side_effect=planning_error
)

app = FakeApplication(app_metadata, fake_services)
with pytest.raises(SystemExit):
app.run()

mock_planner.assert_called_once()
mock_get_plugin_group.assert_not_called()
mock_set.assert_not_called()


@pytest.fixture
def grammar_project_mini(tmp_path):
"""A project that builds on amd64 to riscv64 and s390x."""
Expand Down