Developing¶
Prerequisites¶
Opening this repo in the provided
.devcontainer/
(VS Code Dev Containers, or any Dev Container-compatible tool) gets you
everything below for free — its postCreateCommand.sh installs Helm,
Terraform, the GitHub CLI, kubectl, the Docker CLI + buildx plugin,
and the @devcontainers/cli, on top of the base
mcr.microsoft.com/devcontainers/typescript-node:26-trixie image
(Node 26 already included). Versions are deliberately unpinned there,
matching CI's own unpinned setup-helm/setup-terraform actions.
Outside a Dev Container, you need real, working installs of: Node 26 or
newer (what the image runs),
Docker CLI with the buildx plugin, @devcontainers/cli, and git —
the same set service/Dockerfile installs into the deployable image
itself.
The service¶
cd service
npm install
npm run build # tsc -> dist/
npm run start # node dist/index.js - needs BUILDKIT_ENDPOINT
npm run start runs the real, compiled entrypoint — it needs a real
BUILDKIT_ENDPOINT pointed at a reachable BuildKit daemon before
/health/ready reports ready, and a real git host to clone from before
/build can do anything. See Configuration
for every other source it reads at startup.
As a standalone CLI¶
The same server is published to npm and runnable directly, without cloning this repo:
npx @deepspacecartel/devcontainer-builder --buildkit-endpoint tcp://buildkit.example:1234
Still needs docker (with the buildx plugin), @devcontainers/cli, and
git on PATH locally — the CLI doesn't bundle or replace any of them,
same prerequisites the Docker image bakes in below. Every flag/env
var/settings-file field is identical either way — see
Configuration.
The container image¶
service/Dockerfile has two build targets, sharing one runtime base: Node 26
(node:26-trixie-slim, pinned by digest), git, the Docker CLI +
buildx plugin (no dockerd, see
0001), @devcontainers/cli
pinned to the version the service is checked against
(DEVCONTAINERS_CLI_VERSION build arg), tini
as PID 1 so exited git/ssh/buildx children are reaped, and a non-root
builder user that the image runs as by number (USER 2000:2000), so
Kubernetes' runAsNonRoot can verify it. OCI labels
(org.opencontainers.image.source, .description, .licenses, .version)
link the image to this repository:
# dev: builds from this checkout's source (npm ci, then tsc -> dist/), no
# network dependency beyond npm's own lockfile install. This is also what a bare
# `docker build service/` produces, with no --target at all - use it for
# local iteration and testing against a local cluster.
docker build --target dev -t devcontainer-builder service/
# release: installs a specific version of the published npm package
# instead of building from source, so the image is provably the same
# artifact as what's live on npm. Needs that version to already be
# published - this is what .github/workflows/release.yaml builds.
docker build --target release --build-arg PACKAGE_VERSION=0.1.0 \
-t devcontainer-builder service/
The Helm chart¶
helm lint charts/devcontainer-builder
helm template charts/devcontainer-builder -f my-values.yaml
See the Helm chart reference for every value.
The Terraform module¶
cd terraform/devcontainer-build
terraform init
terraform fmt -check -diff
terraform validate
terraform test # real contract tests, mocked - no live service needed
See the Terraform module reference.
Node/npm availability isn't guaranteed in every environment
If npm/node aren't on PATH, don't assume the TypeScript
compiles — verify it or ask, rather than reporting success on faith.
This documentation site¶
# The two HTTP API nav entries (mkdocs.yml) need three real, generated
# paths to exist first - all gitignored, not source, all regenerated from
# the service's own schemas:
cd service && npm install && npm run build
OPENAPI_OUTPUT_PATH=../docs/openapi.json node scripts/export-openapi.mjs
npx redocly build-docs ../docs/openapi.json -o ../docs/api-reference.html
SWAGGER_UI_OUTPUT_DIR=../docs/api-swagger-ui node scripts/export-swagger-ui.mjs
cd ..
python3 -m venv .venv-docs && . .venv-docs/bin/activate
pip install -r docs/requirements.txt
mkdocs serve # live preview at http://127.0.0.1:8000
mkdocs build --strict # what CI should run on every docs/** change
docs/api-reference.html (Redoc) and docs/api-swagger-ui/ (the exact
static bundle GET /documentation serves live, pointed at the sibling
openapi.json instead of a live route) are both real standalone pages,
each its own top-level nav entry ("HTTP API (Redoc)"/"HTTP API (Swagger
UI)", mkdocs.yml) — not a hand-written Markdown page wrapping a widget
embedded inline in an mkdocs-material page. An earlier version tried the
latter (Redoc only, as a section on a hand-written docs/reference/API.md)
and it silently broke under navigation.instant (this theme's SPA-style
client-side page transitions don't re-run a <script> tag injected via a
normal client-side DOM patch, only a real full page load does).
--strict is what actually catches a broken internal link or a heading
renamed without updating what links into it — run it after any edit
that touches a link or a heading, not just once at the end.
docs/claude/ is deliberately excluded from this site's nav
docs/claude/plans/ and docs/claude/notes/ are internal
planning/investigation records, not part of the published site —
mkdocs build reports them as "exists but not in nav" (an INFO,
not a failure); that's intentional, not an oversight. See the
Decision log for how those differ from a
real ADR.