diff options
author | Jürg Billeter <j@bitron.ch> | 2019-03-13 17:04:53 +0000 |
---|---|---|
committer | Jürg Billeter <j@bitron.ch> | 2019-03-13 17:04:53 +0000 |
commit | ac71ea61bc0cb551c744507d3d7f13ab85387d75 (patch) | |
tree | 5ce4a00361a6f262b885a977f93b1054b83fa6e6 | |
parent | e4aace8d300a75aaebf9f7c2b87e53fab20478b3 (diff) | |
parent | 7604d239440f6cf4dc4401b28a291da0f7dcc721 (diff) | |
download | buildstream-ac71ea61bc0cb551c744507d3d7f13ab85387d75.tar.gz |
Merge branch 'jennis/introduce_artifact_delete' into 'master'
31 files changed, 275 insertions, 31 deletions
@@ -2,6 +2,10 @@ buildstream 1.3.1 ================= + o Added `bst artifact delete` subcommand. This command removes artifacts from + the local cache. Mulitple element names and artifact refs can be specified + as arguments. + o BREAKING CHANGE: The top level commands `checkout`, `push` and `pull` have been moved to the `bst artifact` subcommand group and are now obsolete. For example, you must now use `bst artifact pull hello.bst`. diff --git a/buildstream/_artifactcache.py b/buildstream/_artifactcache.py index 330365025..7da2d959c 100644 --- a/buildstream/_artifactcache.py +++ b/buildstream/_artifactcache.py @@ -415,12 +415,21 @@ class ArtifactCache(): # Args: # ref (artifact_name): The name of the artifact to remove (as # generated by `Element.get_artifact_name`) + # defer_prune (bool): Optionally declare whether pruning should + # occur immediately after the ref is removed. # # Returns: # (int): The amount of space recovered in the cache, in bytes # - def remove(self, ref): - return self.cas.remove(ref) + def remove(self, ref, *, defer_prune=False): + return self.cas.remove(ref, defer_prune=defer_prune) + + # prune(): + # + # Prune the artifact cache of unreachable refs + # + def prune(self): + return self.cas.prune() # get_artifact_directory(): # diff --git a/buildstream/_artifactelement.py b/buildstream/_artifactelement.py index a88e83aab..a7915eb28 100644 --- a/buildstream/_artifactelement.py +++ b/buildstream/_artifactelement.py @@ -59,6 +59,10 @@ class ArtifactElement(Element): def _calculate_cache_key(self, dependencies=None): return self._key + # Override Element._get_cache_key() + def _get_cache_key(self, strength=None): + return self._key + # verify_artifact_ref() # diff --git a/buildstream/_cas/cascache.py b/buildstream/_cas/cascache.py index 802fc13fd..02030bb68 100644 --- a/buildstream/_cas/cascache.py +++ b/buildstream/_cas/cascache.py @@ -791,16 +791,20 @@ class CASCache(): def _reachable_refs_dir(self, reachable, tree, update_mtime=False): if tree.hash in reachable: return + try: + if update_mtime: + os.utime(self.objpath(tree)) - if update_mtime: - os.utime(self.objpath(tree)) + reachable.add(tree.hash) - reachable.add(tree.hash) + directory = remote_execution_pb2.Directory() - directory = remote_execution_pb2.Directory() + with open(self.objpath(tree), 'rb') as f: + directory.ParseFromString(f.read()) - with open(self.objpath(tree), 'rb') as f: - directory.ParseFromString(f.read()) + except FileNotFoundError: + # Just exit early if the file doesn't exist + return for filenode in directory.files: if update_mtime: diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py index 02ca52e85..d8c46ce0c 100644 --- a/buildstream/_frontend/cli.py +++ b/buildstream/_frontend/cli.py @@ -1106,6 +1106,20 @@ def artifact_log(app, artifacts): click.echo_via_pager(data) +################################################################### +# Artifact Delete Command # +################################################################### +@artifact.command(name='delete', short_help="Remove artifacts from the local cache") +@click.option('--no-prune', 'no_prune', default=False, is_flag=True, + help="Do not prune the local cache of unreachable refs") +@click.argument('artifacts', type=click.Path(), nargs=-1) +@click.pass_obj +def artifact_delete(app, artifacts, no_prune): + """Remove artifacts from the local cache""" + with app.initialized(): + app.stream.artifact_delete(artifacts, no_prune) + + ################################################################## # DEPRECATED Commands # ################################################################## diff --git a/buildstream/_stream.py b/buildstream/_stream.py index b0fce3817..5c880427c 100644 --- a/buildstream/_stream.py +++ b/buildstream/_stream.py @@ -30,11 +30,12 @@ from contextlib import contextmanager, suppress from fnmatch import fnmatch from ._artifactelement import verify_artifact_ref -from ._exceptions import StreamError, ImplError, BstError, ArtifactElementError, set_last_task_error +from ._exceptions import StreamError, ImplError, BstError, ArtifactElementError, CASCacheError, set_last_task_error from ._message import Message, MessageType from ._scheduler import Scheduler, SchedStatus, TrackQueue, FetchQueue, BuildQueue, PullQueue, PushQueue from ._pipeline import Pipeline, PipelineSelection from ._profile import Topics, profile_start, profile_end +from .types import _KeyStrength from . import utils, _yaml, _site from . import Scope, Consistency @@ -520,6 +521,45 @@ class Stream(): return logsdirs + # artifact_delete() + # + # Remove artifacts from the local cache + # + # Args: + # targets (str): Targets to remove + # no_prune (bool): Whether to prune the unreachable refs, default False + # + def artifact_delete(self, targets, no_prune): + # Return list of Element and/or ArtifactElement objects + target_objects = self.load_selection(targets, selection=PipelineSelection.NONE, load_refs=True) + + # Some of the targets may refer to the same key, so first obtain a + # set of the refs to be removed. + remove_refs = set() + for obj in target_objects: + for key_strength in [_KeyStrength.STRONG, _KeyStrength.WEAK]: + key = obj._get_cache_key(strength=key_strength) + remove_refs.add(obj.get_artifact_name(key=key)) + + ref_removed = False + for ref in remove_refs: + try: + self._artifacts.remove(ref, defer_prune=True) + except CASCacheError as e: + self._message(MessageType.WARN, "{}".format(e)) + continue + + self._message(MessageType.INFO, "Removed: {}".format(ref)) + ref_removed = True + + # Prune the artifact cache + if ref_removed and not no_prune: + with self._context.timed_activity("Pruning artifact cache"): + self._artifacts.prune() + + if not ref_removed: + self._message(MessageType.INFO, "No artifacts were removed") + # source_checkout() # # Checkout sources of the target element to the specified location diff --git a/doc/source/using_commands.rst b/doc/source/using_commands.rst index 312b907f8..53c36197e 100644 --- a/doc/source/using_commands.rst +++ b/doc/source/using_commands.rst @@ -101,6 +101,13 @@ Artifact subcommands .. click:: buildstream._frontend.cli:artifact_push :prog: bst artifact push +---- + +.. _invoking_artifact_delete: + +.. click:: buildstream._frontend.cli:artifact_delete + :prog: bst artifact delete + Source subcommands ------------------ diff --git a/man/bst-artifact-checkout.1 b/man/bst-artifact-checkout.1 index 2c11852c8..27b9954f4 100644 --- a/man/bst-artifact-checkout.1 +++ b/man/bst-artifact-checkout.1 @@ -1,4 +1,4 @@ -.TH "BST ARTIFACT CHECKOUT" "1" "12-Feb-2019" "" "bst artifact checkout Manual" +.TH "BST ARTIFACT CHECKOUT" "1" "13-Mar-2019" "" "bst artifact checkout Manual" .SH NAME bst\-artifact\-checkout \- Checkout contents of an artifact .SH SYNOPSIS diff --git a/man/bst-artifact-delete.1 b/man/bst-artifact-delete.1 new file mode 100644 index 000000000..43ef9bb47 --- /dev/null +++ b/man/bst-artifact-delete.1 @@ -0,0 +1,12 @@ +.TH "BST ARTIFACT DELETE" "1" "13-Mar-2019" "" "bst artifact delete Manual" +.SH NAME +bst\-artifact\-delete \- Remove artifacts from the local cache +.SH SYNOPSIS +.B bst artifact delete +[OPTIONS] [ARTIFACTS]... +.SH DESCRIPTION +Show logs of all artifacts +.SH OPTIONS +.TP +\fB\-\-no\-prune\fP +Do not prune the local cache of unreachable refs diff --git a/man/bst-artifact-log.1 b/man/bst-artifact-log.1 index 6045e2587..2be77021f 100644 --- a/man/bst-artifact-log.1 +++ b/man/bst-artifact-log.1 @@ -1,4 +1,4 @@ -.TH "BST ARTIFACT LOG" "1" "12-Feb-2019" "" "bst artifact log Manual" +.TH "BST ARTIFACT LOG" "1" "13-Mar-2019" "" "bst artifact log Manual" .SH NAME bst\-artifact\-log \- Show logs of an artifact .SH SYNOPSIS diff --git a/man/bst-artifact-pull.1 b/man/bst-artifact-pull.1 index b01b2bb15..890090a44 100644 --- a/man/bst-artifact-pull.1 +++ b/man/bst-artifact-pull.1 @@ -1,4 +1,4 @@ -.TH "BST ARTIFACT PULL" "1" "12-Feb-2019" "" "bst artifact pull Manual" +.TH "BST ARTIFACT PULL" "1" "13-Mar-2019" "" "bst artifact pull Manual" .SH NAME bst\-artifact\-pull \- Pull a built artifact .SH SYNOPSIS diff --git a/man/bst-artifact-push.1 b/man/bst-artifact-push.1 index bc87c8287..09b068449 100644 --- a/man/bst-artifact-push.1 +++ b/man/bst-artifact-push.1 @@ -1,4 +1,4 @@ -.TH "BST ARTIFACT PUSH" "1" "12-Feb-2019" "" "bst artifact push Manual" +.TH "BST ARTIFACT PUSH" "1" "13-Mar-2019" "" "bst artifact push Manual" .SH NAME bst\-artifact\-push \- Push a built artifact .SH SYNOPSIS diff --git a/man/bst-artifact-server.1 b/man/bst-artifact-server.1 index f196b878d..d5d9b8cde 100644 --- a/man/bst-artifact-server.1 +++ b/man/bst-artifact-server.1 @@ -1,4 +1,4 @@ -.TH "BST-ARTIFACT-SERVER" "1" "12-Feb-2019" "" "bst-artifact-server Manual" +.TH "BST-ARTIFACT-SERVER" "1" "13-Mar-2019" "" "bst-artifact-server Manual" .SH NAME bst-artifact-server \- CAS Artifact Server .SH SYNOPSIS diff --git a/man/bst-artifact.1 b/man/bst-artifact.1 index a4bbeb7b2..aa46e2ef0 100644 --- a/man/bst-artifact.1 +++ b/man/bst-artifact.1 @@ -1,4 +1,4 @@ -.TH "BST ARTIFACT" "1" "12-Feb-2019" "" "bst artifact Manual" +.TH "BST ARTIFACT" "1" "13-Mar-2019" "" "bst artifact Manual" .SH NAME bst\-artifact \- Manipulate cached artifacts .SH SYNOPSIS @@ -23,3 +23,7 @@ Manipulate cached artifacts \fBlog\fP Show logs of an artifact See \fBbst artifact-log(1)\fP for full documentation on the \fBlog\fP command. +.PP +\fBdelete\fP + Remove artifacts from the local cache + See \fBbst artifact-delete(1)\fP for full documentation on the \fBdelete\fP command. diff --git a/man/bst-build.1 b/man/bst-build.1 index deee1301e..edb67a499 100644 --- a/man/bst-build.1 +++ b/man/bst-build.1 @@ -1,4 +1,4 @@ -.TH "BST BUILD" "1" "12-Feb-2019" "" "bst build Manual" +.TH "BST BUILD" "1" "13-Mar-2019" "" "bst build Manual" .SH NAME bst\-build \- Build elements in a pipeline .SH SYNOPSIS diff --git a/man/bst-help.1 b/man/bst-help.1 index 35675db1d..feebf2463 100644 --- a/man/bst-help.1 +++ b/man/bst-help.1 @@ -1,4 +1,4 @@ -.TH "BST HELP" "1" "12-Feb-2019" "" "bst help Manual" +.TH "BST HELP" "1" "13-Mar-2019" "" "bst help Manual" .SH NAME bst\-help \- Print usage information .SH SYNOPSIS diff --git a/man/bst-init.1 b/man/bst-init.1 index 9b7b119d9..6f41d97d9 100644 --- a/man/bst-init.1 +++ b/man/bst-init.1 @@ -1,4 +1,4 @@ -.TH "BST INIT" "1" "12-Feb-2019" "" "bst init Manual" +.TH "BST INIT" "1" "13-Mar-2019" "" "bst init Manual" .SH NAME bst\-init \- Initialize a new BuildStream project .SH SYNOPSIS @@ -18,7 +18,7 @@ interactive session. The project name to use .TP \fB\-\-format\-version\fP INTEGER -The required format version (default: 21) +The required format version (default: 23) .TP \fB\-\-element\-path\fP PATH The subdirectory to store elements in (default: elements) diff --git a/man/bst-shell.1 b/man/bst-shell.1 index d223d0d20..376cddb4b 100644 --- a/man/bst-shell.1 +++ b/man/bst-shell.1 @@ -1,4 +1,4 @@ -.TH "BST SHELL" "1" "12-Feb-2019" "" "bst shell Manual" +.TH "BST SHELL" "1" "13-Mar-2019" "" "bst shell Manual" .SH NAME bst\-shell \- Shell into an element's sandbox environment .SH SYNOPSIS diff --git a/man/bst-show.1 b/man/bst-show.1 index 4fe0b71c2..54d02c3b6 100644 --- a/man/bst-show.1 +++ b/man/bst-show.1 @@ -1,4 +1,4 @@ -.TH "BST SHOW" "1" "12-Feb-2019" "" "bst show Manual" +.TH "BST SHOW" "1" "13-Mar-2019" "" "bst show Manual" .SH NAME bst\-show \- Show elements in the pipeline .SH SYNOPSIS diff --git a/man/bst-source-checkout.1 b/man/bst-source-checkout.1 index f3101b679..aed31db7a 100644 --- a/man/bst-source-checkout.1 +++ b/man/bst-source-checkout.1 @@ -1,4 +1,4 @@ -.TH "BST SOURCE CHECKOUT" "1" "12-Feb-2019" "" "bst source checkout Manual" +.TH "BST SOURCE CHECKOUT" "1" "13-Mar-2019" "" "bst source checkout Manual" .SH NAME bst\-source\-checkout \- Checkout sources for an element .SH SYNOPSIS diff --git a/man/bst-source-fetch.1 b/man/bst-source-fetch.1 index 0fb63a6d7..ddcd557e4 100644 --- a/man/bst-source-fetch.1 +++ b/man/bst-source-fetch.1 @@ -1,4 +1,4 @@ -.TH "BST SOURCE FETCH" "1" "12-Feb-2019" "" "bst source fetch Manual" +.TH "BST SOURCE FETCH" "1" "13-Mar-2019" "" "bst source fetch Manual" .SH NAME bst\-source\-fetch \- Fetch sources in a pipeline .SH SYNOPSIS diff --git a/man/bst-source-track.1 b/man/bst-source-track.1 index 92f8cd310..9a24619a1 100644 --- a/man/bst-source-track.1 +++ b/man/bst-source-track.1 @@ -1,4 +1,4 @@ -.TH "BST SOURCE TRACK" "1" "12-Feb-2019" "" "bst source track Manual" +.TH "BST SOURCE TRACK" "1" "13-Mar-2019" "" "bst source track Manual" .SH NAME bst\-source\-track \- Track new source references .SH SYNOPSIS diff --git a/man/bst-source.1 b/man/bst-source.1 index 897642541..4d396b3d0 100644 --- a/man/bst-source.1 +++ b/man/bst-source.1 @@ -1,4 +1,4 @@ -.TH "BST SOURCE" "1" "12-Feb-2019" "" "bst source Manual" +.TH "BST SOURCE" "1" "13-Mar-2019" "" "bst source Manual" .SH NAME bst\-source \- Manipulate sources for an element .SH SYNOPSIS diff --git a/man/bst-workspace-close.1 b/man/bst-workspace-close.1 index 5c2659f63..0b43cdf22 100644 --- a/man/bst-workspace-close.1 +++ b/man/bst-workspace-close.1 @@ -1,4 +1,4 @@ -.TH "BST WORKSPACE CLOSE" "1" "12-Feb-2019" "" "bst workspace close Manual" +.TH "BST WORKSPACE CLOSE" "1" "13-Mar-2019" "" "bst workspace close Manual" .SH NAME bst\-workspace\-close \- Close workspaces .SH SYNOPSIS diff --git a/man/bst-workspace-list.1 b/man/bst-workspace-list.1 index 6be37d631..f5252670d 100644 --- a/man/bst-workspace-list.1 +++ b/man/bst-workspace-list.1 @@ -1,4 +1,4 @@ -.TH "BST WORKSPACE LIST" "1" "12-Feb-2019" "" "bst workspace list Manual" +.TH "BST WORKSPACE LIST" "1" "13-Mar-2019" "" "bst workspace list Manual" .SH NAME bst\-workspace\-list \- List open workspaces .SH SYNOPSIS diff --git a/man/bst-workspace-open.1 b/man/bst-workspace-open.1 index 4fd70fc15..4a1065d69 100644 --- a/man/bst-workspace-open.1 +++ b/man/bst-workspace-open.1 @@ -1,4 +1,4 @@ -.TH "BST WORKSPACE OPEN" "1" "12-Feb-2019" "" "bst workspace open Manual" +.TH "BST WORKSPACE OPEN" "1" "13-Mar-2019" "" "bst workspace open Manual" .SH NAME bst\-workspace\-open \- Open a new workspace .SH SYNOPSIS diff --git a/man/bst-workspace-reset.1 b/man/bst-workspace-reset.1 index ad2fc707a..d37a7f1c2 100644 --- a/man/bst-workspace-reset.1 +++ b/man/bst-workspace-reset.1 @@ -1,4 +1,4 @@ -.TH "BST WORKSPACE RESET" "1" "12-Feb-2019" "" "bst workspace reset Manual" +.TH "BST WORKSPACE RESET" "1" "13-Mar-2019" "" "bst workspace reset Manual" .SH NAME bst\-workspace\-reset \- Reset a workspace to its original state .SH SYNOPSIS diff --git a/man/bst-workspace.1 b/man/bst-workspace.1 index b90505e1a..337620584 100644 --- a/man/bst-workspace.1 +++ b/man/bst-workspace.1 @@ -1,4 +1,4 @@ -.TH "BST WORKSPACE" "1" "12-Feb-2019" "" "bst workspace Manual" +.TH "BST WORKSPACE" "1" "13-Mar-2019" "" "bst workspace Manual" .SH NAME bst\-workspace \- Manipulate developer workspaces .SH SYNOPSIS @@ -1,4 +1,4 @@ -.TH "BST" "1" "12-Feb-2019" "" "bst Manual" +.TH "BST" "1" "13-Mar-2019" "" "bst Manual" .SH NAME bst \- Build and manipulate BuildStream projects... .SH SYNOPSIS @@ -67,6 +67,9 @@ The mirror to fetch from first, before attempting other mirrors .TP \fB\-\-pull\-buildtrees\fP Include an element's build tree when pulling remote element artifacts +.TP +\fB\-\-cache\-buildtrees\fP [always|failure|never] +Cache artifact build tree content on creation .SH COMMANDS .PP \fBhelp\fP diff --git a/tests/frontend/artifact.py b/tests/frontend/artifact.py index c8301c529..b6f8fb91d 100644 --- a/tests/frontend/artifact.py +++ b/tests/frontend/artifact.py @@ -22,6 +22,7 @@ import os import pytest from buildstream.plugintestutils import cli +from tests.testutils import create_artifact_share # Project directory @@ -62,3 +63,144 @@ def test_artifact_log(cli, datafiles): assert result.exit_code == 0 # The artifact is cached under both a strong key and a weak key assert (log + log) == result.output + + +# Test that we can delete the artifact of the element which corresponds +# to the current project state +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_element(cli, tmpdir, datafiles): + project = os.path.join(datafiles.dirname, datafiles.basename) + element = 'target.bst' + + # Build the element and ensure it's cached + result = cli.run(project=project, args=['build', element]) + result.assert_success() + assert cli.get_element_state(project, element) == 'cached' + + result = cli.run(project=project, args=['artifact', 'delete', element]) + result.assert_success() + assert cli.get_element_state(project, element) != 'cached' + + +# Test that we can delete an artifact by specifying its ref. +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_artifact(cli, tmpdir, datafiles): + project = os.path.join(datafiles.dirname, datafiles.basename) + element = 'target.bst' + + # Configure a local cache + local_cache = os.path.join(str(tmpdir), 'artifacts') + cli.configure({'cachedir': local_cache}) + + # First build an element so that we can find its artifact + result = cli.run(project=project, args=['build', element]) + result.assert_success() + + # Obtain the artifact ref + cache_key = cli.get_element_key(project, element) + artifact = os.path.join('test', os.path.splitext(element)[0], cache_key) + + # Explicitly check that the ARTIFACT exists in the cache + assert os.path.exists(os.path.join(local_cache, 'cas', 'refs', 'heads', artifact)) + + # Delete the artifact + result = cli.run(project=project, args=['artifact', 'delete', artifact]) + result.assert_success() + + # Check that the ARTIFACT is no longer in the cache + assert not os.path.exists(os.path.join(local_cache, 'cas', 'refs', 'heads', artifact)) + + +# Test the `bst artifact delete` command with multiple, different arguments. +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_element_and_artifact(cli, tmpdir, datafiles): + project = os.path.join(datafiles.dirname, datafiles.basename) + element = 'target.bst' + dep = 'compose-all.bst' + + # Configure a local cache + local_cache = os.path.join(str(tmpdir), 'artifacts') + cli.configure({'cachedir': local_cache}) + + # First build an element so that we can find its artifact + result = cli.run(project=project, args=['build', element]) + result.assert_success() + assert cli.get_element_state(project, element) == 'cached' + assert cli.get_element_state(project, dep) == 'cached' + + # Obtain the artifact ref + cache_key = cli.get_element_key(project, element) + artifact = os.path.join('test', os.path.splitext(element)[0], cache_key) + + # Explicitly check that the ARTIFACT exists in the cache + assert os.path.exists(os.path.join(local_cache, 'cas', 'refs', 'heads', artifact)) + + # Delete the artifact + result = cli.run(project=project, args=['artifact', 'delete', artifact, dep]) + result.assert_success() + + # Check that the ARTIFACT is no longer in the cache + assert not os.path.exists(os.path.join(local_cache, 'cas', 'refs', 'heads', artifact)) + + # Check that the dependency ELEMENT is no longer cached + assert cli.get_element_state(project, dep) != 'cached' + + +# Test that we receive the appropriate stderr when we try to delete an artifact +# that is not present in the cache. +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_unbuilt_artifact(cli, tmpdir, datafiles): + project = os.path.join(datafiles.dirname, datafiles.basename) + element = 'target.bst' + + # delete it, just in case it's there + _ = cli.run(project=project, args=['artifact', 'delete', element]) + + # Ensure the element is not cached + assert cli.get_element_state(project, element) != 'cached' + + # Now try and remove it again (now we know its not there) + result = cli.run(project=project, args=['artifact', 'delete', element]) + + cache_key = cli.get_element_key(project, element) + artifact = os.path.join('test', os.path.splitext(element)[0], cache_key) + expected_err = "WARNING Could not find ref '{}'".format(artifact) + assert expected_err in result.stderr + + +# Test that an artifact pulled from it's remote cache (without it's buildtree) will not +# throw an Exception when trying to prune the cache. +@pytest.mark.datafiles(DATA_DIR) +def test_artifact_delete_pulled_artifact_without_buildtree(cli, tmpdir, datafiles): + project = os.path.join(datafiles.dirname, datafiles.basename) + element = 'target.bst' + + # Set up remote and local shares + local_cache = os.path.join(str(tmpdir), 'artifacts') + with create_artifact_share(os.path.join(str(tmpdir), 'remote')) as remote: + cli.configure({ + 'artifacts': {'url': remote.repo, 'push': True}, + 'cachedir': local_cache, + }) + + # Build the element + result = cli.run(project=project, args=['build', element]) + result.assert_success() + + # Make sure it's in the share + cache_key = cli.get_element_key(project, element) + assert remote.has_artifact('test', element, cache_key) + + # Delete and then pull the artifact (without its buildtree) + result = cli.run(project=project, args=['artifact', 'delete', element]) + result.assert_success() + assert cli.get_element_state(project, element) != 'cached' + result = cli.run(project=project, args=['artifact', 'pull', element]) + result.assert_success() + assert cli.get_element_state(project, element) == 'cached' + + # Now delete it again (it should have been pulled without the buildtree, but + # a digest of the buildtree is pointed to in the artifact's metadata + result = cli.run(project=project, args=['artifact', 'delete', element]) + result.assert_success() + assert cli.get_element_state(project, element) != 'cached' diff --git a/tests/frontend/completions.py b/tests/frontend/completions.py index 1f29fdae6..7810a06d5 100644 --- a/tests/frontend/completions.py +++ b/tests/frontend/completions.py @@ -57,6 +57,7 @@ SOURCE_COMMANDS = [ ARTIFACT_COMMANDS = [ 'checkout ', + 'delete ', 'push ', 'pull ', 'log ', |