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

[buildstream] branch jennis/deprecate_bst_checkout created (now f305bd2)

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

not-in-ldap pushed a change to branch jennis/deprecate_bst_checkout
in repository https://gitbox.apache.org/repos/asf/buildstream.git.


      at f305bd2  NEWS: Add entry for the deprecation of bst checkout

This branch includes the following new commits:

     new 1c92ee4  cli: Split classify_artifacts helper up
     new a94ccf0  cli: Factor virtual directory loading out of artifact_log
     new 82f39a2  cli: Add artifact checkout subcommand
     new c7c38be  Mark 'old' checkout command as obsolete
     new 431b779  tests/frontend/buildcheckout.py: Add new tests for checking out artifacts
     new f305bd2  NEWS: Add entry for the deprecation of bst checkout

The 6 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] 01/06: cli: Split classify_artifacts helper up

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

not-in-ldap pushed a commit to branch jennis/deprecate_bst_checkout
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 1c92ee41e412ee0433a786e1a57194052dddc2ef
Author: Richard Maw <ri...@codethink.co.uk>
AuthorDate: Wed Dec 12 18:23:19 2018 +0000

    cli: Split classify_artifacts helper up
    
    Classifying artifact refs is useful for glob expansion
    even in cases where an element target name would be inappropriate.
---
 buildstream/_frontend/cli.py | 52 +++++++++++++++++++++++++++-----------------
 1 file changed, 32 insertions(+), 20 deletions(-)

diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py
index bdcf3ca..ae64afa 100644
--- a/buildstream/_frontend/cli.py
+++ b/buildstream/_frontend/cli.py
@@ -973,36 +973,48 @@ def workspace_list(app):
 #############################################################
 #                     Artifact Commands                     #
 #############################################################
-def _classify_artifacts(names, cas, project_directory):
-    element_targets = []
-    artifact_refs = []
-    element_globs = []
-    artifact_globs = []
-
+def _classify_element_targets(names, project_directory):
+    globs = []
+    targets = []
+    unmatched = []
     for name in names:
         if name.endswith('.bst'):
             if any(c in "*?[" for c in name):
-                element_globs.append(name)
+                globs.append(name)
             else:
-                element_targets.append(name)
+                targets.append(name)
         else:
-            if any(c in "*?[" for c in name):
-                artifact_globs.append(name)
-            else:
-                artifact_refs.append(name)
+            unmatched.append(name)
 
-    if element_globs:
+    if globs:
         for dirpath, _, filenames in os.walk(project_directory):
             for filename in filenames:
-                element_path = os.path.join(dirpath, filename).lstrip(project_directory).lstrip('/')
-                if any(fnmatch(element_path, glob) for glob in element_globs):
-                    element_targets.append(element_path)
+                element_path = os.path.relpath(os.path.join(dirpath, filename), start=project_directory)
+                if any(fnmatch(element_path, glob) for glob in globs):
+                    targets.append(element_path)
+    return targets, unmatched
+
+
+def _classify_artifact_refs(names, cas):
+    globs = []
+    refs = []
+    for name in names:
+        if any(c in "*?[" for c in name):
+            globs.append(name)
+        else:
+            refs.append(name)
+    if globs:
+        refs.extend(ref for ref in cas.list_refs()
+                    if any(fnmatch(ref, glob) for glob in globs))
+    return refs
+
+
+def _classify_artifacts(names, cas, project_directory):
+    targets, unmatched = _classify_element_targets(names, project_directory)
+    refs = _classify_artifact_refs(unmatched, cas)
 
-    if artifact_globs:
-        artifact_refs.extend(ref for ref in cas.list_refs()
-                             if any(fnmatch(ref, glob) for glob in artifact_globs))
+    return targets, refs
 
-    return element_targets, artifact_refs
 
 
 @cli.group(short_help="Manipulate cached artifacts")


[buildstream] 05/06: tests/frontend/buildcheckout.py: Add new tests for checking out artifacts

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

not-in-ldap pushed a commit to branch jennis/deprecate_bst_checkout
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 431b77963dbc272570fb05760c0b0bf3b8d29279
Author: James Ennis <ja...@codethink.com>
AuthorDate: Wed Jan 9 16:19:08 2019 +0000

    tests/frontend/buildcheckout.py: Add new tests for checking out artifacts
    
    bst artifact subcommands are able to handle artifact refs as well as
    element names. These tests ensure that we can checkout an artifact
    or multiple artifacts by using their refs.
---
 tests/frontend/buildcheckout.py | 64 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/tests/frontend/buildcheckout.py b/tests/frontend/buildcheckout.py
index 8c7e22a..52c391d 100644
--- a/tests/frontend/buildcheckout.py
+++ b/tests/frontend/buildcheckout.py
@@ -631,3 +631,67 @@ def test_build_checkout_cross_junction(datafiles, cli, tmpdir):
 
     filename = os.path.join(checkout, 'etc', 'animal.conf')
     assert os.path.exists(filename)
+
+
+@pytest.mark.datafiles(DATA_DIR)
+def test_checkout_single_artifact(datafiles, cli, tmpdir):
+    project = os.path.join(datafiles.dirname, datafiles.basename)
+    checkout = os.path.join(cli.directory, 'checkout')
+
+    result = cli.run(project=project, args=['build', 'import-bin.bst'])
+    result.assert_success()
+
+    key = cli.get_element_key(project, 'import-bin.bst')
+    artifact = os.path.join('test', 'import-bin', key)  # 'test' is the project's name
+
+    result = cli.run(project=project, args=['artifact', 'checkout', artifact, '--directory', checkout])
+    result.assert_success()
+
+    filename = os.path.join(checkout, 'usr', 'bin', 'hello')
+    assert os.path.exists(filename)
+
+
+@pytest.mark.datafiles(DATA_DIR)
+def test_checkout_multiple_artifacts(datafiles, cli, tmpdir):
+    # Now let's built and checkout two of the built artifacts
+    project = os.path.join(datafiles.dirname, datafiles.basename)
+    checkout = os.path.join(cli.directory, 'checkout')
+
+    result = cli.run(project=project, args=['build', 'target.bst'])  # depends on import-bin and import-dev.bst
+    result.assert_success()
+
+    bin_key = cli.get_element_key(project, 'import-bin.bst')
+    artifact1 = os.path.join('test', 'import-bin', bin_key)  # 'test' is the project's name
+    dev_key = cli.get_element_key(project, 'import-dev.bst')
+    artifact2 = os.path.join('test', 'import-dev', dev_key)
+
+    result = cli.run(project=project, args=['artifact', 'checkout', artifact1, artifact2,
+                                            '--directory', checkout])
+    result.assert_success()
+
+    bin_file = os.path.join(checkout, 'usr', 'bin', 'hello')
+    assert os.path.exists(bin_file)
+    dev_file = os.path.join(checkout, 'usr', 'include', 'pony.h')
+    assert os.path.exists(dev_file)
+
+
+@pytest.mark.datafiles(DATA_DIR)
+def test_checkout_element_and_artifact(datafiles, cli, tmpdir):
+    # Now let's built and checkout two of the built artifacts
+    project = os.path.join(datafiles.dirname, datafiles.basename)
+    checkout = os.path.join(cli.directory, 'checkout')
+
+    result = cli.run(project=project, args=['build', 'target.bst'])  # depends on import-bin and import-dev.bst
+    result.assert_success()
+
+    dev_key = cli.get_element_key(project, 'import-dev.bst')
+    artifact = os.path.join('test', 'import-dev', dev_key)
+
+    result = cli.run(project=project, args=['artifact', 'checkout', 'import-bin.bst', artifact,
+                                            '--directory', checkout])
+    result.assert_success()
+
+    bin_file = os.path.join(checkout, 'usr', 'bin', 'hello')
+    assert os.path.exists(bin_file)
+    dev_file = os.path.join(checkout, 'usr', 'include', 'pony.h')
+    assert os.path.exists(dev_file)


[buildstream] 02/06: cli: Factor virtual directory loading out of artifact_log

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

not-in-ldap pushed a commit to branch jennis/deprecate_bst_checkout
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit a94ccf0a4beb694ab170afbff19009b5cd9709fc
Author: Richard Maw <ri...@codethink.co.uk>
AuthorDate: Wed Dec 12 18:27:57 2018 +0000

    cli: Factor virtual directory loading out of artifact_log
    
    Other artifact subcommands will also need to make use of
    loading virtual directories
