Tom Pollard pushed to branch tpollard/829 at BuildStream / buildstream
Commits:
-
43797617
by Richard Maw at 2019-01-21T10:41:37Z
-
ce8dab0f
by Richard Maw at 2019-01-21T10:41:37Z
-
50165081
by Richard Maw at 2019-01-21T10:41:37Z
-
8677a256
by Benjamin Schubert at 2019-01-21T10:41:37Z
-
33782865
by Valentin David at 2019-01-21T11:47:18Z
-
098b777d
by Tom Pollard at 2019-01-21T12:54:27Z
-
d0c98ed5
by Tom Pollard at 2019-01-21T12:54:27Z
8 changed files:
- .gitlab-ci.yml
- buildstream/_frontend/cli.py
- buildstream/_gitsourcebase.py
- buildstream/_stream.py
- tests/frontend/workspace.py
- tests/integration/build-tree.py
- tests/sources/git.py
- tests/testutils/site.py
Changes:
... | ... | @@ -31,6 +31,7 @@ variables: |
31 | 31 |
- df -h
|
32 | 32 |
|
33 | 33 |
script:
|
34 |
+ - mkdir -p "${INTEGRATION_CACHE}"
|
|
34 | 35 |
- useradd -Um buildstream
|
35 | 36 |
- chown -R buildstream:buildstream .
|
36 | 37 |
|
... | ... | @@ -70,6 +71,10 @@ tests-python-3.7-stretch: |
70 | 71 |
# some of our base dependencies declare it as their runtime dependency.
|
71 | 72 |
TOXENV: py37
|
72 | 73 |
|
74 |
+tests-centos-7.6:
|
|
75 |
+ <<: *tests
|
|
76 |
+ image: buildstream/testsuite-centos:7.6-5da27168-32c47d1c
|
|
77 |
+ |
|
73 | 78 |
overnight-fedora-28-aarch64:
|
74 | 79 |
image: buildstream/testsuite-fedora:aarch64-28-5da27168-32c47d1c
|
75 | 80 |
tags:
|
... | ... | @@ -579,7 +579,7 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command) |
579 | 579 |
else:
|
580 | 580 |
scope = Scope.RUN
|
581 | 581 |
|
582 |
- use_buildtree = False
|
|
582 |
+ use_buildtree = None
|
|
583 | 583 |
|
584 | 584 |
with app.initialized():
|
585 | 585 |
if not element:
|
... | ... | @@ -587,7 +587,8 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command) |
587 | 587 |
if not element:
|
588 | 588 |
raise AppError('Missing argument "ELEMENT".')
|
589 | 589 |
|
590 |
- dependencies = app.stream.load_selection((element,), selection=PipelineSelection.NONE)
|
|
590 |
+ dependencies = app.stream.load_selection((element,), selection=PipelineSelection.NONE,
|
|
591 |
+ use_artifact_config=True)
|
|
591 | 592 |
element = dependencies[0]
|
592 | 593 |
prompt = app.shell_prompt(element)
|
593 | 594 |
mounts = [
|
... | ... | @@ -596,18 +597,19 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command) |
596 | 597 |
]
|
597 | 598 |
|
598 | 599 |
cached = element._cached_buildtree()
|
599 |
- if cli_buildtree == "always":
|
|
600 |
- if cached:
|
|
601 |
- use_buildtree = True
|
|
602 |
- else:
|
|
603 |
- raise AppError("No buildtree is cached but the use buildtree option was specified")
|
|
604 |
- elif cli_buildtree == "never":
|
|
605 |
- pass
|
|
606 |
- elif cli_buildtree == "try":
|
|
607 |
- use_buildtree = cached
|
|
600 |
+ if cli_buildtree in ("always", "try"):
|
|
601 |
+ use_buildtree = cli_buildtree
|
|
602 |
+ if not cached and use_buildtree == "always":
|
|
603 |
+ click.echo("Warning: buildtree is not cached locally, will attempt to pull from available remotes")
|
|
608 | 604 |
else:
|
609 |
- if app.interactive and cached:
|
|
610 |
- use_buildtree = bool(click.confirm('Do you want to use the cached buildtree?'))
|
|
605 |
+ # If the value has defaulted to ask and in non interactive mode, don't consider the buildtree, this
|
|
606 |
+ # being the default behaviour of the command
|
|
607 |
+ if app.interactive and cli_buildtree == "ask":
|
|
608 |
+ if cached and bool(click.confirm('Do you want to use the cached buildtree?')):
|
|
609 |
+ use_buildtree = "always"
|
|
610 |
+ elif not cached and bool(click.confirm('Do you want to attempt to pull the cached buildtree?')):
|
|
611 |
+ use_buildtree = "try"
|
|
612 |
+ |
|
611 | 613 |
if use_buildtree and not element._cached_success():
|
612 | 614 |
click.echo("Warning: using a buildtree from a failed build.")
|
613 | 615 |
|
... | ... | @@ -112,7 +112,8 @@ class GitMirror(SourceFetcher): |
112 | 112 |
else:
|
113 | 113 |
remote_name = "origin"
|
114 | 114 |
|
115 |
- self.source.call([self.source.host_git, 'fetch', remote_name, '--prune', '--force', '--tags'],
|
|
115 |
+ self.source.call([self.source.host_git, 'fetch', remote_name, '--prune',
|
|
116 |
+ '+refs/heads/*:refs/heads/*', '+refs/tags/*:refs/tags/*'],
|
|
116 | 117 |
fail="Failed to fetch from remote git repository: {}".format(url),
|
117 | 118 |
fail_temporarily=True,
|
118 | 119 |
cwd=self.mirror)
|
... | ... | @@ -100,16 +100,19 @@ class Stream(): |
100 | 100 |
# targets (list of str): Targets to pull
|
101 | 101 |
# selection (PipelineSelection): The selection mode for the specified targets
|
102 | 102 |
# except_targets (list of str): Specified targets to except from fetching
|
103 |
+ # use_artifact_config (bool): If artifact remote config should be loaded
|
|
103 | 104 |
#
|
104 | 105 |
# Returns:
|
105 | 106 |
# (list of Element): The selected elements
|
106 | 107 |
def load_selection(self, targets, *,
|
107 | 108 |
selection=PipelineSelection.NONE,
|
108 |
- except_targets=()):
|
|
109 |
+ except_targets=(),
|
|
110 |
+ use_artifact_config=False):
|
|
109 | 111 |
elements, _ = self._load(targets, (),
|
110 | 112 |
selection=selection,
|
111 | 113 |
except_targets=except_targets,
|
112 |
- fetch_subprojects=False)
|
|
114 |
+ fetch_subprojects=False,
|
|
115 |
+ use_artifact_config=use_artifact_config)
|
|
113 | 116 |
return elements
|
114 | 117 |
|
115 | 118 |
# shell()
|
... | ... | @@ -124,7 +127,7 @@ class Stream(): |
124 | 127 |
# mounts (list of HostMount): Additional directories to mount into the sandbox
|
125 | 128 |
# isolate (bool): Whether to isolate the environment like we do in builds
|
126 | 129 |
# command (list): An argv to launch in the sandbox, or None
|
127 |
- # usebuildtree (bool): Wheather to use a buildtree as the source.
|
|
130 |
+ # usebuildtree (str): Whether to use a buildtree as the source, given cli option
|
|
128 | 131 |
#
|
129 | 132 |
# Returns:
|
130 | 133 |
# (int): The exit code of the launched shell
|
... | ... | @@ -134,7 +137,7 @@ class Stream(): |
134 | 137 |
mounts=None,
|
135 | 138 |
isolate=False,
|
136 | 139 |
command=None,
|
137 |
- usebuildtree=False):
|
|
140 |
+ usebuildtree=None):
|
|
138 | 141 |
|
139 | 142 |
# Assert we have everything we need built, unless the directory is specified
|
140 | 143 |
# in which case we just blindly trust the directory, using the element
|
... | ... | @@ -149,8 +152,30 @@ class Stream(): |
149 | 152 |
raise StreamError("Elements need to be built or downloaded before staging a shell environment",
|
150 | 153 |
detail="\n".join(missing_deps))
|
151 | 154 |
|
155 |
+ # Check if we require a pull queue attempt, with given artifact state and context
|
|
156 |
+ if usebuildtree:
|
|
157 |
+ if not element._cached_buildtree():
|
|
158 |
+ buildtree = False
|
|
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 |
+ usebuildtree = False
|
|
176 |
+ |
|
152 | 177 |
return element._shell(scope, directory, mounts=mounts, isolate=isolate, prompt=prompt, command=command,
|
153 |
- usebuildtree=usebuildtree)
|
|
178 |
+ usebuildtree=bool(usebuildtree))
|
|
154 | 179 |
|
155 | 180 |
# build()
|
156 | 181 |
#
|
... | ... | @@ -204,6 +204,7 @@ def test_open_multi(cli, tmpdir, datafiles): |
204 | 204 |
assert not ('.bzr' in workspace_lsdir)
|
205 | 205 |
|
206 | 206 |
|
207 |
+@pytest.mark.skipif(os.geteuid() == 0, reason="root may have CAP_DAC_OVERRIDE and ignore permissions")
|
|
207 | 208 |
@pytest.mark.datafiles(DATA_DIR)
|
208 | 209 |
def test_open_multi_unwritable(cli, tmpdir, datafiles):
|
209 | 210 |
workspace_object = WorkspaceCreater(cli, tmpdir, datafiles)
|
... | ... | @@ -140,7 +140,7 @@ def test_buildtree_pulled(cli, tmpdir, datafiles): |
140 | 140 |
res.assert_success()
|
141 | 141 |
|
142 | 142 |
|
143 |
-# This test checks for correct behaviour if a buildtree is not present.
|
|
143 |
+# This test checks for correct behaviour if a buildtree is not present in the local cache.
|
|
144 | 144 |
@pytest.mark.datafiles(DATA_DIR)
|
145 | 145 |
@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
|
146 | 146 |
def test_buildtree_options(cli, tmpdir, datafiles):
|
... | ... | @@ -155,6 +155,7 @@ def test_buildtree_options(cli, tmpdir, datafiles): |
155 | 155 |
result = cli.run(project=project, args=['build', element_name])
|
156 | 156 |
result.assert_success()
|
157 | 157 |
assert cli.get_element_state(project, element_name) == 'cached'
|
158 |
+ assert share.has_artifact('test', element_name, cli.get_element_key(project, element_name))
|
|
158 | 159 |
|
159 | 160 |
# Discard the cache
|
160 | 161 |
cli.configure({
|
... | ... | @@ -167,8 +168,6 @@ def test_buildtree_options(cli, tmpdir, datafiles): |
167 | 168 |
result = cli.run(project=project, args=['pull', '--deps', 'all', element_name])
|
168 | 169 |
result.assert_success()
|
169 | 170 |
|
170 |
- # The above is the simplest way I know to create a local cache without any buildtrees.
|
|
171 |
- |
|
172 | 171 |
# Check it's not using the cached build tree
|
173 | 172 |
res = cli.run(project=project, args=[
|
174 | 173 |
'shell', '--build', element_name, '--use-buildtree', 'never', '--', 'cat', 'test'
|
... | ... | @@ -176,24 +175,51 @@ def test_buildtree_options(cli, tmpdir, datafiles): |
176 | 175 |
res.assert_shell_error()
|
177 | 176 |
assert 'Hi' not in res.output
|
178 | 177 |
|
179 |
- # Check it's not correctly handling the lack of buildtree
|
|
178 |
+ # Check it's not using the cached build tree, default is to ask, and fall back to not
|
|
179 |
+ # for non interactive behavior
|
|
180 | 180 |
res = cli.run(project=project, args=[
|
181 |
- 'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
|
|
181 |
+ 'shell', '--build', element_name, '--', 'cat', 'test'
|
|
182 | 182 |
])
|
183 | 183 |
res.assert_shell_error()
|
184 | 184 |
assert 'Hi' not in res.output
|
185 | 185 |
|
186 |
- # Check it's not using the cached build tree, default is to ask, and fall back to not
|
|
187 |
- # for non interactive behavior
|
|
186 |
+ # Check correctly handling the lack of buildtree, with 'try' not attempting to
|
|
187 |
+ # pull the buildtree as the user context is by default set to not pull them
|
|
188 | 188 |
res = cli.run(project=project, args=[
|
189 |
- 'shell', '--build', element_name, '--', 'cat', 'test'
|
|
189 |
+ 'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
|
|
190 | 190 |
])
|
191 |
- res.assert_shell_error()
|
|
192 | 191 |
assert 'Hi' not in res.output
|
192 |
+ assert 'Attempting to fetch missing artifact buildtrees' not in res.stderr
|
|
193 |
+ assert """Buildtree is not cached locally or in available remotes,
|
|
194 |
+ shell will be loaded without it"""
|
|
193 | 195 |
|
194 |
- # Check it's using the cached build tree
|
|
196 |
+ # Check correctly handling the lack of buildtree, with 'try' attempting and succeeding
|
|
197 |
+ # to pull the buildtree as the user context allow the pulling of buildtrees and it is
|
|
198 |
+ # available in the remote
|
|
199 |
+ res = cli.run(project=project, args=[
|
|
200 |
+ '--pull-buildtrees', 'shell', '--build', element_name, '--use-buildtree', 'try', '--', 'cat', 'test'
|
|
201 |
+ ])
|
|
202 |
+ assert 'Attempting to fetch missing artifact buildtree' in res.stderr
|
|
203 |
+ assert 'Hi' in res.output
|
|
204 |
+ shutil.rmtree(os.path.join(os.path.join(cli.directory, 'artifacts2')))
|
|
205 |
+ assert cli.get_element_state(project, element_name) != 'cached'
|
|
206 |
+ |
|
207 |
+ # Check it's not loading the shell at all with always set for the buildtree, when the
|
|
208 |
+ # user context does not allow for buildtree pulling
|
|
209 |
+ result = cli.run(project=project, args=['pull', '--deps', 'all', element_name])
|
|
210 |
+ result.assert_success()
|
|
195 | 211 |
res = cli.run(project=project, args=[
|
196 | 212 |
'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
|
197 | 213 |
])
|
198 | 214 |
res.assert_main_error(ErrorDomain.PROG_NOT_FOUND, None)
|
215 |
+ assert 'Buildtree is not cached locally or in available remotes' in res.stderr
|
|
199 | 216 |
assert 'Hi' not in res.output
|
217 |
+ assert 'Attempting to fetch missing artifact buildtree' not in res.stderr
|
|
218 |
+ |
|
219 |
+ # Check that when user context is set to pull buildtrees and a remote has the buildtree,
|
|
220 |
+ # 'always' will attempt and succeed at pulling the missing buildtree.
|
|
221 |
+ res = cli.run(project=project, args=[
|
|
222 |
+ '--pull-buildtrees', 'shell', '--build', element_name, '--use-buildtree', 'always', '--', 'cat', 'test'
|
|
223 |
+ ])
|
|
224 |
+ assert 'Hi' in res.output
|
|
225 |
+ assert 'Attempting to fetch missing artifact buildtree' in res.stderr
|
... | ... | @@ -30,7 +30,7 @@ from buildstream import _yaml |
30 | 30 |
from buildstream.plugin import CoreWarnings
|
31 | 31 |
|
32 | 32 |
from tests.testutils import cli, create_repo
|
33 |
-from tests.testutils.site import HAVE_GIT
|
|
33 |
+from tests.testutils.site import HAVE_GIT, HAVE_OLD_GIT
|
|
34 | 34 |
|
35 | 35 |
DATA_DIR = os.path.join(
|
36 | 36 |
os.path.dirname(os.path.realpath(__file__)),
|
... | ... | @@ -664,6 +664,7 @@ def test_invalid_submodule(cli, tmpdir, datafiles, fail): |
664 | 664 |
|
665 | 665 |
|
666 | 666 |
@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
|
667 |
+@pytest.mark.skipif(HAVE_OLD_GIT, reason="old git rm does not update .gitmodules")
|
|
667 | 668 |
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
|
668 | 669 |
@pytest.mark.parametrize("fail", ['warn', 'error'])
|
669 | 670 |
def test_track_invalid_submodule(cli, tmpdir, datafiles, fail):
|
... | ... | @@ -772,6 +773,7 @@ def test_track_fetch(cli, tmpdir, datafiles, ref_format, tag, extra_commit): |
772 | 773 |
|
773 | 774 |
|
774 | 775 |
@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
|
776 |
+@pytest.mark.skipif(HAVE_OLD_GIT, reason="old git describe lacks --first-parent")
|
|
775 | 777 |
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
|
776 | 778 |
@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')])
|
777 | 779 |
@pytest.mark.parametrize("tag_type", [('annotated'), ('lightweight')])
|
... | ... | @@ -2,6 +2,7 @@ |
2 | 2 |
# so we dont have to repeat this everywhere
|
3 | 3 |
#
|
4 | 4 |
import os
|
5 |
+import subprocess
|
|
5 | 6 |
import sys
|
6 | 7 |
|
7 | 8 |
from buildstream import _site, utils, ProgramNotFoundError
|
... | ... | @@ -16,8 +17,12 @@ except ProgramNotFoundError: |
16 | 17 |
try:
|
17 | 18 |
utils.get_host_tool('git')
|
18 | 19 |
HAVE_GIT = True
|
20 |
+ out = str(subprocess.check_output(['git', '--version']), "utf-8")
|
|
21 |
+ version = tuple(int(x) for x in out.split(' ', 2)[2].split('.'))
|
|
22 |
+ HAVE_OLD_GIT = version < (1, 8, 5)
|
|
19 | 23 |
except ProgramNotFoundError:
|
20 | 24 |
HAVE_GIT = False
|
25 |
+ HAVE_OLD_GIT = False
|
|
21 | 26 |
|
22 | 27 |
try:
|
23 | 28 |
utils.get_host_tool('ostree')
|