Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

### Changed

- **Service: an image per devcontainer.json in sub-folders** ([ADR-0016](docs/decisions/0016-one-image-per-devcontainer-json.md)).
Each `.devcontainer/<folder>/devcontainer.json` is built with `--config` as an image of its own,
`<name>-<folder id>`, after the root config's `main` image. `POST /build` lists them all in a new
`images` field. The existing `image`/`registry`/`name`/`tag`/`imageBuildLogId` fields describe
`images[0]`, so a repository with a root config gets the same response as before. A repository with
only sub-folder configs used to fall back to `fallbackImage` (or fail); now it builds those configs,
and the top-level fields describe the first one. The fallback image is used only when there's no
config anywhere. Without a `fallbackImage`, that case is now a 400 (`no devcontainer.json found`)
instead of a 500. New optional request fields: `instances` builds only the listed ids (an unknown
id is a 400 listing the valid ones), and `dryRun` resolves the list and image names without building
or pushing. Two sub-folders that map to the same id are a 400.
- **Breaking:** **Template: `hostRequirements` are minimums**, as in the Dev Container spec, instead of overriding
- **Image: Node 24 LTS** (was Node 20, end-of-life since April 2026), base image pinned by digest.
The npm package's `engines` is now `node >=22`.
Expand Down
5 changes: 0 additions & 5 deletions charts/test-git-server/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ spec:
{{- else }}
mkdir -p "/work/{{ .name }}/$(dirname "{{ $configPath }}")"
cp "/seed/{{ .name }}.json" "/work/{{ .name }}/{{ $configPath }}"
{{- /* The same devcontainer.json at more locations, for repos with several configs (one image each, ADR-0015). */}}
{{- range .extraConfigPaths }}
mkdir -p "/work/{{ $repo.name }}/$(dirname "{{ . }}")"
cp "/seed/{{ $repo.name }}.json" "/work/{{ $repo.name }}/{{ . }}"
{{- end }}
{{- if .dockerfile }}
{{- /* A real Dockerfile alongside devcontainer.json, for repos whose config uses "build": {"dockerfile": ...} instead of a plain "image" reference - the CLI resolves a relative dockerfile path against the config's own directory. */}}
cp "/seed/{{ .name }}.Dockerfile" "/work/{{ .name }}/$(dirname "{{ $configPath }}")/Dockerfile"
Expand Down
22 changes: 0 additions & 22 deletions charts/test-git-server/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,28 +68,6 @@ seedRepos:
configPath: .devcontainer/gamma/devcontainer.json
devcontainerJson: |
{ "image": "mcr.microsoft.com/devcontainers/base:alpine-3.20" }
# Several devcontainer.json files in one repo - one image per config
# (ADR-0015). extraConfigPaths copies the same devcontainer.json to more
# locations. The colliding one's two folders sanitize to the same id
# ("back-end"), which the service rejects before building anything.
- name: devcontainer-json-two-folders
path: location/devcontainer-json-two-folders.git
configPath: .devcontainer/frontend/devcontainer.json
extraConfigPaths: [.devcontainer/backend/devcontainer.json]
devcontainerJson: |
{ "image": "mcr.microsoft.com/devcontainers/base:alpine-3.20" }
- name: devcontainer-json-root-and-folder
path: location/devcontainer-json-root-and-folder.git
configPath: .devcontainer/devcontainer.json
extraConfigPaths: [.devcontainer/tools/devcontainer.json]
devcontainerJson: |
{ "image": "mcr.microsoft.com/devcontainers/base:alpine-3.20" }
- name: devcontainer-json-colliding-folders
path: location/devcontainer-json-colliding-folders.git
configPath: .devcontainer/Back_End/devcontainer.json
extraConfigPaths: [.devcontainer/back-end/devcontainer.json]
devcontainerJson: |
{ "image": "mcr.microsoft.com/devcontainers/base:alpine-3.20" }
# No devcontainer.json anywhere - the "not found at all" case, distinct
# from (and adjacent to) the location-discovery repos above.
- name: devcontainer-json-missing
Expand Down
7 changes: 0 additions & 7 deletions docs/claude/notes/devcontainer-subfolder-config-discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ created: 2026-09-06

# The sub-folder devcontainer.json location isn't auto-discoverable

> **Superseded (2026-10-07)** by
> [ADR-0016](../../decisions/0016-one-image-per-devcontainer-json.md). The
> service now finds every sub-folder config itself, builds one image per
> config with `--config`, and lists them in `POST /build`'s `images`. The
> human choice moved to the caller (the `instances` filter), so no separate
> discovery service is needed. The rest of this note is kept as history.