---
 buildstream/_frontend/cli.py | 55 +++++++++++++++++++++++++-------------------
 1 file changed, 31 insertions(+), 24 deletions(-)

diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py
index ae64afa..60a2445 100644
--- a/buildstream/_frontend/cli.py
+++ b/buildstream/_frontend/cli.py
@@ -7,6 +7,7 @@ from tempfile import TemporaryDirectory
 import click
 from .. import _yaml
 from .._exceptions import BstError, LoadError, AppError
+from ..storage._casbaseddirectory import CasBasedDirectory
 from .._versions import BST_FORMAT_VERSION
 from .complete import main_bashcomplete, complete_path, CompleteUnhandled
 
@@ -1016,6 +1017,35 @@ def _classify_artifacts(names, cas, project_directory):
     return targets, refs
 
 
+def _load_vdirs(app, elements, artifacts):
+    from .._exceptions import CASError
+    from .._message import MessageType
+    from .._pipeline import PipelineSelection
+    cache = app.context.artifactcache
+    vdirs = []
+
+    for ref in artifacts:
+        try:
+            cache_id = cache.cas.resolve_ref(ref, update_mtime=True)
+            vdir = CasBasedDirectory(cache.cas, cache_id)
+            vdirs.append(vdir)
+        except CASError as e:
+            app._message(MessageType.WARN, "Artifact {} is not cached".format(ref), detail=str(e))
+            continue
+
+    if elements:
+        elements = app.stream.load_selection(elements, selection=PipelineSelection.NONE)
+        for element in elements:
+            if not element._cached():
+                app._message(MessageType.WARN, "Element {} is not cached".format(element))
+                continue
+            ref = cache.get_artifact_fullname(element, element._get_cache_key())
+            cache_id = cache.cas.resolve_ref(ref, update_mtime=True)
+            vdir = CasBasedDirectory(cache.cas, cache_id)
+            vdirs.append(vdir)
+
+    return vdirs
+
 
 @cli.group(short_help="Manipulate cached artifacts")
 def artifact():
@@ -1030,10 +1060,6 @@ def artifact():
 @click.pass_obj
 def artifact_log(app, artifacts):
     """Show logs of all artifacts"""
-    from .._exceptions import CASError
-    from .._message import MessageType
-    from .._pipeline import PipelineSelection
-    from ..storage._casbaseddirectory import CasBasedDirectory
 
     with ExitStack() as stack:
         stack.enter_context(app.initialized())
@@ -1042,27 +1068,8 @@ def artifact_log(app, artifacts):
         elements, artifacts = _classify_artifacts(artifacts, cache.cas,
                                                   app.project.directory)
 
-        vdirs = []
+        vdirs = _load_vdirs(app, elements, artifacts)
         extractdirs = []
-        if artifacts:
-            for ref in artifacts:
-                try:
-                    cache_id = cache.cas.resolve_ref(ref, update_mtime=True)
-                    vdir = CasBasedDirectory(cache.cas, cache_id)
-                    vdirs.append(vdir)
-                except CASError as e:
-                    app._message(MessageType.WARN, "Artifact {} is not cached".format(ref), detail=str(e))
-                    continue
-        if elements:
-            elements = app.stream.load_selection(elements, selection=PipelineSelection.NONE)
-            for element in elements:
-                if not element._cached():
-                    app._message(MessageType.WARN, "Element {} is not cached".format(element))
-                    continue
-                ref = cache.get_artifact_fullname(element, element._get_cache_key())
-                cache_id = cache.cas.resolve_ref(ref, update_mtime=True)
-                vdir = CasBasedDirectory(cache.cas, cache_id)
-                vdirs.append(vdir)
 
         for vdir in vdirs:
             # NOTE: If reading the logs feels unresponsive, here would be a good place to provide progress information.


[buildstream] 04/06: Mark 'old' checkout command as obsolete

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

not-in-ldap pushed a commit to branch jennis/deprecate_bst_checkout
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit c7c38be174af9225b73fa6f43485b59f146a7149
Author: James Ennis <ja...@codethink.com>
AuthorDate: Wed Jan 9 12:36:02 2019 +0000

    Mark 'old' checkout command as obsolete
    
    This commit marks 'bst checkout' as a 'hidden' command. If used,
    the user will be prompted to use the new 'bst artifact checkout'
    command.
    
    All tests which used 'bst checkout' have been modified to use
    the new artifact sub-command.
    
    This partially solves #822.
---
 buildstream/_frontend/cli.py            | 88 ++++++++++-----------------------
 contrib/bst-docker-import               |  2 +-
 tests/artifactcache/expiry.py           |  5 +-
 tests/completions/completions.py        | 20 +++++---
 tests/examples/autotools.py             |  2 +-
 tests/examples/developing.py            |  2 +-
 tests/examples/first-project.py         |  2 +-
 tests/examples/flatpak-autotools.py     |  2 +-
 tests/frontend/buildcheckout.py         | 38 +++++++-------
 tests/frontend/compose_splits.py        |  2 +-
 tests/frontend/mirror.py                |  4 +-
 tests/frontend/workspace.py             |  6 +--
 tests/integration/autotools.py          |  4 +-
 tests/integration/cachedfail.py         |  2 +-
 tests/integration/cmake.py              |  4 +-
 tests/integration/compose-symlinks.py   |  3 +-
 tests/integration/compose.py            |  2 +-
 tests/integration/import.py             |  2 +-
 tests/integration/make.py               |  2 +-
 tests/integration/manual.py             |  6 +--
 tests/integration/pip_element.py        |  2 +-
 tests/integration/pip_source.py         |  2 +-
 tests/integration/script.py             | 24 ++++-----
 tests/integration/shell.py              |  2 +-
 tests/integration/source-determinism.py |  4 +-
 tests/integration/stack.py              |  2 +-
 tests/integration/symlinks.py           |  6 +--
 tests/loader/junctions.py               | 16 +++---
 tests/plugins/filter.py                 | 15 +++---
 tests/sources/bzr.py                    |  2 +-
 tests/sources/deb.py                    |  6 +--
 tests/sources/git.py                    | 16 +++---
 tests/sources/local.py                  |  6 +--
 tests/sources/patch.py                  |  8 +--
 tests/sources/previous_source_access.py |  2 +-
 tests/sources/remote.py                 |  8 +--
 tests/sources/tar.py                    | 12 ++---
 tests/sources/zip.py                    |  8 +--
 38 files changed, 159 insertions(+), 180 deletions(-)

diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py
index 759db6b..0d04246 100644
--- a/buildstream/_frontend/cli.py
+++ b/buildstream/_frontend/cli.py
@@ -608,67 +608,6 @@ def shell(app, element, sysroot, mount, isolate, build_, cli_buildtree, command)
 
 
 ##################################################################
-#                        Checkout Command                        #
-##################################################################
-@cli.command(short_help="Checkout a built artifact")
-@click.option('--force', '-f', default=False, is_flag=True,
-              help="Allow files to be overwritten")
-@click.option('--deps', '-d', default='run',
-              type=click.Choice(['run', 'build', 'none']),
-              help='The dependencies to checkout (default: run)')
-@click.option('--integrate/--no-integrate', default=True, is_flag=True,
-              help="Whether to run integration commands")
-@click.option('--hardlinks', default=False, is_flag=True,
-              help="Checkout hardlinks instead of copies (handle with care)")
-@click.option('--tar', default=False, is_flag=True,
-              help="Create a tarball from the artifact contents instead "
-                   "of a file tree. If LOCATION is '-', the tarball "
-                   "will be dumped to the standard output.")
-@click.argument('element', required=False,
-                type=click.Path(readable=False))
-@click.argument('location', type=click.Path(), required=False)
-@click.pass_obj
-def checkout(app, element, location, force, deps, integrate, hardlinks, tar):
-    """Checkout a built artifact to the specified location
-    """
-    from ..element import Scope
-
-    if not element and not location:
-        click.echo("ERROR: LOCATION is not specified", err=True)
-        sys.exit(-1)
-
-    if element and not location:
-        # Nasty hack to get around click's optional args
-        location = element
-        element = None
-
-    if hardlinks and tar:
-        click.echo("ERROR: options --hardlinks and --tar conflict", err=True)
-        sys.exit(-1)
-
-    if deps == "run":
-        scope = Scope.RUN
-    elif deps == "build":
-        scope = Scope.BUILD
-    elif deps == "none":
-        scope = Scope.NONE
-
-    with app.initialized():
-        if not element:
-            element = app.context.guess_element()
-            if not element:
-                raise AppError('Missing argument "ELEMENT".')
-
-        app.stream.checkout(element,
-                            location=location,
-                            force=force,
-                            scope=scope,
-                            integrate=integrate,
-                            hardlinks=hardlinks,
-                            tar=tar)
-
-
-##################################################################
 #                        Source Command                          #
 ##################################################################
 @cli.group(short_help="Manipulate sources for an element")
