# Apply scripts Every run_ script chezmoi executes during apply, when each one fires, and what it does on each OS and role. Eight scripts run during `chezmoi apply`. Six 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 eight are templates; `20-gh-extensions` is a fish script, the rest use `#!/bin/sh` with `set -eu` (except `25-skills`, which uses `set -u`). ## 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. 1. **05-externals (before, every apply)** Offer to replace external targets that exist but are not git checkouts. 2. **10-packages (before, onchange)** Install system packages from `packages.yaml`. 3. **Files, symlinks, externals** chezmoi writes the target state. 4. **15-tools (after, onchange)** rustup and cargo crates, omp, global mise tools, strata on Linux desktops. 5. **20-gh-extensions (after, onchange)** Install gh extensions from a fish session. 6. **25-skills (after, onchange)** Install every skill in prdlk/skills, remove skills from other sources. 7. **30-user-services (after, once)** Enable systemd user units (kitty-day-save, projects-sync.timer). 8. **40-post (after, onchange)** bat cache, font cache. 9. **build-bat-cache (after, onchange)** Rebuild bat cache when its theme or config changes. 10. **install-yazi-plugins (after, onchange)** Reinstall yazi plugins when the manifest changes. | Script | Phase | Frequency | Re-run trigger | | --- | --- | --- | --- | | `.chezmoiscripts/run_before_05-externals.sh.tmpl` | before | every apply | Always runs; cheap unless a target is in the way | | `.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_15-tools.sh.tmpl` | after | onchange | sha256 of `packages.shared.cargo` and `packages.shared.mise` as JSON; also the OS and role, which add the strata step | | `.chezmoiscripts/run_onchange_after_20-gh-extensions.sh.tmpl` | after | onchange | sha256 of `packages.shared.gh_extensions` as JSON | | `.chezmoiscripts/run_onchange_after_25-skills.sh.tmpl` | after | onchange | sha256 of `.skills` 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 | 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 `~/.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: ```sh # 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. ## 05-externals `.chezmoiscripts/run_before_05-externals.sh.tmpl` clears the way for the `git-repo` externals in `.chezmoiexternals/*.toml` and `.chezmoiexternal.toml` (its list is rendered from those files, so new externals are covered without edits). chezmoi clones an external only into a missing or empty target and pulls only in an existing checkout; any other directory at the target stops the apply with a git error. A fresh Omarchy install ships its own `~/.config/kitty`, `ghostty`, `omarchy`, and others, so the first apply would stop there. - Runs before every apply (`run_before_`). With every target missing, empty, or already a checkout it only stats directories and exits 0. - For each target that exists otherwise (a non-empty directory without `.git`, a file, or a symlink), it asks on the terminal: ` exists and is not a git checkout. Replace it with (old copy kept as .bak-