def test_accepted_artifacts(caplog_warning, project): assets_dir = pathlib.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() conduct_project_sanity_check(project) assert len(caplog_warning.records) == 0
def cleanbuild(remote, **kwargs): """Create a snap using a clean environment managed by a build provider. \b Examples: snapcraft cleanbuild The cleanbuild command requires a properly setup lxd environment that can connect to external networks. Refer to the "Ubuntu Desktop and Ubuntu Server" section on https://linuxcontainers.org/lxd/getting-started-cli to get started. If using a remote, a prior setup is required which is described on: https://linuxcontainers.org/lxd/getting-started-cli/#multiple-hosts """ # cleanbuild is a special snow flake, while all the other commands # would work with the host as the build_provider it makes little # sense in this scenario. if sys.platform == "darwin": default_provider = "multipass" else: default_provider = "lxd" build_environment = env.BuilderEnvironmentConfig(default=default_provider) project = get_project( is_managed=build_environment.is_managed_host, **kwargs ) conduct_project_sanity_check(project) snap_filename = lifecycle.cleanbuild( project=project, echoer=echo, remote=remote, build_environment=build_environment ) echo.info("Retrieved {!r}".format(snap_filename))
def cleanbuild(): """Create a snap using a clean environment managed by a build provider. \b Examples: snapcraft cleanbuild The cleanbuild command requires a properly setup lxd environment that can connect to external networks. Refer to the "Ubuntu Desktop and Ubuntu Server" section on https://linuxcontainers.org/lxd/getting-started-cli to get started. If using a remote, a prior setup is required which is described on: https://linuxcontainers.org/lxd/getting-started-cli/#multiple-host This command is no longer available when using the base keyword.""" # We can only end up here with an invalid yaml project = get_project() conduct_project_sanity_check(project) # if we got this far, the environment must be broken raise errors.SnapcraftEnvironmentError( "The cleanbuild command is no longer supported when using the base keyword." )
def test_core_unsupported(project): project.info.base = "core" with pytest.raises(errors.UnsupportedBaseError) as exc_info: conduct_project_sanity_check(project) assert exc_info.value.base == "core"
def _execute( # noqa: C901 step: steps.Step, parts: str, pack_project: bool = False, output: str = None, shell: bool = False, shell_after: bool = False, destructive_mode: bool = False, **kwargs) -> "Project": # fmt: on provider = "host" if destructive_mode else None build_environment = env.BuilderEnvironmentConfig(force_provider=provider) project = get_project(is_managed_host=build_environment.is_managed_host, **kwargs) conduct_project_sanity_check(project) if build_environment.is_managed_host or build_environment.is_host: project_config = project_loader.load_config(project) lifecycle.execute(step, project_config, parts) if pack_project: _pack(project.prime_dir, output=output) else: build_provider_class = build_providers.get_provider_for( build_environment.provider) echo.info("Launching a VM.") with build_provider_class(project=project, echoer=echo) as instance: instance.mount_project() try: if shell: # shell means we want to do everything right up to the previous # step and then go into a shell instead of the requested step. # the "snap" target is a special snowflake that has not made its # way to be a proper step. previous_step = None if pack_project: previous_step = steps.PRIME elif step > steps.PULL: previous_step = step.previous_step() # steps.PULL is the first step, so we would directly shell into it. if previous_step: instance.execute_step(previous_step) elif pack_project: instance.pack_project(output=output) else: instance.execute_step(step) except Exception: if project.debug: instance.shell() else: echo.warning( "Run the same command again with --debug to shell into the environment " "if you wish to introspect this failure.") raise else: if shell or shell_after: instance.shell() return project
def run_check(self): snapcraft_yaml = fixture_setup.SnapcraftYaml( os.path.dirname(self.snap_asset_dir)) snapcraft_yaml.update_part("fake", dict(plugin="nil")) self.useFixture(snapcraft_yaml) project = snapcraft.project.Project( is_managed_host=self.is_managed_host, snapcraft_yaml_file_path=snapcraft_yaml.snapcraft_yaml_file_path, ) conduct_project_sanity_check(project)
def cleanbuild(): """This command is no longer available when using the base keyword.""" # We can only end up here with an invalid yaml project = get_project() conduct_project_sanity_check(project) # if we got this far, the environment must be broken raise errors.SnapcraftEnvironmentError( "The cleanbuild command is no longer supported when using the base keyword." )
def test_unexpected_things(caplog_warning, project): assets_dir = pathlib.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() conduct_project_sanity_check(project) assert caplog_warning.records[0].message == ( "The 'snap' directory is meant specifically for snapcraft, but it " "contains the following non-snapcraft-related paths, which is " "unsupported and will cause unexpected behavior:\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" "If you must store these files within the 'snap' directory, move them " "to 'snap/local', which is ignored by snapcraft.")
def test_icon(tmp_work_path): snapcraft_yaml_path = tmp_work_path / "snap/snapcraft.yaml" snapcraft_yaml_path.parent.mkdir(parents=True) with snapcraft_yaml_path.open("w") as snapcraft_file: print( dedent("""\ name: project-name base: core18 version: "1.0" summary: sanity checks description: sanity checks grade: stable confinement: strict icon: foo.png parts: nil: plugin: nil """), file=snapcraft_file, ) project = Project( is_managed_host=False, snapcraft_yaml_file_path=snapcraft_yaml_path.as_posix(), ) # Test without icon raises error with pytest.raises( snapcraft.internal.errors.SnapcraftEnvironmentError) as exc_info: conduct_project_sanity_check(project) assert exc_info.value.get_brief( ) == "Specified icon 'foo.png' does not exist." # Test with icon passes. (tmp_work_path / "foo.png").touch() conduct_project_sanity_check(project)
def _execute( # noqa: C901 step: steps.Step, parts: str, pack_project: bool = False, output: str = None, shell: bool = False, shell_after: bool = False, destructive_mode: bool = False, **kwargs) -> "Project": _clean_provider_error() provider = "host" if destructive_mode else None build_environment = env.BuilderEnvironmentConfig(force_provider=provider) project = get_project(is_managed_host=build_environment.is_managed_host, **kwargs) echo.wrapped("Using {!r}: Project assets will be " "searched for from the {!r} directory.".format( project.info.snapcraft_yaml_file_path, os.path.relpath(project._get_snapcraft_assets_dir(), project._project_dir), )) conduct_project_sanity_check(project) if build_environment.is_managed_host or build_environment.is_host: project_config = project_loader.load_config(project) lifecycle.execute(step, project_config, parts) if pack_project: _pack(project.prime_dir, output=output) else: build_provider_class = build_providers.get_provider_for( build_environment.provider) try: build_provider_class.ensure_provider() except build_providers.errors.ProviderNotFound as provider_error: if provider_error.prompt_installable: click.echo(str(provider_error)) if click.confirm("Would you like to install it now?"): build_provider_class.setup_provider(echoer=echo) else: raise provider_error else: raise provider_error echo.info("Launching a VM.") with build_provider_class(project=project, echoer=echo) as instance: instance.mount_project() try: if shell: # shell means we want to do everything right up to the previous # step and then go into a shell instead of the requested step. # the "snap" target is a special snowflake that has not made its # way to be a proper step. previous_step = None if pack_project: previous_step = steps.PRIME elif step > steps.PULL: previous_step = step.previous_step() # steps.PULL is the first step, so we would directly shell into it. if previous_step: instance.execute_step(previous_step) elif pack_project: instance.pack_project(output=output) else: instance.execute_step(step) except Exception: _retrieve_provider_error(instance) if project.debug: instance.shell() else: echo.warning( "Run the same command again with --debug to shell into the environment " "if you wish to introspect this failure.") raise else: if shell or shell_after: instance.shell() return project
def _execute( # noqa: C901 step: steps.Step, parts: str, pack_project: bool = False, output: Optional[str] = None, shell: bool = False, shell_after: bool = False, setup_prime_try: bool = False, **kwargs, ) -> "Project": # Cleanup any previous errors. _clean_provider_error() build_provider = get_build_provider(**kwargs) build_provider_flags = get_build_provider_flags(build_provider, **kwargs) apply_host_provider_flags(build_provider_flags) is_managed_host = build_provider == "managed-host" # Temporary fix to ignore target_arch. if kwargs.get("target_arch") is not None and build_provider in ["multipass", "lxd"]: echo.warning( "Ignoring '--target-arch' flag. This flag requires --destructive-mode and is unsupported with Multipass and LXD build providers." ) kwargs.pop("target_arch") project = get_project(is_managed_host=is_managed_host, **kwargs) conduct_project_sanity_check(project, **kwargs) project_path = pathlib.Path(project._project_dir) if project_path.name in ["build-aux", "snap"]: echo.warning( f"Snapcraft is running in directory {project_path.name!r}. If this is the snap assets directory, please run snapcraft from {project_path.parent}." ) if build_provider in ["host", "managed-host"]: project_config = project_loader.load_config(project) lifecycle.execute(step, project_config, parts) if pack_project: _pack( project.prime_dir, compression=project._snap_meta.compression, output=output, ) else: build_provider_class = build_providers.get_provider_for(build_provider) try: build_provider_class.ensure_provider() except build_providers.errors.ProviderNotFound as provider_error: if provider_error.prompt_installable: if echo.is_tty_connected() and echo.confirm( "Support for {!r} needs to be set up. " "Would you like to do it now?".format(provider_error.provider) ): build_provider_class.setup_provider(echoer=echo) else: raise provider_error else: raise provider_error with build_provider_class( project=project, echoer=echo, build_provider_flags=build_provider_flags ) as instance: try: if shell: # shell means we want to do everything right up to the previous # step and then go into a shell instead of the requested step. # the "snap" target is a special snowflake that has not made its # way to be a proper step. previous_step = None if pack_project: previous_step = steps.PRIME elif step > steps.PULL: previous_step = step.previous_step() # steps.PULL is the first step, so we would directly shell into it. if previous_step: instance.execute_step(previous_step) elif pack_project: instance.pack_project(output=output) elif setup_prime_try: instance.expose_prime() instance.execute_step(step) else: instance.execute_step(step) except Exception: _retrieve_provider_error(instance) if project.debug: instance.shell() else: echo.warning( "Run the same command again with --debug to shell into the environment " "if you wish to introspect this failure." ) raise else: if shell or shell_after: instance.shell() return project
def _execute( # noqa: C901 step: steps.Step, parts: str, pack_project: bool = False, output: Optional[str] = None, shell: bool = False, shell_after: bool = False, setup_prime_try: bool = False, **kwargs, ) -> "Project": # Cleanup any previous errors. _clean_provider_error() build_provider = get_build_provider(**kwargs) build_provider_flags = get_build_provider_flags(build_provider, **kwargs) apply_host_provider_flags(build_provider_flags) is_managed_host = build_provider == "managed-host" project = get_project(is_managed_host=is_managed_host, **kwargs) conduct_project_sanity_check(project, **kwargs) if build_provider in ["host", "managed-host"]: project_config = project_loader.load_config(project) lifecycle.execute(step, project_config, parts) if pack_project: _pack(project.prime_dir, output=output) else: build_provider_class = build_providers.get_provider_for(build_provider) try: build_provider_class.ensure_provider() except build_providers.errors.ProviderNotFound as provider_error: if provider_error.prompt_installable: if echo.is_tty_connected() and echo.confirm( "Support for {!r} needs to be set up. " "Would you like to do that it now?".format( provider_error.provider)): build_provider_class.setup_provider(echoer=echo) else: raise provider_error else: raise provider_error with build_provider_class( project=project, echoer=echo, build_provider_flags=build_provider_flags) as instance: instance.mount_project() try: if shell: # shell means we want to do everything right up to the previous # step and then go into a shell instead of the requested step. # the "snap" target is a special snowflake that has not made its # way to be a proper step. previous_step = None if pack_project: previous_step = steps.PRIME elif step > steps.PULL: previous_step = step.previous_step() # steps.PULL is the first step, so we would directly shell into it. if previous_step: instance.execute_step(previous_step) elif pack_project: instance.pack_project(output=output) elif setup_prime_try: instance.expose_prime() instance.execute_step(step) else: instance.execute_step(step) except Exception: _retrieve_provider_error(instance) if project.debug: instance.shell() else: echo.warning( "Run the same command again with --debug to shell into the environment " "if you wish to introspect this failure.") raise else: if shell or shell_after: instance.shell() return project
def _execute( # noqa: C901 step: steps.Step, parts: str, pack_project: bool = False, output: str = None, shell: bool = False, shell_after: bool = False, **kwargs ) -> "Project": # fmt: on if sys.platform == "darwin": default_provider = "multipass" else: default_provider = "host" build_environment = env.BuilderEnvironmentConfig(default=default_provider) project = get_project(is_managed_host=build_environment.is_managed_host, **kwargs) conduct_project_sanity_check(project) # When we are ready to pull the trigger we will trigger this when # project.info.base is set if build_environment.is_multipass: build_provider_class = build_providers.get_provider_for( build_environment.provider ) echo.info("Launching a VM.") with build_provider_class(project=project, echoer=echo) as instance: instance.mount_project() try: if shell: # shell means we want to do everything right up to the previous # step and then go into a shell instead of the requested step. # the "snap" target is a special snowflake that has not made its # way to be a proper step. previous_step = None if pack_project: previous_step = steps.PRIME elif step > steps.PULL: previous_step = step.previous_step() # steps.PULL is the first step, so we would directly shell into it. if previous_step: instance.execute_step(previous_step) elif pack_project: instance.pack_project(output=output) else: instance.execute_step(step) except Exception: if project.debug: instance.shell() else: echo.warning("Run the same command again with --debug to shell into the environment " "if you wish to introspect this failure.") raise else: if shell or shell_after: instance.shell() elif build_environment.is_managed_host or build_environment.is_host: project_config = project_loader.load_config(project) lifecycle.execute(step, project_config, parts) if pack_project: _pack(project.prime_dir, output=output) else: # containerbuild takes a snapcraft command name, not a step lifecycle.containerbuild(command=step.name, project=project, args=parts) if pack_project: _pack(project.prime_dir, output=output) return project
def run_check(self): project = snapcraft.project.Project( is_managed_host=self.is_managed_host) conduct_project_sanity_check(project)
def test_no_snap_dir(caplog_warning, project): conduct_project_sanity_check(project) assert len(caplog_warning.records) == 0
def _execute( # noqa: C901 step: steps.Step, parts: str, pack_project: bool = False, output: str = None, shell: bool = False, shell_after: bool = False, destructive_mode: bool = False, **kwargs) -> "Project": # fmt: on _clean_provider_error() provider = "host" if destructive_mode else None build_environment = env.BuilderEnvironmentConfig(force_provider=provider) try: conduct_build_environment_sanity_check(build_environment.provider) except MultipassMissingInstallableError as e: click.echo("You need multipass installed to build snaps " "(https://github.com/CanonicalLtd/multipass).") if click.confirm("Would you like to install it now?"): _install_multipass() else: raise errors.SnapcraftEnvironmentError( "multipass is required to continue.") from e project = get_project(is_managed_host=build_environment.is_managed_host, **kwargs) conduct_project_sanity_check(project) if build_environment.is_managed_host or build_environment.is_host: project_config = project_loader.load_config(project) lifecycle.execute(step, project_config, parts) if pack_project: _pack(project.prime_dir, output=output) else: build_provider_class = build_providers.get_provider_for( build_environment.provider) echo.info("Launching a VM.") with build_provider_class(project=project, echoer=echo) as instance: instance.mount_project() try: if shell: # shell means we want to do everything right up to the previous # step and then go into a shell instead of the requested step. # the "snap" target is a special snowflake that has not made its # way to be a proper step. previous_step = None if pack_project: previous_step = steps.PRIME elif step > steps.PULL: previous_step = step.previous_step() # steps.PULL is the first step, so we would directly shell into it. if previous_step: instance.execute_step(previous_step) elif pack_project: instance.pack_project(output=output) else: instance.execute_step(step) except Exception: _retrieve_provider_error(instance) if project.debug: instance.shell() else: echo.warning( "Run the same command again with --debug to shell into the environment " "if you wish to introspect this failure.") raise else: if shell or shell_after: instance.shell() return project