@@ -1216,3 +1155,30 @@ def fetch(app, elements, deps, track_, except_, track_cross_junctions):
 def track(app, elements, deps, except_, cross_junctions):
     click.echo("This command is now obsolete. Use `bst source track` instead.", err=True)
     sys.exit(1)
+
+
+##################################################################
+#                        Checkout Command                        #
+##################################################################
+@cli.command(short_help="Checkout a built artifact", hidden=True)
+@click.option('--force', '-f', default=False, is_flag=True,
+              help="Allow files to be overwritten")
+@click.option('--deps', '-d', default='run',
+              type=click.Choice(['run', 'build', 'none']),
+              help='The dependencies to checkout (default: run)')
+@click.option('--integrate/--no-integrate', default=True, is_flag=True,
+              help="Whether to run integration commands")
+@click.option('--hardlinks', default=False, is_flag=True,
+              help="Checkout hardlinks instead of copies (handle with care)")
+@click.option('--tar', default=False, is_flag=True,
+              help="Create a tarball from the artifact contents instead "
+                   "of a file tree. If LOCATION is '-', the tarball "
+                   "will be dumped to the standard output.")
+@click.argument('element', required=False,
+                type=click.Path(readable=False))
+@click.argument('location', type=click.Path(), required=False)
+@click.pass_obj
+def checkout(app, element, location, force, deps, integrate, hardlinks, tar):
+    click.echo("This command is now obsolete. Use `bst artifact checkout` instead " +
+               "and use the --directory option to specify LOCATION", err=True)
+    sys.exit(1)
diff --git a/contrib/bst-docker-import b/contrib/bst-docker-import
index dfb16d7..c15eaf1 100755
--- a/contrib/bst-docker-import
+++ b/contrib/bst-docker-import
@@ -90,7 +90,7 @@ element="$1"
 checkout_tar="bst-checkout-$(basename "$element")-$RANDOM.tar"
 
 echo "INFO: Checking out $element ..." >&2
-$bst_cmd checkout --tar "$element" "$checkout_tar" || die "Failed to checkout $element"
+$bst_cmd artifact checkout "$element" --tar "$checkout_tar" || die "Failed to checkout $element"
 echo "INFO: Successfully checked out $element" >&2
 
 echo "INFO: Importing Docker image ..." >&2
diff --git a/tests/artifactcache/expiry.py b/tests/artifactcache/expiry.py
index 05cbe32..a8b05c3 100644
--- a/tests/artifactcache/expiry.py
+++ b/tests/artifactcache/expiry.py
@@ -132,7 +132,7 @@ def test_expiry_order(cli, datafiles, tmpdir):
     wait_for_cache_granularity()
 
     # Now extract dep.bst
-    res = cli.run(project=project, args=['checkout', 'dep.bst', checkout])
+    res = cli.run(project=project, args=['artifact', 'checkout', 'dep.bst', '--directory', checkout])
     res.assert_success()
 
     # Finally, build something that will cause the cache to overflow
@@ -379,7 +379,8 @@ def test_extract_expiry(cli, datafiles, tmpdir):
     assert cli.get_element_state(project, 'target.bst') == 'cached'
 
     # Force creating extract
-    res = cli.run(project=project, args=['checkout', 'target.bst', os.path.join(str(tmpdir), 'checkout')])
+    res = cli.run(project=project, args=['artifact', 'checkout', 'target.bst',
+                                         '--directory', os.path.join(str(tmpdir), 'checkout')])
     res.assert_success()
 
     extractdir = os.path.join(project, 'cache', 'artifacts', 'extract', 'test', 'target')