## What happened

While building `devcontainer_config_discovery.feature`, `test-git-server`
Expand Down
42 changes: 14 additions & 28 deletions docs/claude/plans/004-devcontainer-instance-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,20 @@ created: 2026-10-06

# List support: `/build` returns a list of images → one Coder agent per image

## Status (2026-10-07): Phase B step 1 (service) implemented; provider, module and template still open

The service side is on `feat/multi-config-builds`, recorded in
[ADR-0016](../../decisions/0016-one-image-per-devcontainer-json.md) (0013 was
already taken). It differs from this plan in these ways:

- **Additive, not breaking.** The 1.x API is stable
([Versioning](../../project/versioning.md)), so the response keeps
`image`/`registry`/`name`/`tag`/`imageBuildLogId` and adds `images[]`. The
top-level fields describe `images[0]`: `main` when there is a root config,
else the first sub-folder item.
- **400, not 422,** for an unknown `instances` id, an id collision, or an
empty id. The "no configs" case is also a 400, and only when there's no
`fallbackImage`. With one, the fallback is a single `main` item, used only
when no config exists at any of the three locations.
- **ids** also drop leading and trailing `-`, so they stay valid in image
names and as DNS labels. Symlinked sub-folders are skipped.
- **`instances: []`** fails the shape check. Use `null` or omit it for "all".
- **Dry runs** count as `status="dry_run"` in `devcontainer_builder_builds_total`.
- **New code:** discovery is in `service/src/config-discovery.ts`, and
`buildInstance()`/`devcontainerBuildArgs()` are in `build.ts`. BDD coverage
is in `devcontainer_config_discovery.feature`, with new seed repos
`devcontainer-json-{two-folders,root-and-folder,colliding-folders}`. Not run
yet, because it needs the cluster.

Next: the provider's `images`/`instances`/dry run in `ModifyPlan` (step 2),
then the module (step 3), the template (step 4), and the guide and README
(step 5). Phase A (the template's list-of-one refactor) is still open.
## Status (2026-10-07): abandoned

Dropped before 1.0 by the maintainer. The service side (#60, ADR-0016) was
merged and then reverted, so `images[]`, `instances` and `dryRun` never ship
in a release and aren't part of the 1.x API. The template refactor (#65) and
the provider's `images`/`instances` (provider #11) were closed unmerged.

The blocker that settled it: Coder names an agent after its Terraform
resource name (`ConvertState` in `provisioner/terraform/resources.go` fails
with `duplicate agent name`), so a `for_each` over `coder_agent` can't give
one agent per image.

A repository whose only configs are in `.devcontainer/<folder>/` behaves as
in 0.5.0: it falls back to `fallbackImage`, or fails.

## Context

Expand Down
12 changes: 3 additions & 9 deletions docs/claude/plans/005-v1-readiness.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ All merged on `main`, each verified as noted:
- **Features:**
- `customizations.kubernetes` for pod resources and node placement #59 (ADR-0015; scratch workspace:
requests and limits applied, hostRequirements and placement ignored with warnings);
- one image per devcontainer.json in sub-folders #60 (ADR-0016, additive API; scratch release:
dry run, a folder build, a root build unchanged, unknown ids → 400).
- one image per devcontainer.json in sub-folders #60: reverted, the feature is abandoned (see
[plan 004](004-devcontainer-instance-list.md)).
- **Tests:**
- route tests and a fake registry #61: 87% → 94% lines, 74% → 88% branches, 129 unit tests;
- BDD `request_validation` on the cluster: 53 of 53 scenarios.
Expand All @@ -76,10 +76,6 @@ All merged on `main`, each verified as noted:

## Still open before tagging

- **Multiple configurations end to end:**
- provider `images`/`instances` (PR in progress);
- template Phase A, keyed by instance with `moved` blocks (PR in progress);
- then Phase B, which needs a provider release.
- **Harbor tutorial** once rts #37 is applied, including whether `DELETE /image` by digest can remove
another tag that shares it.

