Tom Pollard pushed to branch tpollard/829 at BuildStream / buildstream
Commits:
- 
e2423c11
by Tom Pollard at 2019-01-23T13:37:31Z
3 changed files:
Changes:
| ... | ... | @@ -507,7 +507,7 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command) | 
| 507 | 507 |      else:
 | 
| 508 | 508 |          scope = Scope.RUN
 | 
| 509 | 509 |  | 
| 510 | -    use_buildtree = False
 | |
| 510 | +    use_buildtree = None
 | |
| 511 | 511 |  | 
| 512 | 512 |      with app.initialized():
 | 
| 513 | 513 |          if not element:
 | 
| ... | ... | @@ -515,7 +515,8 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command) | 
| 515 | 515 |              if not element:
 | 
| 516 | 516 |                  raise AppError('Missing argument "ELEMENT".')
 | 
| 517 | 517 |  | 
| 518 | -        dependencies = app.stream.load_selection((element,), selection=PipelineSelection.NONE)
 | |
| 518 | +        dependencies = app.stream.load_selection((element,), selection=PipelineSelection.NONE,
 | |
| 519 | +                                                 use_artifact_config=True)
 | |
| 519 | 520 |          element = dependencies[0]
 | 
| 520 | 521 |          prompt = app.shell_prompt(element)
 | 
| 521 | 522 |          mounts = [
 | 
| ... | ... | @@ -524,20 +525,31 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command) | 
| 524 | 525 |          ]
 | 
| 525 | 526 |  | 
| 526 | 527 |          cached = element._cached_buildtree()
 | 
| 527 | -        if cli_buildtree == "always":
 | |
| 528 | -            if cached:
 | |
| 529 | -                use_buildtree = True
 | |
| 530 | -            else:
 | |
| 531 | -                raise AppError("No buildtree is cached but the use buildtree option was specified")
 | |
| 532 | -        elif cli_buildtree == "never":
 | |
| 533 | -            pass
 | |
| 534 | -        elif cli_buildtree == "try":
 | |
| 535 | -            use_buildtree = cached
 | |
| 528 | +        if cli_buildtree in ("always", "try"):
 | |
| 529 | +            use_buildtree = cli_buildtree
 | |
| 530 | +            if not cached and use_buildtree == "always":
 | |
| 531 | +                click.echo("WARNING: buildtree is not cached locally, will attempt to pull from available remotes",
 | |
| 532 | +                           err=True)
 | |
| 536 | 533 |          else:
 | 
| 537 | -            if app.interactive and cached:
 | |
| 538 | -                use_buildtree = bool(click.confirm('Do you want to use the cached buildtree?'))
 | |
| 534 | +            # If the value has defaulted to ask and in non interactive mode, don't consider the buildtree, this
 | |
| 535 | +            # being the default behaviour of the command
 | |
| 536 | +            if app.interactive and cli_buildtree == "ask":
 | |
| 537 | +                if cached and bool(click.confirm('Do you want to use the cached buildtree?')):
 | |
| 538 | +                    use_buildtree = "always"
 | |
| 539 | +                elif not cached:
 | |
| 540 | +                    try:
 | |
| 541 | +                        choice = click.prompt("Do you want to pull & use a cached buildtree?",
 | |
| 542 | +                                              type=click.Choice(['try', 'always', 'never']),
 | |
| 543 | +                                              err=True, show_choices=True)
 | |
| 544 | +                    except click.Abort:
 | |
| 545 | +                        click.echo('Aborting', err=True)
 | |
| 546 | +                        sys.exit(-1)
 | |
| 547 | + | |
| 548 | +                    if choice != "never":
 | |
| 549 | +                        use_buildtree = choice
 | |
| 550 | + | |
| 539 | 551 |          if use_buildtree and not element._cached_success():
 | 
| 540 | -            click.echo("Warning: using a buildtree from a failed build.")
 | |
| 552 | +            click.echo("WARNING: using a buildtree from a failed build.", err=True)
 | |
| 541 | 553 |  | 
| 542 | 554 |          try:
 | 
| 543 | 555 |              exitcode = app.stream.shell(element, scope, prompt,
 | 
| ... | ... | @@ -127,7 +127,7 @@ class Stream(): | 
| 127 | 127 |      #    mounts (list of HostMount): Additional directories to mount into the sandbox
 | 
| 128 | 128 |      #    isolate (bool): Whether to isolate the environment like we do in builds
 | 
| 129 | 129 |      #    command (list): An argv to launch in the sandbox, or None
 | 
| 130 | -    #    usebuildtree (bool): Wheather to use a buildtree as the source.
 | |
| 130 | +    #    usebuildtree (str): Whether to use a buildtree as the source, given cli option
 | |
| 131 | 131 |      #
 | 
| 132 | 132 |      # Returns:
 | 
| 133 | 133 |      #    (int): The exit code of the launched shell
 | 
| ... | ... | @@ -137,7 +137,7 @@ class Stream(): | 
| 137 | 137 |                mounts=None,
 | 
| 138 | 138 |                isolate=False,
 | 
| 139 | 139 |                command=None,
 | 
| 140 | -              usebuildtree=False):
 | |
