diff --git a/DEVELOP.md b/DEVELOP.md
index b6f1dfa5..49de1cd9 100644
--- a/DEVELOP.md
+++ b/DEVELOP.md
@@ -1,11 +1,33 @@
# Contributing to the RAPIDS devcontainers
+This document contains implementation details and design overviews for
+[rapidsai/devcontainers](https://github.com/rapidsai/devcontainers) as a
+centralized source of common scripts and patterns for providing devcontainers.
+
+For the user-level overview providing instructions on how to use the
+devcontainer as a development environment, see
+[USAGE_IN_PROJECT.md](USAGE_IN_PROJECT.md)
+
+For a project maintainer-level overview providing instructions on how to add
+and change .devcontainer.json to suit your project, see [USAGE.md](USAGE.md)
+
+The code in this repository fits into a few main categories:
+
+* Features
+* GitHub Actions automations
+* Scripts
+* matrix.yml
+* Dockerfiles
+
+## Features
+
From the official devcontainer [documentation on Features](https://containers.dev/implementors/features/):
> Development container "Features" are self-contained, shareable units of installation code and development container configuration.
-In short, a "feature" is a layer in a Dockerfile which encapsulates a reusable bit of logic executed when building a Docker image.
-
-This repository defines features to install the following dev tools, compilers, and SDKs:
+Each "feature" specified becomes a `RUN` statement in a temporary Dockerfile,
+and as such each "feature" results in an image layer. The
+[rapidsai/devcontainers repository](https://github.com/rapidsai/devcontainers)
+defines `features` to install the following dev tools, compilers, and SDKs:
* [CMake](features/src/cmake/)
* [CUDA Toolkit](features/src/cuda/)
@@ -21,3 +43,144 @@ This repository defines features to install the following dev tools, compilers,
* [sccache](features/src/sccache/)
* [devcontainer-utils](features/src/utils/)
* [rapids-build-utils](features/src/rapids-build-utils/)
+
+Many of these feature scripts use apt utilities and thus only run on debian-based
+images.
+
+### Utility features
+
+A few of the features install custom tools for the devcontainer environment,
+rather than just installation scripts of external tools and libraries. A brief
+overview of responsibilities for these features follows.
+
+#### `rapids-build-utils`
+
+The `rapids-build-utils` feature is a good place to start when investigating an
+unknown command or behavior in a devcontainer. Most of the scripts in the this
+feature prepare the devcontainer prior to use. The scripts from this project are
+installed by [`install.sh`](./features/src/rapids-build-utils/install.sh). `install.sh`
+creates aliases that begin with a `rapids-` prefix for the .sh scripts in the
+[`rapids-build-utils` bin
+folder](features/src/rapids-build-utils/opt/rapids-build-utils/bin).
+
+The `rapids-build-utils` scripts can also be run manually to force an update the
+devcontainer scripts after modifying a project's manifest.yml file to add new
+projects or change dependencies. See
+[USAGE.md](./USAGE.md#generating-scripts-for-other-projects-manifestyaml-file)
+for more information on customizing manifest.yml.
+
+> **NOTE:** Some scripts in downstream projects, especially those in `ci/`
+folders, call scripts that start with the `rapids-` prefix but are not provided
+by the `rapids-build-utils` feature. These `rapids-` prefixed scripts come from
+the [gha-tools](https://github.com/rapidsai/gha-tools/tree/main/tools)
+repository, which is part of the older RAPIDS unified CI configuration and not
+currently part of the devcontainer features. To run the scripts in a project's
+`ci` folder, use the images at
+[rapidsai/ci-imgs](https://github.com/rapidsai/ci-imgs) instead of the
+devcontainers.
+
+#### devcontainers-utils
+
+These scripts handle git-related configuration, setting up SSH deploy
+keys, GitHub authorization, and the vault setup for S3. The commands here are
+prefixed with `devcontainer-` during installation.
+
+## Github Actions automations
+
+Github Actions runs the build matrix of the many base images defined in [matrix.yml](./matrix.yml).
+These actions are broken up into many reusable pieces. The image build jobs start in
+[release.yml](./.github/workflows/release.yml).
+
+```mermaid
+flowchart TD
+ subgraph Legend
+ workflow
+ action(action)
+ script{{script}}
+ end
+ subgraph Image build Github Actions flowchart
+ release.yml[release.yml workflow dispatch] --> release-features.yml["Publish features (release-features.yml)"]
+ release-features.yml --> image-matrix("Determine image matrix (image-matrix/action.yml)")
+ image-matrix --> write-matrix{{Write matrix by calling image-matrix/action.sh}}
+ write-matrix --> build-test-and-push-linux-image.yml
+ write-matrix --> build-test-and-push-windows-image.yml
+ build-test-and-push-linux-image.yml --> write-json("Write .devcontainer.json (devcontainer-json/action.yml)")
+ write-json --> write-script{{Write devcontainer.json by calling devcontainer-json/action.sh}}
+ build-test-and-push-windows-image.yml --> build-windows("Build windows image (build-windows-image/action.yml)")
+ write-script --> build-linux-image("Build linux limage (build-linux-image/action.yml)")
+ end
+```
+
+These are divided into 3 categories in the diagram:
+* Workflows are `.yml` files in `.github/workflows`
+* Actions are folders in `.github/actions`. One folder per action. Actions must have a `action.yml` file, but may
+also have accompanying shell scripts.
+* Scripts are the shell scripts that are in some of the actions folders. They are broken out in this diagram to help
+show that functionality can be defined outside of action.yml files.
+
+## Base images (matrix.yml)
+
+Base images are composed from individual features in [matrix.yml](./matrix.yml)
+using [YAML
+anchors](https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors/).
+These get built on Github Actions as described
+[above](#github-actions-automations) The devcontainers repo has some generic
+scripts for generating, building and launch devcontainers in the scripts folder.
+Some downstream repos have related, but more specialized scripts to facilitate
+working with their subspace of the configuration matrix. An example is CCCL's
+[make_devcontainers.sh](https://github.com/NVIDIA/cccl/blob/main/.devcontainer/make_devcontainers.sh)
+script.
+
+## Dockerfiles
+
+Dockerfiles do not play much role in this scheme. They serve to [set a few global
+variables and extend from the base image](./.devcontainer/rapids.Dockerfile).
+Changes made to a Dockerfile are usually better achieved by adding or changing a feature
+script instead.
+
+## Build caching with `sccache`
+
+The devcontainers configure CMake to use
+[sccache](https://github.com/mozilla/sccache) as C, C++, CUDA, and Rust compiler
+launchers. Refer to the [sccache
+docs](https://github.com/mozilla/sccache/tree/main/docs) for configuring the
+various storage back-ends.
+
+### Build caching with private S3 buckets
+
+A private S3 bucket can be used as the `sccache` storage back-end.
+
+If you're using a [GitHub action](https://github.com/aws-actions/configure-aws-credentials) to assume AWS roles in CI, or are comfortable distributing and managing S3 credentials, you can define the `SCCACHE_BUCKET`, `AWS_ACCESS_KEY_ID`, and `AWS_SECRET_ACCESS_KEY` variables in the container environment.
+
+### Using GitHub OAuth to issue S3 credentials via Hashicorp Vault
+
+The [`devcontainer-utils`](features/src/utils/) feature includes a `devcontainer-utils-vault-s3-init` script that uses GitHub OAuth and Hashicorp Vault to issue temporary S3 credentials to authorized users.
+
+> **NOTE:** This script runs in the devcontainer's [`postAttachCommand`](https://containers.dev/implementors/json_reference/#lifecycle-scripts), but it does nothing unless `SCCACHE_BUCKET` and `VAULT_HOST` are in the container environment.
+
+The `devcontainer-utils-vault-s3-init` script performs the following actions, exiting early if any step is unsuccessful:
+
+1. Log in via the [GitHub CLI](https://cli.github.com/)
+2. Authenticate via [Vault's GitHub auth method](https://developer.hashicorp.com/vault/docs/auth/github#authentication)
+3. Use Vault to [generate temporary AWS credentials](https://developer.hashicorp.com/vault/api-docs/secret/aws#generate-credentials)
+4. Store results in `~/.aws` and install crontab to re-authenticate
+
+The above steps can be customized via the following environment variables:
+```
+# The hostname of the Vault instance to use
+VAULT_HOST="https://vault.ops.k8s.rapids.ai"
+
+# List of GitHub organizations for which Vault can generate credentials.
+# The scripts assumes the Vault instance exposes an authentication endpoint
+# for each org at `$VAULT_HOST/v1/auth/github-$org/login`.
+# https://developer.hashicorp.com/vault/docs/auth/github#authentication
+VAULT_GITHUB_ORGS="nvidia nv-morpheus nv-legate rapids"
+
+# The TTL for the generated AWS credentials
+VAULT_S3_TTL=43200
+
+# The URI to the Vault API that generates AWS credentials
+# The full URL expands to `$VAULT_HOST/$VAULT_S3_URI?ttl=$VAULT_S3_TTL`
+# https://developer.hashicorp.com/vault/api-docs/secret/aws#generate-credentials
+VAULT_S3_URI="v1/aws/creds/devs"
+```
\ No newline at end of file
diff --git a/README.md b/README.md
index ef43c894..8cc0d1bb 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,23 @@
# RAPIDS [devcontainers](https://containers.dev/)
-This repository contains features and workflows for building development containers to support local dev and CI for NVIDIA [RAPIDS](https://github.com/rapidsai), [CCCL](https://github.com/nvidia/cccl), and [Legate](https://github.com/nv-legate).
+This repository contains centralized features and workflows for building
+development containers ([devcontainers](https://containers.dev/)) to support
+local dev and CI for projects in NVIDIA [RAPIDS](https://github.com/rapidsai),
+[CCCL](https://github.com/nvidia/cccl), and
+[Legate](https://github.com/nv-legate).
-We've chosen to use a monorepo, but it is similar in spirit to the official [devcontainers/features](https://github.com/devcontainers/features) and [devcontainers/images](https://github.com/devcontainers/images) repositories.
+[Devcontainers](https://containers.dev/) are an open standard for specifying the
+creation and execution of Docker containers for developing a codebase.
-### For details on using the RAPIDS devcontainers, see [`USAGE.md`](USAGE.md).
+Downstream repositories that utilize devcontainers use both the `feature`
+scripts that install software, as well as docker images that serve to cache sets
+of installed software. These images serve as base images for the devcontainers
+specified in the downstream repositories.
-### For details on contributing to this repository, see [`DEVELOP.md`](DEVELOP.md).
+## Usage
-### See the list of `rapidsai/devcontainers` tags [on DockerHub](https://hub.docker.com/r/rapidsai/devcontainers/tags).
+### [Using devcontainers to provide a build environment on a project](./USAGE_IN_PROJECT.md)
+
+### [Setting up and maintaining devcontainer configuration in other projects](./USAGE.md)
+
+### [Developing the centralized `feature` scripts and base images in this repository](DEVELOP.md).
diff --git a/USAGE.md b/USAGE.md
index e06c7f89..0d2f4a5c 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -1,19 +1,121 @@
-# Using the RAPIDS devcontainers
+# Adding and adapting devcontainers to a project
-### See the list of `rapidsai/devcontainers` tags [on DockerHub](https://hub.docker.com/r/rapidsai/devcontainers/tags).
+This document describes how to add devcontainers to a project that does not yet
+have them and how to customize the devcontainer to fit a project's
+needs.
-* [Using the pre-built images](#using-the-pre-built-images)
- * [Using in `devcontainer.json`](#using-in-devcontainerjson)
-* [Build caching with sccache](#build-caching-with-sccache)
+For how to use devcontainers to provide development environments, see
+[USAGE_IN_PROJECT.md](./USAGE_IN_PROJECT.md)
+For how to change the centralized installation and configuration scripts that
+are shared among projects, see [DEVELOP.md](./DEVELOP.md).
+
+## Adding devcontainers to a project
+
+Adding devcontainers to a project means adding one or more `devcontainer.json`
+files. One devcontainer is equivalent to one `devcontainer.json` file. Projects
+such as [CCCL](https://github.com/NVIDIA/cccl/blob/main/.devcontainer/README.md)
+have additional scripts that manage many configurations. New projects needing
+only cuda and python can bootstrap themselves by copying the `cuda-*` folders
+from [rapids/devcontainers](./.devcontainer).
+
+## Devcontainers helper scripts
+
+### Generated build scripts
+
+Several scripts are generated based on the contents of `manifest.yaml`. By
+default, `manifest.yaml` comes from [the RAPIDS/devcontainers
+repo](./features/src/rapids-build-utils/opt/rapids-build-utils/manifest.yaml),
+but [a local `manifest.yaml` file can be used instead](#generating-scripts-for-other-projects-manifestyaml-file).
+
+The generated scripts are:
+* `/usr/bin/build-*`
+* `/usr/bin/clean-*`
+* `/usr/bin/clone-*`
+* `/usr/bin/configure-*`
+
+Here the `*` is a placeholder for the project name and the kind of
+build. For example, a project with a python component in `manifest.yaml`
+will have `build-cudf-python`.
+
+These scripts may trigger side-effects. For example, `build-*` scripts are only
+generated for projects that exist in the workspace. When working on `cudf`,
+which depends on `rmm`, the default workspace only mounts `cudf` and generates
+`build-cudf` scripts. To obtain `rmm` build scripts also, run `clone-rmm`,
+which will clone `rmm` into the workspace and generate build scripts for it.
+
+### rapids-build-utils
+
+[`rapids-build-utils`](./features/src/rapids-build-utils/opt/rapids-build-utils/bin)
+scripts are meta-build scripts. They assist with setting up the workspace and
+reloading things when important configuration changes happen.
+`rapids-build-utils` scripts are most often called indirectly by devcontainer
+lifecycle hooks, but are also useful for forcing updates in the workspace.
+`rapids-build-utils` scripts are described in more detail in
+[DEVELOP.md](./DEVELOP.md#rapids-build-utils).
+
+### Generating scripts for other projects: `manifest.yaml` file
+
+Build script generation is controlled with a `manifest.yaml` file. This file
+comes from [the rapidsai/devcontainers
+repo](./features/src/rapids-build-utils/opt/rapids-build-utils/manifest.yaml),
+but can be [overridden with a local manifest.yaml file](#local-changes). New
+projects and changes to dependencies can be submitted in a PR to the
+[rapidsai/devcontainers](https://github.com/rapidsai/devcontainers) repo.
+
+> **NOTE** manifest.yaml's content is pinned in
+`.devcontainer/*/devcontainer.json` indirectly via the pin on rapids-build-utils.
+For changes in manifest.yaml to be reflected in the devcontainer, the manifest.yaml
+changes need to be present in a release of `rapidsai/devcontainers`, and the pin
+on `rapids-build-utils` must be updated in the project's devcontainer.json file(s).
+
+```
+ "features": {
+ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.2": {}
+ },
+```
+
+In this `devcontainer.json` excerpt, the `24.2` value is the pin that would need
+to be updated.
+
+#### Local changes
+
+To work around the delay in getting a change merged into the devcontainers repo
+and have a new tag in place, a local manifest.yaml file can be used. Start by
+copying `manifest.yaml` from the rapidsai/devcontainers repo. This file can
+live anywhere, but this example has it in `.devcontainer/manifest.yaml`.
+
+In the desired devcontainer.json file, add a top-level key with this:
+
+```
+ "containerEnv": {
+ "PROJECT_MANIFEST_YML": "/home/coder/${localWorkspaceFolderBasename}/.devcontainer/manifest.yaml"
+ },
+```
+
+Rebuild or re-open the devcontainer, and the scripts (`build-*`, `clone-*`,
+etc.) will be regenerated using the local `.devcontainer/manifest.yaml` instead
+of the manifest.yaml that comes from the devcontainer.json-pinned
+`rapids-build-utils` version. The `rapids-generate-scripts` command from
+[rapids-build-utils](./features/src/rapids-build-utils/opt/rapids-build-utils/bin)
+will also force a refresh of these scripts without rebuilding the devcontainer.
## Using the pre-built images
-We publish a [matrix](matrix.yml) of pre-built images to DockerHub to accelerate initializing local devcontainers, GitHub Codespaces, and CI jobs.
+The choice of using a pre-built image refers to the `build/args/BASE` or `image`
+entry in `devcontainer.json`. The pre-built images are not meant to be used
+directly. We publish a [matrix](matrix.yml) of pre-built images to DockerHub to
+accelerate initializing local devcontainers, GitHub Codespaces, and CI jobs.
+These use the "feature" scripts to install their components, so these base
+containers effectively cache running their feature scripts.
-The features that comprise the image are noted in the image tags. If no version is defined for a tool or SDK, the image includes the latest available version at image build time.
+The features that comprise the image are noted in the image tags. If no version
+is defined for a tool or SDK, the image includes the latest available version at
+image build time.
-> **NOTE:** `git`, `git-lfs`, `github-cli`, `gitlab-cli`, `cmake`, `ninja`, `sccache`, and our devcontainer-utils [are included](image/.devcontainer/devcontainer.json#L12-L33) in each pre-built image.
+> **NOTE:** `git`, `git-lfs`, `github-cli`, `gitlab-cli`, `cmake`, `ninja`, `sccache`, and [devcontainer-utils](./features/src/utils) [are included](image/.devcontainer/devcontainer.json#L12-L33) in each pre-built image.
+
+See the list of `rapidsai/devcontainers` tags [on DockerHub](https://hub.docker.com/r/rapidsai/devcontainers/tags).
### Using in [`devcontainer.json`](https://containers.dev/implementors/json_reference/#image-specific)
@@ -21,21 +123,32 @@ The pre-built images can be used as the `"image"`, or as the base of a Dockerfil
devcontainer.json using pre-built image
{
"image": "rapidsai/devcontainers:24.06-cpp-llvm16-cuda12.0-nvhpc23.5-ubuntu22.04",
"hostRequirements": { "gpu": true },
"workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind"
}
-You can also build a custom devcontainer by composing individual features:
+### Custom devcontainers
+
+Custom devcontainers can be specified by composing individual features:
devcontainer.json using individual features
{
"image": "ubuntu:22.04",
"features": {
"ghcr.io/rapidsai/devcontainers/features/cmake:24.6": {},
"ghcr.io/rapidsai/devcontainers/features/ninja:24.6": {},
"ghcr.io/rapidsai/devcontainers/features/sccache:24.6": {
"version": "0.5.4"
}
},
"overrideFeatureInstallOrder": [
"ghcr.io/rapidsai/devcontainers/features/cmake",
"ghcr.io/rapidsai/devcontainers/features/ninja",
"ghcr.io/rapidsai/devcontainers/features/sccache"
],
"workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind"
}
-> **NOTE:** Feature updates published since your most recent image build will invalidate your docker image layer cache, meaning it can take the [devcontainers CLI](https://github.com/devcontainers/cli) longer to initialize containers composed from individual features.
+Similarly, any base conatiner can be extended by adding additional features:
+
+devcontainer.json extending base image with additional features
{
"rapidsai/devcontainers:24.06-cpp-llvm16-cuda12.0-nvhpc23.5-ubuntu22.04",
"features": {
"ghcr.io/rapidsai/devcontainers/features/cmake:24.02": {},
"ghcr.io/rapidsai/devcontainers/features/ninja:24.02": {},
"ghcr.io/rapidsai/devcontainers/features/sccache:24.02": {
"version": "0.5.4"
}
},
"overrideFeatureInstallOrder": [
"ghcr.io/rapidsai/devcontainers/features/cmake",
"ghcr.io/rapidsai/devcontainers/features/ninja",
"ghcr.io/rapidsai/devcontainers/features/sccache"
],
"workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind"
}
-## Build caching with `sccache`
+This example is contrived, because base images already all include common build tools such as CMake, ninja and sccache.
-The devcontainers configure CMake to use [sccache](https://github.com/mozilla/sccache) as C, C++, CUDA, and Rust compiler launchers. Refer to the [sccache docs](https://github.com/mozilla/sccache/tree/main/docs) for configuring the various storage back-ends.
-### Build caching with private S3 buckets
+> **NOTE:** Feature updates published since the most recent image build will
+invalidate the docker image layer cache, meaning it can take the [devcontainers
+CLI](https://github.com/devcontainers/cli) longer to initialize containers
+composed from individual features.
-You can use a private S3 bucket as the `sccache` storage back-end.
+## Caching
-If you're using a [GitHub action](https://github.com/aws-actions/configure-aws-credentials) to assume AWS roles in CI, or are comfortable distributing and managing S3 credentials, you can define the `SCCACHE_BUCKET`, `AWS_ACCESS_KEY_ID`, and `AWS_SECRET_ACCESS_KEY` variables in the container environment.
+The main cache tool in use is sccache. Sccache is configured to use S3 as a
+backend. If you're using a [GitHub
+action](https://github.com/aws-actions/configure-aws-credentials) to assume AWS
+roles in CI, or are comfortable distributing and managing S3 credentials, you
+can define the `SCCACHE_BUCKET`, `AWS_ACCESS_KEY_ID`, and
+`AWS_SECRET_ACCESS_KEY` variables in the container environment.
### Using GitHub OAuth to issue S3 credentials via Hashicorp Vault
diff --git a/USAGE_IN_PROJECT.md b/USAGE_IN_PROJECT.md
new file mode 100644
index 00000000..a228bf7c
--- /dev/null
+++ b/USAGE_IN_PROJECT.md
@@ -0,0 +1,185 @@
+# Using devcontainers in projects to provide development environments
+
+This document is aimed at developers who want to use devcontainers to set up
+development environments for themselves. It is intended as a general overview
+that any project employing devcontainers can link to and avoid duplicating.
+
+For how to add devcontainers to a project, or how to change existing devcontainer
+configuration in a project, see [USAGE.md](./USAGE.md).
+
+For how to change the centralized installation and configuration scripts that
+are shared among projects, see [DEVELOP.md](./DEVELOP.md).
+
+## System requirements
+
+Devcontainers can be used on Linux, Mac and Windows. They use Docker, so the
+system requirements and limitations associated with Docker apply here also. On
+Mac and Windows, where a Linux VM must run to support Docker, memory limitations
+of that VM may present problems. Otherwise, devcontainers do not add
+system requirements beyond the needs of the individual projects being built.
+Devcontainers also don't emulate hardware. If a project needs an NVIDIA GPU to
+run, then the devcontainer needs to run on a machine with an NVIDIA GPU.
+Building is different from running, though, and devcontainers can often be used
+to build software that may need a GPU to run.
+
+Devcontainers require Docker. To set that up:
+
+* [Linux - docker engine](https://docs.docker.com/engine/install/)
+* [Mac - docker desktop](https://docs.docker.com/desktop/install/mac-install/)
+* [Windows](https://docs.docker.com/desktop/install/windows-install/)
+
+Docker Desktop has licensing requirements. NVIDIA employees may [request a
+license](https://confluence.nvidia.com/pages/viewpage.action?spaceKey=SWDOCS&title=Requesting+a+Docker+Desktop+License).
+
+### Local vs. Remote Usage
+
+Devcontainers can be used similarly on local machines and on remote machines.
+There are no special steps required, but menu options may differ slightly.
+
+## Quickstart
+
+At this point, you have cloned a repo that nominally has some devcontainer
+configuration. Devcontainer configuration is stored in a `.devcontainer` folder
+in the repository root. The .devcontainer folder will have either a
+`devcontainer.json` file, or some number of folders, such as `cuda12.0-conda`
+and `cuda12.0-pip`. Where folders are present, each will contain a
+`devcontainer.json` file. These files specify how to create and run a container
+with a known-good development environment.
+
+### VS Code (Recommended)
+
+Working with devcontainers in VS Code requires an extension: [Remote -
+Containers
+extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
+When VS Code detects devcontainer.json files, it should prompt you to install
+this extension with a pop-up in the lower-right of your VS Code window. If it
+doesn't, you'll need to manually install the extension another way.
+
+**Steps**
+
+1. Open the cloned directory in VSCode
+
+2. Launch a Dev Container by clicking the pop-up prompt in the lower right of
+the VS Code window that suggests to "Reopen in Container"
+
+ ![Shows "Reopen in Container" prompt when opening the cccl directory in VScode.](./docs-img/reopen_in_container.png)
+
+ - If the pop-up prompt doesn't come up, use the Command Palette to start a Dev Container. Press `[Ctrl|CMD]+Shift+P` to open the Command Palette. Type "Remote-Containers: Reopen in Container" and select it.
+
+ ![Shows "Reopen in Container" in command pallete.](./docs-img/open_in_container_manual.png)
+
+3. Select an environment with the desired build tools from the list:
+
+ ![Shows list of available container environments.](./docs-img/container_list.png)
+
+ The available tools depend on your project. These can be configured when
+ generating the matrix of base images. See [the docs on adapting devcontainers](./USAGE.md#custom-devcontainers)
+ for more info.
+
+4. VSCode will initialize the selected Dev Container. This can take a few
+minutes the first time, as it downloads the base docker image and runs any
+specified feature installations. These steps are cached, so subsequent runs are
+faster.
+
+5. Once initialized, the local project directory is mounted into the container
+to ensure any changes are persistent.
+
+6. You project should now look like any other VS Code project, except that the
+blue box in the lower left of the VS Code window should now read `Dev Container
+@ `. You're done! Any terminal you open will have the build
+environment activated appropriately.
+
+7. The devcontainers add build scripts that fit general usage. Type `build-` and hit
+`TAB` to see options for your project. Check the contributing guide in your repo
+for further instructions.
+
+**Exiting the devcontainer**
+
+If you are in a VS Code devcontainer on a remote (SSH) machine, you can run
+`CTRL|CMD + SHIFT + P` and select `Dev Containers: Reopen in SSH` to return to
+your host machine.
+
+### Docker (Manual Approach)
+
+Your project may have its own launch scripts that account for options in
+libraries and/or tools. The steps here should work with any repo that uses
+devcontainers, but any repo-specific scripts and instructions will probably work
+better.
+
+**Prerequisites**
+
+- [Devcontainer CLI ](https://github.com/devcontainers/cli) - needs NodeJS/npm
+
+**Steps**
+
+1. Download the [launch-devcontainer script](./bin/launch-devcontainer) and
+ [put it somewhere on PATH](https://phoenixnap.com/kb/linux-add-to-path). If your project has its own launch script, use it
+ here instead.
+
+2. Set your current working directory to the root of your repo containing the
+.devcontainer folder
+
+3. Run the launch-devcontainer script. Called without arguments, you'll get a menu of containers to choose from:
+
+```
+$ launch-devcontainer
+Using devcontainers in /workspaces/devcontainers.
+Select a container (or provide it as a positional argument):
+1) cuda11.8-conda
+2) cuda11.8-pip
+3) cuda12.0-conda
+4) cuda12.0-pip
+5) main
+#?
+```
+
+You can also provide devcontainer label (folder name) directly:
+
+```
+launch-devcontainer cuda12.0-conda
+```
+
+4. The devcontainer will be built, and you'll be dropped at a shell prompt
+inside the container. You're done!
+
+5. The devcontainers add build scripts that fit general usage. Type `build-`
+and hit `TAB` to see options for the project. Check the contributing guide in
+your repo for further instructions.
+
+
+## (Optional) Native build tools - CMake, python builds
+
+The generated scripts mentioned above will take care of running
+build tools for you. However, if you need to run the build tools
+manually, you can `cd` into your source code folder, which is
+mounted as a subfolder in `/home/coder`.
+
+## (Optional) Working with upstream projects
+
+Build scripts are generated only for the main project - the one you have
+mounted. Dependencies are automatically downloaded, but these dependencies are
+not built locally by default. If you would like to develop other projects in
+tandem, you can run their `clone-*` scripts. After they have been cloned,
+appropriate `build-*` scripts will be generated. See [the project maintainer
+docs on this
+topic](./USAGE.md#generating-scripts-for-other-projects-manifestyaml-file).
+
+## (Optional) Authenticate with GitHub for `sccache`
+
+After starting the container with any method, there will be a prompt to
+authenticate with GitHub. This grants access to a
+[`sccache`](https://github.com/mozilla/sccache) server shared with CI and
+greatly accelerates local build times. This is currently limited to NVIDIA
+employees belonging to the `NVIDIA` or `rapidsai` GitHub organizations. Assuming
+the GitHub authentication here worked, this should work "out of the box" and
+should not require any additional AWS credentials for any individual.
+
+Without authentication to the remote server, `sccache` will still accelerate local builds by using a filesystem cache.
+
+Follow the instructions in the prompt as below and enter the one-time code at https://github.com/login/device
+
+ ![Shows authentication with GitHub to access sccache bucket.](./docs-img/github_auth.png)
+
+To manually trigger this authentication, execute the `devcontainer-utils-vault-s3-init` script within the container.
+
+For more information about the sccache configuration and authentication, see [the developer documentation](./DEVELOP.md#build-caching-with-sccache).
diff --git a/bin/launch-devcontainer b/bin/launch-devcontainer
new file mode 100755
index 00000000..72b65e27
--- /dev/null
+++ b/bin/launch-devcontainer
@@ -0,0 +1,86 @@
+#!/usr/bin/env bash
+#
+# Launch devcontainers in a command line interface.
+#
+# This script can be run from inside of a git repo that defines devcontainers.
+# The user is prompted to select one of the devcontainers, then the
+# devcontainer is launched. The user can also provide a devcontainer name like
+# "cuda12.0-conda" as an argument to this script. If invoked multiple times
+# with the same devcontainer configuration, the CLI instances will reuse the
+# same running container. After the last shell utilizing that devcontainer is
+# closed, the container is torn down.
+#
+# This script requires the devcontainer CLI.
+
+set -euo pipefail
+
+create_menu() {
+ select option; do
+ if [ 1 -le "$REPLY" ] && [ "$REPLY" -le "$#" ]; then
+ echo "${option}"
+ break;
+ else
+ echo "Invalid choice: Select a number 1-$#" >&2
+ fi
+ done
+}
+
+choose_container() {
+ echo "Select a container (or provide it as a positional argument):" >&2
+ containers=()
+ while IFS= read -r -d '' dir; do
+ containers+=("$(basename $dir)")
+ done < <(find ${devcontainers_root}/.devcontainer -mindepth 1 -maxdepth 1 -type d -print0 | sort -z --version-sort)
+ create_menu "${containers[@]}"
+}
+
+find_container() {
+ docker ps --quiet \
+ --filter label=devcontainer.local_folder=${devcontainers_root} \
+ --filter label=devcontainer.config_file=${devcontainer_config}
+}
+
+container_up() {
+ devcontainer up --config ${devcontainer_config} --workspace-folder ${devcontainers_root}
+}
+
+container_exec() {
+ devcontainer exec --config ${devcontainer_config} --workspace-folder ${devcontainers_root} bash
+}
+
+container_teardown() {
+ # If this is the last active shell, stop and remove the container
+ container_id="$(find_container)"
+ num_active_shells=$(docker exec "${container_id}" ps aux | grep -c "/bin/sh")
+ if [[ ${num_active_shells} -le 1 ]]; then
+ echo "All devcontainers are closed. Stopping and removing container ${container_id}."
+ docker stop "${container_id}"
+ docker rm "${container_id}"
+ fi
+}
+
+main() {
+ if ! command -v devcontainer &> /dev/null; then
+ echo "devcontainer CLI must be installed."
+ echo "Try running 'npm install -g @devcontainers/cli'."
+ exit 1
+ fi
+
+ devcontainers_root=$(git rev-parse --show-toplevel 2>/dev/null || realpath devcontainers)
+ if [ -d ${devcontainers_root} ]; then
+ echo "Using devcontainers in ${devcontainers_root}."
+ else
+ echo "Devcontainers could not be found."
+ exit 1
+ fi
+ container_name=${1:-$(choose_container)}
+ devcontainer_config="${devcontainers_root}/.devcontainer/${container_name}/devcontainer.json"
+
+ if [ -z "$(find_container)" ]; then
+ container_up
+ fi
+ container_exec
+ container_teardown
+}
+
+main
\ No newline at end of file
diff --git a/docs-img/container_list.png b/docs-img/container_list.png
new file mode 100644
index 00000000..09c4510f
Binary files /dev/null and b/docs-img/container_list.png differ
diff --git a/docs-img/github_auth.png b/docs-img/github_auth.png
new file mode 100644
index 00000000..3f52b3a2
Binary files /dev/null and b/docs-img/github_auth.png differ
diff --git a/docs-img/open_in_container_manual.png b/docs-img/open_in_container_manual.png
new file mode 100644
index 00000000..e09435b8
Binary files /dev/null and b/docs-img/open_in_container_manual.png differ
diff --git a/docs-img/reopen_in_container.png b/docs-img/reopen_in_container.png
new file mode 100644
index 00000000..0e1d82dd
Binary files /dev/null and b/docs-img/reopen_in_container.png differ