← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~jugmac00/lpcraft:add-more-hooks into lpcraft:main

 

Jürgen Gmach has proposed merging ~jugmac00/lpcraft:add-more-hooks into lpcraft:main.

Commit message:
Add more hooks

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~jugmac00/lpcraft/+git/lpcraft/+merge/413393

This MP is WIP.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~jugmac00/lpcraft:add-more-hooks into lpcraft:main.
diff --git a/docs/plugins.rst b/docs/plugins.rst
index 4c644d6..ca26449 100644
--- a/docs/plugins.rst
+++ b/docs/plugins.rst
@@ -20,3 +20,8 @@ Plugin discovery
 
 As currently only builtin plugins are supported,
 you need to define a plugin in ``lpcraft/plugins/<plugin>``.
+
+
+.. comments
+
+    XXX jugmac00 2021-12-17: render all available hooks via plugin
diff --git a/lpcraft/commands/run.py b/lpcraft/commands/run.py
index 3c53eef..6985d7c 100644
--- a/lpcraft/commands/run.py
+++ b/lpcraft/commands/run.py
@@ -168,12 +168,30 @@ def _run_job(
     host_architecture = get_host_architecture()
     if host_architecture not in job.architectures:
         return
-    if job.run is None:
+    pm = get_plugin_manager(job)
+    run_from_plugin = pm.hook.lpcraft_execute_run()
+    run_from_configuration = job.run
+    if run_from_configuration is not None:
+        run_commands = run_from_plugin + [run_from_configuration]
+    else:
+        run_commands = run_from_plugin
+
+    if len(run_commands) == 0:
         raise CommandError(
             f"Job {job_name!r} for {job.series}/{host_architecture} "
             f"does not set 'run'"
         )
 
+    if len(run_commands) > 1:
+        raise CommandError(
+            f"Job {job_name!r} for {job.series}/{host_architecture} "
+            f"sets more than one 'run' command. "
+            f"Maybe you have set a run command both in the configuration "
+            f"and in a plugin?"
+        )
+
+    run_command = run_commands[0]
+
     cwd = Path.cwd()
     remote_cwd = env.get_managed_environment_project_path()
 
@@ -186,16 +204,15 @@ def _run_job(
         series=job.series,
         architecture=host_architecture,
     ) as instance:
-        if job.snaps:
-            for snap in job.snaps:
-                emit.progress(f"Running `snap install {snap}`")
-                install_from_store(
-                    executor=instance,
-                    snap_name=snap,
-                    channel="stable",
-                    classic=True,
-                )
-        pm = get_plugin_manager(job)
+        snaps = list(itertools.chain(*pm.hook.lpcraft_install_snaps()))
+        for snap in snaps:
+            emit.progress(f"Running `snap install {snap}`")
+            install_from_store(
+                executor=instance,
+                snap_name=snap,
+                channel="stable",
+                classic=True,
+            )
         packages = list(itertools.chain(*pm.hook.lpcraft_install_packages()))
         if packages:
             packages_cmd = ["apt", "install", "-y"] + packages
@@ -208,11 +225,11 @@ def _run_job(
                     stdout=stream,
                     stderr=stream,
                 )
-        run_cmd = ["bash", "--noprofile", "--norc", "-ec", job.run]
+        full_run_cmd = ["bash", "--noprofile", "--norc", "-ec", run_command]
         emit.progress("Running the job")
-        with emit.open_stream(f"Running {run_cmd}") as stream:
+        with emit.open_stream(f"Running {full_run_cmd}") as stream:
             proc = instance.execute_run(
-                run_cmd,
+                full_run_cmd,
                 cwd=remote_cwd,
                 env=job.environment,
                 stdout=stream,
diff --git a/lpcraft/plugin/hookspecs.py b/lpcraft/plugin/hookspecs.py
index f16f909..2219e31 100644
--- a/lpcraft/plugin/hookspecs.py
+++ b/lpcraft/plugin/hookspecs.py
@@ -11,3 +11,13 @@ hookspec = pluggy.HookspecMarker("lpcraft")
 @hookspec  # type: ignore
 def lpcraft_install_packages() -> list[str]:
     """system packages to be installed"""
+
+
+@hookspec  # type: ignore
+def lpcraft_install_snaps() -> list[str]:
+    """snaps to be installed"""
+
+
+@hookspec  # type: ignore
+def lpcraft_execute_run() -> str:
+    """command to be executed"""
diff --git a/lpcraft/plugin/lib.py b/lpcraft/plugin/lib.py
index b22be5a..ced3cdd 100644
--- a/lpcraft/plugin/lib.py
+++ b/lpcraft/plugin/lib.py
@@ -23,3 +23,9 @@ class InternalPlugins:
         if self.config.packages:
             return self.config.packages
         return []
+
+    @hookimpl  # type: ignore
+    def lpcraft_install_snaps(self) -> list[str]:
+        if self.config.snaps:
+            return self.config.snaps
+        return []
diff --git a/lpcraft/plugin/tests/test_builtin_plugins.py b/lpcraft/plugin/tests/test_plugins.py
similarity index 82%
rename from lpcraft/plugin/tests/test_builtin_plugins.py
rename to lpcraft/plugin/tests/test_plugins.py
index 79b0a1d..d72a50c 100644
--- a/lpcraft/plugin/tests/test_builtin_plugins.py
+++ b/lpcraft/plugin/tests/test_plugins.py
@@ -9,12 +9,12 @@ from unittest.mock import ANY, Mock, call, patch
 from craft_providers.lxd import LXC, launch
 
 from lpcraft.commands.tests import CommandBaseTestCase
-from lpcraft.errors import YAMLError
+from lpcraft.errors import CommandError, YAMLError
 from lpcraft.providers._lxd import LXDProvider, _LXDLauncher
 from lpcraft.providers.tests import FakeLXDInstaller
 
 
-class TestBuiltinPlugins(CommandBaseTestCase):
+class TestPlugins(CommandBaseTestCase):
     def makeLXDProvider(
         self,
         is_ready=True,
@@ -39,9 +39,6 @@ class TestBuiltinPlugins(CommandBaseTestCase):
         mock_get_provider.return_value = provider
         execute_run = launcher.return_value.execute_run
         execute_run.return_value = subprocess.CompletedProcess([], 0)
-        # XXX jugmac00 2021-12-16: the current implementation of the `tox`
-        # plugin does not provide a run command, so we need to specify it
-        # here in the configuration file
         config = dedent(
             """
             pipeline:
@@ -52,7 +49,6 @@ class TestBuiltinPlugins(CommandBaseTestCase):
                     series: focal
                     architectures: amd64
                     packages: [nginx, apache2]
-                    run: tox
                     plugin: tox
             """
         )
@@ -110,3 +106,45 @@ class TestBuiltinPlugins(CommandBaseTestCase):
 
         self.assertEqual(1, result.exit_code)
         self.assertEqual([YAMLError("Unknown plugin")], result.errors)
+
+    @patch("lpcraft.commands.run.get_provider")
+    @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64")
+    def test_defining_two_run_commands_errors(
+        self, mock_get_host_architecture, mock_get_provider
+    ):
+        # defining a run command both in the configuration file and
+        # in a plugin results in an error
+        launcher = Mock(spec=launch)
+        provider = self.makeLXDProvider(lxd_launcher=launcher)
+        mock_get_provider.return_value = provider
+        execute_run = launcher.return_value.execute_run
+        execute_run.return_value = subprocess.CompletedProcess([], 0)
+        config = dedent(
+            """
+            pipeline:
+                - test
+
+            jobs:
+                test:
+                    series: focal
+                    architectures: amd64
+                    packages: [nginx, apache2]
+                    run: tox
+                    plugin: tox
+            """
+        )
+        Path(".launchpad.yaml").write_text(config)
+
+        result = self.run_command("run")
+
+        self.assertEqual(1, result.exit_code)
+        self.assertEqual(
+            result.errors,
+            [
+                CommandError(
+                    "Job 'test' for focal/amd64 sets more than one 'run' "
+                    "command. Maybe you have set a run command both in the "
+                    "configuration and in a plugin?"
+                )
+            ],
+        )
diff --git a/lpcraft/plugins/tox.py b/lpcraft/plugins/tox.py
index 2952371..03b3b9a 100644
--- a/lpcraft/plugins/tox.py
+++ b/lpcraft/plugins/tox.py
@@ -15,3 +15,7 @@ class ToxPlugin:
     @hookimpl  # type: ignore
     def lpcraft_install_packages(self) -> list[str]:
         return ["tox"]
+
+    @hookimpl  # type: ignore
+    def lpcraft_execute_run(self) -> str:
+        return "tox"

Follow ups