|
| 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