feat(freeform): opt-in Claude Code remote control; fix Homebrew persistence across restarts - #184
Closed
rfay wants to merge 8 commits into
Closed
feat(freeform): opt-in Claude Code remote control; fix Homebrew persistence across restarts#184rfay wants to merge 8 commits into
rfay wants to merge 8 commits into
Conversation
…ew across restarts Homebrew (/home/linuxbrew) lived on the ephemeral container filesystem in all three templates, so brew upgrade/install could fail with a cross-device link error (cache and Cellar on different filesystems) and any successful upgrade was lost on the next workspace restart. Mount /home/linuxbrew as a persistent per-workspace volume (mirroring the existing dind-cache pattern) and relocate HOMEBREW_CACHE onto it, fixing this for every brew formula in all templates. On top of that, add enable_claude_code as an opt-in freeform-only feature: upgrades Claude Code at startup and runs it in a tmux session named after the workspace, attachable via a dashboard app button (Coder's native command app type -- no extra daemon). --dangerously-skip-permissions stays a separate, off-by-default toggle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
drupal-core and freeform both gitignore their per-template VERSION file (it's copied from the root VERSION by the Makefile's push_template recipe before every `coder templates push`). drupal-contrib was missing that .gitignore entry, so its copy got committed by accident. Left tracked, every version bump would show up as a dirty uncommitted change in drupal-contrib specifically, and a stray `git checkout .`/`git clean` could silently revert it to a stale version. Add the matching .gitignore entry and untrack the file (kept on disk so the Makefile's cp step still works). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
enable_claude_code/claude_code_skip_permissions were plain Terraform variables, which are only settable by whoever pushes the template (via coder templates push --variable) and apply to every workspace on that template version -- they never show up in coder create/the web UI or coder update. Discovered on staging: the toggle was invisible everywhere a workspace owner would look for it. Convert both to coder_parameters (matching the existing project_names/ vscode_extensions pattern), so each workspace can opt in independently via `coder create --parameter enable_claude_code=true`, `coder update`, or the dashboard. Update tests to use override_data on the new data sources instead of setting Terraform variables directly, and correct the README accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Matches the image already rebuilt and pushed on amd64 with the HOMEBREW_CACHE relocation from this branch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Twice in this branch, an untracked/inconsistently-tracked per-template VERSION file caused real confusion: drupal-contrib/VERSION was accidentally committed while drupal-core/freeform's were gitignored, and then a freeform template push picked up a stale root VERSION with no git-visible record of what was actually deployed. Track all three (they now match root at v0.5). `make push-template-*` still runs `cp VERSION <template>/VERSION` before pushing -- required because the Coder provisioner only receives the contents of --directory <template>, not the repo root -- but that copy now produces a normal, reviewable git diff instead of being silently discarded or drifting unnoticed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The startup script and coder_app both launched plain `claude` (or with --dangerously-skip-permissions) -- Remote Control was never enabled at all, despite that being the entire point of the feature. Found on staging: the session worked but wasn't connectable from claude.ai/code or the mobile app. Add --remote-control $CODER_WORKSPACE_NAME to both the startup script's CLAUDE_CMD and the coder_app's fallback attach/new command, so the tmux session actually registers as a named Remote Control session. Correct the README: the tmux/app-button terminal is a local fallback for first login and troubleshooting, not the intended way to use the feature -- that's claude.ai/code or the Claude mobile app, where the session shows up by name. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ate copies
Each template read its own local VERSION file (file("${path.module}/VERSION"))
because coder templates push only ships the contents of --directory <template>
to the provisioner -- it never had access to the repo root. That meant three
physical copies kept in sync by a Makefile `cp` step, which already caused two
real bugs this branch: an untracked/stale copy after a manual push, and
drupal-contrib's copy being accidentally committed while the other two were
gitignored.
Drop the per-template copies entirely. Each template.tf now just uses
var.image_version directly; the Makefile passes it explicitly via
--variable image_version=$(VERSION) (already computed from the root VERSION
file) when pushing. Root VERSION is now the only copy anywhere.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nup sweep cleanup-deleted-workspaces.sh's orphan detection only matched coder-<owner>-<workspace>-dind-cache -- the new persistent linuxbrew volume was invisible to it, so an orphaned one (from an interrupted destroy, state desync, etc. -- exactly the failure modes this script is a safety net for) would never be flagged or cleaned up. Match both known suffixes, and note in-line that any future persistent per-workspace volume needs to be added here too. Also tightens a comment in workspace-lifecycle-cleanup.sh that attributed volume cleanup to "the destroy provisioner" -- the null_resource destroy provisioner in each template only removes the host-bind-mounted directory; volumes are removed because Terraform destroys them along with the rest of the workspace's resource graph, which is a separate mechanism. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Member
Author
|
This had too much stuff in it. Closing, breaking into multiple PRs. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bug fix 1 — Homebrew upgrades could fail outright (EXDEV cross-device link error).
/home/linuxbrew/.linuxbrew(the Homebrew Cellar) lives on the container's image filesystem, but Homebrew's default cache path ($HOME/.cache/Homebrew) resolves to/home/coder/.cache/Homebrew-- a separate, volume-mounted filesystem. Renaming/hardlinking a downloaded bottle across that boundary throwsEXDEV. Fixed by relocatingHOMEBREW_CACHEvia a DockerfileENVso it always sits on the same filesystem as the Cellar.Bug fix 2 — Homebrew upgrades didn't persist across workspace restarts.
docker_container.workspaceis destroyed and recreated fresh from the image on every workspace stop/start, so anything installed/upgraded on the container's own root filesystem (including Homebrew) was silently lost on the next restart -- only the bind-mounted/home/coderand thedind-cachevolume survived. Fixed in all three templates (freeform,drupal-core,drupal-contrib) by adding a new per-workspacedocker_volumemounted at/home/linuxbrew, matching the existingdind-cachepattern. Docker auto-populates a fresh named volume from the image's existing directory contents on first mount, so no manual seed-copy step is needed -- just an ownership fix mirroring the existing/home/coderchown.New feature — opt-in Claude Code remote control (
freeformtemplate only).Two new variables,
enable_claude_codeandclaude_code_skip_permissions(both defaultfalse). When enabled, the startup script upgrades Claude Code via Homebrew and starts a tmux session named after the workspace runningclaude-- a strict 1:1 workspace-to-session correspondence. A newcoder_appusing Coder's nativecommandattribute (the same mechanism used for terminal-style apps elsewhere) lets you attach from the dashboard with one click -- no ttyd/wetty, no extra daemon or port, consistent with this repo's minimal/infra-only philosophy.--dangerously-skip-permissionsis a separate, off-by-default toggle, never bundled into the base feature. Login is interactive OAuth only -- no API key variables are wired in.Known, documented limitation: the tmux session is shared by every attached client, so
cd/git checkoutfrom inside it affects everyone attached -- routine repo administration should be done from a separate, ordinary terminal instead.Test plan
terraform fmt -recursiveterraform validateforfreeform,drupal-core,drupal-contribterraform testfor all three templates (16/7/7 assertions passing, including new coverage for thelinuxbrewvolume and the Claude Code feature toggles)HOMEBREW_CACHEDockerfile changecoder updatean existingfreeformworkspace and confirmbrew upgradesurvives a stop/start cycleenable_claude_codeon afreeformworkspace, confirm the "Claude Code" app attaches to a tmux session named after the workspace, and confirm interactive OAuth login works🤖 Generated with Claude Code