Пример #1
0
def _sanity_check_build_provider_flags(build_provider: str, **kwargs) -> None:
    destructive_mode = kwargs.get("destructive_mode")
    env_provider = os.getenv("SNAPCRAFT_BUILD_ENVIRONMENT")

    # Specifying --provider=host requires the use of --destructive-mode.
    # Exceptions include:
    # (1) SNAPCRAFT_BUILD_ENVIRONMENT=host.
    # (2) Running inside of a container.
    if (build_provider == "host" and not env_provider == "host"
            and not destructive_mode and not common.is_process_container()):
        raise click.BadArgumentUsage(
            "--provider=host requires --destructive-mode to acknowledge side effects"
        )

    if env_provider and env_provider != build_provider:
        raise click.BadArgumentUsage(
            "mismatch between --provider={} and SNAPCRAFT_BUILD_ENVIRONMENT={}"
            .format(build_provider, env_provider))

    # Error if any sys.argv params are for unsupported providers.
    # Values from environment variables and configuration files only
    # change defaults, so they are safe to ignore due to filtering
    # in get_build_provider_flags().
    for option in _PROVIDER_OPTIONS:
        key: str = option["param_decls"]  # type: ignore
        supported_providers: List[str] = option[
            "supported_providers"]  # type: ignore
        if key in sys.argv and build_provider not in supported_providers:
            raise click.BadArgumentUsage(
                f"{key} cannot be used with build provider {build_provider!r}")
Пример #2
0
def _install_build_snaps(build_snaps: Set[str], content_snaps: Set[str]) -> List[str]:
    if common.is_offline():
        logger.warning("Offline mode, not installing build packages.")
        return []

    if common.is_process_container() and build_snaps:
        installed_snaps: List[str] = []
        logger.warning(
            (
                "The following snaps are required but not installed as snapcraft "
                "is running inside docker or podman container: {}.\n"
                "Please ensure the environment is properly setup before continuing.\n"
                "Ignore this message if the appropriate measures have already been taken".format(
                    ", ".join(build_snaps)
                )
            )
        )
    else:
        installed_snaps = repo.snaps.install_snaps(build_snaps)
        for content_snap in content_snaps:
            try:
                installed_snaps += repo.snaps.install_snaps([content_snap])
            except repo.snaps.errors.SnapUnavailableError:
                logger.warning(
                    f"Could not install snap defined in plug {content_snap!r}. "
                    "The missing library report may have false positives listed if those "
                    "libraries are provided by the content snap."
                )

    return installed_snaps
Пример #3
0
    def __init__(self, *, force_provider: str = None) -> None:
        """Instantiate a BuildEnvironmentConfig.

        :param str force_provider: ignore the hints from the environment and use
                                   the specified provider.
        """
        if force_provider:
            build_provider = force_provider
        elif common.is_process_container():
            build_provider = "host"
        else:
            build_provider = os.environ.get("SNAPCRAFT_BUILD_ENVIRONMENT", "multipass")

        valid_providers = ["host", "multipass", "managed-host", "lxd"]
        if build_provider not in valid_providers:
            raise errors.SnapcraftEnvironmentError(
                "The snapcraft build environment must be one of: {}.".format(
                    humanize_list(items=valid_providers, conjunction="or")
                )
            )

        self.provider = build_provider
        self.is_host = build_provider == "host"
        self.is_multipass = build_provider == "multipass"
        self.is_multipass = build_provider == "lxd"
        self.is_managed_host = build_provider == "managed-host"
Пример #4
0
    def __init__(self, project: project.Project) -> None:
        self.build_snaps: Set[str] = set()
        self.project = project

        # raw_snapcraft_yaml is read only, create a new copy
        snapcraft_yaml = apply_extensions(project.info.get_raw_snapcraft())

        self.validator = Validator(snapcraft_yaml)
        self.validator.validate()

        snapcraft_yaml = self._expand_filesets(snapcraft_yaml)

        self.data = self._expand_env(snapcraft_yaml)

        self.data["architectures"] = _process_architectures(
            self.data.get("architectures"), project.deb_arch)

        self._ensure_no_duplicate_app_aliases()

        grammar_processor = grammar_processing.GlobalGrammarProcessor(
            properties=self.data, project=project)

        self.build_tools = grammar_processor.get_build_packages()
        self.build_tools |= set(project.additional_build_packages)

        # If version: git is used we want to add "git" to build-packages
        if self.data.get("version") == "git":
            self.build_tools.add("git")

        # XXX: Resetting snap_meta due to above mangling of data.
        # Convergence to operating on snap_meta will remove this requirement...
        project._snap_meta = Snap.from_dict(self.data)

        # Always add the base for building for non os and base snaps
        if project.info.base is None and project.info.type in ("app",
                                                               "gadget"):
            raise SnapcraftEnvironmentError(
                "A base is required for snaps of type {!r}.".format(
                    project.info.type))
        if project.info.base is not None:
            # If the base is already installed by other means, skip its installation.
            # But, we should always add it when in a docker environment so
            # the creator of said docker image is aware that it is required.
            if common.is_process_container(
            ) or not repo.snaps.SnapPackage.is_snap_installed(
                    project.info.base):
                self.build_snaps.add(project.info.base)

        self.parts = PartsConfig(
            parts=self.data,
            project=project,
            validator=self.validator,
            build_snaps=self.build_snaps,
            build_tools=self.build_tools,
        )
