"Fossies" - the Fresh Open Source Software Archive

Member "snapcraft-3.8/snapcraft/cli/lifecycle.py" (9 Sep 2019, 10643 Bytes) of package /linux/misc/snapcraft-3.8.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "lifecycle.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.7.2_vs_3.8.

    1 # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
    2 #
    3 # Copyright (C) 2017-2019 Canonical Ltd
    4 #
    5 # This program is free software: you can redistribute it and/or modify
    6 # it under the terms of the GNU General Public License version 3 as
    7 # published by the Free Software Foundation.
    8 #
    9 # This program is distributed in the hope that it will be useful,
   10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12 # GNU General Public License for more details.
   13 #
   14 # You should have received a copy of the GNU General Public License
   15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
   16 
   17 import logging
   18 import typing
   19 import os
   20 
   21 import click
   22 
   23 from . import echo
   24 from ._command import SnapcraftProjectCommand
   25 from ._options import add_build_options, get_build_environment, get_project
   26 from snapcraft.internal import (
   27     build_providers,
   28     deprecations,
   29     errors,
   30     lifecycle,
   31     project_loader,
   32     steps,
   33 )
   34 from snapcraft.project._sanity_checks import conduct_project_sanity_check
   35 from ._errors import TRACEBACK_MANAGED, TRACEBACK_HOST
   36 
   37 
   38 logger = logging.getLogger(__name__)
   39 
   40 
   41 if typing.TYPE_CHECKING:
   42     from snapcraft.internal.project import Project  # noqa: F401
   43 
   44 
   45 # TODO: when snap is a real step we can simplify the arguments here.
   46 def _execute(  # noqa: C901
   47     step: steps.Step,
   48     parts: str,
   49     pack_project: bool = False,
   50     output: str = None,
   51     shell: bool = False,
   52     shell_after: bool = False,
   53     setup_prime_try: bool = False,
   54     **kwargs
   55 ) -> "Project":
   56     # Cleanup any previous errors.
   57     _clean_provider_error()
   58 
   59     build_environment = get_build_environment(**kwargs)
   60     project = get_project(is_managed_host=build_environment.is_managed_host, **kwargs)
   61 
   62     conduct_project_sanity_check(project)
   63 
   64     if build_environment.is_managed_host or build_environment.is_host:
   65         project_config = project_loader.load_config(project)
   66         lifecycle.execute(step, project_config, parts)
   67         if pack_project:
   68             _pack(project.prime_dir, output=output)
   69     else:
   70         build_provider_class = build_providers.get_provider_for(
   71             build_environment.provider
   72         )
   73         try:
   74             build_provider_class.ensure_provider()
   75         except build_providers.errors.ProviderNotFound as provider_error:
   76             if provider_error.prompt_installable:
   77                 if echo.is_tty_connected() and echo.confirm(
   78                     "Support for {!r} needs to be set up. "
   79                     "Would you like to do that it now?".format(provider_error.provider)
   80                 ):
   81                     build_provider_class.setup_provider(echoer=echo)
   82                 else:
   83                     raise provider_error
   84             else:
   85                 raise provider_error
   86 
   87         with build_provider_class(project=project, echoer=echo) as instance:
   88             instance.mount_project()
   89             try:
   90                 if shell:
   91                     # shell means we want to do everything right up to the previous
   92                     # step and then go into a shell instead of the requested step.
   93                     # the "snap" target is a special snowflake that has not made its
   94                     # way to be a proper step.
   95                     previous_step = None
   96                     if pack_project:
   97                         previous_step = steps.PRIME
   98                     elif step > steps.PULL:
   99                         previous_step = step.previous_step()
  100                     # steps.PULL is the first step, so we would directly shell into it.
  101                     if previous_step:
  102                         instance.execute_step(previous_step)
  103                 elif pack_project:
  104                     instance.pack_project(output=output)
  105                 elif setup_prime_try:
  106                     instance.expose_prime()
  107                     instance.execute_step(step)
  108                 else:
  109                     instance.execute_step(step)
  110             except Exception:
  111                 _retrieve_provider_error(instance)
  112                 if project.debug:
  113                     instance.shell()
  114                 else:
  115                     echo.warning(
  116                         "Run the same command again with --debug to shell into the environment "
  117                         "if you wish to introspect this failure."
  118                     )
  119                     raise
  120             else:
  121                 if shell or shell_after:
  122                     instance.shell()
  123     return project
  124 
  125 
  126 def _pack(directory: str, *, output: str) -> None:
  127     snap_name = lifecycle.pack(directory, output)
  128     echo.info("Snapped {}".format(snap_name))
  129 
  130 
  131 def _clean_provider_error() -> None:
  132     if os.path.isfile(TRACEBACK_HOST):
  133         try:
  134             os.remove(TRACEBACK_HOST)
  135         except Exception as e:
  136             logger.debug("can't remove error file: {}", str(e))
  137 
  138 
  139 def _retrieve_provider_error(instance) -> None:
  140     try:
  141         instance.pull_file(TRACEBACK_MANAGED, TRACEBACK_HOST, delete=True)
  142     except Exception as e:
  143         logger.debug("can't retrieve error file: {}", str(e))
  144 
  145 
  146 @click.group()
  147 @add_build_options()
  148 @click.pass_context
  149 def lifecyclecli(ctx, **kwargs):
  150     pass
  151 
  152 
  153 @lifecyclecli.command()
  154 def init():
  155     """Initialize a snapcraft project."""
  156     snapcraft_yaml_path = lifecycle.init()
  157     echo.info("Created {}.".format(snapcraft_yaml_path))
  158     echo.wrapped(
  159         "Go to https://docs.snapcraft.io/the-snapcraft-format/8337 for more "
  160         "information about the snapcraft.yaml format."
  161     )
  162 
  163 
  164 @lifecyclecli.command(cls=SnapcraftProjectCommand)
  165 @click.pass_context
  166 @add_build_options()
  167 @click.argument("parts", nargs=-1, metavar="<part>...", required=False)
  168 def pull(ctx, parts, **kwargs):
  169     """Download or retrieve artifacts defined for a part.
  170 
  171     \b
  172     Examples:
  173         snapcraft pull
  174         snapcraft pull my-part1 my-part2
  175 
  176     """
  177     _execute(steps.PULL, parts, **kwargs)
  178 
  179 
  180 @lifecyclecli.command(cls=SnapcraftProjectCommand)
  181 @add_build_options()
  182 @click.argument("parts", nargs=-1, metavar="<part>...", required=False)
  183 def build(parts, **kwargs):
  184     """Build artifacts defined for a part.
  185 
  186     \b
  187     Examples:
  188         snapcraft build
  189         snapcraft build my-part1 my-part2
  190 
  191     """
  192     _execute(steps.BUILD, parts, **kwargs)
  193 
  194 
  195 @lifecyclecli.command(cls=SnapcraftProjectCommand)
  196 @add_build_options()
  197 @click.argument("parts", nargs=-1, metavar="<part>...", required=False)
  198 def stage(parts, **kwargs):
  199     """Stage the part's built artifacts into the common staging area.
  200 
  201     \b
  202     Examples:
  203         snapcraft stage
  204         snapcraft stage my-part1 my-part2
  205 
  206     """
  207     _execute(steps.STAGE, parts, **kwargs)
  208 
  209 
  210 @lifecyclecli.command(cls=SnapcraftProjectCommand)
  211 @add_build_options()
  212 @click.argument("parts", nargs=-1, metavar="<part>...", required=False)
  213 def prime(parts, **kwargs):
  214     """Final copy and preparation for the snap.
  215 
  216     \b
  217     Examples:
  218         snapcraft prime
  219         snapcraft prime my-part1 my-part2
  220 
  221     """
  222     _execute(steps.PRIME, parts, **kwargs)
  223 
  224 
  225 @lifecyclecli.command("try")
  226 @add_build_options()
  227 def try_command(**kwargs):
  228     """Try a snap on the host, priming if necessary.
  229 
  230     This feature only works on snap enabled systems.
  231 
  232     \b
  233     Examples:
  234         snapcraft try
  235 
  236     """
  237     project = _execute(steps.PRIME, [], setup_prime_try=True, **kwargs)
  238     # project.prime_dir here points to the on-host prime directory.
  239     echo.info("You can now run `snap try {}`.".format(project.prime_dir))
  240 
  241 
  242 @lifecyclecli.command(cls=SnapcraftProjectCommand)
  243 @add_build_options()
  244 @click.argument("directory", required=False)
  245 @click.option("--output", "-o", help="path to the resulting snap.")
  246 def snap(directory, output, **kwargs):
  247     """Create a snap.
  248 
  249     \b
  250     Examples:
  251         snapcraft snap
  252         snapcraft snap --output renamed-snap.snap
  253 
  254     If you want to snap a directory, you should use the pack command
  255     instead.
  256     """
  257     if directory:
  258         deprecations.handle_deprecation_notice("dn6")
  259         _pack(directory, output=output)
  260     else:
  261         _execute(steps.PRIME, parts=[], pack_project=True, output=output, **kwargs)
  262 
  263 
  264 @lifecyclecli.command(cls=SnapcraftProjectCommand)
  265 @click.argument("directory")
  266 @click.option("--output", "-o", help="path to the resulting snap.")
  267 def pack(directory, output, **kwargs):
  268     """Create a snap from a directory holding a valid snap.
  269 
  270     The layout of <directory> should contain a valid meta/snap.yaml in
  271     order to be a valid snap.
  272 
  273     \b
  274     Examples:
  275         snapcraft pack my-snap-directory
  276         snapcraft pack my-snap-directory --output renamed-snap.snap
  277 
  278     """
  279     _pack(directory, output=output)
  280 
  281 
  282 @lifecyclecli.command(cls=SnapcraftProjectCommand)
  283 @click.argument("parts", nargs=-1, metavar="<part>...", required=False)
  284 @click.option(
  285     "--use-lxd",
  286     is_flag=True,
  287     required=False,
  288     help="Forces snapcraft to use LXD for this clean command.",
  289 )
  290 @click.option(
  291     "--destructive-mode",
  292     is_flag=True,
  293     required=False,
  294     help="Forces snapcraft to try and use the current host to clean.",
  295 )
  296 @click.option("--unprime", is_flag=True, required=False, hidden=True)
  297 @click.option("--step", required=False, hidden=True)
  298 def clean(parts, use_lxd, destructive_mode, unprime, step):
  299     """Remove a part's assets.
  300 
  301     \b
  302     Examples:
  303         snapcraft clean
  304         snapcraft clean my-part
  305     """
  306     # This option is only valid in legacy.
  307     if step:
  308         raise click.BadOptionUsage("--step", "no such option: --step")
  309 
  310     build_environment = get_build_environment(
  311         use_lxd=use_lxd, destructive_mode=destructive_mode
  312     )
  313 
  314     try:
  315         project = get_project(is_managed_host=build_environment.is_managed_host)
  316     except errors.ProjectNotFoundError:
  317         # Fresh environment, nothing to clean.
  318         return
  319 
  320     if unprime and not build_environment.is_managed_host:
  321         raise click.BadOptionUsage("--unprime", "no such option: --unprime")
  322 
  323     if build_environment.is_managed_host or build_environment.is_host:
  324         step = steps.PRIME if unprime else None
  325         lifecycle.clean(project, parts, step)
  326     else:
  327         build_provider_class = build_providers.get_provider_for(
  328             build_environment.provider
  329         )
  330         if parts:
  331             with build_provider_class(project=project, echoer=echo) as instance:
  332                 instance.clean(part_names=parts)
  333         else:
  334             build_provider_class(project=project, echoer=echo).clean_project()
  335             # Clear the prime directory on the host
  336             lifecycle.clean(project, parts, steps.PRIME)
  337 
  338 
  339 if __name__ == "__main__":
  340     lifecyclecli.main()