Skip to content
dots
Esc
↑↓navigate↵open⌘Jpreview
On this page

Apply scripts

Every run_ script chezmoi executes during apply, when each one fires, and what it does on each OS and role.

Six scripts run during chezmoi apply. Four live in .chezmoiscripts/, a directory whose scripts run as if they sat at the source root without creating a matching target directory. Two sit at the repo root. All six are templates and all use #!/bin/sh with set -eu.

Execution order

before_ scripts run before any file is written; after_ scripts run after every file, symlink, and external is in place. Within a phase chezmoi runs scripts in ASCII order of their target names, so the numeric prefixes come first.

10-packages (before, onchange)

Install system packages from packages.yaml.

Files, symlinks, externals

chezmoi writes the target state.

20-gh-extensions (after, onchange)

Install gh extensions.

30-user-services (after, once)

Enable systemd user units on Linux.

40-post (after, onchange)

tpm clone, bat cache, font cache.

build-bat-cache (after, onchange)

Rebuild bat cache when its theme or config changes.

install-yazi-plugins (after, onchange)

Reinstall yazi plugins when the manifest changes.
Script Phase Frequency Re-run trigger
.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl before onchange sha256 of .chezmoidata/packages.yaml; also the role and OS, which change the rendered package list
.chezmoiscripts/run_onchange_after_20-gh-extensions.sh.tmpl after onchange sha256 of packages.shared.gh_extensions as JSON
.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 dot_config/yazi/package.toml

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:

# packages.yaml hash: {{ include ".chezmoidata/packages.yaml" | sha256sum }}

run_once_ scripts run once per unique rendered content and again only if that content changes or the previous run failed.

10-packages

.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl installs everything in packages.yaml. It runs before files are applied so the tools exist when configs land.

  1. Uses pacman directly when running as root, otherwise sudo pacman.
  2. pacman -S --needed --noconfirm with every entry in packages.arch.pacman.
  3. For roles desktop and laptop, a second pacman -S --needed --noconfirm with packages.arch.desktop.pacman.
  4. Builds the AUR list: packages.arch.desktop.aur plus packages.arch.aur for desktop/laptop, only packages.arch.aur for other roles.
  5. If the list is non-empty and paru exists, runs paru -S --needed --noconfirm on it. Without paru it prints warning: paru not found — skipping AUR packages: … and continues.
  1. Exits with an error pointing at https://brew.sh if brew is missing.
  2. brew install with every entry in packages.darwin.brew.
  3. brew install --cask with every entry in packages.darwin.cask.

The role is not consulted on macOS.

On any other OS the script renders to just the shebang and comments and does nothing.

20-gh-extensions

.chezmoiscripts/run_onchange_after_20-gh-extensions.sh.tmpl installs each packages.shared.gh_extensions entry on every OS.

  • Exits 0 with warning: gh not installed — skipping extensions if gh is missing.
  • Exits 0 with warning: gh not authenticated — run 'gh auth login', then 'chezmoi apply' again if gh auth status fails.
  • Reads gh extension list, then runs gh extension install <repo> only for extensions not already present.

30-user-services

.chezmoiscripts/run_once_after_30-user-services.sh.tmpl enables session-critical systemd user units on Linux.

  • If ~/.config/systemd/user/nirinit.service exists and systemctl is available, runs systemctl --user daemon-reload and systemctl --user enable nirinit.service. Failures are ignored.
  • On non-Linux systems it renders to #!/bin/sh / exit 0.

Other personal automation units (brain-lens, omp-loop, rclone, hermes, voxtype) are deliberately not enabled; the script comment says to enable them by hand per machine. The repo does not currently contain a dot_config/systemd/ directory, so the unit file has to exist on the machine already for this script to do anything.

40-post

.chezmoiscripts/run_onchange_after_40-post.sh.tmpl is post-apply housekeeping. It has no hash comment, so it only re-runs when the script itself is edited.

Step Condition Action
tmux plugin manager tmux installed and ~/.config/tmux/plugins/tpm missing git clone --depth=1 https://github.com/tmux-plugins/tpm ~/.config/tmux/plugins/tpm
bat cache bat installed bat cache --build
Font cache Linux, fc-cache installed fc-cache -f

Each step ignores failures (|| true). tmux.conf expects tpm at that path; see tmux.

build-bat-cache

run_onchange_after_build-bat-cache.sh.tmpl rebuilds bat’s syntax and theme cache when the managed inputs change. It hashes dot_config/bat/themes/cyberdream.tmTheme and dot_config/bat/config, exits quietly if bat is missing, and otherwise runs bat cache --build. This registers the cyberdream theme and the PureScript syntax cloned by .chezmoiexternal.toml.

install-yazi-plugins

run_onchange_after_install-yazi-plugins.sh.tmpl reinstalls yazi plugins whenever dot_config/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. The installed plugins land in ~/.config/yazi/plugins/, which .chezmoiignore.tmpl excludes. The plugin list is on the yazi page.

Was this page helpful?