Skip to content

Commit fcf50ae

Browse files
committed
fix(devcontainer): make launches zero-build and stop the merge conflict
Three changes, each with its reasoning in the file itself: - Drop the runtime "features" block. The claude-code feature is already baked into the published image by .devcontainer/ci/devcontainer.json, so declaring it again made the spec build a derived image on the first launch of every branch -- the "prebuilt image for fast startup" still paid for a build. - Drop "runArgs": ["--network=host"]. A container per branch is the point of launching with dl; host networking makes every bound port a host port so two branches collide. homeassistant-config and colcon-runner keep the flag on purpose (mDNS discovery, ROS 2 DDS) and are not being changed. - Drop the ~/.ssh and ~/.config/gh mounts, following the precedent already reasoned out in blooop/wayfinder. The gh mount never worked (gh keeps its token in the system keyring; dl supplies GH_TOKEN instead). The ~/.ssh directory mount wrote Host <id>.devpod blocks onto the developer's real ssh config naming a ProxyCommand binary that only exists in the container. SSH_AUTH_SOCK is removed from containerEnv with it. Also move "name" to the end of the file. It is the only per-repo value here, so it is the only line a child repo edits, and while it sat directly above the image/build block -- the block this template changes most -- every `pixi run update-from-template-repo` conflicted on this file, because git cannot split adjacent hunks. Measured against blooop/dbw: reverting only the name made the same merge clean. At the end of the file the two edits are separate hunks and merge cleanly. Also scope the CI build context to .devcontainer. The Dockerfile COPYs nothing, so a repo-root context was hashed and uploaded for no reason.
1 parent 1c0a88a commit fcf50ae

2 files changed

Lines changed: 76 additions & 16 deletions

File tree

