aboutsummaryrefslogtreecommitdiff
path: root/docs/PLAN-dotfiles-separation.org
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-13 13:30:51 -0500
committerCraig Jennings <c@cjennings.net>2026-05-13 13:30:51 -0500
commit3ca68462cbe035c25ddc4128eebe94202a7c411d (patch)
treeaa1c611b64bb1ebc0bd1c57c7a1029d59d1e80b6 /docs/PLAN-dotfiles-separation.org
parentcc26f773de3fdc040ccd027d40e4e074ab4e2e38 (diff)
downloadarchsetup-3ca68462cbe035c25ddc4128eebe94202a7c411d.tar.gz
archsetup-3ca68462cbe035c25ddc4128eebe94202a7c411d.zip
docs: add dotfile-separation spec with phased implementation plan
Covers extracting dotfiles/ into a standalone repo, adding a minimal/ stow target for headless installs, and a three-phase plan with commit boundaries. todo.org tracks the review as a sub-task.
Diffstat (limited to 'docs/PLAN-dotfiles-separation.org')
-rw-r--r--docs/PLAN-dotfiles-separation.org364
1 files changed, 364 insertions, 0 deletions
diff --git a/docs/PLAN-dotfiles-separation.org b/docs/PLAN-dotfiles-separation.org
new file mode 100644
index 0000000..d835acd
--- /dev/null
+++ b/docs/PLAN-dotfiles-separation.org
@@ -0,0 +1,364 @@
+#+TITLE: Plan — Separate dotfiles from archsetup
+#+AUTHOR: Craig Jennings & Claude
+#+DATE: 2026-05-13
+
+* Overview
+
+Extract =dotfiles/= from the =archsetup= repo into a standalone repository at
+=cjennings.net/archsetup-dotfiles.git=. Archsetup keeps the install logic;
+dotfiles live in their own repo and get cloned-then-stowed at install time.
+
+Add a new =minimal/= stow target (Tier B — TUI-tooled headless server) for
+the =DESKTOP_ENV=none= path, alongside the existing =common/=, =dwm/=, and
+=hyprland/= targets.
+
+This is non-disturbing to a running session: all script edits, repo creates,
+and VM tests happen out-of-band. The only disruptive step is the final
+migration of existing machines (Phase 3), which unstows and re-stows local
+dotfile symlinks.
+
+* Decisions
+
+| # | Question | Answer |
+|----+-------------------------------------------+------------------------------|
+| Q1 | Extract dotfiles to a standalone repo? | Yes — now (not deferred) |
+| Q2 | Clone target when DOTFILES_REPO is set? | =~/.dotfiles= (convention) |
+| Q3 | What does DOTFILES_REPO unset mean? | No opt-out — always clones |
+| Q4 | Include a minimal/ headless target? | Yes — Tier B (TUI server) |
+| Q5 | Behavior when dotfiles dir isn't a git checkout? | Error out |
+
+** Rationale notes
+
+- *Q1 = now.* Folds the extraction into this work rather than deferring it.
+ Adds steps (create new repo, push content, update default URL) but
+ produces a clean end-state in one pass.
+- *Q3 = no opt-out.* DOTFILES_REPO always has a default value (the new
+ cjennings.net URL). The =DESKTOP_ENV=none= path stows =minimal/= rather
+ than "no dotfiles at all". Simpler than supporting a SKIP_DOTFILES flag.
+- *Q4 = Tier B.* The headless-tooled server: shells + git + tmux + ssh +
+ TUI apps (btop, htop, mc, ranger, tickrs, topgrade, wavemon) + the
+ =.local/bin/= utility scripts. Craig runs TUI apps on personal servers
+ regularly, so the "bare-bones" tier (Tier A) would feel sparse.
+- *Q5 = error out.* Strict. If the dotfiles dir somehow isn't a git
+ checkout after archsetup's clone (manually modified, tarball extraction),
+ archsetup refuses to proceed rather than silently skipping the
+ =git restore= cleanup step. Prefers loud failure over surprising state.
+
+* Target tree — minimal/
+
+The =minimal/= directory is a *standalone* stow target. It does NOT depend
+on =common/=; everything universal that =common/= ships is duplicated into
+=minimal/=. Drift between the two is a risk; a later refactor of =common/=
+to drop GUI bits (so =common/= itself becomes headless-safe) would
+eliminate the duplication, but that's out of scope here.
+
+#+begin_example
+minimal/
+├── .bash_logout
+├── .bash_profile
+├── .bashrc
+├── .bashrc.d/ # 6 files: aliases, emacs, fzf, git, media, utilities
+├── .config/
+│ ├── btop/ # TUI system monitor
+│ ├── htop/ # TUI system monitor
+│ ├── mc/ # TUI file manager
+│ ├── ranger/ # TUI file manager
+│ ├── tickrs/ # TUI stock ticker
+│ ├── topgrade.toml # CLI updater
+│ ├── user-dirs.dirs
+│ ├── user-dirs.locale
+│ └── wavemon/ # TUI WiFi monitor
+├── .gitconfig # real config (templating later, in open-source cleanup)
+├── .gitignore # user's global gitignore
+├── .hushlogin
+├── .local/
+│ └── bin/ # full .local/bin/ from common/ — all CLI/TUI scripts
+│ # (notify, ifinstalled, et/em/ec, ssh-createkeys,
+│ # refresharchkeys, decryptfile/encryptfile, dab,
+│ # timezone-set, etc.)
+├── .profile
+├── .profile.d/ # 4 files: auto-tmux-session, browser, display, framework
+├── .ssh/ # 4 items: config, decrypt_ssh, set_perms, ssh.tar.gz.gpg
+├── .stow-global-ignore
+├── .stow-local-ignore
+├── .tmux.conf
+├── .zshrc
+└── .zshrc.d/ # 7 files: aliases, arch-downgrade, emacs, fzf, git, media, utilities
+#+end_example
+
+** Excluded from minimal/
+
+| Category | Reason |
+|-------------------------+-------------------------------------------------------|
+| Mail configs | =.mbsyncrc=, =.msmtprc=, =.authcode=, =.authinfo.gpg= — credential-bearing, separate open-source-release cleanup |
+| GTK / Qt theming | =.gtkrc-2.0=, =qt5ct/=, =qt6ct/=, =gtk-3.0/= — GUI-only |
+| X11 only | =.Xmodmap=, =.xscreensaver=, =sxhkd/=, =rofi/=, =feh/= |
+| GUI media tools | =audacious/=, =calibre/=, =mpv/=, =zathura/= — GUI-only; some credential-bearing |
+| Daemons w/ creds | =mpd/= (personal =~cjennings/= paths), =transmission/= (bcrypt RPC pass), =yt-dlp/= (email in config) — separate cleanup |
+| Notification / fonts | =dunst/=, =fontconfig/=, =mimeapps.list= — GUI-related |
+| Personal dirs | =documents/=, =music/=, =pictures/= — content, not config |
+
+** SSH and GPG availability
+
+Confirmed both are installed regardless of =DESKTOP_ENV=:
+
+- =openssh= is installed in =essential_services()= at =archsetup:1094= —
+ always runs.
+- =gnupg= is installed in =desktop_environment()= at =archsetup:1649= —
+ also always runs (function name is misleading; it doesn't skip when
+ =DESKTOP_ENV=none=).
+
+The =desktop_environment()= function is a misnamed grab-bag — it installs
+fonts, qt themes, dunst, gnome-keyring (pure GUI) alongside universal
+authentication tools (gnupg, polkit, pass, rbw). On a =DESKTOP_ENV=none=
+install today, it ships 50+ pointless GUI packages just to get gnupg. A
+later refactor should split universal auth tools out into
+=essential_services= or its own step, but it's out of scope for this work.
+
+GPG key import remains a manual post-install step on every machine
+(unchanged from current behavior). The =gnupg= binary is installed; key
+import is the user's responsibility (=gpg --import keys.asc= from a
+backup or scp from another machine).
+
+The =.ssh/= directory in dotfiles uses a clever pattern: SSH keys are
+GPG-encrypted (=ssh.tar.gz.gpg=) and committed to the repo. After GPG
+keys are imported on a new machine, running =decrypt_ssh= recovers the
+real SSH keys. Shipping the encrypted archive in =minimal/= is safe.
+
+* Implementation plan
+
+Three phases. Each is a discrete unit with a stopping point.
+
+** Phase 1 — Set up the new repo (~30 min, no archsetup changes)
+
+*** Step 1.1 — Create the bare repo on cjennings.net
+
+Manual SSH step (Craig). Probably:
+
+#+begin_src bash
+ssh git@cjennings.net "cd /srv/git && git init --bare archsetup-dotfiles.git"
+#+end_src
+
+Adjust path as needed for Craig's server layout. Confirm the final URL
+(default assumption: =https://git.cjennings.net/archsetup-dotfiles.git=).
+
+*** Step 1.2 — Extract dotfiles/ with filtered history
+
+In a temp working dir (does NOT touch the live archsetup repo):
+
+#+begin_src bash
+git clone --no-local /home/cjennings/code/archsetup /tmp/extract-dotfiles
+cd /tmp/extract-dotfiles
+git filter-repo --subdirectory-filter dotfiles/
+#+end_src
+
+Result: a fresh repo where every commit is rewritten to be about files
+inside =dotfiles/= only. Per-file history and attribution preserved across
+the 275 commits.
+
+If =git-filter-repo= isn't installed, =pacman -S git-filter-repo= or
+=pip install --user git-filter-repo=.
+
+*** Step 1.3 — Add minimal/ tree
+
+In =/tmp/extract-dotfiles/=:
+
+1. Create =minimal/= directory.
+2. Populate per the "Target tree" section above. Most files are direct
+ copies from =common/= (=.bashrc=, =.profile=, etc.) plus the
+ curated TUI app configs from =common/.config/=.
+3. The =.local/bin/= is the full =common/.local/bin/= directory copied
+ over verbatim — every CLI/TUI script.
+4. Commit: =feat: add minimal/ stow target for headless installs=.
+
+*** Step 1.4 — Push to the new remote
+
+#+begin_src bash
+git remote add origin https://git.cjennings.net/archsetup-dotfiles.git
+git push -u origin main
+#+end_src
+
+**Stops here.** =archsetup-dotfiles.git= exists at cjennings.net with
+=common/=, =dwm/=, =hyprland/=, =minimal/=. Archsetup itself unchanged. No
+risk to running session or test infra.
+
+** Phase 2 — Wire archsetup to the new repo (~1-2 hours + 40 min VM test)
+
+*** Step 2.1 — Add config keys to archsetup.conf.example
+
+Under the existing "Git Repositories" block (after the =dwm_repo= /
+=hyprland_repo= entries):
+
+#+begin_example
+# Dotfiles
+DOTFILES_REPO=https://git.cjennings.net/archsetup-dotfiles.git
+DOTFILES_BRANCH=main
+DOTFILES_DIR=$HOME/.dotfiles
+#+end_example
+
+Document that the user's repo must contain =common/= plus =dwm/=,
+=hyprland/=, and/or =minimal/= subdirs that stow cleanly to =~=.
+
+*** Step 2.2 — Update archsetup script
+
+Edits to =/home/cjennings/code/archsetup/archsetup=:
+
+1. *Read config* (around line 114-122): map =DOTFILES_REPO= / =DOTFILES_BRANCH=
+ / =DOTFILES_DIR= env vars to lowercase script variables.
+2. *Set defaults* (around line 136-146, alongside =dwm_repo= etc.):
+ - =dotfiles_repo="${dotfiles_repo:-https://git.cjennings.net/archsetup-dotfiles.git}"=
+ - =dotfiles_branch="${dotfiles_branch:-main}"=
+ - =dotfiles_dir="${dotfiles_dir:-/home/$username/.dotfiles}"=
+3. *Validate* (in =validate_config()=, around line 155+): security-only check on
+ =DOTFILES_REPO= (no leading dash, no whitespace/control chars) — same
+ pattern as the existing =*_REPO= keys after =2c1377b=.
+4. *Clone and stow* (in =user_customizations()=, currently around line 882-1010):
+ - Replace the existing =dotfiles_dir="$user_archsetup_dir/dotfiles"= setup with
+ =git clone --depth 1 --branch "$dotfiles_branch" "$dotfiles_repo" "$dotfiles_dir"=
+ (run as the target user with =sudo -u= or =chown= after).
+ - Per Q5: error out if =[ ! -d "$dotfiles_dir/.git" ]= after the clone.
+ - Stow target based on =$desktop_env=:
+ + =dwm= → stow =common/= + =dwm/=
+ + =hyprland= → stow =common/= + =hyprland/=
+ + =none= → stow =minimal/= only (NOT =common/=)
+ - Guard the waybar-battery sed block (currently around line 856-865)
+ and the =git restore= step (currently around line 896-902) so they
+ only run on the dwm/hyprland paths. The minimal/ path skips both.
+
+*** Step 2.3 — Update test infra
+
+1. =scripts/testing/archsetup-vm.conf=: add =DOTFILES_REPO=/tmp/archsetup-dotfiles-test=
+ (mirrors the =ARCHSETUP_REPO=/tmp/archsetup-test= pattern that's
+ already there).
+2. =scripts/testing/run-test.sh=: before VM launch, clone (or =cp -r=) the
+ dotfiles repo to =/tmp/archsetup-dotfiles-test= so the in-VM
+ =git clone "$dotfiles_repo"= against the local path succeeds.
+
+*** Step 2.4 — VM test
+
+=make test= with default =DESKTOP_ENV=hyprland=. Verify:
+- Dotfiles clone from the new local path succeeds.
+- Stow pattern still works (=common/= + =hyprland/= go to =~/=).
+- No regressions from the install pre/post 2026-05-11 cleanup.
+
+Optionally re-run with =DESKTOP_ENV=none= to validate the new =minimal/=
+path. *Recommended* since it's the new code path.
+
+**Stops here.** =dotfiles/= is still in the archsetup repo. Local machines
+(this one, ratio, velox) are still using =~/.config/foo → ~/code/archsetup/dotfiles/foo=
+symlinks — *no migration done yet*. Running session safe.
+
+** Phase 3 — Cleanup and migration (~15 min per machine, blocks on migration)
+
+*** Step 3.1 — Migrate this machine
+
+#+begin_src bash
+cd ~/code/archsetup
+make unstow hyprland # remove symlinks pointing at archsetup/dotfiles/
+git clone https://git.cjennings.net/archsetup-dotfiles.git ~/.dotfiles
+make stow hyprland DOTFILES=~/.dotfiles
+#+end_src
+
+*Disturbs the running session briefly* — hyprland config, waybar, foot,
+dunst, etc. all get unstowed and re-stowed mid-session. Reload after
+(=hyprctl reload=, restart waybar/dunst). Verify nothing visibly broke.
+
+Repeat for ratio and velox at appropriate times.
+
+*** Step 3.2 — Remove dotfiles/ from archsetup repo
+
+Once all local machines are migrated:
+
+#+begin_src bash
+git rm -r dotfiles/
+git commit -m "chore(archsetup): remove dotfiles/ — moved to archsetup-dotfiles repo"
+#+end_src
+
+The directory is no longer needed since archsetup now clones from the
+external repo.
+
+*** Step 3.3 — Update CLAUDE.md
+
+Update the "Project Structure" and "Makefile Targets" sections to reflect
+the new layout. Document the =DOTFILES_REPO=, =DOTFILES_BRANCH=, and
+=DOTFILES_DIR= config keys. Note the =DOTFILES== Makefile override.
+
+* Commit map
+
+Approximate commit boundaries for each phase. May split or combine as
+implementation reveals natural seams.
+
+** Phase 1 commits (in the new archsetup-dotfiles.git repo)
+
+| C | Subject |
+|-----+-------------------------------------------------------------|
+| D1 | initial import — filter-extracted from archsetup |
+| D2 | feat: add minimal/ stow target for headless installs |
+
+** Phase 2 commits (in archsetup repo)
+
+| C | Subject |
+|-----+-------------------------------------------------------------|
+| A1 | feat(archsetup): add DOTFILES_REPO config keys + validation |
+| A2 | feat(archsetup): clone dotfiles repo, stow per DESKTOP_ENV |
+| A3 | refactor(testing): clone dotfiles repo in VM test setup |
+
+** Phase 3 commits (in archsetup repo)
+
+| C | Subject |
+|-----+-------------------------------------------------------------|
+| A4 | chore(archsetup): remove dotfiles/ — moved to archsetup-dotfiles repo |
+| A5 | docs: document external dotfiles layout in CLAUDE.md |
+
+* Open observations / future work
+
+These come up during the design but are out of scope for this work.
+
+- *=desktop_environment()= is a misnomer.* It installs both pure-GUI
+ packages (fonts, qt themes, dunst, gnome-keyring) AND universal
+ authentication tools (gnupg, polkit, pass, rbw). The latter belong in
+ =essential_services()= or their own step. On =DESKTOP_ENV=none= installs
+ today, you get 50+ pointless GUI packages just to get =gnupg=. Worth a
+ follow-up refactor.
+- *Drift between common/ and minimal/.* Both ship =.bashrc=, =.profile=,
+ =.gitconfig=, etc. If Craig updates one, the other rots. A future
+ refactor could move all universal files out of =common/= and into a new
+ shared base, with =common/= becoming "common GUI bits only". Out of scope
+ here. Could be mitigated short-term by a CI check that errors if the
+ duplicated files diverge.
+- *Personal-info cleanup in shipped configs.* =.gitconfig=, =.ssh/config=,
+ =.config/yt-dlp/config=, =hyprland.conf:3=, etc. are all flagged in the
+ open-source-release audit. This work ships =.gitconfig= and =.ssh/config=
+ as-is (real values, not templated) — the templating belongs in the
+ broader =[#A] Prepare for GitHub open-source release= cluster, where it
+ can be done as a single consistent pass.
+- *Audit of dotfiles/common/ scripts.* The =[#B] Audit dotfiles/common
+ directory= task in todo.org has unfinished subtasks for reviewing
+ =.local/bin/= for unused scripts, orphaned configs, and unused stowed
+ files. =minimal/= currently includes the full =.local/bin/= directory
+ verbatim — a pruning pass on that audit would also improve =minimal/=.
+- *=common/.config/git/= and global gitignore.* The proposed =minimal/=
+ ships =.gitconfig= and =.gitignore= at the top level. If Craig has
+ anything under =.config/git/= worth shipping, add it.
+
+* Stopping points
+
+Pick a stopping point for this session:
+
+1. *Phase 1 only* (~30 min). Get the new repo live, build =minimal/=, push.
+ Stop. Pick up Phase 2 fresh later. *Recommended* — discrete unit, low
+ risk, sets foundation.
+2. *Phases 1 + 2* (~2-3 hours, includes a VM test). Repo live + archsetup
+ wired + VM-tested. Stop before migrating local machines. Phase 3
+ happens whenever the migration is convenient.
+3. *All three phases* (~3-5 hours). Full end-to-end. Long session.
+
+* Before starting
+
+Confirm:
+
+1. New repo URL (default assumption: =https://git.cjennings.net/archsetup-dotfiles.git=).
+2. Path on cjennings.net for creating the bare repo (=/srv/git/=? other?).
+3. Scope: 1, 2, or 3 above.
+4. Anything in the =minimal/= tree that needs adjustment.
+5. Anything in the implementation plan that needs adjustment.