Bootstrapping chezmoi_modify_manager before chezmoi apply

If you use chezmoi_modify_manager together with chezmoi, there is one annoying bootstrap problem:

  • chezmoi_modify_manager is needed while chezmoi apply computes file contents
  • but the usual .chezmoiscripts/run_before_* hooks run too late to install it for the first apply

I wanted a solution that was:

  • automatic
  • shell-based
  • version-pinned
  • rerun only when the installer script changes

This post shows the setup I ended up with.

The problem

At first glance, it seems natural to put an installer script into .chezmoiscripts, for example, as a run_onchange_before_* script.

That helps with normal lifecycle management, but it does not solve the initial bootstrap.

Why? Because chezmoi needs to compute the target state before it runs those scripts, and modify_ files participate in that computation. If those files use chezmoi_modify_manager, then the binary must already exist before chezmoi apply reaches the script phase.

So the core issue is:

chezmoi_modify_manager must be installed before chezmoi apply starts doing the work that depends on it.

The approach

The solution is to split the problem into two:

  1. Put the installer in .chezmoiscripts as a run_onchange_before_* script, so the script is tracked by chezmoi and reruns when its contents change.
  2. Wrap chezmoi with a small shell function that manually runs that installer script before delegating to the real chezmoi apply.

That gives us the best of both worlds:

  • proper chezmoi-managed script in the source repo
  • successful first bootstrap
  • automatic reinstall when the installer changes, such as when bumping the pinned version

The installer script

I placed this file in:

~/.local/share/chezmoi/.chezmoiscripts/run_onchange_before_00-install-chezmoi-modify-manager.sh

The script is version-pinned, architecture-aware, and idempotent. It also keeps a tiny version stamp, so it can skip work when the correct version is already installed.

A few notes:

  • VERSION is deliberately hardcoded for simplicity
  • STAMP stores the installed version so the script can cheaply detect whether work is needed
  • --doctor provides a sanity check after installation

If you later want to upgrade, just change the VERSION string in the script. Because the script is a run_onchange_* script, chezmoi will detect the change.

Manual bootstrapping

After running “chezmoi init <URL>” and before running “chezmoi apply” for the first time, you must remember to manually execute this script

Alternatively, you can use the “wrapper” solution detailed next.

The wrapper

Now for the missing bootstrap step: we need to ensure the installer runs before chezmoi apply.

I used a shell function in my ~/.bashrc (or ~/.zshrc).

Then reload your shell:

With this in place, running:

does the following:

  1. finds the real chezmoi
  2. checks whether the installer script exists in the source directory
  3. runs the installer script first
  4. only then runs the real chezmoi apply

That is exactly what we need.

Why this works

This setup works because it respects the real dependency order.

The dependency is not:

“Run a script sometime before applying files.”

The dependency is:

“Ensure chezmoi_modify_manager exists before chezmoi starts evaluating anything that depends on it.”

A normal run_before_* script is still part of chezmoi’s internal apply pipeline, which is already too late for the very first bootstrap.

The shell wrapper moves the install step out of the pipeline and places it before chezmoi apply begins.

Why keep the installer in .chezmoiscripts at all?

Because it still gives you useful chezmoi behavior:

  • the installer lives in your dotfiles repo
  • it is versioned like everything else
  • changing the script content is part of your normal dotfiles workflow
  • the run_onchange_* naming still reflects the script’s intended lifecycle

In other words, the wrapper solves the bootstrap timing problem, while .chezmoiscripts keeps the implementation under chezmoi ‘s control.

Optional: ensure ~/.local/bin is on your PATH

If your shell does not already include ~/.local/bin, add it:

 

Happy dotfiles! 🙂

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.