diff --git a/src/buildstream/_frontend/cli.py b/src/buildstream/_frontend/cli.py index 964a03e04..081a7d46b 100644 --- a/src/buildstream/_frontend/cli.py +++ b/src/buildstream/_frontend/cli.py @@ -1302,12 +1302,29 @@ def artifact(): ), help="The dependencies we also want to show", ) +@click.option( + "--artifact-remote", + "artifact_remotes", + type=RemoteSpecType(RemoteSpecPurpose.PULL), + multiple=True, + help="A remote for downloading artifacts (Since: 2.7)", +) +@click.option( + "--ignore-project-artifact-remotes", + is_flag=True, + help="Ignore remote artifact cache servers recommended by projects (Since: 2.7)", +) @click.argument("artifacts", type=click.Path(), nargs=-1) @click.pass_obj -def artifact_show(app, deps, artifacts): - """show the cached state of artifacts""" +def artifact_show(app, deps, artifact_remotes, ignore_project_artifact_remotes, artifacts): + """Show the cached state of artifacts""" with app.initialized(): - targets = app.stream.artifact_show(artifacts, selection=deps) + targets = app.stream.artifact_show( + artifacts, + selection=deps, + artifact_remotes=artifact_remotes, + ignore_project_artifact_remotes=ignore_project_artifact_remotes, + ) click.echo(app.logger.show_state_of_artifacts(targets)) sys.exit(0) diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py index c7ec4c660..c6edf4a90 100644 --- a/src/buildstream/_stream.py +++ b/src/buildstream/_stream.py @@ -788,11 +788,25 @@ def _export_artifact(self, tar, location, compression, target, hardlinks, virdir # # Args: # targets (str): Targets to show the cached state of + # artifact_remotes: Artifact cache remotes specified on the commmand line + # ignore_project_artifact_remotes: Whether to ignore artifact remotes specified by projects # - def artifact_show(self, targets, *, selection=_PipelineSelection.NONE): + def artifact_show( + self, + targets, + *, + selection=_PipelineSelection.NONE, + artifact_remotes: Iterable[RemoteSpec] = (), + ignore_project_artifact_remotes: bool = False, + ): # Obtain list of Element and/or ArtifactElement objects target_objects = self.load_selection( - targets, selection=selection, connect_artifact_cache=True, load_artifacts=True + targets, + selection=selection, + connect_artifact_cache=True, + load_artifacts=True, + artifact_remotes=artifact_remotes, + ignore_project_artifact_remotes=ignore_project_artifact_remotes, ) self.query_cache(target_objects) diff --git a/tests/frontend/artifact-show/elements/compose-all.bst b/tests/frontend/artifact-show/elements/compose-all.bst new file mode 100644 index 000000000..ba47081b3 --- /dev/null +++ b/tests/frontend/artifact-show/elements/compose-all.bst @@ -0,0 +1,12 @@ +kind: compose + +depends: +- filename: import-bin.bst + type: build +- filename: import-dev.bst + type: build + +config: + # Dont try running the sandbox, we dont have a + # runtime to run anything in this context. + integrate: False diff --git a/tests/frontend/artifact-show/elements/import-bin.bst b/tests/frontend/artifact-show/elements/import-bin.bst new file mode 100644 index 000000000..a847c0c23 --- /dev/null +++ b/tests/frontend/artifact-show/elements/import-bin.bst @@ -0,0 +1,4 @@ +kind: import +sources: +- kind: local + path: files/bin-files diff --git a/tests/frontend/artifact-show/elements/import-dev.bst b/tests/frontend/artifact-show/elements/import-dev.bst new file mode 100644 index 000000000..152a54667 --- /dev/null +++ b/tests/frontend/artifact-show/elements/import-dev.bst @@ -0,0 +1,4 @@ +kind: import +sources: +- kind: local + path: files/dev-files diff --git a/tests/frontend/artifact-show/elements/manual.bst b/tests/frontend/artifact-show/elements/manual.bst new file mode 100644 index 000000000..142409a08 --- /dev/null +++ b/tests/frontend/artifact-show/elements/manual.bst @@ -0,0 +1,9 @@ +kind: manual + +config: + build-commands: + - echo "hello" + +sources: + - kind: local + path: elements/manual.bst diff --git a/tests/frontend/artifact-show/elements/target.bst b/tests/frontend/artifact-show/elements/target.bst new file mode 100644 index 000000000..b9432fafa --- /dev/null +++ b/tests/frontend/artifact-show/elements/target.bst @@ -0,0 +1,8 @@ +kind: stack +description: | + + Main stack target for the bst build test + +depends: +- import-bin.bst +- compose-all.bst diff --git a/tests/frontend/artifact-show/files/bin-files/usr/bin/hello b/tests/frontend/artifact-show/files/bin-files/usr/bin/hello new file mode 100755 index 000000000..f534a4083 --- /dev/null +++ b/tests/frontend/artifact-show/files/bin-files/usr/bin/hello @@ -0,0 +1,3 @@ +#!/bin/bash + +echo "Hello !" diff --git a/tests/frontend/artifact-show/files/dev-files/usr/include/pony.h b/tests/frontend/artifact-show/files/dev-files/usr/include/pony.h new file mode 100644 index 000000000..40bd0c2e7 --- /dev/null +++ b/tests/frontend/artifact-show/files/dev-files/usr/include/pony.h @@ -0,0 +1,12 @@ +#ifndef __PONY_H__ +#define __PONY_H__ + +#define PONY_BEGIN "Once upon a time, there was a pony." +#define PONY_END "And they lived happily ever after, the end." + +#define MAKE_PONY(story) \ + PONY_BEGIN \ + story \ + PONY_END + +#endif /* __PONY_H__ */ diff --git a/tests/frontend/artifact-show/project.conf b/tests/frontend/artifact-show/project.conf new file mode 100644 index 000000000..7e690f56f --- /dev/null +++ b/tests/frontend/artifact-show/project.conf @@ -0,0 +1,10 @@ +# Project config for frontend build test +name: test +min-version: 2.0 +element-path: elements + +plugins: +- origin: pip + package-name: sample-plugins + sources: + - git diff --git a/tests/frontend/artifact_show.py b/tests/frontend/artifact_show.py index 32e6542c1..1fd67144c 100644 --- a/tests/frontend/artifact_show.py +++ b/tests/frontend/artifact_show.py @@ -21,12 +21,13 @@ from buildstream.exceptions import ErrorDomain from buildstream._testing import cli # pylint: disable=unused-import from tests.testutils import create_artifact_share +from . import configure_project # Project directory DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), - "project", + "artifact-show", ) SIMPLE_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), @@ -155,18 +156,96 @@ def test_artifact_show_glob(cli, tmpdir, datafiles, pattern, expected_prefixes): # Test artifact show artifact in remote @pytest.mark.datafiles(DATA_DIR) -def test_artifact_show_element_available_remotely(cli, tmpdir, datafiles): +@pytest.mark.parametrize("config", ["config-project", "config-user", "config-cli"]) +@pytest.mark.parametrize("by_element_name", [True, False], ids=["by-element-name", "by-artifact-name"]) +def test_artifact_show_available_remotely(cli, tmpdir, datafiles, config, by_element_name): project = str(datafiles) element = "target.bst" + # + # Skip this configuration, BuildStream intentionally ignores the local project.conf + # if an artifact name is specified. + # + if config == "config-project" and not by_element_name: + pytest.skip("No project.conf in context") + # Set up remote and local shares local_cache = os.path.join(str(tmpdir), "artifacts") + cli.configure( + { + "cachedir": local_cache, + } + ) + with create_artifact_share(os.path.join(str(tmpdir), "remote")) as remote: - cli.configure( + extra_cli_args = [] + if config == "config-project": + configure_project( + project, + { + "artifacts": [ + { + "url": remote.repo, + "push": True, + } + ] + }, + ) + elif config == "config-user": + cli.configure( + { + "artifacts": {"servers": [{"url": remote.repo, "push": True}]}, + } + ) + else: + extra_cli_args = ["--artifact-remote", remote.repo] + + # Build the element + result = cli.run(project=project, args=["build"] + extra_cli_args + [element]) + result.assert_success() + + artifact_name = cli.get_artifact_name(project, "test", element) + + # Make sure it's in the share + assert remote.get_artifact(artifact_name) + + # Delete the artifact from the local cache + result = cli.run(project=project, args=["artifact", "delete", element]) + result.assert_success() + assert cli.get_element_state(project, element) != "cached" + + # Do the artifact show and assert + element_or_artifact = element if by_element_name else artifact_name + result = cli.run(project=project, args=["artifact", "show"] + extra_cli_args + [element_or_artifact]) + result.assert_success() + assert "available {}".format(element_or_artifact) in result.output + + +# Test out --ignore-project-artifact-remotes +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_show_ignore_project_remotes(cli, tmpdir, datafiles): + project = str(datafiles) + element = "target.bst" + + # Set up remote and local shares + local_cache = os.path.join(str(tmpdir), "artifacts") + cli.configure( + { + "cachedir": local_cache, + } + ) + + with create_artifact_share(os.path.join(str(tmpdir), "remote")) as remote: + configure_project( + project, { - "artifacts": {"servers": [{"url": remote.repo, "push": True}]}, - "cachedir": local_cache, - } + "artifacts": [ + { + "url": remote.repo, + "push": True, + } + ] + }, ) # Build the element @@ -181,6 +260,12 @@ def test_artifact_show_element_available_remotely(cli, tmpdir, datafiles): result.assert_success() assert cli.get_element_state(project, element) != "cached" + # It is available remotely with the project configured remote result = cli.run(project=project, args=["artifact", "show", element]) result.assert_success() assert "available {}".format(element) in result.output + + # Ignoring project remotes, it is not found in any remote + result = cli.run(project=project, args=["artifact", "show", "--ignore-project-artifact-remotes", element]) + result.assert_success() + assert "not cached {}".format(element) in result.output