|
| 1 | +# Dev Container Features |
| 2 | + |
| 3 | +A collection of small Dev Container Features—each in `src/<feature>`—providing reusable units that compose into devcontainer builds. |
| 4 | + |
| 5 | +## 1. Project Overview |
| 6 | + |
| 7 | +### What this project does |
| 8 | + |
| 9 | +This repository contains a set of Dev Container Features (reusable units) that install tools and configuration into development containers. Each feature lives under `src/<feature>` and exposes a `devcontainer-feature.json` and an `install.sh` entrypoint. Features are designed to run inside container images used by VS Code Remote - Containers and devcontainer tooling. |
| 10 | + |
| 11 | +### Primary languages and build system |
| 12 | + |
| 13 | +- **Primary languages**: Shell scripts (POSIX/sh, Bash) and JSON for feature metadata. Minor documentation in Markdown. |
| 14 | +- **Build/toolchain**: No compiled build system. Tools used during development: `bash`, `sh`, `wget`, `curl`, `apt`/`apk` package managers, `npm` for developer tooling, and Git for versioning. |
| 15 | +- **Development image**: Node image `mcr.microsoft.com/devcontainers/javascript-node:1-18` with `@devcontainers/cli` installed in `postCreateCommand`. |
| 16 | + |
| 17 | +### Target platform |
| 18 | + |
| 19 | +- **Runtime**: Linux containers (Debian/Ubuntu and Alpine supported) |
| 20 | +- **Use case**: VS Code Remote - Containers / devcontainer integration |
| 21 | + |
| 22 | +## 2. Directory Structure |
| 23 | + |
| 24 | +``` |
| 25 | +project-root/ |
| 26 | +├── src/ # Feature implementations |
| 27 | +│ ├── chezmoi/ # Feature: installs chezmoi dotfile manager |
| 28 | +│ │ ├── devcontainer-feature.json |
| 29 | +│ │ ├── install.sh |
| 30 | +│ │ └── README.md |
| 31 | +│ ├── clang/ # Feature: installs clang/llvm toolchain |
| 32 | +│ │ ├── devcontainer-feature.json |
| 33 | +│ │ ├── install.sh |
| 34 | +│ │ └── README.md |
| 35 | +│ └── persist-shell-history/ # Feature: persists shell history across containers |
| 36 | +│ ├── devcontainer-feature.json |
| 37 | +│ ├── install.sh |
| 38 | +│ └── README.md |
| 39 | +├── test/ # Feature tests and test scenarios |
| 40 | +│ ├── chezmoi/ |
| 41 | +│ │ ├── scenarios.json |
| 42 | +│ │ └── test.sh |
| 43 | +│ ├── clang/ |
| 44 | +│ │ ├── scenarios.json |
| 45 | +│ │ └── test.sh |
| 46 | +│ └── persist-shell-history/ |
| 47 | +│ ├── scenarios.json |
| 48 | +│ └── test.sh |
| 49 | +├── .devcontainer.json # VS Code devcontainer config for local development |
| 50 | +├── README.md # Project documentation |
| 51 | +├── LICENSE # License file |
| 52 | +└── AGENTS.md # This file: guidance for AI coding agents |
| 53 | +``` |
| 54 | + |
| 55 | +### Adding new features |
| 56 | + |
| 57 | +1. Create `src/<feature-name>/` directory using kebab-case |
| 58 | +2. Add minimum files: |
| 59 | + - `devcontainer-feature.json`: Metadata for the feature |
| 60 | + - `install.sh`: Installer entrypoint |
| 61 | + - `README.md`: Documentation of options and behavior |
| 62 | +3. Add test files under `test/<feature-name>/`: |
| 63 | + - `scenarios.json`: Test scenarios configuration |
| 64 | + - `test.sh`: Test script |
| 65 | + |
| 66 | +## 3. Key Conventions |
| 67 | + |
| 68 | +### Code style |
| 69 | + |
| 70 | +- **Shell variant**: Prefer POSIX `sh` compatibility; use `bash` for features requiring bash-specific constructs (arrays, etc.) |
| 71 | +- **Shebangs**: Use `#!/usr/bin/env sh` or `#!/usr/bin/env bash`; follow existing style in the feature folder |
| 72 | +- **Set options**: Use `set -e` (exit on error), `set -u` (fail on undefined vars), `set -o pipefail` (fail on pipe errors). Some features provide `KEEP_GOING` flags to skip strict failure behavior |
| 73 | +- **Logging**: Use `set -x` for debug traces in installers; keep output well-structured; use `echo` for user messages |
| 74 | +- **Idempotence**: Installers must be safe to run multiple times; check for existing commands before installing packages |
| 75 | +- **Indentation**: Maintain consistent indentation (2 or 4 spaces) and clear function separation |
| 76 | + |
| 77 | +### Naming conventions |
| 78 | + |
| 79 | +- **Feature directories**: kebab-case (e.g., `persist-shell-history`) |
| 80 | +- **Executables/scripts**: `install.sh` is the standard entrypoint per feature |
| 81 | +- **JSON metadata**: `devcontainer-feature.json` must include: |
| 82 | + - `name`: Human-readable feature name |
| 83 | + - `id`: Unique identifier (usually kebab-case) |
| 84 | + - `version`: Semantic versioning (e.g., `1.6.1`) |
| 85 | + - `description`: Brief description |
| 86 | + - `documentationURL`: Link to feature documentation |
| 87 | + - Optional: `options`, `mounts`, `installsAfter`, `postCreateCommand` |
| 88 | + |
| 89 | +### File organization |
| 90 | + |
| 91 | +- **Feature files**: Implementation scripts and helpers live in `src/<feature>/` |
| 92 | +- **Tests**: Integration tests placed in `test/<feature>/` with `scenarios.json` and `test.sh` pattern |
| 93 | +- **Configuration**: |
| 94 | + - `.devcontainer.json`: VS Code devcontainer configuration |
| 95 | + - `devcontainer-feature.json`: Feature metadata consumed by devcontainer tooling |
| 96 | + - Feature `README.md`: Documents options, environment variables, and any special setup |
| 97 | + |
| 98 | +## 4. Architecture Patterns |
| 99 | + |
| 100 | +- **Independence**: Each feature is self-contained; `devcontainer-feature.json` defines metadata and options, `install.sh` performs installation |
| 101 | +- **Ordering**: Features declare `installsAfter` in JSON to express build order constraints |
| 102 | +- **Error handling**: Default behavior uses `set -e` to abort on errors. Features may provide `KEEP_GOING` option to continue on failure |
| 103 | +- **Idempotency**: Installers check for presence of tools before reinstalling; safe to run multiple times |
| 104 | +- **Distribution abstraction**: Detect OS via `/etc/debian_version` (Debian/Ubuntu) or `/etc/alpine-release` (Alpine); choose package manager (`apt` vs `apk`) accordingly. Keep distro detection centralized in `install.sh` |
| 105 | +- **Build-time execution**: Scripts run sequentially during container build; avoid backgrounding tasks that affect determinism |
| 106 | + |
| 107 | +## 5. Build System |
| 108 | + |
| 109 | +No compiled build system. Development and testing commands: |
| 110 | + |
| 111 | +```bash |
| 112 | +# Inside devcontainer: Install dev tooling |
| 113 | +npm install -g @devcontainers/cli |
| 114 | + |
| 115 | +# Run a feature installer locally (for testing) |
| 116 | +bash src/<feature>/install.sh |
| 117 | + |
| 118 | +# Run linter on shell scripts |
| 119 | +shellcheck src/clang/install.sh |
| 120 | + |
| 121 | +# Run bats tests (if present) |
| 122 | +bats test/foo.bats |
| 123 | +``` |
| 124 | + |
| 125 | +### Environment variables and options |
| 126 | + |
| 127 | +Features read options from environment variables mapped to `devcontainer-feature.json` options: |
| 128 | +- Example: `DOTFILES_REPO`, `CHEZMOI_BRANCH`, `KEEP_GOING`, `ATUIN_*` |
| 129 | +- Document all expected environment variables in feature `README.md` |
| 130 | + |
| 131 | +## 6. Development Workflow |
| 132 | + |
| 133 | +### Adding new features |
| 134 | + |
| 135 | +1. Create `src/<feature-name>/` directory (kebab-case) |
| 136 | +2. Add `devcontainer-feature.json` with schema fields: `name`, `id`, `version` (semver), `description`, `documentationURL`, optional `options`, `installsAfter`, `mounts` |
| 137 | +3. Add `install.sh` as installer entrypoint; make executable; follow distro detection, package checks, and idempotence patterns from existing features |
| 138 | +4. Add `README.md` documenting: |
| 139 | + - What the feature does |
| 140 | + - All options (mapped to environment variables) |
| 141 | + - Environment variables required/supported |
| 142 | + - Any host setup requirements |
| 143 | +5. Add tests under `test/<feature>/` if behavior should be validated automatically |
| 144 | +6. Follow shell style rules from [shell-script-user.instructions.md](vscode-userdata:/home/ckagerer/.config/Code/User/prompts/shell-script-user.instructions.md) |
| 145 | + |
| 146 | +### Testing |
| 147 | + |
| 148 | +- **Static analysis**: Use `shellcheck` for shell script validation |
| 149 | +- **Integration tests**: Use `bats` (Bash Automated Testing System) or custom shell test harness |
| 150 | +- **Test runners**: Provide `test/run.sh` wrapper if adding comprehensive tests |
| 151 | + |
| 152 | +## 7. Important Context |
| 153 | + |
| 154 | +### What AI agents should know |
| 155 | + |
| 156 | +**Critical files**: |
| 157 | +- `devcontainer-feature.json` files under `src/*/`: Control feature metadata and build ordering |
| 158 | +- `install.sh` scripts: Critical runtime entrypoints executed during container build |
| 159 | +- `.devcontainer.json`: Affects local development image and installed dev tools |
| 160 | + |
| 161 | +**Protected files** (do not change without approval): |
| 162 | +- Files under `.github/` (CI workflows) |
| 163 | +- `.devcontainer.json` |
| 164 | +- `LICENSE` |
| 165 | +- Do not edit `devcontainer-feature.json` version fields lightly—maintain semantic versioning |
| 166 | + |
| 167 | +**Performance-sensitive areas**: |
| 168 | +- Network operations in `install.sh` (large downloads): Keep retries and timeouts consistent |
| 169 | +- Avoid heavy CPU work in installer scripts |
| 170 | + |
| 171 | +**API/ABI compatibility**: |
| 172 | +- Features expose behavior via container side-effects, mounts, and `postCreateCommand` |
| 173 | +- Keep option names stable; bump `version` on breaking changes |
| 174 | + |
| 175 | +**Platform-specific code**: |
| 176 | +- All installers must contain distro detection |
| 177 | +- If adding code for a new platform, place it inside `install.sh` and document preconditions in `README.md` |
| 178 | + |
| 179 | +### What to avoid |
| 180 | + |
| 181 | +**Anti-patterns**: |
| 182 | +- Hardcoding home directory paths; use `getent` or environment variables (see `CHEZMOI_USER_HOME` usage in `src/chezmoi/install.sh`) |
| 183 | +- Assuming a single package manager; use distro detection and install guards |
| 184 | +- Modifying `.github/` workflows or `.devcontainer.json` without tests and approval |
| 185 | + |
| 186 | +**Deprecated patterns**: |
| 187 | +- Avoid full `set -e` without cleanup for generated files if `KEEP_GOING` semantics are expected |
| 188 | + |
| 189 | +**Files never to edit manually**: |
| 190 | +- Any file documented as generated |
| 191 | +- CI workflow files in `.github/` (unless explicitly updating CI) |
| 192 | + |
| 193 | +## 8. Code Examples |
| 194 | + |
| 195 | +### Typical `devcontainer-feature.json` |
| 196 | + |
| 197 | +```json |
| 198 | +{ |
| 199 | + "name": "chezmoi", |
| 200 | + "id": "chezmoi", |
| 201 | + "version": "1.6.1", |
| 202 | + "description": "Install chezmoi dotfile manager", |
| 203 | + "documentationURL": "https://github.com/ckagerer/devcontainer-features/tree/main/src/chezmoi", |
| 204 | + "options": { |
| 205 | + "version": { |
| 206 | + "type": "string", |
| 207 | + "default": "latest", |
| 208 | + "description": "Version of chezmoi to install" |
| 209 | + } |
| 210 | + }, |
| 211 | + "postCreateCommand": "/usr/local/share/chezmoi-init.sh" |
| 212 | +} |
| 213 | +``` |
| 214 | + |
| 215 | +### Typical `install.sh` pattern |
| 216 | + |
| 217 | +```sh |
| 218 | +#!/usr/bin/env sh |
| 219 | + |
| 220 | +if [ "${KEEP_GOING:-false}" = "true" ]; then |
| 221 | + set +e |
| 222 | +else |
| 223 | + set -e |
| 224 | +fi |
| 225 | +set -x |
| 226 | + |
| 227 | +# Validate required environment variables |
| 228 | +if [ -z "${DOTFILES_REPO}" ]; then |
| 229 | + echo "DOTFILES_REPO is not set" |
| 230 | + exit 1 |
| 231 | +fi |
| 232 | + |
| 233 | +# Distribution detection |
| 234 | +if [ -f /etc/debian_version ]; then |
| 235 | + apt update |
| 236 | + apt install -y curl |
| 237 | +elif [ -f /etc/alpine-release ]; then |
| 238 | + apk add --no-cache curl |
| 239 | +else |
| 240 | + echo "Unsupported distribution" |
| 241 | + exit 1 |
| 242 | +fi |
| 243 | + |
| 244 | +# Check if already installed |
| 245 | +if command -v chezmoi >/dev/null 2>&1; then |
| 246 | + echo "chezmoi already installed" |
| 247 | + exit 0 |
| 248 | +fi |
| 249 | + |
| 250 | +# Install |
| 251 | +curl -fsSL https://get.chezmoi.io | sh |
| 252 | +``` |
| 253 | + |
| 254 | +## 9. References |
| 255 | + |
| 256 | +- **Project documentation**: [README.md](README.md) (project root) and each feature's `README.md` under [src/](src/) |
| 257 | +- **Devcontainer features specification**: https://containers.dev/implementors/features/ |
| 258 | +- **Coding conventions**: |
| 259 | + - Shell scripts: [shell-script-user.instructions.md](vscode-userdata:/home/ckagerer/.config/Code/User/prompts/shell-script-user.instructions.md) |
| 260 | + - Markdown: [markdown.instructions.md](vscode-userdata:/home/ckagerer/.config/Code/User/prompts/markdown.instructions.md) |
| 261 | + |
| 262 | +## Prescriptive Agent Rules (must follow) |
| 263 | + |
| 264 | +1. **Make minimal edits**: Prefer small, well-tested changes and preserve existing code style |
| 265 | +2. **Never edit generated or protected files** (see section 7); update generator/config instead |
| 266 | +3. **Always add or update tests** for behavior changes; include test command in commit message if relevant |
| 267 | +4. **Run linters and tests locally** (or in devcontainer) before proposing changes |
| 268 | +5. **Merge, don't replace**: If modifying AGENTS.md, preserve existing useful sections |
| 269 | +6. **Document changes**: Note cross-cutting changes in AGENTS.md and link affected files |
| 270 | +7. **Ask before large changes**: If uncertain about file placement or testing strategy, ask before making sweeping modifications |
0 commit comments