Exemplo n.º 1
0
def setup_dirs() -> None:
    """
    Ensure that snapcraft_legacy.common plugindir is setup correctly
    and support running out of a development snapshot
    """
    from snapcraft_legacy.internal import common

    topdir = os.path.abspath(os.path.join(__file__, "..", "..", ".."))

    # Only change the default if we are running from a checkout or from the
    # snap, or in Windows.
    if os.path.exists(os.path.join(topdir, "setup.py")):
        common.set_plugindir(os.path.join(topdir, "snapcraft_legacy", "plugins"))
        common.set_schemadir(os.path.join(topdir, "schema"))
        common.set_extensionsdir(os.path.join(topdir, "extensions"))
        common.set_keyringsdir(os.path.join(topdir, "keyrings"))

    # The default paths are relative to sys.prefix, which works well for
    # Snapcraft as a deb or in a venv. However, the Python plugin installs
    # packages into $SNAP/ as a prefix, while Python itself is contained in
    # $SNAP/usr/. As a result, using sys.prefix (which is '/usr') to find these
    # files won't work in the snap.
    elif common.is_snap():
        snap_path = os.environ.get("SNAP")
        if snap_path is None:
            # Shouldn't happen, but it is certainly an error if it does.
            raise RuntimeError("SNAP not defined, but SNAP_NAME is?")

        parent_dir = os.path.join(snap_path, "share", "snapcraft")
        common.set_plugindir(os.path.join(parent_dir, "plugins"))
        common.set_schemadir(os.path.join(parent_dir, "schema"))
        common.set_extensionsdir(os.path.join(parent_dir, "extensions"))
        common.set_keyringsdir(os.path.join(parent_dir, "keyrings"))

    elif sys.platform == "win32":
        common.set_plugindir(os.path.join(topdir, "snapcraft_legacy", "plugins"))

        data_dir = _find_windows_data_dir(topdir)
        common.set_schemadir(os.path.join(data_dir, "schema"))
        common.set_extensionsdir(os.path.join(data_dir, "extensions"))
        common.set_keyringsdir(os.path.join(data_dir, "keyrings"))

    else:
        # Make sure required data directories exist in the default locations.
        # Plugins directory is not required.
        for d in [
            common.get_schemadir(),
            common.get_extensionsdir(),
            common.get_keyringsdir(),
        ]:
            if not os.path.exists(d):
                raise snapcraft_legacy.internal.errors.SnapcraftDataDirectoryMissingError()
Exemplo n.º 2
0
    def _setup_snapcraft(self) -> None:
        self._save_info(
            data={
                "base": self.project._get_build_base(),
                "created-by-snapcraft-version": snapcraft_legacy._get_version(),
                "host-project-directory": self.project._project_dir,
            }
        )

        registry_filepath = os.path.join(
            self.provider_project_dir, "snap-registry.yaml"
        )

        # We do not want to inject from the host if not running from the snap
        # or if the provider cannot handle snap mounts.
        # This latter problem should go away when API for retrieving snaps
        # through snapd is generally available.
        if self._get_is_snap_injection_capable():
            inject_from_host = common.is_snap()
        else:
            inject_from_host = False

        snap_injector = SnapInjector(
            registry_filepath=registry_filepath,
            runner=self._run,
            file_pusher=self._push_file,
            inject_from_host=inject_from_host,
        )

        snap_injector.add(snap_name="snapd")

        # Prevent injecting core20 twice (Snapcraft's base).
        build_base = self.project._get_build_base()
        if build_base != "core20":
            snap_injector.add(snap_name=build_base)

        # Inject snapcraft and its base.
        snap_injector.add(snap_name="core20")
        snap_injector.add(snap_name="snapcraft")

        snap_injector.apply()
