Skip to content

Commit fb94533

Browse files
authored
Add more examples of using the CLI (devcontainers#107)
1 parent b101324 commit fb94533

15 files changed

Lines changed: 298 additions & 0 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ Hello, VS Code Remote - Containers!
100100

101101
Congrats, you've just run the dev container CLI and seen it in action!
102102

103+
## More CLI examples
104+
105+
The [example-usage](./example-usage) folder contains some simple shell scripts to illustrate how the CLI can be used to:
106+
107+
- Inject tools for use inside a development container
108+
- Use a dev container as your CI build environment to build an application (even if it is not deployed as a container)
109+
- Build a container image from a devcontainer.json file that includes [dev container features](https://containers.dev/implementors/features/#devcontainer-json-properties)
110+
103111
## Build from sources
104112

105113
This repository has a [dev container configuration](https://github.com/devcontainers/cli/tree/main/.devcontainer), which you can use to ensure you have the right dependencies installed.

example-usage/README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Dev Container CLI Examples
2+
3+
This folder contains a set of basic examples that use the devcontainer CLI for different use cases. It includes example scripts to:
4+
5+
1. [Use three different tools from a development container](#tool-examples)
6+
2. [Use a dev container as your CI build environment](#ci-build-environment-example) (even if your app is not deployed as a container)
7+
3. [Build a container image](#building-an-image-from-devcontainerjson) from a devcontainer.json file that includes [dev container features](https://containers.dev/implementors/features/#devcontainer-json-properties)
8+
9+
Each should run on macOS or Linux. For Windows, you can use these scripts from WSL2.
10+
11+
## Pre-requisites
12+
13+
1. Install Node.js 16 (e.g., using [nvm](https://github.com/nvm-sh/nvm))
14+
2. Install [node-gyp pre-requisites](https://github.com/nodejs/node-gyp):
15+
- **Linux:** Use your distro's package manager. E.g. on Ubuntu/Debian: `sudo apt-get update && sudo apt-get install python3-minimal gcc g++ make`
16+
- **macOS:** Install the XCode Command Line Tools ([more info](https://github.com/nodejs/node-gyp/blob/main/README.md#on-macos))
17+
3. Make sure you have an OpenSSH compliant `ssh` command available and in your path if you plan to use the `Vim via SSH` example (it should already be there on macOS, and in Linux/WSL, you can install `openssh-client` using your distro's package manager if its missing)
18+
3. Install the latest dev container CLI: `npm install -g @devcontainers/cli`
19+
20+
## Using the examples
21+
22+
All examples use the contents of the `workspace` folder for their configuration, which is where you can make modifications if you'd like. The example scripts are then in different sub-folders.
23+
24+
### Tool examples
25+
26+
You can use these examples by opening a terminal and typing one of the following:
27+
28+
- `tool-vscode-server/start.sh` - [VS Code Server](https://code.visualstudio.com/docs/remote/vscode-server) (official)
29+
- `tool-openvscode-server/start.sh` - [openvscode-server](https://github.com/gitpod-io/openvscode-server)
30+
- `tool-vim-via-ssh/start.sh` - Vim via an SSH connection. SSH is used primarily to demonstrate how this could be achieved from other SSH supporting client tools.
31+
32+
When switching between examples, pass `true` in as an argument to get the container recreated to avoid port conflicts. e.g., `./start.sh true`
33+
34+
In the first two examples, you'll be instructed to go to `http://localhost:8000` in a browser.
35+
36+
This also adds a desktop to the container that can be accessed from a web browser at `http://localhost:6080` and you can connect using the password `vscode`.
37+
38+
#### How the tool examples work
39+
40+
These examples demonstrate the use of the dev container CLI to:
41+
42+
1. Simplify setup using the "[dev container features](https://containers.dev/implementors/features/#devcontainer-json-properties)" concept. For example, SSH support is added just using a feature reference. See `workspace/.devcontainer/devcontainer.json` for more information.
43+
44+
2. How the dev container CLI can be used to inject tools without building them into the base image:
45+
46+
1. Use `devcontainer up` to spin up the container and mount a `server` and `workspace` folder into the container.
47+
2. Use `devcontainer exec` to run a script from this mounted folder to set up the appropriate server.
48+
3. In the `vim` example, a temporary SSH key is set up and configured, and then SSH is used from the command line to connect to the container once it is up and running. See `tool-vim-via-ssh/start.sh` for details.
49+
50+
Currently the `appPort` property is used in `devcontainer.json` instead of `forwardPorts` due to a gap in the current dev container CLI ([see here](https://github.com/devcontainers/cli/issues/22)).
51+
52+
### CI build environment example
53+
54+
This example illustrates how you can use the dev container CLI to build your application in any CI system. (Note there is also a [GitHub Action](https://github.com/marketplace/actions/devcontainers-ci) and [Azure DevOps task](https://marketplace.visualstudio.com/items?itemName=devcontainers.ci) if you are using those automation systems, but this example will focus on direct use of the CLI.)
55+
56+
You can use the example by opening a terminal and typing the following:
57+
58+
```
59+
ci-app-build-script/build-app.sh
60+
```
61+
62+
After the build completes, you can find the built application in the `workspace/dist` folder.
63+
64+
The initial build can take a bit since it is building the dev container image, which is an example of where [pre-building an image](#building-an-image-from-devcontainerjson) helps.
65+
66+
#### How the CI example works
67+
68+
This example demonstrates the use of the dev container CLI to:
69+
70+
1. Simplify setup using the "[dev container features](https://containers.dev/implementors/features/#devcontainer-json-properties)" concept. For example, SSH support is added just using a feature reference. See `workspace/.devcontainer/devcontainer.json` for more information.
71+
72+
2. Execute an application build script inside a dev container as follows:
73+
74+
1. Use `devcontainer up` to spin up the container and mount the the `workspace` folder into the container.
75+
2. Use `devcontainer exec` to run a build script from the mounted folder inside the development container.
76+
3. Delete the container when the build is finished.
77+
78+
All environment variables are automatically available from `exec`, including those that are are set in the non-root user's `.bashrc` file. The dev container CLI also automatically adjusts to UID/GID differences for the user inside the container on Linux to ensure the workspace folder is writable.
79+
80+
### Building an image from devcontainer.json
81+
82+
You can use the example by opening a terminal and typing the following:
83+
84+
```
85+
image-build/build-image.sh
86+
```
87+
88+
The resulting image name defaults to `devcontainer-cli-test-image`, but you can change it with the first argument, and configure it to push to a registry by setting the second argument to true. The third argument allows you to build for multiple architectures.
89+
90+
```
91+
image-build/build-image.sh ghcr.io/my-org/my-image-name-here true "linux/amd64 linux/arm64"
92+
```
93+
94+
Ultimately, this script just calls the `devcontainer build` command to do all the work. Once built, you can refer to the specified image name directly in a devcontainer.json file using the `image` property.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/sh
2+
set -e
3+
cd "$(dirname $0)"
4+
5+
6+
build_date="$(date +%s)"
7+
8+
# Create a label for use during cleanup since the devcontainer CLI does
9+
# not have a "remove" or "down" command yet (though this is planned).
10+
id_label="ci-container=${build_date}"
11+
12+
# Run build
13+
devcontainer up --id-label ${id_label} --workspace-folder ../workspace
14+
set +e
15+
devcontainer exec --id-label ${id_label} --workspace-folder ../workspace scripts/execute-app-build.sh
16+
set -e
17+
18+
# Clean up.
19+
echo "\nCleaning up..."
20+
docker rm -f $(docker ps -aq --filter label=${id_label})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/sh
2+
set -e
3+
cd "$(dirname $0)"
4+
5+
image_name="${1:-"devcontainer-cli-test-image"}"
6+
7+
# Push will upload the image to a registry when done (if logged in via docker CLI)
8+
push_flag="${2:-false}"
9+
10+
# If more than one plaftorm is specified, then push must be set.
11+
platforms="${3:-linux/amd64}"
12+
13+
devcontainer build --image-name $image_name --platform "$platforms" --push $push_flag --workspace-folder ../workspace
14+
15+
echo "\nImage ${image_name} built successfully!"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/sh
2+
set -e
3+
if [ ! -e "$HOME/.openvscodeserver" ]; then
4+
echo "Downloading openvscode-server..."
5+
curl -fsSL https://github.com/gitpod-io/openvscode-server/releases/download/openvscode-server-v1.69.2/openvscode-server-v1.69.2-linux-x64.tar.gz -o /tmp/openvscode-server.tar.gz
6+
mkdir -p $HOME/.openvscodeserver
7+
echo "Extracting..."
8+
tar --strip 1 -xzf /tmp/openvscode-server.tar.gz -C $HOME/.openvscodeserver/
9+
rm -f /tmp/openvscode-server.tar.gz
10+
fi
11+
if [ "$(ps -ef | grep '\.openvscode-server' | wc -l)" = "1" ]; then
12+
$HOME/.openvscodeserver/bin/openvscode-server serve-local --without-connection-token --host 0.0.0.0 --port 8000
13+
else
14+
echo "\ncode-server is already running.\n\nConnect using: http://localhost:8000\n"
15+
fi
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/sh
2+
set -e
3+
cd "$(dirname $0)"
4+
5+
remove_flag=""
6+
if [ "$1" = "true" ]; then
7+
remove_flag="--remove-existing-container"
8+
fi
9+
10+
devcontainer up $remove_flag --mount "type=bind,source=$(pwd)/server,target=/server" --workspace-folder ../workspace
11+
devcontainer exec --workspace-folder . /server/init-openvscode-server.sh
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/sh
2+
set -e
3+
if ! type vim > /dev/null 2>&1; then
4+
echo "Installing vim..."
5+
sudo apt-get update
6+
sudo apt-get install -y vim
7+
fi
8+
9+
# Copy generated keys
10+
mkdir -p $HOME/.ssh
11+
cat /server/temp-ssh-key.pub > $HOME/.ssh/authorized_keys
12+
chmod 644 $HOME/.ssh/authorized_keys
13+
chmod 700 $HOME/.ssh
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/sh
2+
set -e
3+
cd "$(dirname $0)"
4+
5+
remove_flag=""
6+
if [ "$1" = "true" ]; then
7+
remove_flag="--remove-existing-container"
8+
fi
9+
10+
# Generate certificate
11+
cd server
12+
rm -f temp-ssh-key*
13+
ssh-keygen -q -N '' -t rsa -f temp-ssh-key
14+
cd ..
15+
16+
# Start container
17+
devcontainer up $remove_flag --mount "type=bind,source=$(pwd)/server,target=/server" --workspace-folder ../workspace
18+
19+
# Install vim (if needed) and add pub key to SSH allow list
20+
devcontainer exec --workspace-folder workspace /server/init-vim.sh
21+
22+
# Connect
23+
ssh -t -i server/temp-ssh-key -o NoHostAuthenticationForLocalhost=yes -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null -p 2222 vscode@localhost exec bash -c vim
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/bin/sh
2+
set -e
3+
INSTALL_LOCATION="$HOME/.local/bin"
4+
INSTALL_TARGET=unknown-linux-gnu
5+
6+
if [ ! -e "$HOME/.vscode-cli" ]; then
7+
# Adapted from https://aka.ms/install-vscode-server/setup.sh
8+
install_arch=x86_64
9+
arch=$(uname -m)
10+
if [ $arch = "aarch64" ] || [ $arch = "arm64" ]; then
11+
install_arch=aarch64
12+
fi
13+
install_url=https://aka.ms/vscode-server-launcher/$install_arch-$INSTALL_TARGET
14+
echo "Installing from $install_url"
15+
mkdir -p $INSTALL_LOCATION
16+
if type curl > /dev/null 2>&1; then
17+
curl -sSLf $install_url -o $INSTALL_LOCATION/code-server
18+
elif type wget > /dev/null 2>&1; then
19+
wget -q $install_url -O $INSTALL_LOCATION/code-server
20+
else
21+
echo "Installation failed. Please install curl or wget in your container image."
22+
exit 1
23+
fi
24+
chmod +x $INSTALL_LOCATION/code-server
25+
fi
26+
if ! pidof code-server > /dev/null 2>&1; then
27+
$INSTALL_LOCATION/code-server serve-local --without-connection-token --accept-server-license-terms --host 0.0.0.0 --port 8000
28+
else
29+
echo "\ncode-server is already running.\n\nConnect using: http://localhost:8000\n"
30+
fi
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/sh
2+
set -e
3+
cd "$(dirname $0)"
4+
5+
remove_flag=""
6+
if [ "$1" = "true" ]; then
7+
remove_flag="--remove-existing-container"
8+
fi
9+
10+
devcontainer up $remove_flag --mount "type=bind,source=$(pwd)/server,target=/server" --workspace-folder ../workspace
11+
devcontainer exec --workspace-folder . /server/init-vscode-server.sh

0 commit comments

Comments
 (0)