You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildstream.apache.org by gi...@apache.org on 2020/12/29 13:18:00 UTC

[buildstream] branch jennis/introduce_artifact_delete created (now 5c12d11)

This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a change to branch jennis/introduce_artifact_delete
in repository https://gitbox.apache.org/repos/asf/buildstream.git.


      at 5c12d11  docs: Add bst artifact delete to using_commands.rst

This branch includes the following new commits:

     new 2d46569  _artifactcache.py: Add optional defer_prune flag to remove()
     new 1878169  _artifactcache.py: Add prune() method
     new e8573a6  _artifactelement.py: Add _get_cache_key() override
     new 404fa91  cli.py: Add artifact delete command
     new e53cfa1  cascache.py: Ensure path exists before trying to update the mtime
     new c8d019f  NEWS: Add news entry for bst artifact delete
     new 04f7523  Update man pages now that bst artifact delete exists
     new 5c12d11  docs: Add bst artifact delete to using_commands.rst

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[buildstream] 05/08: cascache.py: Ensure path exists before trying to update the mtime

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch jennis/introduce_artifact_delete
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit e53cfa19e1ed43a0ac9259819f4522f780361f59
Author: James Ennis <ja...@codethink.com>
AuthorDate: Fri Jan 11 11:14:34 2019 +0000

    cascache.py: Ensure path exists before trying to update the mtime
    
    If an artifact is pulled from the cache without its buildtree,
    CASCache.prune() will fail when it tries to update the mtimes
    of the build tree's object files.
    
    A new integration test has been added to tests/integration/artifact.py
    which reflects this.
---
 buildstream/_cas/cascache.py | 16 ++++++++++------
 tests/frontend/artifact.py   | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/buildstream/_cas/cascache.py b/buildstream/_cas/cascache.py
index 802fc13..02030bb 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/tests/frontend/artifact.py b/tests/frontend/artifact.py
index 3c3203d..b6f8fb9 100644
--- a/tests/frontend/artifact.py
+++ b/tests/frontend/artifact.py
@@ -166,3 +166,41 @@ def test_artifact_delete_unbuilt_artifact(cli, tmpdir, datafiles):
     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'


[buildstream] 04/08: cli.py: Add artifact delete command

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch jennis/introduce_artifact_delete
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 404fa91a7e0af026a7a2aeb2bd4ac2d2dc100f23
Author: James Ennis <ja...@codethink.co.uk>
AuthorDate: Tue Mar 5 16:02:19 2019 +0000

    cli.py: Add artifact delete command
    
    This command provides a --no-prune option because or a large cache, pruning
    can be an expensive operation. If a developer wishes to quicky rebuild an artifact,
    they may consider using this option.
---
 buildstream/_frontend/cli.py  |  14 ++++++
 buildstream/_stream.py        |  42 ++++++++++++++++-
 tests/frontend/artifact.py    | 104 ++++++++++++++++++++++++++++++++++++++++++
 tests/frontend/completions.py |   1 +
 4 files changed, 160 insertions(+), 1 deletion(-)

diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py
index 02ca52e..d8c46ce 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 b0fce38..5c88042 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/tests/frontend/artifact.py b/tests/frontend/artifact.py
index c8301c5..3c3203d 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,106 @@ 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
diff --git a/tests/frontend/completions.py b/tests/frontend/completions.py
index 1f29fda..7810a06 100644
--- a/tests/frontend/completions.py
+++ b/tests/frontend/completions.py
@@ -57,6 +57,7 @@ SOURCE_COMMANDS = [
 
 ARTIFACT_COMMANDS = [
     'checkout ',
+    'delete ',
     'push ',
     'pull ',
     'log ',


[buildstream] 06/08: NEWS: Add news entry for bst artifact delete

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch jennis/introduce_artifact_delete
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit c8d019fe08b384ca7a3e7bd85c1af9e9e96cd5af
Author: James Ennis <ja...@codethink.co.uk>
AuthorDate: Wed Mar 13 12:10:57 2019 +0000

    NEWS: Add news entry for bst artifact delete
---
 NEWS | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/NEWS b/NEWS
index 0ff7702..85bf27c 100644
--- a/NEWS
+++ b/NEWS
@@ -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`.


[buildstream] 01/08: _artifactcache.py: Add optional defer_prune flag to remove()

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch jennis/introduce_artifact_delete
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 2d46569fd4948e65eb461cd43e8c8fdf090756ee
Author: James Ennis <ja...@codethink.co.uk>
AuthorDate: Tue Mar 5 15:55:39 2019 +0000

    _artifactcache.py: Add optional defer_prune flag to remove()
---
 buildstream/_artifactcache.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/buildstream/_artifactcache.py b/buildstream/_artifactcache.py
index 3303650..81b0e70 100644
--- a/buildstream/_artifactcache.py
+++ b/buildstream/_artifactcache.py
@@ -415,12 +415,14 @@ 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)
 
     # get_artifact_directory():
     #


[buildstream] 08/08: docs: Add bst artifact delete to using_commands.rst

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch jennis/introduce_artifact_delete
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 5c12d1180db7d6d289f2504a9d8d24f858e42fda
Author: James Ennis <ja...@codethink.co.uk>
AuthorDate: Wed Mar 13 12:14:12 2019 +0000

    docs: Add bst artifact delete to using_commands.rst
---
 doc/source/using_commands.rst | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/doc/source/using_commands.rst b/doc/source/using_commands.rst
index 312b907..53c3619 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
 ------------------


[buildstream] 02/08: _artifactcache.py: Add prune() method

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch jennis/introduce_artifact_delete
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 1878169f940deac8b8fcacdaeee0284764c255d6
Author: James Ennis <ja...@codethink.co.uk>
AuthorDate: Wed Mar 13 12:31:29 2019 +0000

    _artifactcache.py: Add prune() method
---
 buildstream/_artifactcache.py | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/buildstream/_artifactcache.py b/buildstream/_artifactcache.py
index 81b0e70..7da2d95 100644
--- a/buildstream/_artifactcache.py
+++ b/buildstream/_artifactcache.py
@@ -424,6 +424,13 @@ class ArtifactCache():
     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():
     #
     # Get virtual directory for cached artifact of the specified Element.


[buildstream] 03/08: _artifactelement.py: Add _get_cache_key() override

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch jennis/introduce_artifact_delete
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit e8573a6ca3d02ba678a4d4acd2eee7851e6ac201
Author: James Ennis <ja...@codethink.co.uk>
AuthorDate: Tue Mar 5 16:01:01 2019 +0000

    _artifactelement.py: Add _get_cache_key() override
---
 buildstream/_artifactelement.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/buildstream/_artifactelement.py b/buildstream/_artifactelement.py
index a88e83a..a7915eb 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()
 #


[buildstream] 07/08: Update man pages now that bst artifact delete exists

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch jennis/introduce_artifact_delete
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 04f75238603ace3dad94900c47b41ca453ad5160
Author: James Ennis <ja...@codethink.co.uk>
AuthorDate: Wed Mar 13 12:13:52 2019 +0000

    Update man pages now that bst artifact delete exists
---
 man/bst-artifact-checkout.1 |  2 +-
 man/bst-artifact-delete.1   | 12 ++++++++++++
 man/bst-artifact-log.1      |  2 +-
 man/bst-artifact-pull.1     |  2 +-
 man/bst-artifact-push.1     |  2 +-
 man/bst-artifact-server.1   |  2 +-
 man/bst-artifact.1          |  6 +++++-
 man/bst-build.1             |  2 +-
 man/bst-help.1              |  2 +-
 man/bst-init.1              |  4 ++--
 man/bst-shell.1             |  2 +-
 man/bst-show.1              |  2 +-
 man/bst-source-checkout.1   |  2 +-
 man/bst-source-fetch.1      |  2 +-
 man/bst-source-track.1      |  2 +-
 man/bst-source.1            |  2 +-
 man/bst-workspace-close.1   |  2 +-
 man/bst-workspace-list.1    |  2 +-
 man/bst-workspace-open.1    |  2 +-
 man/bst-workspace-reset.1   |  2 +-
 man/bst-workspace.1         |  2 +-
 man/bst.1                   |  5 ++++-
 22 files changed, 41 insertions(+), 22 deletions(-)

diff --git a/man/bst-artifact-checkout.1 b/man/bst-artifact-checkout.1
index 2c11852..27b9954 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 0000000..43ef9bb
--- /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 6045e25..2be7702 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 b01b2bb..890090a 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 bc87c82..09b0684 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 f196b87..d5d9b8c 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 a4bbeb7..aa46e2e 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 deee130..edb67a4 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 35675db..feebf24 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 9b7b119..6f41d97 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 d223d0d..376cddb 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 4fe0b71..54d02c3 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 f3101b6..aed31db 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 0fb63a6..ddcd557 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 92f8cd3..9a24619 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 8976425..4d396b3 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 5c2659f..0b43cdf 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 6be37d6..f525267 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 4fd70fc..4a1065d 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 ad2fc70..d37a7f1 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 b90505e..3376205 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
diff --git a/man/bst.1 b/man/bst.1
index b1f6e9f..ff5e79b 100644
--- a/man/bst.1
+++ b/man/bst.1
@@ -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