‎.devcontainer/ci/devcontainer.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "python_template-build",
33
"build": {
44
"dockerfile": "../Dockerfile",
5-
"context": "../.."
5+
"context": "."
66
},
77
"features": {
88
"../claude-code": {},

‎.devcontainer/devcontainer.json‎

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
{
2-
"name": "python_template",
3-
42
// Uses the template's prebuilt image by default for fast startup.
53
// To build locally: pixi run dev-use-local
64
// To use this repo's own prebuilt image: pixi run dev-use-prebuilt
@@ -10,13 +8,38 @@
108
// },
119
"image": "ghcr.io/blooop/python_template/devcontainer:latest",
1210

13-
"features": {
14-
"./claude-code": {},
15-
// "ghcr.io/devcontainers/features/docker-in-docker:2": {},
16-
// "ghcr.io/devcontainers/features/common-utils:2": {}
17-
},
11+
// There is deliberately no "features" block. The claude-code feature is
12+
// already baked into the image above: .devcontainer/ci/devcontainer.json,
13+
// which is the config CI builds that image from, declares
14+
// "../claude-code". Declaring it again here made the devcontainer spec
15+
// build a derived image on the first launch of every branch, reinstalling
16+
// something the pulled image already carried -- so the "prebuilt image for
17+
// fast startup" above still paid for a build. With this block gone the
18+
// launch is a pull and nothing else.
19+
//
20+
// Adding a feature back here is allowed and costs that derived build again.
21+
// If that ever becomes the normal case, the fix is devlaunch's design --
22+
// a "build:" block plus customizations.devpod.prebuildRepository, which
23+
// caches the fully assembled image, features included. That needs CI to
24+
// publish with `devpod build` rather than the devcontainers/ci action;
25+
// devpod's prebuild hash is computed from the Dockerfile and build context,
26+
// so it does not apply to an "image:" config like this one at all.
1827

1928
"initializeCommand": ".devcontainer/claude-code/init-host.sh",
29+
30+
// There is deliberately no "runArgs": ["--network=host"].
31+
//
32+
// A container per branch is the reason these repos are launched with `dl`,
33+
// and host networking takes it away: the container joins the host's network
34+
// namespace, so every port a test or dev server binds is a host port and
35+
// two branches of this repo collide on the first one they share. The
36+
// default bridge network keeps them apart at no cost -- nothing in this
37+
// template reaches for the host's network.
38+
//
39+
// Two sibling repos keep the flag on purpose and should not be "fixed" to
40+
// match this file: homeassistant-config (mDNS/discovery needs the host LAN)
41+
// and colcon-runner (ROS 2 DDS multicast discovery).
42+
2043
"customizations": {
2144
"vscode": {
2245
"settings": {},
@@ -31,21 +54,58 @@
3154
]
3255
}
3356
},
34-
"runArgs": [
35-
"--network=host"
36-
],
57+
3758
"containerEnv": {
3859
"CLAUDE_CONFIG_DIR": "/home/vscode/.claude",
3960
"XDG_CONFIG_HOME": "/home/vscode/.config",
4061
"XDG_CACHE_HOME": "/home/vscode/.cache",
41-
"XDG_DATA_HOME": "/home/vscode/.local/share",
42-
"SSH_AUTH_SOCK": "/home/vscode/.ssh/agent.sock"
62+
"XDG_DATA_HOME": "/home/vscode/.local/share"
4363
},
64+
65+
// Two mounts, and the two that are gone were removed for reasons rather
66+
// than tidiness -- both follow the precedent already reasoned out in
67+
// blooop/wayfinder's devcontainer.json.
68+
//
69+
// ~/.config/gh is gone because it never worked: `gh` keeps its token in the
70+
// system keyring, so the mounted hosts.yml carries no oauth_token and
71+
// `gh auth status` inside the container reports the token as invalid.
72+
// GitHub auth arrives as GH_TOKEN instead, which `dl` forwards into every
73+
// workspace it starts (from GH_TOKEN, GITHUB_TOKEN or `gh auth token`,
74+
// whichever answers first). Opened by something other than `dl` -- a plain
75+
// `devpod up`, or VS Code's Reopen in Container -- this container has no
76+
// `gh` login; export GH_TOKEN yourself for those.
77+
//
78+
// ~/.ssh is gone because mounting the directory put entries on the
79+
// developer's real config that nothing outside the container could honour:
80+
// devpod running in here writes `Host <id>.devpod` blocks whose
81+
// ProxyCommand names a binary that exists only inside this container, and
82+
// those outlived the container they pointed at. It also handed over the
83+
// private key, which was never load-bearing -- devpod forwards git
84+
// credentials and can forward an ssh agent, which lends the use of a key
85+
// without copying it. SSH_AUTH_SOCK is gone from containerEnv with it,
86+
// rather than being left as a path nothing fills.
4487
"mounts": [
4588
"source=${localWorkspaceFolderBasename}-pixi,target=${containerWorkspaceFolder}/.pixi,type=volume",
46-
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind",
47-
"source=${localEnv:HOME}/.config/gh,target=/home/vscode/.config/gh,type=bind",
4889
"source=${localEnv:HOME}/.claude,target=/home/vscode/.claude,type=bind"
4990
],
50-
"postCreateCommand": "sudo chown vscode .pixi && pixi install && pixi run prek-install"
91+
92+
"postCreateCommand": "sudo chown vscode .pixi && pixi install && pixi run prek-install",
93+
94+
// Last in the file on purpose, and it is the one line here that is load
95+
// bearing for every repo cut from this template.
96+
//
97+
// "name" is the only per-repo value in this file, so it is the only line a
98+
// child repo edits -- and while it sat on the line above the image/build
99+
// block, which is the block this template changes most, every
100+
// `pixi run update-from-template-repo` conflicted on this file. git cannot
101+
// split adjacent hunks, so the child's one-line name edit collided with the
102+
// template's block edit every time. Measured against blooop/dbw: reverting
103+
// only the name made the same merge clean.
104+
//
105+
// At the end of the file the two edits are separate hunks and merge without
106+
// conflict -- the child keeps its name, and template changes to the image,
107+
// the mounts and postCreateCommand all land on their own. Moving it costs
108+
// one restructuring conflict per child, once; leaving it here costs a
109+
// conflict on this file forever.
110+
"name": "python_template"
51111
}

0 commit comments

Comments
 (0)