| 140 | +              usebuildtree=None):
 | |
| 141 | 141 |  | 
| 142 | 142 |          # Assert we have everything we need built, unless the directory is specified
 | 
| 143 | 143 |          # in which case we just blindly trust the directory, using the element
 | 
| ... | ... | @@ -152,8 +152,31 @@ class Stream(): | 
| 152 | 152 |                  raise StreamError("Elements need to be built or downloaded before staging a shell environment",
 | 
| 153 | 153 |                                    detail="\n".join(missing_deps))
 | 
| 154 | 154 |  | 
| 155 | +        buildtree = False
 | |
| 156 | +        # Check if we require a pull queue attempt, with given artifact state and context
 | |
| 157 | +        if usebuildtree:
 | |
| 158 | +            if not element._cached_buildtree():
 | |
| 159 | +                require_buildtree = self._buildtree_pull_required([element])
 | |
| 160 | +                # Attempt a pull queue for the given element if remote and context allow it
 | |
| 161 | +                if require_buildtree:
 | |
| 162 | +                    self._message(MessageType.INFO, "Attempting to fetch missing artifact buildtree")
 | |
| 163 | +                    self._add_queue(PullQueue(self._scheduler))
 | |
| 164 | +                    self._enqueue_plan(require_buildtree)
 | |
| 165 | +                    self._run()
 | |
| 166 | +                    # Now check if the buildtree was successfully fetched
 | |
| 167 | +                    if element._cached_buildtree():
 | |
| 168 | +                        buildtree = True
 | |
| 169 | +                if not buildtree:
 | |
| 170 | +                    if usebuildtree == "always":
 | |
| 171 | +                        raise StreamError("Buildtree is not cached locally or in available remotes")
 | |
| 172 | +                    else:
 | |
| 173 | +                        self._message(MessageType.INFO, """Buildtree is not cached locally or in available remotes,
 | |
| 174 | +                                                        shell will be loaded without it""")
 | |
| 175 | +            else:
 | |
| 176 | +                buildtree = True
 | |
| 177 | + | |
| 155 | 178 |          return element._shell(scope, directory, mounts=mounts, isolate=isolate, prompt=prompt, command=command,
 | 
| 156 | -                              usebuildtree=usebuildtree)
 | |
| 179 | +                              usebuildtree=buildtree)
 | |
| 157 | 180 |  | 
| 158 | 181 |      # build()
 | 
| 159 | 182 |      #
 | 
| ... | ... | @@ -101,7 +101,7 @@ def test_buildtree_from_failure(cli_integration, tmpdir, datafiles): | 
| 101 | 101 |          'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
 | 
| 102 | 102 |      ])
 | 
| 103 | 103 |      res.assert_success()
 | 
| 104 | -    assert "Warning: using a buildtree from a failed build" in res.output
 | |
| 104 | +    assert "WARNING: using a buildtree from a failed build" in res.stderr
 | |
| 105 | 105 |      assert 'Hi' in res.output
 | 
| 106 | 106 |  | 
| 107 | 107 |  | 
| ... | ... | @@ -141,7 +141,7 @@ def test_buildtree_pulled(cli, tmpdir, datafiles): | 
| 141 | 141 |          res.assert_success()
 | 
| 142 | 142 |  | 
| 143 | 143 |  | 
| 144 | -# This test checks for correct behaviour if a buildtree is not present.
 | |
| 144 | +# This test checks for correct behaviour if a buildtree is not present in the local cache.
 | |
| 145 | 145 |  @pytest.mark.datafiles(DATA_DIR)
 | 
| 146 | 146 |  @pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
 | 
| 147 | 147 |  def test_buildtree_options(cli, tmpdir, datafiles):
 | 
| ... | ... | @@ -156,6 +156,7 @@ def test_buildtree_options(cli, tmpdir, datafiles): | 
| 156 | 156 |          result = cli.run(project=project, args=['build', element_name])
 | 
| 157 | 157 |          result.assert_success()
 | 
| 158 | 158 |          assert cli.get_element_state(project, element_name) == 'cached'
 | 
| 159 | +        assert share.has_artifact('test', element_name, cli.get_element_key(project, element_name))
 | |
| 159 | 160 |  | 
| 160 | 161 |          # Discard the cache
 | 
