fix(zsh): seed a placeholder ~/.zshrc

zsh/newuser runs zsh-newuser-install on every interactive login while the
user has none of .zshenv, .zprofile, .zshrc or .zlogin. The whole
configuration lives in /etc/zshrc, which is not a personal file, so the
wizard never stopped coming up.

The seeded file carries comments only: sourcing the shared zshrc from it,
as the Arch counterpart does, would load it a second time.
This commit is contained in:
2026-08-20 15:39:09 +02:00
parent ec0823e798
commit f5b4f0174d
2 changed files with 45 additions and 0 deletions
+31
View File
@@ -1,5 +1,36 @@
# Decisions
## 2026-08-20 A placeholder ~/.zshrc stops the zsh new-user wizard
- **Who**: Dominik, ClaudeCode
- **Why**: `zsh-newuser-install` came up on every interactive login on the test
VM. Per zsh's own documentation the `zsh/newuser` module runs it for as long
as the user has none of `.zshenv`, `.zprofile`, `.zshrc` or `.zlogin` in
`$HOME`/`$ZDOTDIR` (`zshall`, "New User Installation", and the function
description in `zshcontrib`); it exits at once for root, which is why only the
user account saw it. Here the whole configuration is `/etc/zshrc`, which is
not a personal file, so the condition never cleared. Independent of the
ordering fixes below — the trigger is the set of files in the home directory,
not the content of `/etc/zshrc`. The NixOS `programs.zsh` module does not
cover the case.
- **Tradeoffs**: (1) **Which side** — disabling the `zsh/newuser` module
system-wide would need no home state, but the documented off switch is on the
administrator's zsh build, not a NixOS option, and it would also take the
wizard away from a user who wants it. Seeding the file uses the mechanism zsh
itself names and reuses the `systemd.user.tmpfiles` seed already in the module
for waypaper and swaylock. (2) **What is in the file** — on Arch this file
reads `source /etc/zsh/zshrc.moonarch`, and copying that here would source the
shared zshrc twice, once from `/etc/zshrc` and once from the home file:
duplicate aliases, a second `compinit`, the plugins loaded twice and
`$HOME/.local/bin` prepended to `PATH` twice. The seeded file therefore
carries comments only. It doubles as the documented place for the user's own
additions, which zsh reads after `/etc/zshrc`.
- **How**: `modules/desktop.nix``userZshrc` via `pkgs.writeText`, seeded as
`C %h/.zshrc 0644 - - - ${userZshrc}` next to the two existing rules. `C`
copies once and never overwrites, so later edits to the file survive a
rebuild. Verified by evaluating `systemd.user.tmpfiles.rules` for the `testvm`
host.
## 2026-08-20 The sourced zshrc is defended against the /etc/zshrc defaults
- **Who**: Dominik, ClaudeCode
+14
View File
@@ -35,6 +35,19 @@ let
substitute ${etc}/swaylock/config "$out" \
--replace-fail /usr/share/moonarch/wallpaper-blur.jpg ${wallpaperBlur}
'';
# zsh/newuser runs its setup wizard on every interactive login for as long as
# the user has none of .zshenv, .zprofile, .zshrc or .zlogin — the whole
# configuration living in /etc/zshrc does not count. Seeded below to stop
# that, and deliberately without configuration: on Arch this file carries
# `source /etc/zsh/zshrc.moonarch`, which here would source the shared zshrc
# a second time, on top of the one /etc/zshrc already did.
userZshrc = pkgs.writeText "zshrc" ''
# The moonarch zsh configuration is system-wide, in /etc/zshrc.
#
# This file keeps zsh from running zsh-newuser-install and is the place for
# your own additions: zsh reads it after /etc/zshrc, so it wins over it.
'';
in
{
# --- XDG configs -> /etc/xdg/ ---
@@ -275,5 +288,6 @@ in
systemd.user.tmpfiles.rules = [
"C %h/.config/waypaper/config.ini 0644 - - - ${waypaperConfig}"
"C %h/.config/swaylock/config 0644 - - - ${swaylockConfig}"
"C %h/.zshrc 0644 - - - ${userZshrc}"
];
}