Skip to content

Commit 997a2db

Browse files
Developer contribution guide for Dev Containers CLI (#1188)
Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Co-authored-by: Abdurrahmaan Iqbal <abdurriq@github.com>
1 parent f9ea7fe commit 997a2db

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed

docs/contributing-code.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Contributing Code
2+
3+
This guide covers everything you need to set up a development environment, build, test, and submit code changes to the Dev Containers CLI. For the proposal and specification process, see [CONTRIBUTING.md](../CONTRIBUTING.md).
4+
5+
## Prerequisites
6+
7+
- [Node.js](https://nodejs.org/) >= 20
8+
- [Docker](https://www.docker.com/) (required for running integration tests — they create real containers)
9+
- [Git](https://git-scm.com/)
10+
- [yarn](https://yarnpkg.com/) (used for dependency installation)
11+
12+
## Setting up your development environment
13+
14+
Fork and clone the repository:
15+
16+
```sh
17+
git clone https://github.com/<your-username>/cli.git
18+
cd cli
19+
```
20+
21+
### Option A: Dev Container (recommended)
22+
23+
The repository includes a [dev container configuration](../.devcontainer/devcontainer.json) that provides a ready-to-go environment with Node.js, TypeScript, and Docker-in-Docker pre-configured.
24+
25+
1. Open the cloned repository in VS Code.
26+
2. When prompted, select **Reopen in Container** (requires the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)). Alternatively, open the repository in [GitHub Codespaces](https://github.com/features/codespaces).
27+
3. The `postCreateCommand` automatically runs `yarn install` to install all dependencies.
28+
29+
You are ready to build and test.
30+
31+
### Option B: Local setup
32+
33+
1. Install Node.js >= 20 and Docker.
34+
2. Install dependencies:
35+
36+
```sh
37+
yarn install
38+
```
39+
40+
Ensure Docker is running — it is needed for the integration test suite.
41+
42+
Some tests build containers for non-native architectures (e.g., `linux/arm64` on an x64 host, or vice versa). To run these locally, register QEMU emulators:
43+
44+
```sh
45+
docker run --privileged --rm tonistiigi/binfmt --install all
46+
```
47+
48+
This is needed once per boot (or per WSL session on Windows). On macOS with Docker Desktop, cross-architecture emulation is built in and this step is not required.
49+
50+
3. *(Optional)* Install [Podman](https://podman.io/) if you want to run the Podman-specific tests. The CLI supports both Docker and Podman as container engines, and the test suite includes a separate set of tests (`cli.podman.test.ts`) that verify Podman compatibility using `--docker-path podman`. These tests will fail with `spawn podman ENOENT` if Podman is not installed — this is expected and does not indicate a code problem. The CI GitHub workflow runs these tests on `ubuntu-latest` where Podman is pre-installed.
51+
52+
## Project structure
53+
54+
The CLI is written in TypeScript and organized as multiple sub-projects using [TypeScript project references](https://www.typescriptlang.org/docs/handbook/project-references.html):
55+
56+
| Sub-project | Path | Purpose |
57+
| --- | --- | --- |
58+
| `spec-common` | `src/spec-common/` | Shared utilities (async helpers, CLI host, process management, shell server) |
59+
| `spec-configuration` | `src/spec-configuration/` | Configuration parsing, OCI registry interactions, Features/Templates configuration |
60+
| `spec-node` | `src/spec-node/` | Core CLI logic — container lifecycle, Docker/Compose integration, Feature utilities |
61+
| `spec-shutdown` | `src/spec-shutdown/` | Docker CLI wrapper utilities (container inspection, execution, lifecycle management) |
62+
| `spec-utils` | `src/spec-utils/` | General utilities (logging, HTTP requests, filesystem helpers) |
63+
64+
Key files:
65+
66+
- `devcontainer.js` — Entry point that loads the bundled CLI from `dist/spec-node/devContainersSpecCLI.js`.
67+
- `esbuild.js` — Build script that bundles the TypeScript output with esbuild.
68+
- `src/test/` — Test files and fixture configurations under `src/test/configs/`.
69+
70+
## Development workflow
71+
72+
### 1. Build
73+
74+
Start the dev build watchers — run these in separate terminals (or use the [VS Code build task](#vs-code-integration)):
75+
76+
```sh
77+
npm run watch # incremental esbuild (rebuilds on save)
78+
npm run type-check-watch # tsc in watch mode (reports type errors)
79+
```
80+
81+
For a one-shot build instead, run `npm run compile`. To remove all build output, run `npm run clean`.
82+
83+
### 2. Run
84+
85+
After building, invoke the CLI directly:
86+
87+
```sh
88+
node devcontainer.js --help
89+
node devcontainer.js up --workspace-folder <path>
90+
node devcontainer.js build --workspace-folder <path>
91+
node devcontainer.js run-user-commands --workspace-folder <path>
92+
```
93+
94+
### 3. Test
95+
96+
Tests use [Mocha](https://mochajs.org/) and [Chai](https://www.chaijs.com/) and require Docker because they create and tear down real containers.
97+
98+
```sh
99+
npm test # all tests
100+
npm run test-container-features # Features tests only
101+
npm run test-container-templates # Templates tests only
102+
```
103+
104+
#### Adding tests
105+
106+
- Place new test files in `src/test/` with a `.test.ts` suffix.
107+
- Place test fixture `devcontainer.json` configurations under `src/test/configs/<your-config-name>/`.
108+
- Use the helpers in `src/test/testUtils.ts` (`shellExec`, `devContainerUp`, `devContainerDown`) for container lifecycle management in tests.
109+
110+
### 4. Validate and submit
111+
112+
Before committing, run the same checks CI runs:
113+
114+
```sh
115+
npm run type-check # full type-check
116+
npm run package # production build (minified) + pack into .tgz
117+
npm run precommit # lint, formatting, copyright headers
118+
npm test # full test suite (may take a very long time to run, consider running a subset of tests during development)
119+
```
120+
121+
Then push your branch and open a pull request against `main`. Link any related [repo issues](https://github.com/devcontainers/cli/issues) or [specification issues](https://github.com/microsoft/dev-container-spec/issues) in the PR description.
122+
123+
## VS Code integration
124+
125+
The repository includes VS Code configuration in `.vscode/` for building, debugging, and testing.
126+
127+
### Build task
128+
129+
The default build task (**Ctrl+Shift+B** / **Cmd+Shift+B**) is **Build Dev Containers CLI**. It runs `npm run watch` and `npm run type-check-watch` in parallel so you get both bundled output and type errors as you edit.
130+
131+
### Debug configurations
132+
133+
Two launch configurations are provided in `.vscode/launch.json`:
134+
135+
- **Launch CLI - up** — Runs the CLI's `up` command against `src/test/configs/example/`. Edit the `args` array to point at a different config or subcommand.
136+
- **Launch Tests** — Runs the full Mocha test suite under the debugger.
137+
138+
### Editor settings
139+
140+
The workspace recommends the [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) for inline lint feedback. The workspace settings (`.vscode/settings.json`) configure format-on-save, tab indentation, and the workspace TypeScript SDK.
141+
142+
## Troubleshooting
143+
144+
### Docker not available
145+
146+
Tests will fail if Docker is not running. Make sure the Docker daemon is started. If using the dev container, Docker-in-Docker is configured automatically.
147+
148+
### `node-pty` native module build failures
149+
150+
The `node-pty` dependency includes native code. If you see build errors during `yarn install`, ensure you have the required build tools for your platform (e.g., `build-essential` on Debian/Ubuntu, Xcode Command Line Tools on macOS).
151+
152+
### Leftover test containers
153+
154+
If tests are interrupted, containers may be left running. Single-container tests label their containers with `devcontainer.local_folder`:
155+
156+
```sh
157+
docker rm -f $(docker ps -aq --filter "label=devcontainer.local_folder")
158+
```
159+
160+
Compose-based tests also create sidecar containers (e.g., `db` services) that don't carry that label. To remove those, filter by the compose config path:
161+
162+
```sh
163+
docker rm -f $(docker ps -a --format '{{.ID}} {{.Label "com.docker.compose.project.config_files"}}' | grep src/test/configs | awk '{print $1}')
164+
```
165+
166+
### Podman test failures
167+
168+
If you don't have Podman installed, `cli.podman.test.ts` will fail with `spawn podman ENOENT`. This is safe to ignore — CI will run them. See [Local setup](#option-b-local-setup) for details on installing Podman or skipping these tests.

0 commit comments

Comments
 (0)