diff options
| author | 2026-09-18 15:12:24 +0200 | |
|---|---|---|
| committer | 2026-09-18 15:12:24 +0200 | |
| commit | 4937d0d6f29895b514dcf8cd20c73a1aec446144 (patch) | |
| tree | 7ff388ba4cca94e95489d30452b25badd7e49aa9 /openspec/changes/add-dotfiles-stow-bootstrap | |
| parent | 72b640f5ae65755c1d932b7b8ee88d2367e4036f (diff) | |
| download | desktop-flake-4937d0d6f29895b514dcf8cd20c73a1aec446144.tar.gz desktop-flake-4937d0d6f29895b514dcf8cd20c73a1aec446144.tar.bz2 desktop-flake-4937d0d6f29895b514dcf8cd20c73a1aec446144.zip | |
Archive four completed changes, sync main specs
add-nix-flake-bootstrap, add-host-bootstrap-script,
add-dotfiles-stow-bootstrap, and add-developer-environment all had
every task complete (verified via a real nix flake check and a real
NixOS host build). Archived in dependency order so the two changes
sharing the nix-flake-bootstrap capability merged additively. Main
specs now exist for the first time: nix-flake-bootstrap (10
requirements), dotfiles-management (4), developer-environment (4).
add-gnome-desktop remains active (not yet implemented).
Diffstat (limited to 'openspec/changes/add-dotfiles-stow-bootstrap')
5 files changed, 0 insertions, 149 deletions
diff --git a/openspec/changes/add-dotfiles-stow-bootstrap/.openspec.yaml b/openspec/changes/add-dotfiles-stow-bootstrap/.openspec.yaml deleted file mode 100644 index d28e909..0000000 --- a/openspec/changes/add-dotfiles-stow-bootstrap/.openspec.yaml +++ /dev/null | |||
| @@ -1,2 +0,0 @@ | |||
| 1 | schema: spec-driven | ||
| 2 | created: 2026-09-17 | ||
diff --git a/openspec/changes/add-dotfiles-stow-bootstrap/design.md b/openspec/changes/add-dotfiles-stow-bootstrap/design.md deleted file mode 100644 index 4bcc11b..0000000 --- a/openspec/changes/add-dotfiles-stow-bootstrap/design.md +++ /dev/null | |||
| @@ -1,35 +0,0 @@ | |||
| 1 | ## Context | ||
| 2 | |||
| 3 | See proposal.md - Why. This builds on the shared `modules/home` layer from `add-nix-flake-bootstrap`. The target dotfiles repo (`~/dotfiles`) already exists on the development machine, is genuinely GNU-Stow-structured (confirmed by reading it directly: top-level directories are packages mirroring `$HOME`, its own README says "Run `stow [dir]`"), has real git history across three remotes, and includes a submodule (`vim/.vim/vim-plug`). Nix is still not installed on this machine, so nothing here can be locally evaluated or executed — same constraint as the two prior changes. | ||
| 4 | |||
| 5 | ## Goals / Non-Goals | ||
| 6 | |||
| 7 | **Goals:** | ||
| 8 | - Make the clone-and-stow step part of ordinary `home-manager switch` activation, requiring no separate manual command. | ||
| 9 | - Never touch an already-existing dotfiles checkout (idempotent and non-destructive by construction). | ||
| 10 | - Keep nix-config decoupled from the dotfiles repo's specific package list — no hardcoded package names to keep in sync. | ||
| 11 | - Leave the actual repo URL for the user to fill in, per their explicit request. | ||
| 12 | |||
| 13 | **Non-Goals:** | ||
| 14 | - Deciding *which* of the dotfiles packages are relevant per platform (e.g. `herbstluftwm`/`xorg` are X11-only). No filtering is implemented; harmless unused symlinks are accepted rather than hardcoding platform-specific package knowledge into nix-config. | ||
| 15 | - Managing secrets or anything requiring authentication beyond a plain `git clone` of a public URL (the user described the URL as public). | ||
| 16 | - Migrating the *content* of `~/dotfiles/git/.gitconfig` itself — only which system (home-manager vs. the dotfiles repo) owns `~/.gitconfig` is in scope. | ||
| 17 | |||
| 18 | ## Decisions | ||
| 19 | |||
| 20 | **Use a home-manager `home.activation` script, not a systemd/launchd service or a separate CLI command.** This is the standard home-manager idiom for "run this during activation" needs (already established precedent in this repo's design philosophy of using first-class module-system features over bespoke tooling). It runs as part of `home-manager switch`, requiring no additional invocation, and integrates with home-manager's dry-run convention (`$DRY_RUN_CMD`). | ||
| 21 | |||
| 22 | **Package enumeration is generic (loop over top-level non-hidden directories), not a hardcoded list.** The alternative — listing `bash`, `git`, `neovim`, `tmux`, etc. explicitly in nix-config — was rejected because it re-couples nix-config to the dotfiles repo's exact contents, meaning every new package added there would require a corresponding nix-config change. A generic directory-enumeration rule stays correct as the dotfiles repo evolves and naturally excludes the observed stray entries (`README.md`, `nvim.log`, `.DS_Store`, `.git*`, `.claude/`) without needing to name them. | ||
| 23 | |||
| 24 | **`programs.git.enable` is removed rather than kept alongside the stowed `git/.gitconfig`.** Home-manager's git module always writes `~/.gitconfig` once enabled — there's no way to keep the module "enabled" while suppressing that file, so the two approaches are fundamentally exclusive for the same path. Given the user chose to let the dotfiles repo own git config, the home-manager side is fully removed rather than left half-configured. `pkgs.git` moves to `cli-tools.nix` so the binary itself doesn't disappear. | ||
| 25 | |||
| 26 | **Per-package `stow` invocations, not one invocation for all packages.** GNU Stow's default behavior aborts the whole operation if any named package conflicts. Invoking it once per package, with a non-fatal warning on failure, means one conflicting package (or a not-yet-relevant one on this platform) never blocks the rest from being linked. | ||
| 27 | |||
| 28 | **`homeConfigurations."tom@darwin"` is added now, not deferred.** Unlike the nix-darwin deferral in earlier changes (which was about an untestable *system*-level integration), this is a lightweight, already-proven pattern (the same `mkHome` helper, just a different `system` string) — and without it, this feature has no way to actually be exercised on the machine it matters most for. | ||
| 29 | |||
| 30 | ## Risks / Trade-offs | ||
| 31 | |||
| 32 | - **Removing `modules/home/git.nix` is a breaking change for git identity** → Mitigation: called out explicitly in the proposal's Impact section; `~/dotfiles/git/.gitconfig` already exists with real content on this machine, so the practical gap is only during the window before `home.dotfiles.repoUrl` is filled in and first activated. | ||
| 33 | - **No local way to verify the activation script's actual clone/stow behavior, since Nix isn't installed here** → Mitigation: same as prior changes — careful manual construction and review now; real verification deferred until Nix is available. This is the actual acceptance gate, not any individual task checkbox. | ||
| 34 | - **Unused symlinks for platform-irrelevant packages (`herbstluftwm`, `xorg`) will exist on this Mac** → Mitigation: accepted per the Non-Goals above; an inert dangling-but-harmless symlink is a smaller cost than hardcoding package names. | ||
| 35 | - **A conflict warning could go unnoticed since it's non-fatal** → Mitigation: the warning is written to stderr on every activation until resolved (not silenced after the first occurrence), keeping it visible without being disruptive. | ||
diff --git a/openspec/changes/add-dotfiles-stow-bootstrap/proposal.md b/openspec/changes/add-dotfiles-stow-bootstrap/proposal.md deleted file mode 100644 index 54ddf31..0000000 --- a/openspec/changes/add-dotfiles-stow-bootstrap/proposal.md +++ /dev/null | |||
| @@ -1,28 +0,0 @@ | |||
| 1 | ## Why | ||
| 2 | |||
| 3 | The user maintains a separate `~/dotfiles` repo (already Stow-structured: top-level directories mirror `$HOME`, e.g. `git/.gitconfig` → `~/.gitconfig`) that today has to be cloned and stowed by hand on every machine. This change makes that automatic as part of home-manager activation, so a fresh machine ends up with the same personal dotfiles in place without a manual step. | ||
| 4 | |||
| 5 | ## What Changes | ||
| 6 | |||
| 7 | - Add `modules/home/dotfiles.nix`: a home-manager module exposing `home.dotfiles.repoUrl` (a plain string option, empty by default) and a `home.activation.dotfiles` script that, when a URL is set, clones the repo to `~/dotfiles` (only if not already present — never reclones over an existing checkout) and stows every top-level, non-hidden directory into `$HOME`, independently per package so one conflicting package doesn't block the rest. | ||
| 8 | - **BREAKING**: remove home-manager's own git configuration management — delete `modules/home/git.nix` and its import, and drop the `programs.git.userName`/`userEmail` block from `users/tom.nix`. `~/dotfiles/git/.gitconfig` becomes the sole source of git config once stowed, since home-manager's `programs.git.enable` and a stowed `~/.gitconfig` cannot coexist (home-manager always writes that file once the module is enabled). | ||
| 9 | - Add `pkgs.git` to `modules/home/cli-tools.nix` so the `git` binary stays available independent of whether the dotfiles feature is configured. | ||
| 10 | - Add `homeConfigurations."tom@darwin"` (aarch64-darwin) to `flake.nix`, alongside the existing `tom@linux`, so this feature is actually buildable/usable on the user's Mac once Nix is installed there. The underlying configuration is unchanged between the two — only the per-system flake output is new. | ||
| 11 | - `users/tom.nix` sets `home.dotfiles.repoUrl = "";` with a TODO comment — **left as an open task for the user to fill in the actual git URL later**, not invented or guessed here. | ||
| 12 | - Update `README.md` to document the feature and the open `repoUrl` task. | ||
| 13 | |||
| 14 | ## Capabilities | ||
| 15 | |||
| 16 | ### New Capabilities | ||
| 17 | - `dotfiles-management`: automatic clone-and-stow of a personal dotfiles repository into `$HOME` as part of home-manager activation. | ||
| 18 | |||
| 19 | ### Modified Capabilities | ||
| 20 | (none — `nix-flake-bootstrap` is untouched at the requirements level; the `tom@darwin` output and `cli-tools.nix`/`git.nix` changes are implementation details of the new `dotfiles-management` capability, not a change to what `nix-flake-bootstrap` itself promises) | ||
| 21 | |||
| 22 | ## Impact | ||
| 23 | |||
| 24 | - Affected code: `modules/home/dotfiles.nix` (new), `modules/home/default.nix`, `modules/home/git.nix` (deleted), `modules/home/cli-tools.nix`, `users/tom.nix`, `flake.nix`, `README.md`. | ||
| 25 | - Depends on the already-implemented shared `modules/home` layer from `add-nix-flake-bootstrap`. | ||
| 26 | - No live host or remote exists yet, and Nix is still not installed on this development machine, so the activation script's actual clone/stow behavior can only be verified by manual review here, not executed — same constraint as the two prior changes. | ||
| 27 | - `~/dotfiles` already exists on this machine with real commit history; the activation script's "clone only if missing" behavior is required precisely so this change never touches that existing checkout. | ||
| 28 | - The `git` config change is user-visible: anyone relying on `programs.git.userName`/`userEmail` being set via Nix will instead need `~/dotfiles/git/.gitconfig` (or `home.dotfiles.repoUrl` configured) to get a working git identity — there is no fallback once `modules/home/git.nix` is removed. | ||
diff --git a/openspec/changes/add-dotfiles-stow-bootstrap/specs/dotfiles-management/spec.md b/openspec/changes/add-dotfiles-stow-bootstrap/specs/dotfiles-management/spec.md deleted file mode 100644 index dacbbb1..0000000 --- a/openspec/changes/add-dotfiles-stow-bootstrap/specs/dotfiles-management/spec.md +++ /dev/null | |||
| @@ -1,45 +0,0 @@ | |||
| 1 | ## Purpose | ||
| 2 | |||
| 3 | Automatically clones a personal dotfiles repository into the user's home directory and symlinks its contents into place, so a fresh machine ends up with the same personal configuration without a manual clone-and-stow step. | ||
| 4 | |||
| 5 | ## ADDED Requirements | ||
| 6 | |||
| 7 | ### Requirement: Dotfiles cloning is opt-in via a configured repo URL | ||
| 8 | The dotfiles clone-and-stow behavior SHALL only run when a repository URL has been explicitly configured; leaving it unset SHALL leave the home directory untouched by this feature. | ||
| 9 | |||
| 10 | #### Scenario: No repo URL configured | ||
| 11 | - **WHEN** no dotfiles repository URL has been set | ||
| 12 | - **THEN** no clone or symlinking occurs during activation | ||
| 13 | |||
| 14 | #### Scenario: Repo URL configured | ||
| 15 | - **WHEN** a dotfiles repository URL has been set | ||
| 16 | - **THEN** activation clones and symlinks that repository as described by the other requirements in this capability | ||
| 17 | |||
| 18 | ### Requirement: Existing dotfiles checkout is never overwritten | ||
| 19 | If the dotfiles repository has already been cloned locally, the system SHALL NOT re-clone or otherwise modify that existing checkout. | ||
| 20 | |||
| 21 | #### Scenario: Checkout already present | ||
| 22 | - **WHEN** activation runs and a local dotfiles checkout already exists | ||
| 23 | - **THEN** no clone operation is performed against that location, and the existing checkout's contents and git history are left untouched | ||
| 24 | |||
| 25 | #### Scenario: Checkout missing | ||
| 26 | - **WHEN** activation runs and no local dotfiles checkout exists yet | ||
| 27 | - **THEN** the configured repository is cloned, including any git submodules it declares | ||
| 28 | |||
| 29 | ### Requirement: Dotfiles packages are symlinked into the home directory | ||
| 30 | Each top-level package directory in the cloned dotfiles repository SHALL be symlinked into the home directory following the same package-to-target-path convention as GNU Stow (a package directory's contents mirror their target location under the home directory). | ||
| 31 | |||
| 32 | #### Scenario: A package is linked | ||
| 33 | - **WHEN** the dotfiles repository contains a top-level directory whose contents mirror a location under the home directory | ||
| 34 | - **THEN** that content becomes reachable via a symlink at the corresponding path under the home directory | ||
| 35 | |||
| 36 | #### Scenario: Non-package entries are ignored | ||
| 37 | - **WHEN** the dotfiles repository contains top-level entries that are not package directories (ordinary files, or hidden/dot-prefixed directories such as version-control metadata) | ||
| 38 | - **THEN** those entries are not treated as packages and produce no symlinks | ||
| 39 | |||
| 40 | ### Requirement: A conflicting package does not block the others | ||
| 41 | If symlinking one package would conflict with an existing file or symlink not owned by this process, the system SHALL skip that package and continue processing the remaining packages, rather than aborting entirely. | ||
| 42 | |||
| 43 | #### Scenario: One package conflicts | ||
| 44 | - **WHEN** one dotfiles package's target path is already occupied by an unrelated existing file | ||
| 45 | - **THEN** that package is skipped with a visible warning, and every other package is still processed | ||
diff --git a/openspec/changes/add-dotfiles-stow-bootstrap/tasks.md b/openspec/changes/add-dotfiles-stow-bootstrap/tasks.md deleted file mode 100644 index 5f00a3b..0000000 --- a/openspec/changes/add-dotfiles-stow-bootstrap/tasks.md +++ /dev/null | |||
| @@ -1,39 +0,0 @@ | |||
| 1 | ## 1. Dotfiles module | ||
| 2 | |||
| 3 | - [x] 1.1 Create `modules/home/dotfiles.nix` defining `options.home.dotfiles.repoUrl` (string, default `""`) — verify by reading the option definition against the template in design.md | ||
| 4 | - [x] 1.2 Implement `config = lib.mkIf (cfg.repoUrl != "") { ... }` wrapping the feature so it's fully inert when unset — verify the `mkIf` condition guards both `home.packages` and `home.activation.dotfiles` | ||
| 5 | - [x] 1.3 Add `home.packages = [ pkgs.git pkgs.stow ];` inside the `mkIf` block — verify both packages are listed | ||
| 6 | - [x] 1.4 Implement `home.activation.dotfiles` using `lib.hm.dag.entryAfter [ "writeBoundary" ]`: clone `cfg.repoUrl` to `~/dotfiles` with `--recurse-submodules` only if `~/dotfiles/.git` doesn't already exist, using `$DRY_RUN_CMD` on the clone — verify the existence check precedes the clone and no unconditional clone/rm exists | ||
| 7 | - [x] 1.5 In the same activation script, loop over non-hidden top-level directories under `~/dotfiles` and run `stow -d ~/dotfiles -t <homeDirectory> <package>` per directory (via `$DRY_RUN_CMD`), with `|| echo "Warning: ..." >&2` so one failure doesn't abort the loop — verify the loop skips hidden entries (`case "$pkg_name" in .*) continue ;; esac`) and each `stow` call is independently fault-tolerant | ||
| 8 | - [x] 1.6 Add `modules/home/dotfiles.nix` to `modules/home/default.nix`'s imports — verify by grep confirming the import is present | ||
| 9 | |||
| 10 | ## 2. Remove home-manager git config management | ||
| 11 | |||
| 12 | - [x] 2.1 Delete `modules/home/git.nix` — verify the file no longer exists | ||
| 13 | - [x] 2.2 Remove `./git.nix` from `modules/home/default.nix`'s imports — verify by grep confirming no reference to `git.nix` remains in that file | ||
| 14 | - [x] 2.3 Remove the `programs.git = { userName = ...; userEmail = ...; };` block from `users/tom.nix` — verify by reading the file and confirming no `programs.git` reference remains | ||
| 15 | |||
| 16 | ## 3. Keep git available independent of the dotfiles feature | ||
| 17 | |||
| 18 | - [x] 3.1 Add `pkgs.git` to `modules/home/cli-tools.nix`'s package list — verify by reading the updated package list | ||
| 19 | |||
| 20 | ## 4. Darwin home-manager output | ||
| 21 | |||
| 22 | - [x] 4.1 Add `homeConfigurations."tom@darwin" = lib.mkHome { username = "tom"; system = "aarch64-darwin"; };` to `flake.nix`, alongside the existing `tom@linux` entry — verify by grep confirming both entries exist | ||
| 23 | |||
| 24 | ## 5. Open task: repo URL | ||
| 25 | |||
| 26 | - [x] 5.1 In `users/tom.nix`, add `home.dotfiles.repoUrl = "";` with a comment marking it as an intentional placeholder for the user to fill in later — verify the value is an empty string, not a guessed or invented URL, and the comment is present (left blank as specified; the user has since supplied the real URL directly — `home.dotfiles.repoUrl = "https://git.tomvanderlee.com/dotfiles"` — so this open item is now resolved, not just scaffolded) | ||
| 27 | |||
| 28 | ## 6. Documentation | ||
| 29 | |||
| 30 | - [x] 6.1 Update `README.md` to document the dotfiles feature (what it does, that it's opt-in via `home.dotfiles.repoUrl`) and note the open task of filling in the real URL — verify by reading the updated section for consistency with the actual module behavior (also fixed two now-stale mentions: the layout line still listing "git" as a managed module, and the non-NixOS section only mentioning `tom@linux`) | ||
| 31 | |||
| 32 | ## 7. Validation | ||
| 33 | |||
| 34 | - [x] 7.1 Run `nix flake check` and confirm the new module and `tom@darwin` output evaluate without error (blocked — Nix is not installed on this machine; run this once Nix is available, e.g. via a real install or CI — this is the real acceptance gate, not the individual checkboxes above) (run by the user on a Linux machine with Nix, 2026-09-18: passed with no errors) | ||
| 35 | - [x] 7.2 Manually trace through each spec scenario in `specs/dotfiles-management/spec.md` against the implemented module, confirming each is satisfied by inspection given #7.1 cannot run locally (all 7 scenarios traced and satisfied: `mkIf` gates the opt-in behavior; the `.git`-existence guard means an existing checkout is never touched and the clone branch uses `--recurse-submodules`; the directory-only glob plus `case .*) continue` excludes both non-directory entries and hidden directories from being treated as packages; each `stow` call's `|| echo "Warning..."` keeps one conflict from aborting the loop under `set -e`) | ||
| 36 | |||
| 37 | --- | ||
| 38 | |||
| 39 | **Verification note:** as with the two prior changes, Nix is not installed on this machine, so task 7.1 (`nix flake check`) could not be executed and remains the outstanding acceptance gate for all three changes now. `shellcheck` was unavailable locally in the prior change too, but this change's activation script logic is plain POSIX-ish shell embedded in a Nix string (not a standalone script file), so there was no separate file to syntax-check with `bash -n` either — review here was manual/by-inspection only. | ||
