feat: set-up — wire dotfiles installer (PR-6b)#43
Merged
Conversation
Implements the core value of `set-up` per `docs/subcommand-specs/set-up/SPEC.md`: convert an already-running container into a DevContainer by applying configuration + image metadata and executing lifecycle hooks, emitting a single-line JSON result on stdout. ## CLI surface `deacon set-up --container-id <id> [--config <path>] [--skip-post-create] [--skip-non-blocking-commands] [--remote-env NAME=VALUE]... [--include-configuration] [--include-merged-configuration] [--container-data-folder <path>]` ## What this PR includes - `--container-id` resolution + inspect validation. Missing container fails with the upstream-aligned summary `"Dev container not found."` - Optional `--config` load via `ConfigLoader::load_with_extends` (extends chain honored per CLAUDE.md). Missing path fails with `"Dev container config (<path>) not found."` - Image-metadata extraction from the container's `devcontainer.metadata` label. Tolerates BOTH the JSON-array form (PR-2 / #27) and the single-object form for older images. - Config merge: file config wins over image metadata on scalar fields (spec §4 `mergeConfiguration(config.config, imageMetadata)`). - Variable substitution for both `configuration` and `mergedConfiguration`. - Lifecycle hook execution via `ContainerLifecycle` (onCreate → updateContent → postCreate → postStart → postAttach), gated by `--skip-post-create` (skips ALL phases per spec §2) and `--skip-non-blocking-commands` (stops after the configured `waitFor`). - JSON output per spec §10: `{outcome: "success", configuration?, mergedConfiguration?}`. `containerId` is intentionally excluded (spec §16 design decision). ## Deferred to PR-6b - `/etc/environment` + `/etc/profile` root-side patches with system markers under `/var/devcontainer/` - Dotfiles installer with target-path marker (would reuse `crates/deacon/src/commands/up/dotfiles.rs`) - A second substitution pass against the live container environment (`${containerEnv:VAR}`) — current pass uses the configured `container_env`, not a live `docker exec` env probe These are spec §5 phases 3a and 3c; both marked "best-effort" with graceful fallback on failure. Splitting them out keeps PR-6a reviewable. ## Tests 14 new unit tests in `set_up::tests`: - `--remote-env` parsing (accepts `NAME=VALUE`, rejects malformed input) - `--config` loading (default when absent, error when missing path) - Image-metadata label parsing (missing → None, array form, single-object form, invalid JSON → error) - Config merging (file wins over metadata; file-only when no metadata) - Argument defaults and JSON-result shape (outcome field, optional fields) Verification: - `cargo fmt --all -- --check` - `cargo clippy --all-targets -- -D warnings` - `cargo test -p deacon --lib` → 227 pass - `make test-nextest-fast` → 1927 pass (no regression) Refs: issue #34 (Tier 1 progress tracker), plan `/home/vscode/.claude/plans/let-s-come-up-with-recursive-sutherland.md` (PR-6 sequencing rationale). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to PR-6a (#36). Adds the three `--dotfiles-*` flags from spec §2 to the `set-up` CLI surface and threads them as a `DotfilesConfig` into `ContainerLifecycle`. The lifecycle helper already implements clone + auto-detected installer (`install.sh` / `bootstrap` / `setup` / `script/*`) + target-path marker for idempotency — set-up just needed to opt in. ## New flags - `--dotfiles-repository <URL or owner/repo>` - `--dotfiles-install-command <string>` (overrides auto-detection) - `--dotfiles-target-path <path>` (defaults to `~/dotfiles`) No repository → no clone. The lifecycle helper's marker-at-target-path guard provides idempotency across re-runs (spec §6, §16 design decision). ## Tests 3 new unit tests in `set_up::tests::build_dotfiles_config_*`: - `returns_none_when_no_repository` — no repo URL means no opt-in, even if the other flags are set - `forwards_all_three_fields` — all flags round-trip into `DotfilesConfig` - `leaves_defaults_to_lifecycle_helper` — `target_path` / `install_command` stay `None` so the helper computes its standard defaults Verification: - `cargo fmt --all -- --check` - `cargo clippy --all-targets -- -D warnings` - `cargo test -p deacon --lib set_up` → 17 pass (14 from PR-6a + 3 new) ## Deferred to PR-6c - `/etc/environment` + `/etc/profile` root-side patches with system markers under `/var/devcontainer/` (spec §5 phase 3a; net-new code that needs root exec + heredoc plumbing) - Live-container `${containerEnv:VAR}` second-pass substitution Refs: issue #34 (Tier 1 progress tracker); PR-6a (#36). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # crates/deacon/src/cli.rs # crates/deacon/src/commands/set_up.rs
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.
Re-opened replacement for #37 — the original PR was auto-closed when its base branch
pr6-set-up-subcommand(#36) was deleted on merge. Branch content unchanged; base retargeted tomainso the squashed PR-6a commit on main reconciles cleanly.See #37 for the original PR description and reviews.
🤖 Generated with Claude Code