Пример #5
0
def _sanity_check_build_provider_flags(build_provider: str, **kwargs) -> None:
    destructive_mode = kwargs.get("destructive_mode")
    env_provider = os.getenv("SNAPCRAFT_BUILD_ENVIRONMENT")

    # Specifying --provider=host requires the use of --destructive-mode.
    # Exceptions include:
    # (1) SNAPCRAFT_BUILD_ENVIRONMENT=host.
    # (2) Running inside of a container.
    if (
        build_provider == "host"
        and not env_provider == "host"
        and not destructive_mode
        and not common.is_process_container()
    ):
        raise click.BadArgumentUsage(
            "--provider=host requires --destructive-mode to acknowledge side effects"
        )

    if env_provider and env_provider != build_provider:
        raise click.BadArgumentUsage(
            "mismatch between --provider={} and SNAPCRAFT_BUILD_ENVIRONMENT={}".format(
                build_provider, env_provider
            )
        )

    # Error if any sys.argv params are for unsupported providers.
    # Values from environment variables and configuration files only
    # change defaults, so they are safe to ignore due to filtering
    # in get_build_provider_flags().
    for option in _PROVIDER_OPTIONS:
        key: str = option["param_decls"]  # type: ignore
        supported_providers: List[str] = option["supported_providers"]  # type: ignore
        if key in sys.argv and build_provider not in supported_providers:
            raise click.BadArgumentUsage(
                f"{key} cannot be used with build provider {build_provider!r}"
            )

    # Check if running as sudo but only if the host is not managed-host where Snapcraft
    # runs as root already. This effectively avoids the warning when using the default
    # build provider (Multipass) that uses "sudo" to get "root".
    if (
        build_provider != "managed-host"
        and os.getenv("SUDO_USER")
        and os.geteuid() == 0
    ):
        warning(
            "Running with 'sudo' may cause permission errors and is discouraged. Use 'sudo' when cleaning."
        )
Пример #6
0
def get_build_provider(**kwargs) -> str:
    """Get build provider and determine if running as managed instance."""

    provider = kwargs.get("provider")

    if not provider:
        if kwargs.get("use_lxd"):
            provider = "lxd"
        elif kwargs.get("destructive_mode"):
            provider = "host"
        elif common.is_process_container():
            provider = "host"
        else:
            # Default is multipass.
            provider = "multipass"

    _sanity_check_build_provider_flags(provider, **kwargs)

    return provider
Пример #7
0
def get_build_provider(skip_sanity_checks: bool = False, **kwargs) -> str:
    """Get build provider and determine if running as managed instance."""

    provider = kwargs.get("provider")

    if not provider:
        if kwargs.get("use_lxd"):
            provider = "lxd"
        elif kwargs.get("destructive_mode"):
            provider = "host"
        elif common.is_process_container():
            provider = "host"
        else:
            # Default is multipass.
            provider = "multipass"

    # Sanity checks may be skipped for the purpose of checking legacy.
    if not skip_sanity_checks:
        _sanity_check_build_provider_flags(provider, **kwargs)

    return provider
Пример #8
0
def execute(
    step: steps.Step,
    project_config: "project_loader._config.Config",
    part_names: Sequence[str] = None,
):
    """Execute until step in the lifecycle for part_names or all parts.

    Lifecycle execution will happen for each step iterating over all
    the available parts, if part_names is specified, only those parts
    will run.

    If one of the parts to execute has an after keyword, execution is
    forced until the stage step for such part. If part_names was provided
    and after is not in this set, an exception will be raised.

    :param str step: A valid step in the lifecycle: pull, build, prime or snap.
    :param project_config: Fully loaded project (old logic moving either to
                           Project or the PluginHandler).
    :param list part_names: A list of parts to execute the lifecycle on.
    :raises RuntimeError: If a prerequesite of the part needs to be staged
                          and such part is not in the list of parts to iterate
                          over.
    :returns: A dict with the snap name, version, type and architectures.
    """
    installed_packages = repo.Repo.install_build_packages(
        project_config.build_tools)
    if installed_packages is None:
        raise ValueError(
            "The repo backend is not returning the list of installed packages")

    content_snaps = project_config.project._get_content_snaps()
    required_snaps = project_config.build_snaps | content_snaps

    if common.is_process_container():
        installed_snaps = []  # type: List[str]
        logger.warning((
            "The following snaps are required but not installed as snapcraft "
            "is running inside docker or podman container: {}.\n"
            "Please ensure the environment is properly setup before continuing.\n"
            "Ignore this message if the appropriate measures have already been taken"
            .format(", ".join(required_snaps))))
    else:
        installed_snaps = repo.snaps.install_snaps(required_snaps)

    try:
        global_state = states.GlobalState.load(
            filepath=project_config.project._get_global_state_file_path())
    except FileNotFoundError:
        global_state = states.GlobalState()
    global_state.append_build_packages(installed_packages)
    global_state.append_build_snaps(installed_snaps)
    # Let's not call out to the Snap Store if we do not need to.
    if global_state.get_required_grade() is None:
        global_state.set_required_grade(
            _get_required_grade(
                base=project_config.project.info.base,
                arch=project_config.project.deb_arch,
            ))
    global_state.save(
        filepath=project_config.project._get_global_state_file_path())

    executor = _Executor(project_config)
    executor.run(step, part_names)
    if not executor.steps_were_run:
        logger.warn(
            "The requested action has already been taken. Consider\n"
            "specifying parts, or clean the steps you want to run again.")

    return {
        "name": project_config.data["name"],
        "version": project_config.data.get("version"),
        "arch": project_config.data["architectures"],
        "type": project_config.data.get("type", ""),
    }