Split niri, hypr, fish into submodules; tmux, tmux-palette, television, yazi into externals

This commit is contained in:
Prad Nukala committed 2026-10-14 13:40:53 -04:00
1 parent ab29365a3d
commit 954e1e1ae0
216 files changed
+138 -9708

No files matched your search

+2 -2
View File
@@ -165,8 +165,8 @@ See [bash and zsh](/shell) for the rest of those files.
| `e.h` | not defined | `cd ~/.local/share/chezmoi/dot_config/hypr && yazi` |
| `e.k` | `yazi ~/.config/kitty` | `yazi ~/.local/share/chezmoi/dot_config/kitty` |
| `e.n` | `yazi ~/.config/niri` | `yazi ~/.local/share/chezmoi/dot_config/niri` |
| `e.y` | `yazi ~/.config/yazi` | `yazi ~/.local/share/chezmoi/yazi` |
| `e.t` | `nvim ~/.config/tmux/tmux.conf` | `nvim ~/.local/share/chezmoi/dot_config/tmux/tmux.conf` |
| `e.y` | `yazi ~/.config/yazi` | ✓ |
| `e.t` | `nvim ~/.config/tmux/tmux.conf` | ✓ |
| `cdd` | `cd ~/Developer && yazi` | not defined |
| `,,` | not defined | `cd ~/.local/share/chezmoi && yazi` |
| `..` | not defined | `cd ..` |
+42 -6
View File
@@ -31,14 +31,48 @@ Externals are content chezmoi fetches instead of storing in the repo. Every entr
| --- | --- | --- |
| `~/.config/nvim` | [prdlk/nvim](https://github.com/prdlk/nvim) | Neovim config, kept in its own repo |
| `~/.config/bat/syntaxes/sublime-purescript-syntax` | [tellnobody1/sublime-purescript-syntax](https://github.com/tellnobody1/sublime-purescript-syntax) | PureScript syntax for bat, compiled into bat's cache by the [bat cache scripts](/chezmoi/scripts#build-bat-cache) |
| `~/.config/tmux/plugins/tpm` | [tmux-plugins/tpm](https://github.com/tmux-plugins/tpm) | tmux plugin manager, run from the bottom of `tmux.conf` ([tmux](/terminal/tmux)) |
| `~/.config/tmux/plugins/tpm` | [tmux-plugins/tpm](https://github.com/tmux-plugins/tpm) | tmux plugin manager, run from the bottom of `tmux.conf` ([tmux](/terminal/tmux)). Cloned inside the tmux checkout, whose `.gitignore` excludes `plugins/` |
### No refreshPeriod
No entry sets `refreshPeriod`, so it stays at chezmoi's default of never. chezmoi clones each repo on a fresh machine and otherwise leaves it alone: `chezmoi apply` and `chezmoi update` do not pull. The local nvim checkout always wins over the remote, so in-progress Neovim edits and unpushed commits are never disturbed by an apply.
No entry in `.chezmoiexternal.toml` sets `refreshPeriod`, so it stays at chezmoi's default of never. chezmoi clones each repo on a fresh machine and otherwise leaves it alone: `chezmoi apply` and `chezmoi update` do not pull. The local nvim checkout always wins over the remote, so in-progress Neovim edits and unpushed commits are never disturbed by an apply.
To update explicitly, run `chezmoi apply --refresh-externals` (`-R`), which pulls with `git pull --ff-only`. `--ff-only` makes the pull fail instead of creating a merge commit when the local checkout has diverged.
## .chezmoiexternals/ [#app-externals]
Per-app config repos, one file each in `.chezmoiexternals/<app>.toml`. Each is a `git-repo` external with `refreshPeriod = "168h"` and `pull.args = ["--ff-only"]`, so `chezmoi apply` fast-forwards them at most once a week. The repos hold plain config files (no chezmoi prefixes) and are not part of `chezmoi managed` contents.
```toml .chezmoiexternals/tmux.toml
[".config/tmux"]
type = "git-repo"
url = "https://github.com/prdlk/tmux.git"
refreshPeriod = "168h"
[".config/tmux".pull]
args = ["--ff-only"]
```
| File | Target | Repo |
| --- | --- | --- |
| `tmux.toml` | `~/.config/tmux` | [prdlk/tmux](https://github.com/prdlk/tmux) |
| `tmux-palette.toml` | `~/.config/tmux-palette` | [prdlk/tmux-palette](https://github.com/prdlk/tmux-palette) |
| `television.toml` | `~/.config/television` | [prdlk/television](https://github.com/prdlk/television) |
| `yazi.toml` | `~/.config/yazi` | [prdlk/yazi](https://github.com/prdlk/yazi) |
Edit these in place under `~/.config/<app>`, then commit and push from that checkout.
## Submodules [#submodules]
Three source directories are git submodules, listed in `.gitmodules`. They keep chezmoi source naming and are applied in symlink mode like the rest of the tree. `chezmoi init` clones them.
| Source | Target | Repo |
| --- | --- | --- |
| `dot_config/niri` | `~/.config/niri` | [prdlk/niri](https://github.com/prdlk/niri) |
| `dot_config/hypr` | `~/.config/hypr` | [prdlk/hypr](https://github.com/prdlk/hypr) |
| `dot_config/fish` | `~/.config/fish` | [prdlk/fish](https://github.com/prdlk/fish) |
Edit them inside the submodule in the chezmoi source, commit there, then commit the updated submodule pointer in the root repo.
## .chezmoiignore.tmpl [#chezmoiignore]
Patterns match target paths (relative to `$HOME`). Matching entries are neither written nor removed. The file is a template, so the desktop block appears only on machines where it applies. Source entries that start with `.` (`.github/`, `.claude/`, `.crush/`, `.gitignore`) are ignored by chezmoi without needing a rule.
@@ -69,11 +103,12 @@ Files at the source root that would otherwise land in `$HOME` as `~/README.md`,
| `.config/lazygit/.omc/**` | Tool state written inside the lazygit config dir |
| `.config/tmux/plugins/**`, except `!.config/tmux/plugins/tpm` | Plugins tpm installs with `prefix I`; tpm itself is the external above |
| `.config/fish/fish_variables` | fish universal variables, rewritten by fish at runtime |
| `.config/fish/conf.d/secrets.fish` | Plaintext credentials. Local-only, never in git (the fish repo's `.gitignore` also lists it) |
| `.config/nvim/lazy-lock.json` | lazy.nvim lockfile inside the nvim external |
| `.config/nvim/.git`, `.config/nvim/.git/**` | The external's git metadata |
| `.config/nvim/.omc/**`, `.config/nvim/.crush/**`, `.config/nvim/.understand-anything/**` | Agent tool state inside the nvim checkout |
`.gitignore` covers the source side of the same problem: `dot_config/lazygit/dot_omc`, `dot_config/tmux/plugins`, `/yazi/plugins/*` (except the vendored `bunny.yazi`), and `__pycache__/` never get committed even though symlink mode lets tools write into the source tree.
`.gitignore` covers the source side of the same problem: `dot_config/lazygit/dot_omc` and `__pycache__/` never get committed even though symlink mode lets tools write into the source tree.
### mise
@@ -90,6 +125,8 @@ mise tool versions stay local to each machine and are not managed.
{{ if or (ne .chezmoi.os "linux") (and (ne .role "desktop") (ne .role "laptop")) }}
.config/niri
.config/niri/**
.config/hypr
.config/hypr/**
.config/noctalia
.config/noctalia/**
.config/niri-dynamic-workspaces
@@ -103,11 +140,10 @@ mise tool versions stay local to each machine and are not managed.
.local/bin/niri-launch
.config/fish/auto-Niri.fish
.local/bin/cp-appid
.config/tmux/niri-title.sh
{{ end }}
```
The block is emitted, and these paths skipped, unless the OS is `linux` and the role is `desktop` or `laptop`. `mac` and `server` machines, and any non-Linux machine regardless of role, never receive the niri compositor config (`dot_config/niri/`), fontconfig (`dot_config/fontconfig/fonts.conf`), default-app associations (`dot_config/mimeapps.list`), the fish auto-start hook (`dot_config/fish/auto-Niri.fish`), the tmux window-title helper, the systemd user units (`dot_config/systemd/user/kitty-day-save.service`), or the `niri-launch`, `cp-appid` and `kitty-day-save` scripts. The `noctalia` and `niri-dynamic-workspaces` rules match nothing in the current source tree; they only take effect if those configs are added later.
The block is emitted, and these paths skipped, unless the OS is `linux` and the role is `desktop` or `laptop`. `mac` and `server` machines, and any non-Linux machine regardless of role, never receive the niri and Hyprland configs (`dot_config/niri/`, `dot_config/hypr/`), fontconfig (`dot_config/fontconfig/fonts.conf`), default-app associations (`dot_config/mimeapps.list`), the fish auto-start hook (`dot_config/fish/auto-Niri.fish`), the systemd user units (`dot_config/systemd/user/kitty-day-save.service`), or the `niri-launch`, `cp-appid` and `kitty-day-save` scripts. The `noctalia` and `niri-dynamic-workspaces` rules match nothing in the current source tree; they only take effect if those configs are added later.
| Machine | Desktop layer |
| --- | --- |
@@ -115,7 +151,7 @@ The block is emitted, and these paths skipped, unless the OS is `linux` and the
| Linux, `server` or `mac` | Ignored |
| macOS or other OS, any role | Ignored |
See [Desktop](/desktop/niri) for what the layer contains. Gaps in the gate: the block lists `niri-launch` but not its siblings (`niri-launch-ai`, `niri-launch-browser`, `niri-launch-terminal`, `niri-launch-webapp`), and the Hyprland and waybar configs (`dot_config/hypr/`, `dot_config/waybar/`) are not gated at all, so those apply on every role. `dot_config/niri/scripts/` is covered by `.config/niri/**`.
See [Desktop](/desktop/niri) for what the layer contains. Gaps in the gate: the block lists `niri-launch` but not its siblings (`niri-launch-ai`, `niri-launch-browser`, `niri-launch-terminal`, `niri-launch-webapp`), and the waybar config (`dot_config/waybar/`) is not gated at all, so it applies on every role. `dot_config/niri/scripts/` is covered by `.config/niri/**`.
### Secrets in runtime state
+13 -8
View File
@@ -27,7 +27,13 @@ The repo is a [chezmoi](https://chezmoi.io) source directory. It lives at `~/.lo
- packages.yaml
- skills.yaml
- .chezmoiexternal.toml
- .chezmoiexternals/
- television.toml
- tmux.toml
- tmux-palette.toml
- yazi.toml
- .chezmoiignore.tmpl
- .gitmodules
- .chezmoiscripts/
- run_onchange_before_10-packages.sh.tmpl
- run_onchange_after_20-gh-extensions.sh.tmpl
@@ -59,15 +65,12 @@ The repo is a [chezmoi](https://chezmoi.io) source directory. It lives at `~/.lo
- niri/
- shell/
- starship.toml
- tmux/
- topgrade.toml
- waybar/
- symlink_yazi.tmpl
- zsh/
- dot_local/
- bin/
- dot_leetcode/
- yazi/
- docs/
- public/
- blume.config.ts
@@ -81,11 +84,13 @@ The repo is a [chezmoi](https://chezmoi.io) source directory. It lives at `~/.lo
| --- | --- | --- |
| `.chezmoi.toml.tmpl` | `~/.config/chezmoi/chezmoi.toml` | Config template rendered by `chezmoi init`. See [Config and roles](/chezmoi/config). |
| `.chezmoidata/` | — | YAML merged into template data (`.packages`, `.aliases`, `.skills`). See [Packages and aliases](/chezmoi/data) and [Skills](/ai/skills). |
| `.chezmoiexternal.toml` | — | Git repos cloned into `$HOME` (nvim config, a bat syntax). |
| `.chezmoiexternal.toml` | — | Git repos cloned into `$HOME` (nvim config, a bat syntax, tpm). |
| `.chezmoiexternals/` | — | One `git-repo` external per app: tmux, tmux-palette, television, yazi. Plain config repos cloned into `~/.config/<app>`. See [Externals and ignore rules](/chezmoi/externals-and-ignore#app-externals). |
| `.gitmodules` | — | `dot_config/niri`, `dot_config/hypr`, `dot_config/fish` are git submodules. See [Submodules](/chezmoi/externals-and-ignore#submodules). |
| `.chezmoiignore.tmpl` | — | Target paths chezmoi skips; gates the Linux desktop layer by role. |
| `.chezmoiscripts/`, root `run_*` | — | Install and housekeeping scripts. See [Apply scripts](/chezmoi/scripts). |
| `dot_config/` | `~/.config/` | Per-tool config. Documented under [Shell](/shell), [Terminal](/terminal), [Tools](/tools), [Desktop](/desktop). |
| `dot_local/bin/` | `~/.local/bin/` | 34 standalone scripts shared by every shell. |
| `dot_local/bin/` | `~/.local/bin/` | 39 standalone scripts shared by every shell. |
| `dot_leetcode/` | `~/.leetcode/` | leetcode CLI workspace state. |
| `docs/`, `public/`, `blume.config.ts`, `package.json`, `bun.lock`, `README.md` | — | This docs site and the repo README. Listed in `.chezmoiignore.tmpl`, never applied. |
@@ -99,10 +104,10 @@ chezmoi encodes target names and attributes in source file names. These are the
| --- | --- | --- |
| `dot_` | Target name starts with `.` | `dot_config` → `~/.config`, `dot_zshrc` → `~/.zshrc` |
| `private_` | Target has group/world permissions cleared (`0600`/`0700`) | `dot_config/atuin/private_config.toml`, `dot_config/gh/private_config.yml`, `dot_config/hunk/private_state.json` |
| `executable_` | Target gets the executable bit | 40 files: every script in `dot_local/bin/`, `dot_config/niri/scripts/`, `dot_config/tmux/executable_niri-title.sh` |
| `executable_` | Target gets the executable bit | 44 files: every script in `dot_local/bin/` (including `executable_tmux-niri-title`) and `dot_config/niri/scripts/` |
| `exact_` | Directory is exact: entries in the target that are not in the source are deleted | `dot_config/niri/exact_config.d/` (numbered `10-…kdl` through `95-…kdl` fragments) |
| `empty_` | File is kept even when empty | `dot_config/herdr/empty_dot_plugins.lock` |
| `symlink_` | Target is a symlink; file content is the link destination | `dot_config/kitty/symlink_current-theme.conf` (→ `themes/cyberdream.conf`), `dot_config/symlink_television.tmpl`, `dot_config/symlink_yazi.tmpl` |
| `symlink_` | Target is a symlink; file content is the link destination | `dot_config/kitty/symlink_current-theme.conf` (→ `themes/cyberdream.conf`) |
| `.tmpl` | Rendered with Go templates and chezmoi data | `dot_gitconfig.tmpl`, `dot_config/shell/aliases.sh.tmpl`, all scripts, the config and ignore files |
| `run_once_` / `run_onchange_` + `before_` / `after_` | Script, with run frequency and phase | See [Apply scripts](/chezmoi/scripts) |
@@ -120,7 +125,7 @@ What that means in practice:
- Tools that rewrite their own config write through the symlink into the repo. The runtime state that would otherwise pollute the source (plugin dirs, lock files, `fish_variables`, `__pycache__`) is excluded in `.chezmoiignore.tmpl` and `.gitignore`; see [Externals and ignore rules](/chezmoi/externals-and-ignore).
:::note
Several fish aliases (`e.f`, `e.h`, `e.k`, `e.n`, `e.y`, `e.t`, `,,`) open the chezmoi source paths directly. Symlink mode makes that equivalent to editing the live config.
Several fish aliases (`e.f`, `e.h`, `e.k`, `e.n`, `,,`) open the chezmoi source paths directly. Symlink mode makes that equivalent to editing the live config. `e.y` and `e.t` open the yazi and tmux external checkouts in `~/.config`.
:::
## Daily workflow
+2 -2
View File
@@ -28,7 +28,7 @@ Six scripts run during `chezmoi apply`. Four live in `.chezmoiscripts/`, a direc
| `.chezmoiscripts/run_once_after_30-user-services.sh.tmpl` | after | once | Runs once per distinct rendered content |
| `.chezmoiscripts/run_onchange_after_40-post.sh.tmpl` | after | onchange | Only edits to the script itself (or an OS change) |
| `run_onchange_after_build-bat-cache.sh.tmpl` | after | onchange | sha256 of `dot_config/bat/themes/cyberdream.tmTheme` and `dot_config/bat/config` |
| `run_onchange_after_install-yazi-plugins.sh.tmpl` | after | onchange | sha256 of `yazi/package.toml` |
| `run_onchange_after_install-yazi-plugins.sh.tmpl` | after | onchange | sha256 of `~/.config/yazi/package.toml` (empty when the file is absent) |
`run_onchange_` scripts re-run whenever their rendered content differs from the last successful run. The hash triggers work by writing a checksum of another file into a comment, so editing that file changes the script's content:
@@ -115,4 +115,4 @@ Each step ignores failures (`|| true`). tpm is cloned by [`.chezmoiexternal.toml
## install-yazi-plugins [#install-yazi-plugins]
`run_onchange_after_install-yazi-plugins.sh.tmpl` reinstalls yazi plugins whenever `yazi/package.toml` changes. It exits quietly if `ya` is missing or `${XDG_CONFIG_HOME:-$HOME/.config}/yazi/package.toml` does not exist, and otherwise runs `ya pkg install`. If that fails, it deletes ya's clone cache (`${XDG_CACHE_HOME:-$HOME/.cache}/yazi/packages`), since clones made by an older ya can break a newer one, and retries once. `~/.config/yazi` links to the source dir, so the plugins land in the repo's `yazi/plugins/`, which `.gitignore` excludes apart from the vendored `bunny.yazi`. The plugin list is on the [yazi page](/tools/yazi).
`run_onchange_after_install-yazi-plugins.sh.tmpl` reinstalls yazi plugins whenever `~/.config/yazi/package.toml` changes (the template hashes it via `joinPath .chezmoi.homeDir`, guarded with `stat`). It exits quietly if `ya` is missing or `${XDG_CONFIG_HOME:-$HOME/.config}/yazi/package.toml` does not exist, and otherwise runs `ya pkg install`. If that fails, it deletes ya's clone cache (`${XDG_CACHE_HOME:-$HOME/.cache}/yazi/packages`), since clones made by an older ya can break a newer one, and retries once. `~/.config/yazi` is the [prdlk/yazi](https://github.com/prdlk/yazi) external checkout, so the plugins land in its `plugins/`, which that repo's `.gitignore` excludes apart from the vendored `bunny.yazi`. The plugin list is on the [yazi page](/tools/yazi).