Exemplo n.º 3
0
    def _configure_apt(self):
        # Do not install recommends.
        apt.apt_pkg.config.set("Apt::Install-Recommends", "False")

        # Ensure repos are provided by trusted third-parties.
        apt.apt_pkg.config.set("Acquire::AllowInsecureRepositories", "False")

        # Methods and solvers dir for when in the SNAP.
        snap_dir = os.getenv("SNAP")
        if common.is_snap() and snap_dir and os.path.exists(snap_dir):
            apt_dir = os.path.join(snap_dir, "usr", "lib", "apt")
            apt.apt_pkg.config.set("Dir", apt_dir)
            # yes apt is broken like that we need to append os.path.sep
            methods_dir = os.path.join(apt_dir, "methods")
            apt.apt_pkg.config.set("Dir::Bin::methods",
                                   methods_dir + os.path.sep)
            solvers_dir = os.path.join(apt_dir, "solvers")
            apt.apt_pkg.config.set("Dir::Bin::solvers::",
                                   solvers_dir + os.path.sep)
            apt_key_path = os.path.join(snap_dir, "usr", "bin", "apt-key")
            apt.apt_pkg.config.set("Dir::Bin::apt-key", apt_key_path)
            gpgv_path = os.path.join(snap_dir, "usr", "bin", "gpgv")
            apt.apt_pkg.config.set("Apt::Key::gpgvcommand", gpgv_path)

        apt.apt_pkg.config.set("Dir::Etc::Trusted", "/etc/apt/trusted.gpg")
        apt.apt_pkg.config.set("Dir::Etc::TrustedParts",
                               "/etc/apt/trusted.gpg.d/")
        apt.apt_pkg.config.set("Dir::State", "/var/lib/apt")

        # Clear up apt's Post-Invoke-Success as we are not running
        # on the system.
        apt.apt_pkg.config.clear("APT::Update::Post-Invoke-Success")

        self.progress = apt.progress.text.AcquireProgress()
        if is_dumb_terminal():
            # Make output more suitable for logging.
            self.progress.pulse = lambda owner: True
            self.progress._width = 0
Exemplo n.º 4
0
def get_snap_tool_path(command_name: str) -> str:
    """Return the path command found in the snap.

    If snapcraft is not running as a snap, shutil.which() is used
    to resolve the command using PATH.

    :param command_name: the name of the command to resolve a path for.
    :raises ToolMissingError: if command_name was not found.
    :return: Path to command
    """
    if common.is_snap():
        snap_path = os.getenv("SNAP")
        if snap_path is None:
            raise RuntimeError("SNAP not defined, but SNAP_NAME is?")

        command_path = _find_command_path_in_root(snap_path, command_name)
    else:
        command_path = shutil.which(command_name)

    if command_path is None:
        raise errors.ToolMissingError(command_name=command_name)

    return command_path
Exemplo n.º 5
0
    def _run_scriptlet(self, scriptlet_name: str, scriptlet: str, workdir: str,
                       step: steps.Step) -> None:
        if common.is_snap():
            # Since the snap is classic, there is no $PATH pointing into the snap, which
            # means snapcraftctl won't be found. We can't use aliases since they don't
            # persist into subshells. However, we know that snapcraftctl lives in its own
            # directory, so adding that to the PATH should have no ill side effects.
            snapcraftctl_env = 'export PATH="$PATH:$SNAP/bin/scriptlet-bin"'
        else:
            snapcraftctl_env = ""

        with tempfile.TemporaryDirectory(dir=self._partdir) as tempdir:
            call_fifo = _NonBlockingRWFifo(
                os.path.join(tempdir, "function_call"))
            feedback_fifo = _NonBlockingRWFifo(
                os.path.join(tempdir, "call_feedback"))

            # snapcraftctl only works consistently if it's using the exact same
            # interpreter as that used by snapcraft itself, thus the definition
            # of SNAPCRAFT_INTERPRETER.
            script = textwrap.dedent("""\
                set -e
                export SNAPCRAFTCTL_CALL_FIFO={call_fifo}
                export SNAPCRAFTCTL_FEEDBACK_FIFO={feedback_fifo}
                export SNAPCRAFT_INTERPRETER={interpreter}
                {snapcraftctl_env}

                {env}

                {shell_flags}

                {scriptlet}""").format(
                shell_flags=self._shell_flags,
                interpreter=sys.executable,
                call_fifo=call_fifo.path,
                feedback_fifo=feedback_fifo.path,
                scriptlet=scriptlet,
                snapcraftctl_env=snapcraftctl_env,
                env=self._env_generator(step),
            )

            with tempfile.TemporaryFile(mode="w+") as script_file:
                print(script, file=script_file)
                script_file.flush()
                script_file.seek(0)

                process = subprocess.Popen([self._shell],
                                           stdin=script_file,
                                           cwd=workdir)

            status = None
            try:
                while status is None:
                    function_call = call_fifo.read()
                    if function_call:
                        # Handle the function and let caller know that function
                        # call has been handled (must contain at least a
                        # newline, anything beyond is considered an error by
                        # snapcraftctl)
                        self._handle_builtin_function(scriptlet_name,
                                                      function_call.strip())
                        feedback_fifo.write("\n")

                    status = process.poll()

                    # Don't loop TOO busily
                    time.sleep(0.1)
            except Exception as error:
                feedback_fifo.write(f"{error!s}\n")
                raise error
            finally:
                call_fifo.close()
                feedback_fifo.close()

            if process.returncode != 0:
                raise errors.ScriptletRunError(scriptlet_name=scriptlet_name,
                                               code=status)