diff --git a/docs/requirements.txt b/docs/requirements.txt
index 04a7cde9cc..2934552d82 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,7 +1,7 @@
attrs==23.1.0
catkin-pkg==0.5.2
click==8.1.7
-craft-application==2.3.0
+craft-application==2.4.0
craft-archives==1.1.3
craft-cli==2.5.1
craft-grammar==1.1.1
diff --git a/requirements-devel.txt b/requirements-devel.txt
index a2b0559177..a8eb933313 100644
--- a/requirements-devel.txt
+++ b/requirements-devel.txt
@@ -11,7 +11,7 @@ click==8.1.7
codespell==2.2.6
colorama==0.4.6
coverage==7.4.4
-craft-application==2.3.0
+craft-application==2.4.0
craft-archives==1.1.3
craft-cli==2.5.1
craft-grammar==1.1.2
diff --git a/requirements.txt b/requirements.txt
index 349394f9a7..71841f0991 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,7 @@ cffi==1.16.0
chardet==5.2.0
charset-normalizer==3.3.2
click==8.1.7
-craft-application==2.3.0
+craft-application==2.4.0
craft-archives==1.1.3
craft-cli==2.5.1
craft-grammar==1.1.2
diff --git a/snapcraft/commands/remote.py b/snapcraft/commands/remote.py
index 0bc0157fe4..52196eee41 100644
--- a/snapcraft/commands/remote.py
+++ b/snapcraft/commands/remote.py
@@ -25,6 +25,7 @@
from typing import Any, cast
import lazr.restfulclient.errors
+from craft_application import errors
from craft_application.application import filter_plan
from craft_application.commands import ExtensibleCommand
from craft_application.errors import RemoteBuildError
@@ -113,6 +114,38 @@ def _fill_parser(self, parser: argparse.ArgumentParser) -> None:
default=os.getenv("CRAFT_BUILD_FOR"),
help="Set architecture to build for",
)
+ parser.add_argument(
+ "--project", help="upload to the specified Launchpad project"
+ )
+
+ def _validate(self, parsed_args: argparse.Namespace) -> None:
+ """Do pre-build validation."""
+ if os.getenv("SUDO_USER") and os.geteuid() == 0:
+ emit.progress(
+ "Running with 'sudo' may cause permission errors and is discouraged.",
+ permanent=True,
+ )
+ # Give the user a bit of time to process this before proceeding.
+ time.sleep(1)
+
+ if (
+ not parsed_args.launchpad_accept_public_upload
+ and (
+ not parsed_args.project
+ or not self._services.remote_build.is_project_private()
+ )
+ and not confirm_with_user(_CONFIRMATION_PROMPT, default=False)
+ ):
+ raise errors.RemoteBuildError(
+ "Remote build needs explicit acknowledgement that data sent to build servers "
+ "is public.",
+ details=(
+ "In non-interactive runs, please use the option "
+ "`--launchpad-accept-public-upload`."
+ ),
+ reportable=False,
+ retcode=77,
+ )
# pylint: disable=too-many-statements
def _run(self, parsed_args: argparse.Namespace, **kwargs: Any) -> int | None:
@@ -122,31 +155,15 @@ def _run(self, parsed_args: argparse.Namespace, **kwargs: Any) -> int | None:
:raises AcceptPublicUploadError: If the user does not agree to upload data.
"""
- if os.getenv("SUDO_USER") and os.geteuid() == 0:
- emit.progress(
- "Running with 'sudo' may cause permission errors and is discouraged.",
- permanent=True,
- )
- # Give the user a bit of time to process this before proceeding.
- time.sleep(1)
+ if parsed_args.project:
+ self._services.remote_build.set_project(parsed_args.project)
+ self._validate(parsed_args)
emit.progress(
"remote-build is experimental and is subject to change. Use with caution.",
permanent=True,
)
- if not parsed_args.launchpad_accept_public_upload and not confirm_with_user(
- _CONFIRMATION_PROMPT, default=False
- ):
- emit.progress(
- "Remote build needs explicit acknowledgement that data sent to build servers is "
- "public.\n"
- "In non-interactive runs, please use the option "
- "`--launchpad-accept-public-upload`.",
- permanent=True,
- )
- return 77
-
builder = self._services.remote_build
project = cast(models.Project, self._services.project)
config = cast(dict[str, Any], self.config)
@@ -158,10 +175,8 @@ def _run(self, parsed_args: argparse.Namespace, **kwargs: Any) -> int | None:
emit.trace(f"Project directory: {project_dir}")
- build_planner = self._app.BuildPlannerClass.unmarshal(project.marshal())
- full_build_plan = build_planner.get_build_plan()
possible_build_plan = filter_plan(
- full_build_plan,
+ self._app.BuildPlannerClass.unmarshal(project.marshal()).get_build_plan(),
platform=parsed_args.platform,
build_for=parsed_args.build_for,
host_arch=None,
@@ -179,22 +194,16 @@ def _run(self, parsed_args: argparse.Namespace, **kwargs: Any) -> int | None:
return 78 # Configuration error
if parsed_args.build_for and not architectures:
- if parsed_args.build_for in SUPPORTED_ARCHS:
- # allow the user to build for a single architecture
- # if the snapcraft.yaml not defined the platforms
- architectures = [parsed_args.build_for]
- else:
- emit.progress(
- f"build-for '{parsed_args.build_for}' is not supported.",
- permanent=True,
+ if parsed_args.build_for not in SUPPORTED_ARCHS:
+ raise errors.RemoteBuildError(
+ f"build-for '{parsed_args.build_for}' is not supported.", retcode=78
)
- return 78 # Configuration error
+ # Allow the user to build for a single architecture if snapcraft.yaml
+ # doesn't define architectures.
+ architectures = [parsed_args.build_for]
emit.debug(f"Architectures to build: {architectures}")
- if not architectures:
- architectures = None
-
if parsed_args.launchpad_timeout:
emit.debug(f"Setting timeout to {parsed_args.launchpad_timeout} seconds")
builder.set_timeout(parsed_args.launchpad_timeout)
@@ -208,14 +217,16 @@ def _run(self, parsed_args: argparse.Namespace, **kwargs: Any) -> int | None:
"Starting new build. It may take a while to upload large projects."
)
try:
- builds = builder.start_builds(project_dir, architectures=architectures)
+ builds = builder.start_builds(
+ project_dir, architectures=architectures or None
+ )
except RemoteBuildError:
emit.progress("Starting build failed.", permanent=True)
emit.progress("Cleaning up")
builder.cleanup()
raise
except lazr.restfulclient.errors.Conflict:
- emit.progress("Remote repository is already existing.", permanent=True)
+ emit.progress("Remote repository already exists.", permanent=True)
emit.progress("Cleaning up")
builder.cleanup()
return 75
@@ -226,13 +237,10 @@ def _run(self, parsed_args: argparse.Namespace, **kwargs: Any) -> int | None:
if confirm_with_user("Cancel builds?", default=True):
emit.progress("Cancelling builds.")
builder.cancel_builds()
- emit.progress("Cleaning up")
- builder.cleanup()
returncode = 0
- else:
- if returncode != 75: # TimeoutError
- emit.progress("Cleaning up")
- builder.cleanup()
+ if returncode != 75: # TimeoutError
+ emit.progress("Cleaning up")
+ builder.cleanup()
return returncode
def _monitor_and_complete(
diff --git a/snapcraft/meta/component_yaml.py b/snapcraft/meta/component_yaml.py
index be2cc5fcf0..5b8797c4b1 100644
--- a/snapcraft/meta/component_yaml.py
+++ b/snapcraft/meta/component_yaml.py
@@ -25,7 +25,11 @@
class ComponentMetadata(BaseMetadata):
- """The component.yaml model."""
+ """The component.yaml model.
+
+ Component hooks are not included in the component's metadata.
+ Instead, they are included in the snap's metadata.
+ """
component: str
type: str
diff --git a/snapcraft/meta/snap_yaml.py b/snapcraft/meta/snap_yaml.py
index 0e81be1cbe..7c6d686e5f 100644
--- a/snapcraft/meta/snap_yaml.py
+++ b/snapcraft/meta/snap_yaml.py
@@ -235,6 +235,7 @@ class ComponentMetadata(SnapcraftMetadata): # type: ignore # (pydantic plugin i
summary: SummaryStr
description: str
type: str
+ hooks: dict[str, models.Hook] | None
@override
class Config(BaseMetadata.Config):
diff --git a/snapcraft/models/project.py b/snapcraft/models/project.py
index 1d5a236114..cc42aeb29f 100644
--- a/snapcraft/models/project.py
+++ b/snapcraft/models/project.py
@@ -541,6 +541,7 @@ class Component(models.CraftBaseModel):
description: str
type: Literal["test"]
version: Optional[VersionStr] # type: ignore[assignment]
+ hooks: dict[str, Hook] | None
@pydantic.validator("version")
@classmethod
diff --git a/snapcraft/services/remotebuild.py b/snapcraft/services/remotebuild.py
index a9ff6df595..3b53a25db2 100644
--- a/snapcraft/services/remotebuild.py
+++ b/snapcraft/services/remotebuild.py
@@ -15,33 +15,12 @@
# along with this program. If not, see .
"""Snapcraft Lifecycle Service."""
-from collections.abc import Collection
-from typing import Any
from craft_application import launchpad
from craft_application.services import remotebuild
-from overrides import override
class RemoteBuild(remotebuild.RemoteBuildService):
"""Snapcraft remote build service."""
RecipeClass = launchpad.models.SnapRecipe
-
- @override
- def _new_recipe(
- self,
- name: str,
- repository: launchpad.models.GitRepository,
- architectures: Collection[str] | None = None,
- **_: Any, # noqa: ANN401
- ) -> launchpad.models.Recipe:
- """Create a new recipe."""
- return launchpad.models.SnapRecipe.new(
- self.lp,
- name,
- self.lp.username,
- architectures=architectures,
- project=self._lp_project.name,
- git_ref=repository.git_https_url,
- )
diff --git a/tests/spread/core22/components/expected-snap.yaml b/tests/spread/core22/components/expected-snap.yaml
new file mode 100644
index 0000000000..666523c206
--- /dev/null
+++ b/tests/spread/core22/components/expected-snap.yaml
@@ -0,0 +1,25 @@
+name: hello-components
+version: '1.0'
+summary: Build a snap with components
+description: Build a snap with components
+architectures:
+- amd64
+base: core22
+apps:
+ hello:
+ command: bin/hello
+confinement: strict
+grade: devel
+environment:
+ LD_LIBRARY_PATH: ${SNAP_LIBRARY_PATH}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+ PATH: $SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH
+components:
+ share:
+ summary: Hello World
+ description: Hello World
+ type: test
+ hooks:
+ install:
+ plugs:
+ - home
+ - network
diff --git a/tests/spread/core22/components/snapcraft.yaml b/tests/spread/core22/components/snapcraft.yaml
index 2bc7174e5e..1271676ca4 100644
--- a/tests/spread/core22/components/snapcraft.yaml
+++ b/tests/spread/core22/components/snapcraft.yaml
@@ -16,6 +16,9 @@ components:
summary: Hello World
description: Hello World
version: "1.0"
+ hooks:
+ install:
+ plugs: ["home", "network"]
parts:
hello:
diff --git a/tests/spread/core22/components/task.yaml b/tests/spread/core22/components/task.yaml
index 2c8f3c1905..f53e18bbc9 100644
--- a/tests/spread/core22/components/task.yaml
+++ b/tests/spread/core22/components/task.yaml
@@ -11,13 +11,19 @@ restore: |
execute: |
snapcraft pack
- # assert contents of default partition
+ # assert snap contents
unsquashfs -dest "snap-contents" "hello-components_1.0_amd64.snap"
if [ ! -e "snap-contents/bin/hello" ]; then
echo "Expected 'bin/hello' in snap contents"
exit 1
fi
+ # assert snap metadata
+ if ! diff -u snap-contents/meta/snap.yaml expected-snap.yaml; then
+ echo "Metadata for the snap is incorrect."
+ exit 1
+ fi
+
# assert component was packed
component_file="hello-components+share_1.0.comp"
if [ ! -e "${component_file}" ]; then
@@ -38,7 +44,7 @@ execute: |
fi
# assert contents of component metadata
- if ! diff component-contents/meta/component.yaml expected-component.yaml; then
+ if ! diff -u component-contents/meta/component.yaml expected-component.yaml; then
echo "Metadata for the share component is incorrect."
exit 1
fi
diff --git a/tests/spread/core24/components/expected-snap.yaml b/tests/spread/core24/components/expected-snap.yaml
new file mode 100644
index 0000000000..ec39cb42a2
--- /dev/null
+++ b/tests/spread/core24/components/expected-snap.yaml
@@ -0,0 +1,25 @@
+name: hello-components
+version: '1.0'
+summary: Build a snap with components
+description: Build a snap with components
+architectures:
+- amd64
+base: core24
+apps:
+ hello:
+ command: bin/hello
+confinement: strict
+grade: devel
+environment:
+ LD_LIBRARY_PATH: ${SNAP_LIBRARY_PATH}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+ PATH: $SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH
+components:
+ share:
+ summary: Hello World
+ description: Hello World
+ type: test
+ hooks:
+ install:
+ plugs:
+ - home
+ - network
diff --git a/tests/spread/core24/components/snapcraft.yaml b/tests/spread/core24/components/snapcraft.yaml
index 6c9079a33b..ecacc9e443 100644
--- a/tests/spread/core24/components/snapcraft.yaml
+++ b/tests/spread/core24/components/snapcraft.yaml
@@ -17,6 +17,9 @@ components:
summary: Hello World
description: Hello World
version: "1.0"
+ hooks:
+ install:
+ plugs: ["home", "network"]
parts:
hello:
diff --git a/tests/spread/core24/components/task.yaml b/tests/spread/core24/components/task.yaml
index 2c8f3c1905..f53e18bbc9 100644
--- a/tests/spread/core24/components/task.yaml
+++ b/tests/spread/core24/components/task.yaml
@@ -11,13 +11,19 @@ restore: |
execute: |
snapcraft pack
- # assert contents of default partition
+ # assert snap contents
unsquashfs -dest "snap-contents" "hello-components_1.0_amd64.snap"
if [ ! -e "snap-contents/bin/hello" ]; then
echo "Expected 'bin/hello' in snap contents"
exit 1
fi
+ # assert snap metadata
+ if ! diff -u snap-contents/meta/snap.yaml expected-snap.yaml; then
+ echo "Metadata for the snap is incorrect."
+ exit 1
+ fi
+
# assert component was packed
component_file="hello-components+share_1.0.comp"
if [ ! -e "${component_file}" ]; then
@@ -38,7 +44,7 @@ execute: |
fi
# assert contents of component metadata
- if ! diff component-contents/meta/component.yaml expected-component.yaml; then
+ if ! diff -u component-contents/meta/component.yaml expected-component.yaml; then
echo "Metadata for the share component is incorrect."
exit 1
fi
diff --git a/tests/spread/general/store/task.yaml b/tests/spread/general/store/task.yaml
index 651cbf3fe9..2a41cf3f47 100644
--- a/tests/spread/general/store/task.yaml
+++ b/tests/spread/general/store/task.yaml
@@ -3,7 +3,9 @@ summary: Test the store workflow
manual: true
environment:
- SNAP: "/snapcraft/tests/spread/core22/components/"
+ # use a core22 snap with components but no component hooks
+ # this can be changed after the snap store and review-tools have more support for components
+ SNAP: "/snapcraft/tests/spread/core22/components-environment/"
SNAPCRAFT_STORE_CREDENTIALS/ubuntu_one: "$(HOST: echo ${SNAPCRAFT_STORE_CREDENTIALS_STAGING})"
SNAPCRAFT_STORE_CREDENTIALS/legacy: "$(HOST: echo ${SNAPCRAFT_STORE_CREDENTIALS_STAGING_LEGACY})"
SNAPCRAFT_STORE_CREDENTIALS/candid: "$(HOST: echo ${SNAPCRAFT_STORE_CREDENTIALS_STAGING_CANDID})"
@@ -52,7 +54,8 @@ execute: |
# Get information about our snap.
cd "$SNAP"
snap_file=$(ls ./*.snap)
- component_file=$(ls ./*.comp)
+ foo_component_file=$(ls ./test-snapcraft*+foo_1.0.comp)
+ bar_baz_component_file=$(ls ./test-snapcraft*+bar-baz_1.0.comp)
snap_name=$(grep "name: " snapcraft.yaml | sed -e "s/name: \(.*$\)/\1/")
# Login mechanism
@@ -68,7 +71,7 @@ execute: |
snapcraft list
# Push and Release
- snapcraft upload "${snap_file}" --release edge --component "share=${component_file}"
+ snapcraft upload "${snap_file}" --release edge --component "foo=${foo_component_file}" --component "bar-baz=${bar_baz_component_file}"
# Show revisions
snapcraft list-revisions "${snap_name}"
diff --git a/tests/unit/commands/test_remote.py b/tests/unit/commands/test_remote.py
index d536c5d1d3..e4de911a47 100644
--- a/tests/unit/commands/test_remote.py
+++ b/tests/unit/commands/test_remote.py
@@ -154,16 +154,56 @@ def mock_run_legacy(mocker):
@pytest.mark.parametrize("base", CURRENT_BASES - {"core22"})
-@pytest.mark.usefixtures("mock_argv", "fake_services")
+@pytest.mark.parametrize("project_name", ["something", "something-else"])
+def test_set_project(
+ mocker, snapcraft_yaml, base, mock_confirm, fake_services, project_name
+):
+ """Check that a project name gets set if the user provides a project."""
+ mocker.patch("sys.argv", ["snapcraft", "remote-build", "--project", project_name])
+
+ snapcraft_yaml_dict = {"base": base, "build-base": "devel", "grade": "devel"}
+ snapcraft_yaml(**snapcraft_yaml_dict)
+ app = application.create_app()
+ remote_build = app.services.remote_build
+ remote_build.is_project_private = lambda: False
+
+ app.run()
+
+ assert remote_build._project_name == project_name
+ mock_confirm.assert_called_once()
+
+
+@pytest.mark.parametrize("base", CURRENT_BASES - {"core22"})
+@pytest.mark.parametrize("project_name", ["something", "something_else"])
+def test_no_confirmation_for_private_project(
+ mocker, snapcraft_yaml, base, mock_confirm, fake_services, project_name
+):
+ """If a user uploads to a private project, we don't need a confirmation prompt."""
+ mocker.patch("sys.argv", ["snapcraft", "remote-build", "--project", project_name])
+
+ snapcraft_yaml_dict = {"base": base, "build-base": "devel", "grade": "devel"}
+ snapcraft_yaml(**snapcraft_yaml_dict)
+ app = application.create_app()
+ remote_build = app.services.remote_build
+ remote_build.is_project_private = lambda: True
+
+ app.run()
+
+ assert remote_build._project_name == project_name
+ mock_confirm.assert_not_called()
+
+
+@pytest.mark.parametrize("base", CURRENT_BASES - {"core22"})
+@pytest.mark.usefixtures("mock_argv")
def test_command_user_confirms_upload(
- snapcraft_yaml,
- base,
- mock_confirm,
+ snapcraft_yaml, base, mock_confirm, fake_services
):
"""Check if the confirmation prompt is shown."""
snapcraft_yaml_dict = {"base": base, "build-base": "devel", "grade": "devel"}
snapcraft_yaml(**snapcraft_yaml_dict)
+ fake_services.remote_build.is_project_private = lambda: False
app = application.create_app()
+
app.run()
mock_confirm.assert_called_once_with(
@@ -1352,7 +1392,7 @@ def test_build_for_no_platforms(
@pytest.mark.parametrize("base", CURRENT_BASES - {"core22"})
def test_build_for_error(
- emitter,
+ capsys,
mocker,
snapcraft_yaml,
base,
@@ -1382,7 +1422,9 @@ def test_build_for_error(
app = application.create_app()
assert app.run() == 78
- emitter.assert_progress("build-for 'nonexistent' is not supported.", permanent=True)
+ _, err = capsys.readouterr()
+
+ assert "build-for 'nonexistent' is not supported." in err
##################
diff --git a/tests/unit/meta/test_component_yaml.py b/tests/unit/meta/test_component_yaml.py
index 3ec0d78162..9d75289c83 100644
--- a/tests/unit/meta/test_component_yaml.py
+++ b/tests/unit/meta/test_component_yaml.py
@@ -50,6 +50,10 @@ def stub_project_data():
"summary": "test summary",
"description": "test description",
"version": "1.0",
+ "hooks": {
+ "install": {"plugs": ["home", "network"]},
+ "post-refresh": {},
+ },
},
},
}
diff --git a/tests/unit/meta/test_snap_yaml.py b/tests/unit/meta/test_snap_yaml.py
index 60e2ef87fe..aa2f0a42fe 100644
--- a/tests/unit/meta/test_snap_yaml.py
+++ b/tests/unit/meta/test_snap_yaml.py
@@ -1,6 +1,6 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
-# Copyright 2022-2023 Canonical Ltd.
+# Copyright 2022-2024 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
@@ -16,11 +16,12 @@
import textwrap
from pathlib import Path
+from typing import cast
import pydantic
import pytest
import yaml
-from craft_application.models import SummaryStr, VersionStr
+from craft_application.models import SummaryStr, UniqueStrList, VersionStr
from snapcraft import models
from snapcraft.meta import snap_yaml
@@ -418,6 +419,21 @@ def complex_project():
description: test
type: test
version: "1.0"
+ hooks:
+ install:
+ command-chain:
+ - test
+ environment:
+ test-variable-1: test
+ test-variable-2: test
+ plugs:
+ - home
+ - network
+ passthrough:
+ somefield:
+ - some
+ - value
+ post-refresh: {}
component-b:
summary: test
description: test
@@ -558,6 +574,21 @@ def test_complex_snap_yaml(complex_project, new_dir):
summary: test
description: test
type: test
+ hooks:
+ install:
+ command-chain:
+ - test
+ environment:
+ test-variable-1: test
+ test-variable-2: test
+ plugs:
+ - home
+ - network
+ passthrough:
+ somefield:
+ - some
+ - value
+ post-refresh: {}
component-b:
summary: test
description: test
@@ -1339,6 +1370,14 @@ def test_component_metadata_from_component():
description="test",
type="test",
version=VersionStr("1.0"),
+ hooks={
+ "install": models.Hook(
+ plugs=cast(UniqueStrList, ["home", "network"]),
+ command_chain=["test"],
+ environment={"test-variable-1": "test", "test-variable-2": "test"},
+ passthrough={"somefield": ["some", "value"]},
+ )
+ },
)
metadata = snap_yaml.ComponentMetadata.from_component(component)
@@ -1346,3 +1385,4 @@ def test_component_metadata_from_component():
assert metadata.summary == component.summary
assert metadata.description == component.description
assert metadata.type == component.type
+ assert metadata.hooks == component.hooks
diff --git a/tests/unit/models/test_projects.py b/tests/unit/models/test_projects.py
index 90e044f6ac..34c81ba3f2 100644
--- a/tests/unit/models/test_projects.py
+++ b/tests/unit/models/test_projects.py
@@ -2050,12 +2050,14 @@ class TestComponents:
@pytest.fixture
def stub_component_data(self):
- return {
+ data: dict[str, Any] = {
"type": "test",
"summary": "test summary",
"description": "test description",
"version": "1.0",
+ "hooks": None,
}
+ return data
def test_components_valid(self, project, project_yaml_data, stub_component_data):
components = {"foo": stub_component_data, "bar": stub_component_data}
diff --git a/tests/unit/services/test_package_components.py b/tests/unit/services/test_package_components.py
index 3598f5e909..acdf2325f9 100644
--- a/tests/unit/services/test_package_components.py
+++ b/tests/unit/services/test_package_components.py
@@ -37,6 +37,18 @@ def extra_project_params(extra_project_params):
"summary": SummaryStr("first component"),
"description": "lorem ipsum",
"version": VersionStr("1.0"),
+ "hooks": {
+ "install": {
+ "command-chain": ["test"],
+ "environment": {
+ "test-variable-1": "test",
+ "test-variable-2": "test",
+ },
+ "plugs": ["home", "network"],
+ "passthrough": {"somefield": ["some", "value"]},
+ },
+ "post-refresh": {},
+ },
},
"secondcomponent": {
"type": "test",
@@ -133,6 +145,21 @@ def test_write_metadata(
summary: first component
description: lorem ipsum
type: test
+ hooks:
+ install:
+ command-chain:
+ - test
+ environment:
+ test-variable-1: test
+ test-variable-2: test
+ plugs:
+ - home
+ - network
+ passthrough:
+ somefield:
+ - some
+ - value
+ post-refresh: {}
secondcomponent:
summary: second component
description: lorem ipsum
diff --git a/tests/unit/services/test_remotebuild.py b/tests/unit/services/test_remotebuild.py
deleted file mode 100644
index 071d7dcedb..0000000000
--- a/tests/unit/services/test_remotebuild.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
-#
-# Copyright 2024 Canonical Ltd.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 3 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-"""Tests for the Snapcraft Remote Build service."""
-from unittest import mock
-
-
-def test_new_snap_recipe(mocker, remote_build_service):
- """Test that a new SnapRecipe is created."""
- mock_new_recipe = mocker.patch("craft_application.launchpad.models.SnapRecipe.new")
- git_repo = mock.Mock(self_link="http://whatever")
-
- remote_build_service._lp_project = mock.Mock()
- remote_build_service._lp_project.name = "mytest"
-
- remote_build_service._new_recipe(
- name="mytest",
- repository=git_repo,
- architectures=["riscv64"],
- )
-
- mock_new_recipe.assert_called_once_with(
- remote_build_service.lp,
- "mytest",
- "craft_test_user",
- architectures=["riscv64"],
- project="mytest",
- git_ref=git_repo.git_https_url,
- )