diff --git a/tests/completions/completions.py b/tests/completions/completions.py
index 372ed78..69ad81f 100644
--- a/tests/completions/completions.py
+++ b/tests/completions/completions.py
@@ -8,7 +8,6 @@ DATA_DIR = os.path.dirname(os.path.realpath(__file__))
 MAIN_COMMANDS = [
     'artifact ',
     'build ',
-    'checkout ',
     'help ',
     'init ',
     'pull ',
@@ -48,6 +47,11 @@ MAIN_OPTIONS = [
     "--version ",
 ]
 
+ARTIFACT_COMMANDS = [
+    'checkout ',
+    'log ',
+]
+
 SOURCE_COMMANDS = [
     'checkout ',
     'fetch ',
@@ -79,7 +83,7 @@ MIXED_ELEMENTS = PROJECT_ELEMENTS + INVALID_ELEMENTS
 
 
 def assert_completion(cli, cmd, word_idx, expected, cwd=None):
-    result = cli.run(cwd=cwd, env={
+    result = cli.run(project='.', cwd=cwd, env={
         '_BST_COMPLETION': 'complete',
         'COMP_WORDS': cmd,
         'COMP_CWORD': str(word_idx)
@@ -119,6 +123,7 @@ def assert_completion_failed(cli, cmd, word_idx, expected, cwd=None):
     ('bst ', 1, MAIN_COMMANDS),
     ('bst pu', 1, ['pull ', 'push ']),
     ('bst pul', 1, ['pull ']),
+    ('bst artifact ', 2, ARTIFACT_COMMANDS),
     ('bst source ', 2, SOURCE_COMMANDS),
     ('bst w ', 1, ['workspace ']),
     ('bst workspace ', 2, WORKSPACE_COMMANDS),
@@ -218,8 +223,9 @@ def test_option_directory(datafiles, cli, cmd, word_idx, expected, subdir):
      ['compose-all.bst ', 'compose-include-bin.bst ', 'compose-exclude-dev.bst '], 'files'),
 
     # Also try multi arguments together
-    ('project', 'bst --directory ../ checkout t ', 4, ['target.bst '], 'files'),
-    ('project', 'bst --directory ../ checkout target.bst ', 5, ['bin-files/', 'dev-files/'], 'files'),
+    ('project', 'bst --directory ../ artifact checkout t ', 5, ['target.bst '], 'files'),
+    ('project', 'bst --directory ../ artifact checkout --directory ', 6,
+     ['bin-files/', 'dev-files/'], 'files'),
 
     # When running in the project directory
     ('no-element-path', 'bst show ', 2,
@@ -242,8 +248,9 @@ def test_option_directory(datafiles, cli, cmd, word_idx, expected, subdir):
      ['compose-all.bst ', 'compose-include-bin.bst ', 'compose-exclude-dev.bst '], 'files'),
 
     # Also try multi arguments together
-    ('no-element-path', 'bst --directory ../ checkout t ', 4, ['target.bst '], 'files'),
-    ('no-element-path', 'bst --directory ../ checkout target.bst ', 5, ['bin-files/', 'dev-files/'], 'files'),
+    ('no-element-path', 'bst --directory ../ artifact checkout t ', 5, ['target.bst '], 'files'),
+    ('no-element-path', 'bst --directory ../ artifact checkout --directory ', 6,
+     ['bin-files/', 'dev-files/'], 'files'),
 
     # When element-path have sub-folders
     ('sub-folders', 'bst show base', 2, ['base/wanted.bst '], None),
@@ -276,6 +283,7 @@ def test_argument_element_invalid(datafiles, cli, project, cmd, word_idx, expect
     ('bst help p', 2, ['pull ', 'push ']),
     ('bst help p', 2, ['pull ', 'push ']),
     ('bst help source ', 3, SOURCE_COMMANDS),
+    ('bst help artifact ', 3, ARTIFACT_COMMANDS),
     ('bst help w', 2, ['workspace ']),
     ('bst help workspace ', 3, WORKSPACE_COMMANDS),
 ])
diff --git a/tests/examples/autotools.py b/tests/examples/autotools.py
index af440cc..30f5076 100644
--- a/tests/examples/autotools.py
+++ b/tests/examples/autotools.py
@@ -25,7 +25,7 @@ def test_autotools_build(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', 'hello.bst'])
     result.assert_success()
 
-    result = cli.run(project=project, args=['checkout', 'hello.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'hello.bst', '--directory', checkout])
     result.assert_success()
 
     assert_contains(checkout, ['/usr', '/usr/lib', '/usr/bin',
diff --git a/tests/examples/developing.py b/tests/examples/developing.py
index 3b09962..166fcf3 100644
--- a/tests/examples/developing.py
+++ b/tests/examples/developing.py
@@ -26,7 +26,7 @@ def test_autotools_build(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', 'hello.bst'])
     result.assert_success()
 
-    result = cli.run(project=project, args=['checkout', 'hello.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'hello.bst', '--directory', checkout])
     result.assert_success()
 
     assert_contains(checkout, ['/usr', '/usr/lib', '/usr/bin',
diff --git a/tests/examples/first-project.py b/tests/examples/first-project.py
index dac1814..821d2c1 100644
--- a/tests/examples/first-project.py
+++ b/tests/examples/first-project.py
@@ -23,7 +23,7 @@ def test_first_project_build_checkout(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', 'hello.bst'])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', 'hello.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'hello.bst', '--directory', checkout])
     assert result.exit_code == 0
 
     assert_contains(checkout, ['/hello.world'])
diff --git a/tests/examples/flatpak-autotools.py b/tests/examples/flatpak-autotools.py
index d63771e..4153a95 100644
--- a/tests/examples/flatpak-autotools.py
+++ b/tests/examples/flatpak-autotools.py
@@ -44,7 +44,7 @@ def test_autotools_build(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', 'hello.bst'])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', 'hello.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'hello.bst', '--directory', checkout])
     assert result.exit_code == 0
 
     assert_contains(checkout, ['/usr', '/usr/lib', '/usr/bin',
diff --git a/tests/frontend/buildcheckout.py b/tests/frontend/buildcheckout.py
index 287fb60..8c7e22a 100644
--- a/tests/frontend/buildcheckout.py
+++ b/tests/frontend/buildcheckout.py
@@ -44,10 +44,10 @@ def test_build_checkout(datafiles, cli, strict, hardlinks):
     assert not os.listdir(builddir)
 
     # Prepare checkout args
-    checkout_args = strict_args(['checkout'], strict)
+    checkout_args = strict_args(['artifact', 'checkout'], strict)
     if hardlinks == "hardlinks":
         checkout_args += ['--hardlinks']
-    checkout_args += ['target.bst', checkout]
+    checkout_args += ['target.bst', '--directory', checkout]
 
     # Now check it out
     result = cli.run(project=project, args=checkout_args)
@@ -138,7 +138,8 @@ def test_build_checkout_deps(datafiles, cli, deps):
     assert not os.listdir(builddir)
 
     # Now check it out
-    result = cli.run(project=project, args=['checkout', element_name, '--deps', deps, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name,
+                                            '--deps', deps, '--directory', checkout])
     result.assert_success()
 
     # Verify output of this element
@@ -169,7 +170,7 @@ def test_build_checkout_unbuilt(datafiles, cli):
     checkout = os.path.join(cli.directory, 'checkout')
 
     # Check that checking out an unbuilt element fails nicely
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkout])
     result.assert_main_error(ErrorDomain.STREAM, "uncached-checkout-attempt")
 
 
@@ -185,7 +186,7 @@ def test_build_checkout_tarball(datafiles, cli):
     assert os.path.isdir(builddir)
     assert not os.listdir(builddir)
 
-    checkout_args = ['checkout', '--tar', 'target.bst', checkout]
+    checkout_args = ['artifact', 'checkout', '--tar', checkout, 'target.bst']
 
     result = cli.run(project=project, args=checkout_args)
     result.assert_success()
@@ -207,7 +208,7 @@ def test_build_checkout_tarball_stdout(datafiles, cli):
     assert os.path.isdir(builddir)
     assert not os.listdir(builddir)
 
-    checkout_args = ['checkout', '--tar', 'target.bst', '-']
+    checkout_args = ['artifact', 'checkout', '--tar', '-', 'target.bst']
 
     result = cli.run(project=project, args=checkout_args, binary_capture=True)
     result.assert_success()
@@ -233,13 +234,13 @@ def test_build_checkout_tarball_is_deterministic(datafiles, cli):
     assert os.path.isdir(builddir)
     assert not os.listdir(builddir)
 
-    checkout_args = ['checkout', '--force', '--tar', 'target.bst']
+    checkout_args = ['artifact', 'checkout', '--force', 'target.bst']
 
-    checkout_args1 = checkout_args + [tarball1]
+    checkout_args1 = checkout_args + ['--tar', tarball1]
     result = cli.run(project=project, args=checkout_args1)
     result.assert_success()
 
-    checkout_args2 = checkout_args + [tarball2]
+    checkout_args2 = checkout_args + ['--tar', tarball2]
     result = cli.run(project=project, args=checkout_args2)
     result.assert_success()
 
@@ -276,10 +277,10 @@ def test_build_checkout_nonempty(datafiles, cli, hardlinks):
         f.write("Hello")
 
     # Prepare checkout args
-    checkout_args = ['checkout']
+    checkout_args = ['artifact', 'checkout']
     if hardlinks == "hardlinks":
         checkout_args += ['--hardlinks']
-    checkout_args += ['target.bst', checkout]
+    checkout_args += ['target.bst', '--directory', checkout]
 
     # Now check it out
     result = cli.run(project=project, args=checkout_args)
@@ -308,10 +309,10 @@ def test_build_checkout_force(datafiles, cli, hardlinks):
         f.write("Hello")
 
     # Prepare checkout args
-    checkout_args = ['checkout', '--force']
+    checkout_args = ['artifact', 'checkout', '--force']
     if hardlinks == "hardlinks":
         checkout_args += ['--hardlinks']
-    checkout_args += ['target.bst', checkout]
+    checkout_args += ['target.bst', '--directory', checkout]
 
     # Now check it out
     result = cli.run(project=project, args=checkout_args)
@@ -345,7 +346,7 @@ def test_build_checkout_force_tarball(datafiles, cli):
     with open(tarball, "w") as f:
         f.write("Hello")
 
-    checkout_args = ['checkout', '--force', '--tar', 'target.bst', tarball]
+    checkout_args = ['artifact', 'checkout', '--force', '--tar', tarball, 'target.bst']
 
     result = cli.run(project=project, args=checkout_args)
     result.assert_success()
@@ -393,7 +394,7 @@ def test_fetch_build_checkout(cli, tmpdir, datafiles, strict, kind):
 
     # Now check it out
     result = cli.run(project=project, args=strict_args([
-        'checkout', element_name, checkout
+        'artifact', 'checkout', element_name, '--directory', checkout
     ], strict))
     result.assert_success()
 
@@ -537,7 +538,7 @@ def test_build_checkout_junction(cli, tmpdir, datafiles):
 
     # Now check it out
     result = cli.run(project=project, args=[
-        'checkout', 'junction-dep.bst', checkout
+        'artifact', 'checkout', 'junction-dep.bst', '--directory', checkout
     ])
     result.assert_success()
 
@@ -600,7 +601,7 @@ def test_build_checkout_workspaced_junction(cli, tmpdir, datafiles):
 
     # Now check it out
     result = cli.run(project=project, args=[
-        'checkout', 'junction-dep.bst', checkout
+        'artifact', 'checkout', 'junction-dep.bst', '--directory', checkout
     ])
     result.assert_success()
 
@@ -624,7 +625,8 @@ def test_build_checkout_cross_junction(datafiles, cli, tmpdir):
     result = cli.run(project=project, args=['build', 'junction.bst:import-etc.bst'])
     result.assert_success()
 
-    result = cli.run(project=project, args=['checkout', 'junction.bst:import-etc.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'junction.bst:import-etc.bst',
+                                            '--directory', checkout])
     result.assert_success()
 
     filename = os.path.join(checkout, 'etc', 'animal.conf')
diff --git a/tests/frontend/compose_splits.py b/tests/frontend/compose_splits.py
index b5a2e76..97558b6 100644
--- a/tests/frontend/compose_splits.py
+++ b/tests/frontend/compose_splits.py
@@ -24,7 +24,7 @@ def test_compose_splits(datafiles, cli, target):
 
     # Now check it out
     result = cli.run(project=project, args=[
-        'checkout', target, checkout
+        'artifact', 'checkout', target, '--directory', checkout
     ])
     result.assert_success()
 
diff --git a/tests/frontend/mirror.py b/tests/frontend/mirror.py
index ccfe2ca..3cadd15 100644
--- a/tests/frontend/mirror.py
+++ b/tests/frontend/mirror.py
@@ -856,7 +856,7 @@ def test_mirror_fallback_git_only_submodules(cli, tmpdir, datafiles):
     result.assert_success()
 
     checkout = os.path.join(str(tmpdir), 'checkout')
-    result = cli.run(project=project_dir, args=['checkout', element_name, checkout])
+    result = cli.run(project=project_dir, args=['artifact', 'checkout', element_name, '--directory', checkout])
     result.assert_success()
 
     assert os.path.exists(os.path.join(checkout, 'bin', 'bin', 'hello'))
@@ -952,7 +952,7 @@ def test_mirror_fallback_git_with_submodules(cli, tmpdir, datafiles):
     result.assert_success()
 
     checkout = os.path.join(str(tmpdir), 'checkout')
-    result = cli.run(project=project_dir, args=['checkout', element_name, checkout])
+    result = cli.run(project=project_dir, args=['artifact', 'checkout', element_name, '--directory', checkout])
     result.assert_success()
 
     assert os.path.exists(os.path.join(checkout, 'bin', 'bin', 'hello'))
diff --git a/tests/frontend/workspace.py b/tests/frontend/workspace.py
index 00c0bd8..7c5f37d 100644
--- a/tests/frontend/workspace.py
+++ b/tests/frontend/workspace.py
@@ -655,7 +655,7 @@ def test_build(cli, tmpdir_factory, datafiles, kind, strict, from_workspace, gue
 
     # Checkout the result
     result = cli.run(project=project,
-                     args=args_dir + ['checkout'] + args_elm + [checkout])
+                     args=args_dir + ['artifact', 'checkout', '--directory', checkout] + args_elm)
     result.assert_success()
 
     # Check that the pony.conf from the modified workspace exists
@@ -757,7 +757,7 @@ def test_detect_modifications(cli, tmpdir, datafiles, modification, strict):
 
     # Checkout the result
     result = cli.run(project=project, args=[
-        'checkout', element_name, checkout
+        'artifact', 'checkout', element_name, '--directory', checkout
     ])
     result.assert_success()
 
@@ -1033,7 +1033,7 @@ def test_cache_key_workspace_in_dependencies(cli, tmpdir, datafiles, strict):
 
     # Checkout the result
     result = cli.run(project=project, args=[
-        'checkout', back_dep_element_name, checkout
+        'artifact', 'checkout', back_dep_element_name, '--directory', checkout
     ])
     result.assert_success()
 
diff --git a/tests/integration/autotools.py b/tests/integration/autotools.py
index 1dc7eea..c7070ad 100644
--- a/tests/integration/autotools.py
+++ b/tests/integration/autotools.py
@@ -28,7 +28,7 @@ def test_autotools_build(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     assert_contains(checkout, ['/usr', '/usr/lib', '/usr/bin',
@@ -51,7 +51,7 @@ def test_autotools_confroot_build(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     assert_contains(checkout, ['/usr', '/usr/lib', '/usr/bin',
diff --git a/tests/integration/cachedfail.py b/tests/integration/cachedfail.py
index d902bac..2c79e6a 100644
--- a/tests/integration/cachedfail.py
+++ b/tests/integration/cachedfail.py
@@ -54,7 +54,7 @@ def test_build_checkout_cached_fail(cli, tmpdir, datafiles):
 
     # Now check it out
     result = cli.run(project=project, args=[
-        'checkout', 'element.bst', checkout
+        'artifact', 'checkout', 'element.bst', '--directory', checkout
     ])
     result.assert_success()
 
diff --git a/tests/integration/cmake.py b/tests/integration/cmake.py
index 235dee3..e82ec65 100644
--- a/tests/integration/cmake.py
+++ b/tests/integration/cmake.py
@@ -25,7 +25,7 @@ def test_cmake_build(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     assert_contains(checkout, ['/usr', '/usr/bin', '/usr/bin/hello'])
@@ -41,7 +41,7 @@ def test_cmake_confroot_build(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     assert_contains(checkout, ['/usr', '/usr/bin', '/usr/bin/hello'])
diff --git a/tests/integration/compose-symlinks.py b/tests/integration/compose-symlinks.py
index bf279fa..2599d8b 100644
--- a/tests/integration/compose-symlinks.py
+++ b/tests/integration/compose-symlinks.py
@@ -36,7 +36,8 @@ def test_compose_symlinks(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', 'compose-symlinks/compose.bst'])
     result.assert_success()
 
-    result = cli.run(project=project, args=['checkout', 'compose-symlinks/compose.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'compose-symlinks/compose.bst',
+                                            '--directory', checkout])
     result.assert_success()
 
     assert set(walk_dir(checkout)) == set(['/sbin', '/usr', '/usr/sbin',
diff --git a/tests/integration/compose.py b/tests/integration/compose.py
index 36e1da7..6c18ea9 100644
--- a/tests/integration/compose.py
+++ b/tests/integration/compose.py
@@ -97,7 +97,7 @@ def test_compose_include(cli, tmpdir, datafiles, include_domains,
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     assert set(walk_dir(checkout)) == set(expected)
diff --git a/tests/integration/import.py b/tests/integration/import.py
index 6925a0a..8426405 100644
--- a/tests/integration/import.py
+++ b/tests/integration/import.py
@@ -53,7 +53,7 @@ def test_import(cli, tmpdir, datafiles, source, target, path, expected):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    cli.run(project=project, args=['checkout', element_name, checkout])
+    cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert res.exit_code == 0
 
     assert set(walk_dir(checkout)) == set(expected)
diff --git a/tests/integration/make.py b/tests/integration/make.py
index 3b8d1e4..806b874 100644
--- a/tests/integration/make.py
+++ b/tests/integration/make.py
@@ -28,7 +28,7 @@ def test_make_build(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     assert_contains(checkout, ['/usr', '/usr/bin',
diff --git a/tests/integration/manual.py b/tests/integration/manual.py
index c6a905d..2a3dc3b 100644
--- a/tests/integration/manual.py
+++ b/tests/integration/manual.py
@@ -52,7 +52,7 @@ def test_manual_element(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    cli.run(project=project, args=['checkout', element_name, checkout])
+    cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout, 'test')) as f:
@@ -86,7 +86,7 @@ def test_manual_element_environment(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    cli.run(project=project, args=['checkout', element_name, checkout])
+    cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout, 'test')) as f:
@@ -119,7 +119,7 @@ def test_manual_element_noparallel(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    cli.run(project=project, args=['checkout', element_name, checkout])
+    cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout, 'test')) as f:
diff --git a/tests/integration/pip_element.py b/tests/integration/pip_element.py
index 13ada09..523bc20 100644
--- a/tests/integration/pip_element.py
+++ b/tests/integration/pip_element.py
@@ -47,7 +47,7 @@ def test_pip_build(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     assert_contains(checkout, ['/usr', '/usr/lib', '/usr/bin',
diff --git a/tests/integration/pip_source.py b/tests/integration/pip_source.py
index d6cbb98..3948fc4 100644
--- a/tests/integration/pip_source.py
+++ b/tests/integration/pip_source.py
@@ -58,7 +58,7 @@ def test_pip_source_import(cli, tmpdir, datafiles, setup_pypi_repo):
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     assert_contains(checkout, ['/.bst_pip_downloads',
diff --git a/tests/integration/script.py b/tests/integration/script.py
index 6203c32..ff1b8aa 100644
--- a/tests/integration/script.py
+++ b/tests/integration/script.py
@@ -49,7 +49,7 @@ def test_script(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    res = cli.run(project=project, args=['checkout', element_name, checkout])
+    res = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout, 'test')) as f:
@@ -81,7 +81,7 @@ def test_script_root(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    res = cli.run(project=project, args=['checkout', element_name, checkout])
+    res = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout, 'test')) as f:
@@ -135,7 +135,7 @@ def test_script_cwd(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    res = cli.run(project=project, args=['checkout', element_name, checkout])
+    res = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout, 'test')) as f:
@@ -154,7 +154,7 @@ def test_script_layout(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    cli.run(project=project, args=['checkout', element_name, checkout])
+    cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout, 'test')) as f:
@@ -175,8 +175,8 @@ def test_regression_cache_corruption(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', canary_element_name])
     assert res.exit_code == 0
 
-    res = cli.run(project=project, args=['checkout', canary_element_name,
-                                         checkout_original])
+    res = cli.run(project=project, args=['artifact', 'checkout', canary_element_name,
+                                         '--directory', checkout_original])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout_original, 'canary')) as f:
@@ -185,8 +185,8 @@ def test_regression_cache_corruption(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    res = cli.run(project=project, args=['checkout', canary_element_name,
-                                         checkout_after])
+    res = cli.run(project=project, args=['artifact', 'checkout', canary_element_name,
+                                         '--directory', checkout_after])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout_after, 'canary')) as f:
@@ -215,8 +215,8 @@ def test_regression_cache_corruption_2(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', canary_element_name])
     assert res.exit_code == 0
 
-    res = cli.run(project=project, args=['checkout', canary_element_name,
-                                         checkout_original])
+    res = cli.run(project=project, args=['artifact', 'checkout', canary_element_name,
+                                         '--directory', checkout_original])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout_original, 'canary')) as f:
@@ -225,8 +225,8 @@ def test_regression_cache_corruption_2(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    res = cli.run(project=project, args=['checkout', canary_element_name,
-                                         checkout_after])
+    res = cli.run(project=project, args=['artifact', 'checkout', canary_element_name,
+                                         '--directory', checkout_after])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout_after, 'canary')) as f:
diff --git a/tests/integration/shell.py b/tests/integration/shell.py
index 983cab6..f77e101 100644
--- a/tests/integration/shell.py
+++ b/tests/integration/shell.py
@@ -328,7 +328,7 @@ def test_sysroot(cli, tmpdir, datafiles):
     # Build and check out a sysroot
     res = cli.run(project=project, args=['build', base_element])
     res.assert_success()
-    res = cli.run(project=project, args=['checkout', base_element, checkout_dir])
+    res = cli.run(project=project, args=['artifact', 'checkout', base_element, '--directory', checkout_dir])
     res.assert_success()
 
     # Mutate the sysroot
diff --git a/tests/integration/source-determinism.py b/tests/integration/source-determinism.py
index fa12593..98fe88d 100644
--- a/tests/integration/source-determinism.py
+++ b/tests/integration/source-determinism.py
@@ -87,7 +87,7 @@ def test_deterministic_source_umask(cli, tmpdir, datafiles, kind, integration_ca
             result = cli.run(project=project, args=['build', element_name])
             result.assert_success()
 
-            result = cli.run(project=project, args=['checkout', element_name, checkoutdir])
+            result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkoutdir])
             result.assert_success()
 
             with open(os.path.join(checkoutdir, 'ls-l'), 'r') as f:
@@ -150,7 +150,7 @@ def test_deterministic_source_local(cli, tmpdir, datafiles, integration_cache):
             result = cli.run(project=project, args=['build', element_name])
             result.assert_success()
 
-            result = cli.run(project=project, args=['checkout', element_name, checkoutdir])
+            result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkoutdir])
             result.assert_success()
 
             with open(os.path.join(checkoutdir, 'ls-l'), 'r') as f:
diff --git a/tests/integration/stack.py b/tests/integration/stack.py
index e48e00c..93b3880 100644
--- a/tests/integration/stack.py
+++ b/tests/integration/stack.py
@@ -26,7 +26,7 @@ def test_stack(cli, tmpdir, datafiles):
     res = cli.run(project=project, args=['build', element_name])
     assert res.exit_code == 0
 
-    cli.run(project=project, args=['checkout', element_name, checkout])
+    cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert res.exit_code == 0
 
     with open(os.path.join(checkout, 'hi')) as f:
diff --git a/tests/integration/symlinks.py b/tests/integration/symlinks.py
index 7b8703c..51bf6b1 100644
--- a/tests/integration/symlinks.py
+++ b/tests/integration/symlinks.py
@@ -28,7 +28,7 @@ def test_absolute_symlinks_made_relative(cli, tmpdir, datafiles):
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     symlink = os.path.join(checkout, 'opt', 'orgname')
@@ -52,7 +52,7 @@ def test_allow_overlaps_inside_symlink_with_dangling_target(cli, tmpdir, datafil
     result = cli.run(project=project, args=['build', element_name])
     assert result.exit_code == 0
 
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == 0
 
     # See the dangling-symlink*.bst elements for details on what we are testing.
@@ -73,6 +73,6 @@ def test_detect_symlink_overlaps_pointing_outside_sandbox(cli, tmpdir, datafiles
     # ...but when we compose them together, the overlaps create paths that
     # point outside the sandbox which BuildStream needs to detect before it
     # tries to actually write there.
-    result = cli.run(project=project, args=['checkout', element_name, checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', element_name, '--directory', checkout])
     assert result.exit_code == -1
     assert "Destination path resolves to a path outside of the staging area" in result.stderr
diff --git a/tests/loader/junctions.py b/tests/loader/junctions.py
index d97c9f7..90608d0 100644
--- a/tests/loader/junctions.py
+++ b/tests/loader/junctions.py
@@ -39,7 +39,7 @@ def test_simple_build(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the checkout contains the expected files from both projects
@@ -70,7 +70,7 @@ def test_nested_simple(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the checkout contains the expected files from all subprojects
@@ -94,7 +94,7 @@ def test_nested_double(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the checkout contains the expected files from all subprojects
@@ -167,7 +167,7 @@ def test_options_default(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     assert(os.path.exists(os.path.join(checkoutdir, 'pony.txt')))
@@ -184,7 +184,7 @@ def test_options(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     assert(not os.path.exists(os.path.join(checkoutdir, 'pony.txt')))
@@ -201,7 +201,7 @@ def test_options_inherit(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     assert(not os.path.exists(os.path.join(checkoutdir, 'pony.txt')))
@@ -262,7 +262,7 @@ def test_git_build(cli, tmpdir, datafiles):
     # Build (with implicit fetch of subproject), checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the checkout contains the expected files from both projects
@@ -303,7 +303,7 @@ def test_build_git_cross_junction_names(cli, tmpdir, datafiles):
     # Build (with implicit fetch of subproject), checkout
     result = cli.run(project=project, args=['build', 'base.bst:target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'base.bst:target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'base.bst:target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the checkout contains the expected files from both projects
diff --git a/tests/plugins/filter.py b/tests/plugins/filter.py
index 31b23c1..6c0a3ee 100644
--- a/tests/plugins/filter.py
+++ b/tests/plugins/filter.py
@@ -18,7 +18,7 @@ def test_filter_include(datafiles, cli, tmpdir):
     result.assert_success()
 
     checkout = os.path.join(tmpdir.dirname, tmpdir.basename, 'checkout')
-    result = cli.run(project=project, args=['checkout', 'output-include.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'output-include.bst', '--directory', checkout])
     result.assert_success()
     assert os.path.exists(os.path.join(checkout, "foo"))
     assert not os.path.exists(os.path.join(checkout, "bar"))
@@ -31,7 +31,8 @@ def test_filter_include_dynamic(datafiles, cli, tmpdir):
     result.assert_success()
 
     checkout = os.path.join(tmpdir.dirname, tmpdir.basename, 'checkout')
-    result = cli.run(project=project, args=['checkout', 'output-dynamic-include.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'output-dynamic-include.bst',
+                                            '--directory', checkout])
     result.assert_success()
     assert os.path.exists(os.path.join(checkout, "foo"))
     assert not os.path.exists(os.path.join(checkout, "bar"))
@@ -44,7 +45,7 @@ def test_filter_exclude(datafiles, cli, tmpdir):
     result.assert_success()
 
     checkout = os.path.join(tmpdir.dirname, tmpdir.basename, 'checkout')
-    result = cli.run(project=project, args=['checkout', 'output-exclude.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'output-exclude.bst', '--directory', checkout])
     result.assert_success()
     assert not os.path.exists(os.path.join(checkout, "foo"))
     assert os.path.exists(os.path.join(checkout, "bar"))
@@ -57,7 +58,7 @@ def test_filter_orphans(datafiles, cli, tmpdir):
     result.assert_success()
 
     checkout = os.path.join(tmpdir.dirname, tmpdir.basename, 'checkout')
-    result = cli.run(project=project, args=['checkout', 'output-orphans.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'output-orphans.bst', '--directory', checkout])
     result.assert_success()
     assert os.path.exists(os.path.join(checkout, "baz"))
 
@@ -137,7 +138,7 @@ def test_filter_workspace_build(datafiles, cli, tmpdir):
     result = cli.run(project=project, args=['build', 'output-orphans.bst'])
     result.assert_success()
     checkout_dir = os.path.join(tempdir, "checkout")
-    result = cli.run(project=project, args=['checkout', 'output-orphans.bst', checkout_dir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'output-orphans.bst', '--directory', checkout_dir])
     result.assert_success()
     assert os.path.exists(os.path.join(checkout_dir, "quux"))
 
@@ -157,7 +158,7 @@ def test_filter_workspace_close(datafiles, cli, tmpdir):
     result = cli.run(project=project, args=['build', 'output-orphans.bst'])
     result.assert_success()
     checkout_dir = os.path.join(tempdir, "checkout")
-    result = cli.run(project=project, args=['checkout', 'output-orphans.bst', checkout_dir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'output-orphans.bst', '--directory', checkout_dir])
     result.assert_success()
     assert not os.path.exists(os.path.join(checkout_dir, "quux"))
 
@@ -177,7 +178,7 @@ def test_filter_workspace_reset(datafiles, cli, tmpdir):
     result = cli.run(project=project, args=['build', 'output-orphans.bst'])
     result.assert_success()
     checkout_dir = os.path.join(tempdir, "checkout")
-    result = cli.run(project=project, args=['checkout', 'output-orphans.bst', checkout_dir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'output-orphans.bst', '--directory', checkout_dir])
     result.assert_success()
     assert not os.path.exists(os.path.join(checkout_dir, "quux"))
 
diff --git a/tests/sources/bzr.py b/tests/sources/bzr.py
index 9895180..a56005f 100644
--- a/tests/sources/bzr.py
+++ b/tests/sources/bzr.py
@@ -36,7 +36,7 @@ def test_fetch_checkout(cli, tmpdir, datafiles):
     assert result.exit_code == 0
     result = cli.run(project=project, args=['build', 'target.bst'])
     assert result.exit_code == 0
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     assert result.exit_code == 0
 
     # Assert we checked out the file as it was commited
diff --git a/tests/sources/deb.py b/tests/sources/deb.py
index b925fc9..b40358e 100644
--- a/tests/sources/deb.py
+++ b/tests/sources/deb.py
@@ -114,7 +114,7 @@ def test_stage_default_basedir(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the content of the first directory is checked out (base-dir: '')
@@ -142,7 +142,7 @@ def test_stage_no_basedir(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the full content of the tarball is checked out (base-dir: '')
@@ -170,7 +170,7 @@ def test_stage_explicit_basedir(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the content of the first directory is checked out (base-dir: '')
diff --git a/tests/sources/git.py b/tests/sources/git.py
index f9aa62b..a584e82 100644
--- a/tests/sources/git.py
+++ b/tests/sources/git.py
@@ -95,7 +95,7 @@ def test_submodule_fetch_checkout(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Assert we checked out both files at their expected location
@@ -134,7 +134,7 @@ def test_submodule_fetch_source_enable_explicit(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Assert we checked out both files at their expected location
@@ -173,7 +173,7 @@ def test_submodule_fetch_source_disable(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Assert we checked out both files at their expected location
@@ -212,7 +212,7 @@ def test_submodule_fetch_submodule_does_override(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Assert we checked out both files at their expected location
@@ -256,7 +256,7 @@ def test_submodule_fetch_submodule_individual_checkout(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Assert we checked out files at their expected location
@@ -301,7 +301,7 @@ def test_submodule_fetch_submodule_individual_checkout_explicit(cli, tmpdir, dat
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Assert we checked out files at their expected location
@@ -341,7 +341,7 @@ def test_submodule_fetch_project_override(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Assert we checked out both files at their expected location
@@ -858,7 +858,7 @@ def test_git_describe(cli, tmpdir, datafiles, ref_storage, tag_type):
 
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkout])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkout])
     result.assert_success()
 
     if tag_type == 'annotated':
diff --git a/tests/sources/local.py b/tests/sources/local.py
index de12473..4a0851d 100644
--- a/tests/sources/local.py
+++ b/tests/sources/local.py
@@ -77,7 +77,7 @@ def test_stage_file(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the checkout contains the expected file
@@ -92,7 +92,7 @@ def test_stage_directory(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the checkout contains the expected file and directory and other file
@@ -117,7 +117,7 @@ def test_stage_symlink(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the checkout contains the expected file and directory and other file
diff --git a/tests/sources/patch.py b/tests/sources/patch.py
index 39d4336..51ae690 100644
--- a/tests/sources/patch.py
+++ b/tests/sources/patch.py
@@ -75,7 +75,7 @@ def test_stage_and_patch(cli, tmpdir, datafiles):
     # Build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Test the file.txt was patched and changed
@@ -113,7 +113,7 @@ def test_stage_separate_patch_dir(cli, tmpdir, datafiles):
     # Track, fetch, build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Test the file.txt was patched and changed
@@ -129,7 +129,7 @@ def test_stage_multiple_patches(cli, tmpdir, datafiles):
     # Track, fetch, build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Test the file.txt was patched and changed
@@ -145,7 +145,7 @@ def test_patch_strip_level(cli, tmpdir, datafiles):
     # Track, fetch, build, checkout
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Test the file.txt was patched and changed
diff --git a/tests/sources/previous_source_access.py b/tests/sources/previous_source_access.py
index f090ac8..26640d1 100644
--- a/tests/sources/previous_source_access.py
+++ b/tests/sources/previous_source_access.py
@@ -35,7 +35,7 @@ def test_custom_transform_source(cli, tmpdir, datafiles):
     ])
     destpath = os.path.join(cli.directory, 'checkout')
     result = cli.run(project=project, args=[
-        'checkout', 'target.bst', destpath
+        'artifact', 'checkout', 'target.bst', '--directory', destpath
     ])
     result.assert_success()
     # Assert that files from both sources exist, and that they have
diff --git a/tests/sources/remote.py b/tests/sources/remote.py
index 6062c9c..b8cf049 100644
--- a/tests/sources/remote.py
+++ b/tests/sources/remote.py
@@ -89,7 +89,7 @@ def test_simple_file_build(cli, tmpdir, datafiles):
     result.assert_success()
 
     result = cli.run(project=project, args=[
-        'checkout', 'target.bst', checkoutdir
+        'artifact', 'checkout', 'target.bst', '--directory', checkoutdir
     ])
     result.assert_success()
     # Note that the url of the file in target.bst is actually /dir/file
@@ -122,7 +122,7 @@ def test_simple_file_custom_name_build(cli, tmpdir, datafiles):
     result.assert_success()
 
     result = cli.run(project=project, args=[
-        'checkout', 'target.bst', checkoutdir
+        'artifact', 'checkout', 'target.bst', '--directory', checkoutdir
     ])
     result.assert_success()
     assert(not os.path.exists(os.path.join(checkoutdir, 'file')))
@@ -169,7 +169,7 @@ def test_executable(cli, tmpdir, datafiles):
     ])
 
     result = cli.run(project=project, args=[
-        'checkout', 'target-custom-executable.bst', checkoutdir
+        'artifact', 'checkout', 'target-custom-executable.bst', '--directory', checkoutdir
     ])
     mode = os.stat(os.path.join(checkoutdir, 'some-custom-file')).st_mode
     assert (mode & stat.S_IEXEC)
@@ -202,7 +202,7 @@ def test_use_netrc(cli, datafiles, server_type, tmpdir):
         result.assert_success()
         result = cli.run(project=project, args=['build', 'target.bst'])
         result.assert_success()
-        result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+        result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
         result.assert_success()
 
         checkout_file = os.path.join(checkoutdir, 'file')
diff --git a/tests/sources/tar.py b/tests/sources/tar.py
index 35eb837..406d670 100644
--- a/tests/sources/tar.py
+++ b/tests/sources/tar.py
@@ -139,7 +139,7 @@ def test_stage_default_basedir(cli, tmpdir, datafiles, srcdir):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the content of the first directory is checked out (base-dir: '*')
@@ -168,7 +168,7 @@ def test_stage_no_basedir(cli, tmpdir, datafiles, srcdir):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the full content of the tarball is checked out (base-dir: '')
@@ -197,7 +197,7 @@ def test_stage_explicit_basedir(cli, tmpdir, datafiles, srcdir):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the content of the first directory is checked out (base-dir: '*')
@@ -233,7 +233,7 @@ def test_stage_contains_links(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the content of the first directory is checked out (base-dir: '*')
@@ -262,7 +262,7 @@ def test_stage_default_basedir_lzip(cli, tmpdir, datafiles, srcdir):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target-lz.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target-lz.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target-lz.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the content of the first directory is checked out (base-dir: '*')
@@ -348,7 +348,7 @@ def test_use_netrc(cli, datafiles, server_type, tmpdir):
         result.assert_success()
         result = cli.run(project=project, args=['build', 'target.bst'])
         result.assert_success()
-        result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+        result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
         result.assert_success()
 
         original_dir = os.path.join(str(datafiles), 'content', 'a')
diff --git a/tests/sources/zip.py b/tests/sources/zip.py
index 36e0334..009862e 100644
--- a/tests/sources/zip.py
+++ b/tests/sources/zip.py
@@ -123,7 +123,7 @@ def test_stage_default_basedir(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the content of the first directory is checked out (base-dir: '*')
@@ -151,7 +151,7 @@ def test_stage_no_basedir(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the full content of the tarball is checked out (base-dir: '')
@@ -179,7 +179,7 @@ def test_stage_explicit_basedir(cli, tmpdir, datafiles):
     result.assert_success()
     result = cli.run(project=project, args=['build', 'target.bst'])
     result.assert_success()
-    result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+    result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
     result.assert_success()
 
     # Check that the content of the first directory is checked out (base-dir: '*')
@@ -221,7 +221,7 @@ def test_use_netrc(cli, datafiles, server_type, tmpdir):
         result.assert_success()
         result = cli.run(project=project, args=['build', 'target.bst'])
         result.assert_success()
-        result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
+        result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
         result.assert_success()
 
         original_dir = os.path.join(str(datafiles), 'content', 'a')


[buildstream] 03/06: cli: Add artifact checkout subcommand

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

not-in-ldap pushed a commit to branch jennis/deprecate_bst_checkout
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 82f39a2e85fbe90a97f340a419c2f005e33a3159
Author: Richard Maw <ri...@codethink.co.uk>
AuthorDate: Wed Dec 12 18:32:39 2018 +0000

    cli: Add artifact checkout subcommand
    
    This implements a superset of the features of `bst checkout`,
    but uses a --directory option rather than a location argument,
    and --tar takes a location instead of being a flag.
    
    If multiple element targets or any artifact refs are passed
    then it does not support creating a tarball, running integration commands
    or including dependencies of the targets.
---
 buildstream/_frontend/cli.py | 81 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)

diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py
index 60a2445..759db6b 100644
--- a/buildstream/_frontend/cli.py
+++ b/buildstream/_frontend/cli.py
@@ -1086,6 +1086,87 @@ def artifact_log(app, artifacts):
                     click.echo_via_pager(data)
 
 
+#####################################################################
+#                     Artifact Checkout Command                     #
+#####################################################################
+@artifact.command(name='checkout', short_help="Checkout contents of an artifact")
+@click.option('--force', '-f', default=False, is_flag=True,
+              help="Allow files to be overwritten")
+@click.option('--deps', '-d', default=None,
+              type=click.Choice(['run', 'build', 'none']),
+              help='The dependencies to checkout (default: run)')
+@click.option('--integrate/--no-integrate', default=None, is_flag=True,
+              help="Whether to run integration commands")
+@click.option('--hardlinks', default=False, is_flag=True,
+              help="Checkout hardlinks instead of copying if possible")
+@click.option('--tar', default=None, metavar='LOCATION',
+              type=click.Path(),
+              help="Create a tarball from the artifact contents instead "
+                   "of a file tree. If LOCATION is '-', the tarball "
+                   "will be dumped to the standard output.")
+@click.option('--directory', default=None,
+              type=click.Path(file_okay=False),
+              help="The directory to write the tarball to")
+@click.argument('artifacts', type=click.Path(), nargs=-1)
+@click.pass_obj
+def artifact_checkout(app, force, deps, integrate, hardlinks, tar, directory, artifacts):
+    """Checkout contents of an artifact"""
+    from ..element import Scope
+
+    if hardlinks and tar is not None:
+        click.echo("ERROR: options --hardlinks and --tar conflict", err=True)
+        sys.exit(-1)
+
+    if tar is None and directory is None:
+        click.echo("ERROR: One of --directory or --tar must be provided", err=True)
+        sys.exit(-1)
+
+    if tar is not None and directory is not None:
+        click.echo("ERROR: options --directory and --tar conflict", err=True)
+        sys.exit(-1)
+
+    if tar is not None:
+        location = tar
+        tar = True
+    else:
+        location = os.getcwd() if directory is None else directory
+        tar = False
+
+    with app.initialized():
+        cache = app.context.artifactcache
+
+        elements, artifacts = _classify_artifacts(artifacts, cache.cas,
+                                                  app.project.directory)
+
+        if not elements and not artifacts:
+            element = app.context.guess_element()
+            if element is not None:
+                elements = [element]
+
+        if (artifacts or len(elements) > 1) and (integrate is not None or deps is not None or tar):
+            raise AppError("--integrate, --deps and --tar may not be used with artifact refs or multiple elements")
+
+        if elements and not artifacts and len(elements) == 1:
+            if deps == "build":
+                scope = Scope.BUILD
+            elif deps == "none":
+                scope = Scope.NONE
+            else:
+                scope = Scope.RUN
+            app.stream.checkout(elements[0],
+                                location=location,
+                                force=force,
+                                scope=scope,
+                                integrate=True if integrate is None else integrate,
+                                hardlinks=hardlinks,
+                                tar=tar)
+            return
+
+        for vdir in _load_vdirs(app, elements, artifacts):
+            vdir = vdir.descend(["files"])
+            vdir.export_files(directory, can_link=hardlinks, can_destroy=force)
+
+
 ##################################################################
 #                      DEPRECATED Commands                       #
 ##################################################################


[buildstream] 06/06: NEWS: Add entry for the deprecation of bst checkout

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

not-in-ldap pushed a commit to branch jennis/deprecate_bst_checkout
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit f305bd27b807c9bd02805ca4faf2733479b330d6
Author: James Ennis <ja...@codethink.com>
AuthorDate: Wed Jan 9 13:03:48 2019 +0000

    NEWS: Add entry for the deprecation of bst checkout
---
 NEWS | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/NEWS b/NEWS
index 7ee63f9..f6dbbb1 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,21 @@
 buildstream 1.3.1
 =================
 
+   o BREAKING CHANGE: Moved `checkout` to the artifact subcommands. `bst checkout` is
+     now obsolete.
+
+     The command is now `bst artifact checkout`. `bst checkout` has been deprecated.
+     This implements the features of `bst checkout`, although the mandatory LOCATION
+     argument should now be specified with the `--directory` option.
+
+     `artifact checkout` can handle both element names and artifact refs, of which it
+     can take multiple.
+
+     `--deps`, `--tar` and `--integrate` options are only supported when passed a
+     single ELEMENT target. In addition to this, `--tar` is no longer a flag, it is
+     a mutually incompatible option to `--directory`. For example,
+     `bst artifact checkout foo.bst --tar foo.tar.gz`.
+
   o Added `bst artifact log` subcommand for viewing build logs.
 
   o BREAKING CHANGE: The bst source-bundle command has been removed. The