Expand Down Expand Up @@ -117,6 +113,4 @@ All merged on `main`, each verified as noted:
`hostRequirements`. Needs: the service exposing it through `GET /devcontainer`, the provider's
data source, and the template mapping (capped like today), plus an ADR.
- **Harbor** (rts #37): a self-hosted registry, after which come private-registry tutorials.
- **Multiple Dev Container configurations** ([plan 004](004-devcontainer-instance-list.md)):
- the service side can land additively (`images[]` next to today's single-image fields);
- the template side needs a provider release.
- **Multiple Dev Container configurations:** abandoned ([plan 004](004-devcontainer-instance-list.md)).
2 changes: 1 addition & 1 deletion docs/concepts/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ and per-domain reference pages.
|---|---|
| `health.feature` | Liveness/readiness signals, including a deliberately-never-ready pod |
| `request_validation.feature` | `/build`'s up-front shape check, independent of whether the values are usable |
| `devcontainer_config_discovery.feature` | Every devcontainer.json location the spec allows (root, `.devcontainer/`, `.devcontainer/<folder>/`), one image per config, `instances`, `dryRun`, and the 400s for an unbuildable list |
| `devcontainer_config_discovery.feature` | The devcontainer CLI's real auto-discovery locations (root, `.devcontainer/`, *not* an arbitrary sub-folder) |
| `devcontainer_config_content.feature` | A discoverable-but-unusable `devcontainer.json` fails clearly, including the `"build":{"dockerfile":...}` path |
| `image_resolution.feature` | Name/tag/registry defaulting and mapping-rule resolution |
| `registry_auth.feature` | Ambient vs. per-request registry push credentials against a real auth-enforcing registry |
Expand Down
128 changes: 0 additions & 128 deletions docs/decisions/0016-one-image-per-devcontainer-json.md

This file was deleted.

3 changes: 1 addition & 2 deletions docs/decisions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ change, never rewritten in place).
| [0010](0010-command-output-capture.md) | Capture git/docker subprocess output to files instead of stdout | accepted |
| [0011](0011-devcontainer-metadata-endpoint.md) | `GET /devcontainer`: merged Dev Container metadata, read from the image | accepted |
| [0012](0012-dev-container-to-kubernetes-runtime-mapping.md) | Dev Container → Kubernetes runtime mapping | accepted (`hostRequirements` part superseded by [0014](0014-host-requirements-are-minimums.md)) |
| [0013](0013-fallback-config-for-repos-without-one.md) | A fallback image for repositories without a devcontainer.json | accepted (sub-folder consequence superseded by [0016](0016-one-image-per-devcontainer-json.md)) |
| [0013](0013-fallback-config-for-repos-without-one.md) | A fallback image for repositories without a devcontainer.json | accepted |
| [0014](0014-host-requirements-are-minimums.md) | `hostRequirements` are minimums | accepted |
| [0015](0015-kubernetes-customizations.md) | `customizations.kubernetes` for pod resources and node placement | accepted |
| [0016](0016-one-image-per-devcontainer-json.md) | One image per devcontainer.json, listed in `POST /build`'s response | accepted |
2 changes: 1 addition & 1 deletion docs/reference/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ concern documented in
| Clone timeout (seconds) | `--clone-timeout` | `CLONE_TIMEOUT_SECONDS` | `build.cloneTimeoutSeconds` | `600` |
| Build+push timeout (seconds) | `--build-timeout` | `BUILD_TIMEOUT_SECONDS` | `build.timeoutSeconds` | `3600` |
| Concurrent builds per instance | `--max-concurrent-builds` | `MAX_CONCURRENT_BUILDS` | `build.maxConcurrent` | `4` (`0` = no limit) |
| Fallback image (repos without any devcontainer.json, not even in a sub-folder, [ADR-0013](../decisions/0013-fallback-config-for-repos-without-one.md)) | `--fallback-image` | `FALLBACK_IMAGE` | `build.fallbackImage` | *(unset — such builds fail with 400)*; the chart sets `mcr.microsoft.com/devcontainers/base:ubuntu` |
| Fallback image (repos without a devcontainer.json, [ADR-0013](../decisions/0013-fallback-config-for-repos-without-one.md)) | `--fallback-image` | `FALLBACK_IMAGE` | `build.fallbackImage` | *(unset — such builds fail)*; the chart sets `mcr.microsoft.com/devcontainers/base:ubuntu` |
| Sentry/GlitchTip DSN | `--sentry-dsn` | `SENTRY_DSN` | `sentry.dsn` | *(unset — error tracking off)* |
| Service name | `--service-name` | `SERVICE_NAME` | `observability.serviceName` | `devcontainer-builder` |
| Deployment environment | `--environment` | `DEPLOYMENT_ENVIRONMENT` | `observability.environment` | `development` |
Expand Down
Loading
Loading