Will Salmon pushed to branch willsalmon/log_formating at BuildStream / buildstream
Commits:
-
dbb3d232
by James Ennis at 2019-02-01T15:51:32Z
-
7e4205cb
by James Ennis at 2019-02-01T15:51:32Z
-
4109a34a
by James Ennis at 2019-02-01T17:11:29Z
-
c9345014
by James Ennis at 2019-02-04T13:53:42Z
-
3ab09651
by James Ennis at 2019-02-04T14:47:43Z
-
cd8e5e27
by Tom Pollard at 2019-02-05T11:22:01Z
-
a46650d0
by Tom Pollard at 2019-02-05T12:41:30Z
-
5ed13116
by William Salmon at 2019-02-05T13:49:39Z
13 changed files:
- NEWS
- buildstream/_frontend/cli.py
- buildstream/_frontend/widget.py
- buildstream/_stream.py
- buildstream/plugins/elements/filter.py
- buildstream/plugins/elements/filter.yaml
- tests/elements/filter.py
- tests/elements/filter/basic/elements/deps-permitted.bst
- + tests/elements/filter/basic/elements/output-include-nonexistent-domain.bst
- tests/frontend/completions.py
- tests/frontend/logging.py
- tests/frontend/pull.py
- tests/frontend/push.py
Changes:
... | ... | @@ -122,6 +122,10 @@ buildstream 1.3.1 |
122 | 122 |
'shell', 'show', 'source-checkout', 'track', 'workspace close' and 'workspace reset'
|
123 | 123 |
commands are affected.
|
124 | 124 |
|
125 |
+ o bst 'build' now has '--remote, -r' option, inline with bst 'push' & 'pull'.
|
|
126 |
+ Providing a remote will limit build's pull/push remote actions to the given
|
|
127 |
+ remote specifically, ignoring those defined via user or project configuration.
|
|
128 |
+ |
|
125 | 129 |
|
126 | 130 |
=================
|
127 | 131 |
buildstream 1.1.5
|
... | ... | @@ -338,10 +338,12 @@ def init(app, project_name, format_version, element_path, force): |
338 | 338 |
help="Allow tracking to cross junction boundaries")
|
339 | 339 |
@click.option('--track-save', default=False, is_flag=True,
|
340 | 340 |
help="Deprecated: This is ignored")
|
341 |
+@click.option('--remote', '-r', default=None,
|
|
342 |
+ help="The URL of the remote cache (defaults to the first configured cache)")
|
|
341 | 343 |
@click.argument('elements', nargs=-1,
|
342 | 344 |
type=click.Path(readable=False))
|
343 | 345 |
@click.pass_obj
|
344 |
-def build(app, elements, all_, track_, track_save, track_all, track_except, track_cross_junctions):
|
|
346 |
+def build(app, elements, all_, track_, track_save, track_all, track_except, track_cross_junctions, remote):
|
|
345 | 347 |
"""Build elements in a pipeline
|
346 | 348 |
|
347 | 349 |
Specifying no elements will result in building the default targets
|
... | ... | @@ -376,7 +378,8 @@ def build(app, elements, all_, track_, track_save, track_all, track_except, trac |
376 | 378 |
track_except=track_except,
|
377 | 379 |
track_cross_junctions=track_cross_junctions,
|
378 | 380 |
ignore_junction_targets=ignore_junction_targets,
|
379 |
- build_all=all_)
|
|
381 |
+ build_all=all_,
|
|
382 |
+ remote=remote)
|
|
380 | 383 |
|
381 | 384 |
|
382 | 385 |
##################################################################
|
... | ... | @@ -1012,7 +1015,7 @@ def artifact_checkout(app, force, deps, integrate, hardlinks, tar, directory, el |
1012 | 1015 |
@click.option('--deps', '-d', default='none',
|
1013 | 1016 |
type=click.Choice(['none', 'all']),
|
1014 | 1017 |
help='The dependency artifacts to pull (default: none)')
|
1015 |
-@click.option('--remote', '-r',
|
|
1018 |
+@click.option('--remote', '-r', default=None,
|
|
1016 | 1019 |
help="The URL of the remote cache (defaults to the first configured cache)")
|
1017 | 1020 |
@click.argument('elements', nargs=-1,
|
1018 | 1021 |
type=click.Path(readable=False))
|
... | ... | @@ -8,7 +8,7 @@ |
8 | 8 |
#
|
9 | 9 |
# This library is distributed in the hope that it will be useful,
|
10 | 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11 |
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
11 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12 | 12 |
# Lesser General Public License for more details.
|
13 | 13 |
#
|
14 | 14 |
# You should have received a copy of the GNU Lesser General Public
|
... | ... | @@ -94,12 +94,24 @@ class FixedText(Widget): |
94 | 94 |
|
95 | 95 |
# Used to add the wallclock time this message was created at
|
96 | 96 |
class WallclockTime(Widget):
|
97 |
+ def __init__(self, context, content_profile, format_profile, output_format=False):
|
|
98 |
+ self._output_format = output_format
|
|
99 |
+ super(WallclockTime, self).__init__(context, content_profile, format_profile)
|
|
100 |
+ |
|
97 | 101 |
def render(self, message):
|
102 |
+ |
|
98 | 103 |
fields = [self.content_profile.fmt("{:02d}".format(x)) for x in
|
99 | 104 |
[message.creation_time.hour,
|
100 | 105 |
message.creation_time.minute,
|
101 |
- message.creation_time.second]]
|
|
102 |
- return self.format_profile.fmt(":").join(fields)
|
|
106 |
+ message.creation_time.second,
|
|
107 |
+ ]
|
|
108 |
+ ]
|
|
109 |
+ text = self.format_profile.fmt(":").join(fields)
|
|
110 |
+ |
|
111 |
+ if self._output_format == 'us':
|
|
112 |
+ text += self.format_profile.fmt(".{:06d}".format(message.creation_time.microsecond))
|
|
113 |
+ |
|
114 |
+ return text
|
|
103 | 115 |
|
104 | 116 |
|
105 | 117 |
# A widget for rendering the debugging column
|
... | ... | @@ -326,6 +338,7 @@ class LogLine(Widget): |
326 | 338 |
"elapsed": TimeCode(context, content_profile, format_profile, microseconds=False),
|
327 | 339 |
"elapsed-us": TimeCode(context, content_profile, format_profile, microseconds=True),
|
328 | 340 |
"wallclock": WallclockTime(context, content_profile, format_profile),
|
341 |
+ "wallclock-us": WallclockTime(context, content_profile, format_profile, output_format='us'),
|
|
329 | 342 |
"key": CacheKey(context, content_profile, format_profile, err_profile),
|
330 | 343 |
"element": ElementName(context, content_profile, format_profile),
|
331 | 344 |
"action": TypeName(context, content_profile, format_profile),
|
... | ... | @@ -197,26 +197,36 @@ class Stream(): |
197 | 197 |
# ignore_junction_targets (bool): Whether junction targets should be filtered out
|
198 | 198 |
# build_all (bool): Whether to build all elements, or only those
|
199 | 199 |
# which are required to build the target.
|
200 |
+ # remote (str): The URL of a specific remote server to push to, or None
|
|
201 |
+ #
|
|
202 |
+ # If `remote` specified as None, then regular configuration will be used
|
|
203 |
+ # to determine where to push artifacts to.
|
|
200 | 204 |
#
|
201 | 205 |
def build(self, targets, *,
|
202 | 206 |
track_targets=None,
|
203 | 207 |
track_except=None,
|
204 | 208 |
track_cross_junctions=False,
|
205 | 209 |
ignore_junction_targets=False,
|
206 |
- build_all=False):
|
|
210 |
+ build_all=False,
|
|
211 |
+ remote=None):
|
|
207 | 212 |
|
208 | 213 |
if build_all:
|
209 | 214 |
selection = PipelineSelection.ALL
|
210 | 215 |
else:
|
211 | 216 |
selection = PipelineSelection.PLAN
|
212 | 217 |
|
218 |
+ use_config = True
|
|
219 |
+ if remote:
|
|
220 |
+ use_config = False
|
|
221 |
+ |
|
213 | 222 |
elements, track_elements = \
|
214 | 223 |
self._load(targets, track_targets,
|
215 | 224 |
selection=selection, track_selection=PipelineSelection.ALL,
|
216 | 225 |
track_except_targets=track_except,
|
217 | 226 |
track_cross_junctions=track_cross_junctions,
|
218 | 227 |
ignore_junction_targets=ignore_junction_targets,
|
219 |
- use_artifact_config=True,
|
|
228 |
+ use_artifact_config=use_config,
|
|
229 |
+ artifact_remote_url=remote,
|
|
220 | 230 |
fetch_subprojects=True,
|
221 | 231 |
dynamic_plan=True)
|
222 | 232 |
|
... | ... | @@ -20,25 +20,127 @@ |
20 | 20 |
"""
|
21 | 21 |
filter - Extract a subset of files from another element
|
22 | 22 |
=======================================================
|
23 |
-This filters another element by producing an output that is a subset of
|
|
24 |
-the filtered element.
|
|
23 |
+Filter another element by producing an output that is a subset of
|
|
24 |
+the parent element's output. Subsets are defined by the parent element's
|
|
25 |
+:ref:`split rules <public_split_rules>`.
|
|
25 | 26 |
|
26 |
-To specify the element to filter, specify it as the one and only build
|
|
27 |
-dependency to filter. See :ref:`Dependencies <format_dependencies>`
|
|
28 |
-for what dependencies are and how to specify them.
|
|
27 |
+Overview
|
|
28 |
+--------
|
|
29 |
+A filter element must have exactly one *build* dependency, where said
|
|
30 |
+dependency is the 'parent' element which we would like to filter.
|
|
31 |
+Runtime dependencies may also be specified, which can be useful to propagate
|
|
32 |
+forward from this filter element onto its reverse dependencies.
|
|
33 |
+See :ref:`Dependencies <format_dependencies>` to see how we specify dependencies.
|
|
29 | 34 |
|
30 |
-Dependencies aside from the filtered element may be specified, but
|
|
31 |
-they must be runtime dependencies only. This can be useful to propagate
|
|
32 |
-runtime dependencies forward from this filter element onto its reverse
|
|
33 |
-dependencies.
|
|
35 |
+When workspaces are opened, closed or reset on a filter element, or this
|
|
36 |
+element is tracked, the filter element will transparently pass on the command
|
|
37 |
+to its parent element (the sole build-dependency).
|
|
34 | 38 |
|
35 |
-When workspaces are opened, closed or reset on this element, or this
|
|
36 |
-element is tracked, instead of erroring due to a lack of sources, this
|
|
37 |
-element will transparently pass on the command to its sole build-dependency.
|
|
39 |
+Example
|
|
40 |
+-------
|
|
41 |
+Consider a simple import element, ``import.bst`` which imports the local files
|
|
42 |
+'foo', 'bar' and 'baz' (each stored in ``files/``, relative to the project's root):
|
|
38 | 43 |
|
39 |
-The default configuration and possible options are as such:
|
|
40 |
- .. literalinclude:: ../../../buildstream/plugins/elements/filter.yaml
|
|
41 |
- :language: yaml
|
|
44 |
+.. code:: yaml
|
|
45 |
+ |
|
46 |
+ kind: import
|
|
47 |
+ |
|
48 |
+ # Specify sources to import
|
|
49 |
+ sources:
|
|
50 |
+ - kind: local
|
|
51 |
+ path: files
|
|
52 |
+ |
|
53 |
+ # Specify public domain data, visible to other elements
|
|
54 |
+ public:
|
|
55 |
+ bst:
|
|
56 |
+ split-rules:
|
|
57 |
+ foo:
|
|
58 |
+ - /foo
|
|
59 |
+ bar:
|
|
60 |
+ - /bar
|
|
61 |
+ |
|
62 |
+.. note::
|
|
63 |
+ |
|
64 |
+ We can make an element's metadata visible to all reverse dependencies by making use
|
|
65 |
+ of the ``public:`` field. See the :ref:`public data documentation <format_public>`
|
|
66 |
+ for more information.
|
|
67 |
+ |
|
68 |
+In this example, ``import.bst`` will serve as the 'parent' of the filter element, thus
|
|
69 |
+its output will be filtered. It is important to understand that the artifact of the
|
|
70 |
+above element will contain the files: 'foo', 'bar' and 'baz'.
|
|
71 |
+ |
|
72 |
+Now, to produce an element whose artifact contains the file 'foo', and exlusively 'foo',
|
|
73 |
+we can define the following filter, ``filter-foo.bst``:
|
|
74 |
+ |
|
75 |
+.. code:: yaml
|
|
76 |
+ |
|
77 |
+ kind: filter
|
|
78 |
+ |
|
79 |
+ # Declare the sole build-dependency of the filter element
|
|
80 |
+ depends:
|
|
81 |
+ - filename: import.bst
|
|
82 |
+ type: build
|
|
83 |
+ |
|
84 |
+ # Declare a list of domains to include in the filter's artifact
|
|
85 |
+ config:
|
|
86 |
+ include:
|
|
87 |
+ - foo
|
|
88 |
+ |
|
89 |
+.. note::
|
|
90 |
+ |
|
91 |
+ We can also specify build-dependencies with a 'build-depends' field which has been
|
|
92 |
+ available since :ref:`format version 14 <project_format_version>`. See the
|
|
93 |
+ :ref:`Build-Depends documentation <format_build_depends>` for more detail.
|
|
94 |
+ |
|
95 |
+It should be noted that an 'empty' ``include:`` list would, by default, include all
|
|
96 |
+split-rules specified in the parent element, which, in this example, would be the
|
|
97 |
+files 'foo' and 'bar' (the file 'baz' was not covered by any split rules).
|
|
98 |
+ |
|
99 |
+Equally, we can use the ``exclude:`` statement to create the same artifact (which
|
|
100 |
+only contains the file 'foo') by declaring the following element, ``exclude-bar.bst``:
|
|
101 |
+ |
|
102 |
+.. code:: yaml
|
|
103 |
+ |
|
104 |
+ kind: filter
|
|
105 |
+ |
|
106 |
+ # Declare the sole build-dependency of the filter element
|
|
107 |
+ depends:
|
|
108 |
+ - filename: import.bst
|
|
109 |
+ type: build
|
|
110 |
+ |
|
111 |
+ # Declare a list of domains to exclude in the filter's artifact
|
|
112 |
+ config:
|
|
113 |
+ exclude:
|
|
114 |
+ - bar
|
|
115 |
+ |
|
116 |
+In addition to the ``include:`` and ``exclude:`` fields, there exists an ``include-orphans:``
|
|
117 |
+(Boolean) field, which defaults to ``False``. This will determine whether to include files
|
|
118 |
+which are not present in the 'split-rules'. For example, if we wanted to filter out all files
|
|
119 |
+which are not included as split rules we can define the following element, ``filter-misc.bst``:
|
|
120 |
+ |
|
121 |
+.. code:: yaml
|
|
122 |
+ |
|
123 |
+ kind: filter
|
|
124 |
+ |
|
125 |
+ # Declare the sole build-dependency of the filter element
|
|
126 |
+ depends:
|
|
127 |
+ - filename: import.bst
|
|
128 |
+ type: build
|
|
129 |
+ |
|
130 |
+ # Filter out all files which are not declared as split rules
|
|
131 |
+ config:
|
|
132 |
+ exclude:
|
|
133 |
+ - foo
|
|
134 |
+ - bar
|
|
135 |
+ include-orphans: True
|
|
136 |
+ |
|
137 |
+The artifact of ``filter-misc.bst`` will only contain the file 'baz'.
|
|
138 |
+ |
|
139 |
+Below is more information regarding the the default configurations and possible options
|
|
140 |
+of the filter element:
|
|
141 |
+ |
|
142 |
+.. literalinclude:: ../../../buildstream/plugins/elements/filter.yaml
|
|
143 |
+ :language: yaml
|
|
42 | 144 |
"""
|
43 | 145 |
|
44 | 146 |
from buildstream import Element, ElementError, Scope
|
... | ... | @@ -66,6 +168,8 @@ class FilterElement(Element): |
66 | 168 |
self.include = self.node_get_member(node, list, 'include')
|
67 | 169 |
self.exclude = self.node_get_member(node, list, 'exclude')
|
68 | 170 |
self.include_orphans = self.node_get_member(node, bool, 'include-orphans')
|
171 |
+ self.include_provenance = self.node_provenance(node, member_name='include')
|
|
172 |
+ self.exclude_provenance = self.node_provenance(node, member_name='exclude')
|
|
69 | 173 |
|
70 | 174 |
def preflight(self):
|
71 | 175 |
# Exactly one build-depend is permitted
|
... | ... | @@ -105,6 +209,31 @@ class FilterElement(Element): |
105 | 209 |
def assemble(self, sandbox):
|
106 | 210 |
with self.timed_activity("Staging artifact", silent_nested=True):
|
107 | 211 |
for dep in self.dependencies(Scope.BUILD, recurse=False):
|
212 |
+ # Check that all the included/excluded domains exist
|
|
213 |
+ pub_data = dep.get_public_data('bst')
|
|
214 |
+ split_rules = pub_data.get('split-rules', {})
|
|
215 |
+ unfound_includes = []
|
|
216 |
+ for domain in self.include:
|
|
217 |
+ if domain not in split_rules:
|
|
218 |
+ unfound_includes.append(domain)
|
|
219 |
+ unfound_excludes = []
|
|
220 |
+ for domain in self.exclude:
|
|
221 |
+ if domain not in split_rules:
|
|
222 |
+ unfound_excludes.append(domain)
|
|
223 |
+ |
|
224 |
+ detail = []
|
|
225 |
+ if unfound_includes:
|
|
226 |
+ detail.append("Unknown domains were used in {}".format(self.include_provenance))
|
|
227 |
+ detail.extend([' - {}'.format(domain) for domain in unfound_includes])
|
|
228 |
+ |
|
229 |
+ if unfound_excludes:
|
|
230 |
+ detail.append("Unknown domains were used in {}".format(self.exclude_provenance))
|
|
231 |
+ detail.extend([' - {}'.format(domain) for domain in unfound_excludes])
|
|
232 |
+ |
|
233 |
+ if detail:
|
|
234 |
+ detail = '\n'.join(detail)
|
|
235 |
+ raise ElementError("Unknown domains declared.", detail=detail)
|
|
236 |
+ |
|
108 | 237 |
dep.stage_artifact(sandbox, include=self.include,
|
109 | 238 |
exclude=self.exclude, orphans=self.include_orphans)
|
110 | 239 |
return ""
|
... | ... | @@ -2,20 +2,20 @@ |
2 | 2 |
# Filter element configuration
|
3 | 3 |
config:
|
4 | 4 |
|
5 |
- # A list of domains to include from each artifact, as
|
|
6 |
- # they were defined in the element's 'split-rules'.
|
|
5 |
+ # A list of domains to include in each artifact, as
|
|
6 |
+ # they were defined as public data in the parent
|
|
7 |
+ # element's 'split-rules'.
|
|
7 | 8 |
#
|
8 |
- # Since domains can be added, it is not an error to
|
|
9 |
- # specify domains which may not exist for all of the
|
|
10 |
- # elements in this composition.
|
|
9 |
+ # If a domain is specified that does not exist, the
|
|
10 |
+ # filter element will fail to build.
|
|
11 | 11 |
#
|
12 | 12 |
# The default empty list indicates that all domains
|
13 |
- # from each dependency should be included.
|
|
13 |
+ # of the parent's artifact should be included.
|
|
14 | 14 |
#
|
15 | 15 |
include: []
|
16 | 16 |
|
17 | 17 |
# A list of domains to exclude from each artifact, as
|
18 |
- # they were defined in the element's 'split-rules'.
|
|
18 |
+ # they were defined in the parent element's 'split-rules'.
|
|
19 | 19 |
#
|
20 | 20 |
# In the case that a file is spoken for by a domain
|
21 | 21 |
# in the 'include' list and another in the 'exclude'
|
... | ... | @@ -23,7 +23,7 @@ config: |
23 | 23 |
exclude: []
|
24 | 24 |
|
25 | 25 |
# Whether to include orphan files which are not
|
26 |
- # included by any of the 'split-rules' present on
|
|
27 |
- # a given element.
|
|
26 |
+ # included by any of the 'split-rules' present in
|
|
27 |
+ # the parent element.
|
|
28 | 28 |
#
|
29 | 29 |
include-orphans: False
|
... | ... | @@ -484,3 +484,14 @@ def test_filter_include_with_indirect_deps(datafiles, cli, tmpdir): |
484 | 484 |
# indirect dependencies shouldn't be staged and filtered
|
485 | 485 |
assert not os.path.exists(os.path.join(checkout, "foo"))
|
486 | 486 |
assert not os.path.exists(os.path.join(checkout, "bar"))
|
487 |
+ |
|
488 |
+ |
|
489 |
+@pytest.mark.datafiles(os.path.join(DATA_DIR, 'basic'))
|
|
490 |
+def test_filter_fails_for_nonexisting_domain(datafiles, cli, tmpdir):
|
|
491 |
+ project = os.path.join(datafiles.dirname, datafiles.basename)
|
|
492 |
+ result = cli.run(project=project, args=['build', 'output-include-nonexistent-domain.bst'])
|
|
493 |
+ result.assert_main_error(ErrorDomain.STREAM, None)
|
|
494 |
+ |
|
495 |
+ error = "Unknown domains were used in output-include-nonexistent-domain.bst [line 7 column 2]"
|
|
496 |
+ assert error in result.stderr
|
|
497 |
+ assert '- unknown_file' in result.stderr
|
1 | 1 |
kind: filter
|
2 | 2 |
depends:
|
3 |
-- filename: output-include.bst
|
|
3 |
+- filename: input.bst
|
|
4 | 4 |
type: build
|
5 | 5 |
- filename: output-exclude.bst
|
6 | 6 |
type: runtime
|
1 |
+kind: filter
|
|
2 |
+depends:
|
|
3 |
+- filename: input.bst
|
|
4 |
+ type: build
|
|
5 |
+config:
|
|
6 |
+ include:
|
|
7 |
+ - unknown_file
|
|
8 |
+ |
... | ... | @@ -141,7 +141,8 @@ def test_commands(cli, cmd, word_idx, expected): |
141 | 141 |
('bst --no-colors build -', 3, ['--all ', '--track ', '--track-all ',
|
142 | 142 |
'--track-except ',
|
143 | 143 |
'--track-cross-junctions ', '-J ',
|
144 |
- '--track-save ']),
|
|
144 |
+ '--track-save ',
|
|
145 |
+ '--remote ', '-r ']),
|
|
145 | 146 |
|
146 | 147 |
# Test the behavior of completing after an option that has a
|
147 | 148 |
# parameter that cannot be completed, vs an option that has
|
... | ... | @@ -52,7 +52,8 @@ def test_custom_logging(cli, tmpdir, datafiles): |
52 | 52 |
element_path = os.path.join(project, 'elements')
|
53 | 53 |
element_name = 'fetch-test-git.bst'
|
54 | 54 |
|
55 |
- custom_log_format = '%{elapsed},%{elapsed-us},%{wallclock},%{key},%{element},%{action},%{message}'
|
|
55 |
+ custom_log_format = ('%{elapsed},%{elapsed-us},%{wallclock},%{wallclock-us},'
|
|
56 |
+ '%{key},%{element},%{action},%{message}')
|
|
56 | 57 |
user_config = {'logging': {'message-format': custom_log_format}}
|
57 | 58 |
cli.configure(user_config)
|
58 | 59 |
|
... | ... | @@ -77,7 +78,8 @@ def test_custom_logging(cli, tmpdir, datafiles): |
77 | 78 |
result = cli.run(project=project, args=['source', 'fetch', element_name])
|
78 | 79 |
result.assert_success()
|
79 | 80 |
|
80 |
- m = re.search(r"\d\d:\d\d:\d\d,\d\d:\d\d:\d\d.\d{6},\d\d:\d\d:\d\d,\s*,.*,SUCCESS,Checking sources", result.stderr)
|
|
81 |
+ m = re.search(r"\d\d:\d\d:\d\d,\d\d:\d\d:\d\d.\d{6},\d\d:\d\d:\d\d,\d\d:\d\d:\d\d.\d{6}\s*,.*"
|
|
82 |
+ r",SUCCESS,Checking sources", result.stderr)
|
|
81 | 83 |
assert(m is not None)
|
82 | 84 |
|
83 | 85 |
|
... | ... | @@ -408,3 +408,56 @@ def test_pull_missing_notifies_user(caplog, cli, tmpdir, datafiles): |
408 | 408 |
|
409 | 409 |
assert "INFO Remote ({}) does not have".format(share.repo) in result.stderr
|
410 | 410 |
assert "SKIPPED Pull" in result.stderr
|
411 |
+ |
|
412 |
+ |
|
413 |
+@pytest.mark.datafiles(DATA_DIR)
|
|
414 |
+def test_build_remote_option(caplog, cli, tmpdir, datafiles):
|
|
415 |
+ project = os.path.join(datafiles.dirname, datafiles.basename)
|
|
416 |
+ caplog.set_level(1)
|
|
417 |
+ |
|
418 |
+ with create_artifact_share(os.path.join(str(tmpdir), 'artifactshare1')) as shareuser,\
|
|
419 |
+ create_artifact_share(os.path.join(str(tmpdir), 'artifactshare2')) as shareproject,\
|
|
420 |
+ create_artifact_share(os.path.join(str(tmpdir), 'artifactshare3')) as sharecli:
|
|
421 |
+ |
|
422 |
+ # Add shareproject repo url to project.conf
|
|
423 |
+ with open(os.path.join(project, "project.conf"), "a") as projconf:
|
|
424 |
+ projconf.write("artifacts:\n url: {}\n push: True".format(shareproject.repo))
|
|
425 |
+ |
|
426 |
+ # Configure shareuser remote in user conf
|
|
427 |
+ cli.configure({
|
|
428 |
+ 'artifacts': {'url': shareuser.repo, 'push': True}
|
|
429 |
+ })
|
|
430 |
+ |
|
431 |
+ # Push the artifacts to the shareuser and shareproject remotes.
|
|
432 |
+ # Assert that shareuser and shareproject have the artfifacts cached,
|
|
433 |
+ # but sharecli doesn't, then delete locally cached elements
|
|
434 |
+ result = cli.run(project=project, args=['build', 'target.bst'])
|
|
435 |
+ result.assert_success()
|
|
436 |
+ all_elements = ['target.bst', 'import-bin.bst', 'compose-all.bst']
|
|
437 |
+ for element_name in all_elements:
|
|
438 |
+ assert element_name in result.get_pushed_elements()
|
|
439 |
+ assert_not_shared(cli, sharecli, project, element_name)
|
|
440 |
+ assert_shared(cli, shareuser, project, element_name)
|
|
441 |
+ assert_shared(cli, shareproject, project, element_name)
|
|
442 |
+ cli.remove_artifact_from_cache(project, element_name)
|
|
443 |
+ |
|
444 |
+ # Now check that a build with cli set as sharecli results in nothing being pulled,
|
|
445 |
+ # as it doesn't have them cached and shareuser/shareproject should be ignored. This
|
|
446 |
+ # will however result in the artifacts being built and pushed to it
|
|
447 |
+ result = cli.run(project=project, args=['build', '--remote', sharecli.repo, 'target.bst'])
|
|
448 |
+ result.assert_success()
|
|
449 |
+ for element_name in all_elements:
|
|
450 |
+ assert element_name not in result.get_pulled_elements()
|
|
451 |
+ assert_shared(cli, sharecli, project, element_name)
|
|
452 |
+ cli.remove_artifact_from_cache(project, element_name)
|
|
453 |
+ |
|
454 |
+ # Now check that a clean build with cli set as sharecli should result in artifacts only
|
|
455 |
+ # being pulled from it, as that was provided via the cli and is populated
|
|
456 |
+ result = cli.run(project=project, args=['build', '--remote', sharecli.repo, 'target.bst'])
|
|
457 |
+ result.assert_success()
|
|
458 |
+ for element_name in all_elements:
|
|
459 |
+ assert cli.get_element_state(project, element_name) == 'cached'
|
|
460 |
+ assert element_name in result.get_pulled_elements()
|
|
461 |
+ assert shareproject.repo not in result.stderr
|
|
462 |
+ assert shareuser.repo not in result.stderr
|
|
463 |
+ assert sharecli.repo in result.stderr
|
... | ... | @@ -416,3 +416,33 @@ def test_push_already_cached(caplog, cli, tmpdir, datafiles): |
416 | 416 |
assert not result.get_pushed_elements(), "No elements should have been pushed since the cache was populated"
|
417 | 417 |
assert "INFO Remote ({}) already has ".format(share.repo) in result.stderr
|
418 | 418 |
assert "SKIPPED Push" in result.stderr
|
419 |
+ |
|
420 |
+ |
|
421 |
+@pytest.mark.datafiles(DATA_DIR)
|
|
422 |
+def test_build_remote_option(caplog, cli, tmpdir, datafiles):
|
|
423 |
+ project = os.path.join(datafiles.dirname, datafiles.basename)
|
|
424 |
+ caplog.set_level(1)
|
|
425 |
+ |
|
426 |
+ with create_artifact_share(os.path.join(str(tmpdir), 'artifactshare1')) as shareuser,\
|
|
427 |
+ create_artifact_share(os.path.join(str(tmpdir), 'artifactshare2')) as shareproject,\
|
|
428 |
+ create_artifact_share(os.path.join(str(tmpdir), 'artifactshare3')) as sharecli:
|
|
429 |
+ |
|
430 |
+ # Add shareproject repo url to project.conf
|
|
431 |
+ with open(os.path.join(project, "project.conf"), "a") as projconf:
|
|
432 |
+ projconf.write("artifacts:\n url: {}\n push: True".format(shareproject.repo))
|
|
433 |
+ |
|
434 |
+ # Configure shareuser remote in user conf
|
|
435 |
+ cli.configure({
|
|
436 |
+ 'artifacts': {'url': shareuser.repo, 'push': True}
|
|
437 |
+ })
|
|
438 |
+ |
|
439 |
+ result = cli.run(project=project, args=['build', '--remote', sharecli.repo, 'target.bst'])
|
|
440 |
+ |
|
441 |
+ # Artifacts should have only been pushed to sharecli, as that was provided via the cli
|
|
442 |
+ result.assert_success()
|
|
443 |
+ all_elements = ['target.bst', 'import-bin.bst', 'compose-all.bst']
|
|
444 |
+ for element_name in all_elements:
|
|
445 |
+ assert element_name in result.get_pushed_elements()
|
|
446 |
+ assert_shared(cli, sharecli, project, element_name)
|
|
447 |
+ assert_not_shared(cli, shareuser, project, element_name)
|
|
448 |
+ assert_not_shared(cli, shareproject, project, element_name)
|