| 161 | 162 |          cli.configure({
 | 
| ... | ... | @@ -168,8 +169,6 @@ def test_buildtree_options(cli, tmpdir, datafiles): | 
| 168 | 169 |          result = cli.run(project=project, args=['artifact', 'pull', '--deps', 'all', element_name])
 | 
| 169 | 170 |          result.assert_success()
 | 
| 170 | 171 |  | 
| 171 | -        # The above is the simplest way I know to create a local cache without any buildtrees.
 | |
| 172 | - | |
| 173 | 172 |          # Check it's not using the cached build tree
 | 
| 174 | 173 |          res = cli.run(project=project, args=[
 | 
| 175 | 174 |              'shell', '--build', element_name, '--use-buildtree', 'never', '--', 'cat', 'test'
 | 
| ... | ... | @@ -177,24 +176,51 @@ def test_buildtree_options(cli, tmpdir, datafiles): | 
| 177 | 176 |          res.assert_shell_error()
 | 
| 178 | 177 |          assert 'Hi' not in res.output
 | 
| 179 | 178 |  | 
| 180 | -        # Check it's not correctly handling the lack of buildtree
 | |
| 179 | +        # Check it's not using the cached build tree, default is to ask, and fall back to not
 | |
| 180 | +        # for non interactive behavior
 | |
| 181 | 181 |          res = cli.run(project=project, args=[
 | 
| 182 | -            'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
 | |
| 182 | +            'shell', '--build', element_name, '--', 'cat', 'test'
 | |
| 183 | 183 |          ])
 | 
| 184 | 184 |          res.assert_shell_error()
 | 
| 185 | 185 |          assert 'Hi' not in res.output
 | 
| 186 | 186 |  | 
| 187 | -        # Check it's not using the cached build tree, default is to ask, and fall back to not
 | |
| 188 | -        # for non interactive behavior
 | |
| 187 | +        # Check correctly handling the lack of buildtree, with 'try' not attempting to
 | |
| 188 | +        # pull the buildtree as the user context is by default set to not pull them
 | |
| 189 | 189 |          res = cli.run(project=project, args=[
 | 
| 190 | -            'shell', '--build', element_name, '--', 'cat', 'test'
 | |
| 190 | +            'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
 | |
| 191 | 191 |          ])
 | 
| 192 | -        res.assert_shell_error()
 | |
| 193 | 192 |          assert 'Hi' not in res.output
 | 
| 193 | +        assert 'Attempting to fetch missing artifact buildtrees' not in res.stderr
 | |
| 194 | +        assert """Buildtree is not cached locally or in available remotes,
 | |
| 195 | +                shell will be loaded without it"""
 | |
| 194 | 196 |  | 
| 195 | -        # Check it's using the cached build tree
 | |
| 197 | +        # Check correctly handling the lack of buildtree, with 'try' attempting and succeeding
 | |
| 198 | +        # to pull the buildtree as the user context allow the pulling of buildtrees and it is
 | |
| 199 | +        # available in the remote
 | |
| 200 | +        res = cli.run(project=project, args=[
 | |
| 201 | +            '--pull-buildtrees', 'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
 | |
| 202 | +        ])
 | |
| 203 | +        assert 'Attempting to fetch missing artifact buildtree' in res.stderr
 | |
| 204 | +        assert 'Hi' in res.output
 | |
| 205 | +        shutil.rmtree(os.path.join(os.path.join(cli.directory, 'artifacts2')))
 | |
| 206 | +        assert cli.get_element_state(project, element_name) != 'cached'
 | |
| 207 | + | |
| 208 | +        # Check it's not loading the shell at all with always set for the buildtree, when the
 | |
| 209 | +        # user context does not allow for buildtree pulling
 | |
| 210 | +        result = cli.run(project=project, args=['artifact', 'pull', '--deps', 'all', element_name])
 | |
| 211 | +        result.assert_success()
 | |
| 196 | 212 |          res = cli.run(project=project, args=[
 | 
| 197 | 213 |              'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
 | 
| 198 | 214 |          ])
 | 
| 199 | 215 |          res.assert_main_error(ErrorDomain.PROG_NOT_FOUND, None)
 | 
| 216 | +        assert 'Buildtree is not cached locally or in available remotes' in res.stderr
 | |
| 200 | 217 |          assert 'Hi' not in res.output
 | 
| 218 | +        assert 'Attempting to fetch missing artifact buildtree' not in res.stderr
 | |
| 219 | + | |
| 220 | +        # Check that when user context is set to pull buildtrees and a remote has the buildtree,
 | |
| 221 | +        # 'always' will attempt and succeed at pulling the missing buildtree.
 | |
| 222 | +        res = cli.run(project=project, args=[
 | |
| 223 | +            '--pull-buildtrees', 'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
 | |
| 224 | +        ])
 | |
| 225 | +        assert 'Hi' in res.output
 | |
| 226 | +        assert 'Attempting to fetch missing artifact buildtree' in res.stderr | 
