def test_get_snap_project_no_base(snapcraft_yaml, new_dir): with pytest.raises(errors.ProjectValidationError) as raised: Project.unmarshal(snapcraft_yaml(base=None)) assert str(raised.value) == ( "Bad snapcraft.yaml content:\n" "- Snap base must be declared when type is not base, kernel or snapd")
def _update_project_variables(project: Project, project_vars: Dict[str, str]): """Update project fields with values set during lifecycle processing.""" try: if project_vars["version"]: project.version = project_vars["version"] if project_vars["grade"]: project.grade = project_vars["grade"] # type: ignore except pydantic.ValidationError as err: _raise_formatted_validation_error(err) raise errors.SnapcraftError(f"error setting variable: {err}")
def update_project_metadata( project: Project, *, project_vars: Dict[str, str], metadata_list: List[ExtractedMetadata], assets_dir: Path, prime_dir: Path, ) -> None: """Set project fields using corresponding adopted entries. Fields are validated on assignment by pydantic. :param project: The project to update. :param project_vars: The variables updated during lifecycle execution. :param metadata_list: List containing parsed information from metadata files. :raises SnapcraftError: If project update failed. """ _update_project_variables(project, project_vars) for metadata in metadata_list: # Data specified in the project yaml has precedence over extracted data if metadata.title and not project.title: project.title = metadata.title if metadata.summary and not project.summary: project.summary = metadata.summary if metadata.description and not project.description: project.description = metadata.description if metadata.version and not project.version: project.version = metadata.version if metadata.grade and not project.grade: project.grade = metadata.grade # type: ignore emit.debug(f"project icon: {project.icon!r}") emit.debug(f"metadata icon: {metadata.icon!r}") if not project.icon: _update_project_icon(project, metadata=metadata, assets_dir=assets_dir) _update_project_app_desktop_file(project, metadata=metadata, assets_dir=assets_dir, prime_dir=prime_dir) # Fields that must not end empty for field in MANDATORY_ADOPTABLE_FIELDS: if not getattr(project, field): raise errors.SnapcraftError( f"Field {field!r} was not adopted from metadata")
def run(self, parsed_args): snap_project = get_snap_project() yaml_data = process_yaml(snap_project.project_file) expanded_yaml_data = extensions.apply_extensions( yaml_data, arch=get_host_architecture(), target_arch=get_host_architecture(), ) Project.unmarshal(expanded_yaml_data) emit.message( yaml.safe_dump(expanded_yaml_data, indent=4, sort_keys=False))
def test_lifecycle_clean_part_names(snapcraft_yaml, project_vars, new_dir, mocker): """Clean project inside provider if called with part names.""" project = Project.unmarshal(snapcraft_yaml(base="core22")) run_in_provider_mock = mocker.patch("snapcraft.parts.lifecycle._run_in_provider") parts_lifecycle._run_command( "clean", project=project, parse_info={}, assets_dir=Path(), start_time=datetime.now(), parallel_build_count=8, parsed_args=argparse.Namespace( directory=None, output=None, destructive_mode=False, use_lxd=False, parts=["part1"], ), ) assert run_in_provider_mock.mock_calls == [ call( project, "clean", argparse.Namespace( directory=None, output=None, destructive_mode=False, use_lxd=False, parts=["part1"], ), ) ]
def test_accepted_artifacts(new_dir, emitter, snapcraft_yaml): project = Project.unmarshal(snapcraft_yaml) assets_dir = Path("snap") file_assets = [ ".snapcraft/state", "gui/icon.png", "gui/other-icon.png", "plugins/plugin1.py", "plugins/data-file", "hooks/configure", "hooks/new-hook", "keys/key1.asc", "keys/key2.asc", "local/file", "local/dir/file", ] for file_asset in file_assets: asset_path = assets_dir / file_asset asset_path.parent.mkdir(parents=True, exist_ok=True) asset_path.touch() run_project_checks(project, assets_dir=Path("snap")) assert emitter.interactions == []
def test_update_project_metadata_desktop_no_apps(project_yaml_data, new_dir): yaml_data = project_yaml_data( { "version": "1.0", "adopt-info": "part", "parts": {}, } ) project = Project(**yaml_data) metadata = ExtractedMetadata( common_id="test.id", desktop_file_paths=["metadata/foo.desktop", "metadata/bar.desktop"], ) # create desktop file Path("metadata").mkdir() Path("metadata/foo.desktop").touch() Path("metadata/bar.desktop").touch() prj_vars = {"version": "", "grade": "stable"} update_project_metadata( project, project_vars=prj_vars, metadata_list=[metadata], assets_dir=new_dir / "assets", prime_dir=new_dir, ) assert project.apps is None
def test_lifecycle_debug_shell(snapcraft_yaml, cmd, new_dir, mocker): """Adoptable fields shouldn't be empty after adoption.""" mocker.patch("craft_parts.executor.Executor.execute", side_effect=Exception) mock_shell = mocker.patch("subprocess.run") project = Project.unmarshal(snapcraft_yaml(base="core22")) with pytest.raises(errors.PartsLifecycleError): parts_lifecycle._run_command( cmd, project=project, parse_info={}, assets_dir=Path(), start_time=datetime.now(), parallel_build_count=8, parsed_args=argparse.Namespace( directory=None, output=None, debug=True, destructive_mode=True, shell=False, shell_after=False, use_lxd=False, parts=["part1"], ), ) assert mock_shell.mock_calls == [call(["bash"], check=False, cwd=None)]
def test_lifecycle_run_command_clean(snapcraft_yaml, project_vars, new_dir, mocker): """Clean provider project when called without parts.""" project = Project.unmarshal(snapcraft_yaml(base="core22")) clean_mock = mocker.patch( "snapcraft.providers.LXDProvider.clean_project_environments", return_value=["instance-name"], ) parts_lifecycle._run_command( "clean", project=project, parse_info={}, assets_dir=Path(), start_time=datetime.now(), parallel_build_count=8, parsed_args=argparse.Namespace( directory=None, output=None, destructive_mode=False, use_lxd=False, parts=None, ), ) assert clean_mock.mock_calls == [ call( project_name="mytest", project_path=new_dir, build_on=get_host_architecture(), build_for=get_host_architecture(), ) ]
def test_update_project_metadata_icon( project_yaml_data, project_entries, icon_exists, asset_exists, expected_icon, new_dir, ): yaml_data = project_yaml_data( {"version": "1.0", "adopt-info": "part", "parts": {}, **project_entries} ) project = Project(**yaml_data) metadata = ExtractedMetadata(icon="metadata_icon.png") # create icon file if icon_exists: Path("metadata_icon.png").touch() # create icon file in assets dir if asset_exists: Path("assets/gui").mkdir(parents=True) Path("assets/gui/icon.svg").touch() prj_vars = {"version": "", "grade": "stable"} update_project_metadata( project, project_vars=prj_vars, metadata_list=[metadata], assets_dir=new_dir / "assets", prime_dir=new_dir, ) assert project.icon == expected_icon
def test_update_project_metadata_fields( appstream_file, project_entries, expected, new_dir ): yaml_data = { "name": "my-project", "base": "core22", "confinement": "strict", "adopt-info": "part", "parts": {}, **project_entries, } project = Project(**yaml_data) metadata = ExtractedMetadata( version="4.5.6", summary="metadata summary", description="metadata description", title="metadata title", grade="devel", ) prj_vars = {"version": "", "grade": ""} update_project_metadata( project, project_vars=prj_vars, metadata_list=[metadata], assets_dir=new_dir, prime_dir=new_dir, ) assert project.version == expected["version"] assert project.summary == expected["summary"] assert project.description == expected["description"] assert project.title == expected["title"] assert project.grade == expected["grade"]
def test_lifecycle_clean_managed(snapcraft_yaml, project_vars, new_dir, mocker): project = Project.unmarshal(snapcraft_yaml(base="core22")) run_in_provider_mock = mocker.patch( "snapcraft.parts.lifecycle._run_in_provider") clean_mock = mocker.patch("snapcraft.parts.PartsLifecycle.clean") mocker.patch("snapcraft.utils.is_managed_mode", return_value=True) mocker.patch( "snapcraft.utils.get_managed_environment_home_path", return_value=new_dir / "home", ) parts_lifecycle._run_command( "clean", project=project, parse_info={}, assets_dir=Path(), start_time=datetime.now(), parallel_build_count=8, parsed_args=argparse.Namespace( directory=None, output=None, destructive_mode=False, use_lxd=False, parts=["part1"], ), ) assert run_in_provider_mock.mock_calls == [] assert clean_mock.mock_calls == [call(part_names=["part1"])]
def test_icon(new_dir): content = dedent("""\ name: project-name base: core22 version: "1.0" summary: sanity checks description: sanity checks grade: stable confinement: strict icon: foo.png parts: nil: plugin: nil """) yaml_data = yaml.safe_load(content) project = Project.unmarshal(yaml_data) # Test without icon raises error with pytest.raises(errors.SnapcraftError) as raised: run_project_checks(project, assets_dir=Path("snap")) assert str(raised.value) == "Specified icon 'foo.png' does not exist." # Test with icon passes. (new_dir / "foo.png").touch() run_project_checks(project, assets_dir=Path("snap"))
def _run_in_provider(project: Project, command_name: str, parsed_args: "argparse.Namespace") -> None: """Pack image in provider instance.""" emit.debug("Checking build provider availability") provider_name = "lxd" if parsed_args.use_lxd else None provider = providers.get_provider(provider_name) provider.ensure_provider_is_available() cmd = ["snapcraft", command_name] if hasattr(parsed_args, "parts"): cmd.extend(parsed_args.parts) if getattr(parsed_args, "output", None): cmd.extend(["--output", parsed_args.output]) if emit.get_mode() == EmitterMode.VERBOSE: cmd.append("--verbose") elif emit.get_mode() == EmitterMode.QUIET: cmd.append("--quiet") elif emit.get_mode() == EmitterMode.DEBUG: cmd.append("--verbosity=debug") elif emit.get_mode() == EmitterMode.TRACE: cmd.append("--verbosity=trace") if parsed_args.debug: cmd.append("--debug") if getattr(parsed_args, "shell", False): cmd.append("--shell") if getattr(parsed_args, "shell_after", False): cmd.append("--shell-after") if getattr(parsed_args, "enable_manifest", False): cmd.append("--enable-manifest") build_information = getattr(parsed_args, "manifest_build_information", None) if build_information: cmd.append("--manifest-build-information") cmd.append(build_information) output_dir = utils.get_managed_environment_project_path() emit.progress("Launching instance...") with provider.launched_environment( project_name=project.name, project_path=Path().absolute(), base=project.get_effective_base(), bind_ssh=parsed_args.bind_ssh, build_on=get_host_architecture(), build_for=get_host_architecture(), ) as instance: try: with emit.pause(): instance.execute_run(cmd, check=True, cwd=output_dir) capture_logs_from_instance(instance) except subprocess.CalledProcessError as err: capture_logs_from_instance(instance) raise providers.ProviderError( f"Failed to execute {command_name} in instance.") from err
def _clean_provider(project: Project, parsed_args: "argparse.Namespace") -> None: """Clean the provider environment. :param project: The project to clean. """ emit.debug("Clean build provider") provider_name = "lxd" if parsed_args.use_lxd else None provider = providers.get_provider(provider_name) instance_names = provider.clean_project_environments( project_name=project.name, project_path=Path().absolute(), build_on=project.get_build_on(), build_for=project.get_build_for(), ) if instance_names: emit.message(f"Removed instance: {', '.join(instance_names)}") else: emit.message("No instances to remove")
def test_unexpected_things(new_dir, emitter, snapcraft_yaml): project = Project.unmarshal(snapcraft_yaml) assets_dir = Path("snap") file_assets = [ "dir1/foo", "dir1/keys/key1.asc", "dir1/keys/key2.asc", "dir1/local/dir/file", "dir1/local/file", "dir1/plugins/data-file", "dir1/plugins/plugin1.py", "dir2/foo", "dir2/hooks/configure", "dir2/hooks/new-hook", "gui/icon.jpg", ] for file_asset in file_assets: asset_path = assets_dir / file_asset asset_path.parent.mkdir(parents=True, exist_ok=True) asset_path.touch() run_project_checks(project, assets_dir=Path("snap")) assert emitter.assert_progress( "The 'snap' directory is meant specifically for snapcraft, but it contains\n" "the following non-snapcraft-related paths:\n" "- dir1\n" "- dir1/foo\n" "- dir1/keys\n" "- dir1/keys/key1.asc\n" "- dir1/keys/key2.asc\n" "- dir1/local\n" "- dir1/local/dir\n" "- dir1/local/dir/file\n" "- dir1/local/file\n" "- dir1/plugins\n" "- dir1/plugins/data-file\n" "- dir1/plugins/plugin1.py\n" "- dir2\n" "- dir2/foo\n" "- dir2/hooks\n" "- dir2/hooks/configure\n" "- dir2/hooks/new-hook\n" "- gui/icon.jpg\n" "\n" "This is unsupported and may cause unexpected behavior. If you must store\n" "these files within the 'snap' directory, move them to 'snap/local'\n" "which is ignored by snapcraft.", permanent=True, )
def test_lifecycle_pack_managed(cmd, snapcraft_yaml, project_vars, new_dir, mocker): project = Project.unmarshal(snapcraft_yaml(base="core22")) run_in_provider_mock = mocker.patch( "snapcraft.parts.lifecycle._run_in_provider") run_mock = mocker.patch("snapcraft.parts.PartsLifecycle.run") pack_mock = mocker.patch("snapcraft.pack.pack_snap") mocker.patch("snapcraft.meta.snap_yaml.write") mocker.patch("snapcraft.utils.is_managed_mode", return_value=True) mocker.patch( "snapcraft.utils.get_managed_environment_home_path", return_value=new_dir / "home", ) parts_lifecycle._run_command( cmd, project=project, parse_info={}, assets_dir=Path(), start_time=datetime.now(), parallel_build_count=8, parsed_args=argparse.Namespace( directory=None, output=None, debug=False, bind_ssh=False, enable_manifest=False, manifest_image_information=None, destructive_mode=False, shell=False, shell_after=False, use_lxd=False, parts=[], ), ) assert run_in_provider_mock.mock_calls == [] assert run_mock.mock_calls == [ call("prime", debug=False, shell=False, shell_after=False) ] assert pack_mock.mock_calls[:1] == [ call( new_dir / "home/prime", output=None, compression="xz", name="mytest", version="0.1", target_arch=get_host_architecture(), ) ]
def test_setup_assets_remote_icon(self, desktop_file, yaml_data, new_dir): # create primed tree (no icon) desktop_file("prime/test.desktop") # define project # pylint: disable=line-too-long project = Project.unmarshal( yaml_data( { "adopt-info": "part", "icon": "https://dashboard.snapcraft.io/site_media/appmedia/2018/04/Snapcraft-logo-bird.png", "apps": { "app1": { "command": "test.sh", "common-id": "my-test", "desktop": "test.desktop", }, }, }, )) # pylint: enable=line-too-long setup_assets( project, assets_dir=Path("snap"), project_dir=Path.cwd(), prime_dir=Path("prime"), ) # desktop file should be in meta/gui and named after app desktop_path = Path("prime/meta/gui/app1.desktop") assert desktop_path.is_file() # desktop file content should make icon relative to ${SNAP} content = desktop_path.read_text() assert content == textwrap.dedent("""\ [Desktop Entry] Name=appstream-desktop Exec=test-project.app1 Type=Application Icon=${SNAP}/meta/gui/icon.png """) # icon was downloaded icon_path = Path("prime/meta/gui/icon.png") assert icon_path.is_file() assert icon_path.stat().st_size > 0
def run(command_name: str, parsed_args: "argparse.Namespace") -> None: """Run the parts lifecycle. :raises SnapcraftError: if the step name is invalid, or the project yaml file cannot be loaded. :raises LegacyFallback: if the project's base is not core22. """ emit.debug(f"command: {command_name}, arguments: {parsed_args}") snap_project = get_snap_project() yaml_data = process_yaml(snap_project.project_file) start_time = datetime.now() build_plan = get_build_plan(yaml_data, parsed_args) if parsed_args.provider: raise errors.SnapcraftError("Option --provider is not supported.") # Register our own plugins and callbacks plugins.register() callbacks.register_prologue(_set_global_environment) callbacks.register_pre_step(_set_step_environment) build_count = utils.get_parallel_build_count() for build_on, build_for in build_plan: emit.verbose(f"Running on {build_on} for {build_for}") yaml_data_for_arch = apply_yaml(yaml_data, build_on, build_for) parse_info = _extract_parse_info(yaml_data_for_arch) _expand_environment( yaml_data_for_arch, parallel_build_count=build_count, target_arch=build_for, ) project = Project.unmarshal(yaml_data_for_arch) try: _run_command( command_name, project=project, parse_info=parse_info, parallel_build_count=build_count, assets_dir=snap_project.assets_dir, start_time=start_time, parsed_args=parsed_args, ) except PermissionError as err: raise errors.FilePermissionError(err.filename, reason=err.strerror)
def test_snapcraft_yaml_load(new_dir, snapcraft_yaml, filename, mocker): """Snapcraft.yaml should be parsed as a valid yaml file.""" yaml_data = snapcraft_yaml(base="core22", filename=filename) run_command_mock = mocker.patch("snapcraft.parts.lifecycle._run_command") mocker.patch("snapcraft.utils.get_parallel_build_count", return_value=5) parts_lifecycle.run( "pull", argparse.Namespace( parts=["part1"], destructive_mode=True, use_lxd=False, provider=None, enable_manifest=False, manifest_image_information=None, bind_ssh=False, build_for=None, ), ) project = Project.unmarshal(yaml_data) if filename == "build-aux/snap/snapcraft.yaml": assets_dir = Path("build-aux/snap") else: assets_dir = Path("snap") assert run_command_mock.mock_calls == [ call( "pull", project=project, parse_info={}, assets_dir=assets_dir, parallel_build_count=5, start_time=mocker.ANY, parsed_args=argparse.Namespace( parts=["part1"], destructive_mode=True, use_lxd=False, provider=None, enable_manifest=False, manifest_image_information=None, bind_ssh=False, build_for=None, ), ), ]
def test_update_project_metadata_multiple_apps(project_yaml_data, new_dir): yaml_data = project_yaml_data( { "version": "1.0", "adopt-info": "part", "parts": {}, "apps": { "foo": { "command": "foo", "common-id": "foo.id", }, "bar": { "command": "bar", "common-id": "bar.id", }, }, } ) project = Project(**yaml_data) metadata1 = ExtractedMetadata( common_id="foo.id", desktop_file_paths=["metadata/foo.desktop"], ) metadata2 = ExtractedMetadata( common_id="bar.id", desktop_file_paths=["metadata/bar.desktop"], ) # create desktop files Path("metadata").mkdir() Path("metadata/foo.desktop").touch() Path("metadata/bar.desktop").touch() prj_vars = {"version": "", "grade": "stable"} update_project_metadata( project, project_vars=prj_vars, metadata_list=[metadata1, metadata2], assets_dir=new_dir / "assets", prime_dir=new_dir, ) assert project.apps is not None assert project.apps["foo"].desktop == "metadata/foo.desktop" assert project.apps["bar"].desktop == "metadata/bar.desktop"
def test_lifecycle_metadata_empty(field, snapcraft_yaml, new_dir): """Adoptable fields shouldn't be empty after adoption.""" yaml_data = snapcraft_yaml(base="core22") yaml_data.pop(field) yaml_data["adopt-info"] = "part" project = Project.unmarshal(yaml_data) with pytest.raises(errors.SnapcraftError) as raised: update_project_metadata( project, project_vars={"version": "", "grade": ""}, metadata_list=[], assets_dir=new_dir, prime_dir=new_dir, ) assert str(raised.value) == f"Field {field!r} was not adopted from metadata"
def test_lifecycle_pack_metadata_error(cmd, snapcraft_yaml, new_dir, mocker): project = Project.unmarshal(snapcraft_yaml(base="core22")) run_mock = mocker.patch("snapcraft.parts.PartsLifecycle.run") mocker.patch("snapcraft.utils.is_managed_mode", return_value=True) mocker.patch( "snapcraft.utils.get_managed_environment_home_path", return_value=new_dir / "home", ) mocker.patch( "snapcraft.parts.PartsLifecycle.project_vars", new_callable=PropertyMock, return_value={ "version": "0.1", "grade": "invalid" }, # invalid value ) pack_mock = mocker.patch("snapcraft.pack.pack_snap") mocker.patch("snapcraft.meta.snap_yaml.write") with pytest.raises(errors.SnapcraftError) as raised: parts_lifecycle._run_command( cmd, project=project, assets_dir=Path(), start_time=datetime.now(), parse_info={}, parallel_build_count=8, parsed_args=argparse.Namespace( directory=None, output=None, debug=False, destructive_mode=False, shell=False, shell_after=False, use_lxd=False, parts=[], ), ) assert str(raised.value) == ( "error setting grade: unexpected value; permitted: 'stable', 'devel'") assert run_mock.mock_calls == [ call("prime", debug=False, shell=False, shell_after=False) ] assert pack_mock.mock_calls == []
def test_lifecycle_adopt_project_vars(snapcraft_yaml, new_dir): """Adoptable fields shouldn't be empty after adoption.""" yaml_data = snapcraft_yaml(base="core22") yaml_data.pop("version") yaml_data.pop("grade") yaml_data["adopt-info"] = "part" project = Project.unmarshal(yaml_data) update_project_metadata( project, project_vars={"version": "42", "grade": "devel"}, metadata_list=[], assets_dir=new_dir, prime_dir=new_dir, ) assert project.version == "42" assert project.grade == "devel"
def test_gadget_missing(yaml_data, new_dir): project = Project.unmarshal( yaml_data({ "type": "gadget", "version": "1.0", "summary": "summary", "description": "description", })) with pytest.raises(errors.SnapcraftError) as raised: setup_assets( project, assets_dir=Path("snap"), project_dir=Path.cwd(), prime_dir=Path("prime"), ) assert str(raised.value) == "gadget.yaml is required for gadget snaps"
def test_setup_assets_no_apps(self, desktop_file, yaml_data, new_dir): desktop_file("prime/test.desktop") Path("prime/usr/share/icons").mkdir(parents=True) Path("prime/usr/share/icons/icon.svg").touch() Path("snap/gui").mkdir() # define project project = Project.unmarshal(yaml_data({"adopt-info": "part"})) # setting up assets does not crash setup_assets( project, assets_dir=Path("snap"), project_dir=Path.cwd(), prime_dir=Path("prime"), ) assert os.listdir("prime/meta/gui") == []
def test_setup_assets_icon_in_assets_dir(self, desktop_file, yaml_data, new_dir): desktop_file("prime/test.desktop") Path("snap/gui").mkdir(parents=True) Path("snap/gui/icon.svg").touch() # define project project = Project.unmarshal( yaml_data( { "adopt-info": "part", "apps": { "app1": { "command": "test.sh", "common-id": "my-test", "desktop": "test.desktop", }, }, }, )) setup_assets( project, assets_dir=Path("snap"), project_dir=Path.cwd(), prime_dir=Path("prime"), ) # desktop file should be in meta/gui and named after app desktop_path = Path("prime/meta/gui/app1.desktop") assert desktop_path.is_file() # desktop file content should make icon relative to ${SNAP} content = desktop_path.read_text() assert content == textwrap.dedent("""\ [Desktop Entry] Name=appstream-desktop Exec=test-project.app1 Type=Application Icon=${SNAP}/snap/gui/icon.svg """) # icon file exists Path("prime/snap/gui/icon.svg").is_file()
def test_gadget(yaml_data, gadget_yaml_file, new_dir): project = Project.unmarshal( yaml_data({ "type": "gadget", "version": "1.0", "summary": "summary", "description": "description", })) setup_assets( project, assets_dir=Path("snap"), project_dir=Path.cwd(), prime_dir=Path("prime"), ) # gadget file should be in meta/ gadget_path = Path("prime/meta/gadget.yaml") assert gadget_path.is_file()
def test_lifecycle_shell_after(snapcraft_yaml, cmd, new_dir, mocker): """Adoptable fields shouldn't be empty after adoption.""" last_step = None def _fake_execute(_, action: Action, **kwargs): # pylint: disable=unused-argument nonlocal last_step last_step = action.step mocker.patch("craft_parts.executor.Executor.execute", new=_fake_execute) mock_shell = mocker.patch("subprocess.run") project = Project.unmarshal(snapcraft_yaml(base="core22")) parts_lifecycle._run_command( cmd, project=project, parse_info={}, assets_dir=Path(), start_time=datetime.now(), parallel_build_count=8, parsed_args=argparse.Namespace( directory=None, output=None, debug=False, destructive_mode=True, shell=False, shell_after=True, use_lxd=False, parts=["part1"], ), ) expected_last_step = Step.PULL if cmd == "build": expected_last_step = Step.BUILD if cmd == "stage": expected_last_step = Step.STAGE if cmd == "prime": expected_last_step = Step.PRIME assert last_step == expected_last_step assert mock_shell.mock_calls == [call(["bash"], check=False, cwd=None)]
def test_update_project_metadata(project_yaml_data, appstream_file, new_dir): project = Project.unmarshal(project_yaml_data({"adopt-info": "part"})) metadata = ExtractedMetadata( common_id="common.id", title="title", summary="summary", description="description", version="1.2.3", icon="assets/icon.png", desktop_file_paths=["assets/file.desktop"], ) assets_dir = Path("assets") prime_dir = Path("prime") # set up project apps project.apps = { "app1": _project_app({"command": "bin/app1"}), "app2": _project_app({"command": "bin/app2", "common_id": "other.id"}), "app3": _project_app({"command": "bin/app3", "common_id": "common.id"}), } prime_dir.mkdir() (prime_dir / "assets").mkdir() (prime_dir / "assets/icon.png").touch() (prime_dir / "assets/file.desktop").touch() prj_vars = {"version": "0.1", "grade": "stable"} update_project_metadata( project, project_vars=prj_vars, metadata_list=[metadata], assets_dir=assets_dir, prime_dir=prime_dir, ) assert project.title == "title" assert project.summary == "summary" # already set in project assert project.description == "description" # already set in project assert project.version == "0.1" # already set in project assert project.icon == "assets/icon.png" assert project.apps["app3"].desktop == "assets/file.desktop"