richardmaw-codethink pushed to branch richardmaw/artifact-log at BuildStream / buildstream
Commits:
-
1b6de30e
by Richard Maw at 2018-11-01T17:11:37Z
-
8b705598
by Richard Maw at 2018-11-01T17:11:37Z
-
932064b9
by Richard Maw at 2018-11-01T17:11:37Z
3 changed files:
Changes:
1 |
+===============
|
|
2 |
+buildstream 1.4
|
|
3 |
+===============
|
|
4 |
+ |
|
5 |
+ o Added `bst artifact log` subcommand for viewing build logs.
|
|
6 |
+ |
|
1 | 7 |
=================
|
2 | 8 |
buildstream 1.3.1
|
3 | 9 |
=================
|
... | ... | @@ -852,3 +852,56 @@ def source_bundle(app, element, force, directory, |
852 | 852 |
def artifact():
|
853 | 853 |
"""Manipulate cached Artifacts"""
|
854 | 854 |
pass
|
855 |
+ |
|
856 |
+ |
|
857 |
+################################################################
|
|
858 |
+# Artifact Log Command #
|
|
859 |
+################################################################
|
|
860 |
+@artifact.command(name='log', short_help="Show logs of an artifact")
|
|
861 |
+@click.option('-e', '--element', 'elements', help="Show logs for artifacts of this element",
|
|
862 |
+ type=click.Path(readable=False), multiple=True, required=False)
|
|
863 |
+@click.argument('artifacts', type=click.Path(), nargs=-1)
|
|
864 |
+@click.pass_obj
|
|
865 |
+def artifact_log(app, elements, artifacts):
|
|
866 |
+ """Show logs of all artifacts"""
|
|
867 |
+ from tempfile import TemporaryDirectory
|
|
868 |
+ from .._exceptions import ArtifactError
|
|
869 |
+ from .._message import MessageType
|
|
870 |
+ from .._pipeline import PipelineSelection
|
|
871 |
+ |
|
872 |
+ with app.initialized(), TemporaryDirectory(dir=os.path.join(app.context.artifactdir, 'tmp')) as td:
|
|
873 |
+ cache = app.context.artifactcache
|
|
874 |
+ |
|
875 |
+ refs = []
|
|
876 |
+ if artifacts:
|
|
877 |
+ for ref in artifacts:
|
|
878 |
+ try:
|
|
879 |
+ o = cache.resolve_ref(ref)
|
|
880 |
+ except ArtifactError as e:
|
|
881 |
+ app._message(MessageType.WARN, "Artifact {} is not cached".format(ref), detail=str(e))
|
|
882 |
+ continue
|
|
883 |
+ refs.append((ref, o))
|
|
884 |
+ if elements:
|
|
885 |
+ elements = app.stream.load_selection(elements, selection=PipelineSelection.NONE)
|
|
886 |
+ for element in elements:
|
|
887 |
+ if not element._cached():
|
|
888 |
+ app._message(MessageType.WARN, "Element {} is not cached".format(element))
|
|
889 |
+ continue
|
|
890 |
+ ref = cache.get_artifact_fullname(element, element._get_cache_key())
|
|
891 |
+ o = cache.resolve_ref(ref)
|
|
892 |
+ refs.append((ref, o))
|
|
893 |
+ |
|
894 |
+ logs = []
|
|
895 |
+ for ref, o in refs:
|
|
896 |
+ # TODO: CASCache._get_subdir is a local-private method
|
|
897 |
+ logsdir = cache._get_subdir(o, "logs")
|
|
898 |
+ destdir = os.path.join(td, ref)
|
|
899 |
+ # TODO: CASCache._checkout is a local-private method
|
|
900 |
+ cache._checkout(destdir, logsdir)
|
|
901 |
+ logs.extend(os.path.join(destdir, log) for log in os.listdir(destdir))
|
|
902 |
+ |
|
903 |
+ for log in logs:
|
|
904 |
+ # NOTE: Should click gain the ability to pass files to the pager this can be optimised.
|
|
905 |
+ with open(log) as f:
|
|
906 |
+ data = f.read()
|
|
907 |
+ click.echo_via_pager(data)
|
1 |
+#
|
|
2 |
+# Copyright (C) 2018 Codethink Limited
|
|
3 |
+# Copyright (C) 2018 Bloomberg Finance LP
|
|
4 |
+#
|
|
5 |
+# This program is free software; you can redistribute it and/or
|
|
6 |
+# modify it under the terms of the GNU Lesser General Public
|
|
7 |
+# License as published by the Free Software Foundation; either
|
|
8 |
+# version 2 of the License, or (at your option) any later version.
|
|
9 |
+#
|
|
10 |
+# This library is distributed in the hope that it will be useful,
|
|
11 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13 |
+# Lesser General Public License for more details.
|
|
14 |
+#
|
|
15 |
+# You should have received a copy of the GNU Lesser General Public
|
|
16 |
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
17 |
+#
|
|
18 |
+# Authors: Richard Maw <richard maw codethink co uk>
|
|
19 |
+#
|
|
20 |
+ |
|
21 |
+import os
|
|
22 |
+import pytest
|
|
23 |
+ |
|
24 |
+from tests.testutils import cli_integration as cli
|
|
25 |
+ |
|
26 |
+ |
|
27 |
+pytestmark = pytest.mark.integration
|
|
28 |
+ |
|
29 |
+ |
|
30 |
+# Project directory
|
|
31 |
+DATA_DIR = os.path.join(
|
|
32 |
+ os.path.dirname(os.path.realpath(__file__)),
|
|
33 |
+ "project",
|
|
34 |
+)
|
|
35 |
+ |
|
36 |
+ |
|
37 |
+@pytest.mark.integration
|
|
38 |
+@pytest.mark.datafiles(DATA_DIR)
|
|
39 |
+def test_artifact_log(cli, tmpdir, datafiles):
|
|
40 |
+ project = os.path.join(datafiles.dirname, datafiles.basename)
|
|
41 |
+ |
|
42 |
+ # Get the cache key of our test element
|
|
43 |
+ result = cli.run(project=project, silent=True, args=[
|
|
44 |
+ '--no-colors',
|
|
45 |
+ 'show', '--deps', 'none', '--format', '%{full-key}',
|
|
46 |
+ 'base.bst'
|
|
47 |
+ ])
|
|
48 |
+ key = result.output.strip()
|
|
49 |
+ |
|
50 |
+ # Ensure we have an artifact to read
|
|
51 |
+ result = cli.run(project=project, args=['build', 'base.bst'])
|
|
52 |
+ assert result.exit_code == 0
|
|
53 |
+ |
|
54 |
+ # Read the log via the element name
|
|
55 |
+ result = cli.run(project=project, args=['artifact', 'log', '-e', 'base.bst'])
|
|
56 |
+ assert result.exit_code == 0
|
|
57 |
+ log = result.output
|
|
58 |
+ |
|
59 |
+ # Read the log via the key
|
|
60 |
+ result = cli.run(project=project, args=['artifact', 'log', 'test/base/' + key])
|
|
61 |
+ assert result.exit_code == 0
|
|
62 |
+ assert log == result.output
|