Skip to content

Templates

Delimiter Layers

This repository uses custom chezmoi delimiters ([{ }]) so that just's {{ }} passes through untouched. See Conventions for the rules.

Two-Phase Example

chezmoi:template:left-delimiter=[{ right-delimiter=}]

# Chezmoi injects this at apply time
chezmoi_src := "[{ .chezmoi.sourceDir }]"

# Just evaluates this at runtime
some-recipe:
    @echo "Source: {{ chezmoi_src }}"

Three-Layer Escaping (Chezmoi → Just → Docker)

When Docker/Go format strings appear inside just recipes, there are three {{ }} consumers:

Layer Delimiters When Example
Chezmoi [{ }] chezmoi apply [{ .isMacos }]
Just {{ }} recipe execution {{ name }}, {{ invdir }}
Docker/Go {{ }} command runtime {{.ID}}, {{.LogPath}}

Just interprets {{ }} first, so Docker format strings need escaping — double the opening braces: {{{{.ID}}{{.ID}}. The closing }} does not need doubling.

# Source (.tmpl)                          # Rendered just file           # Shell output
docker ps --format '{{{{.ID}}'            # → '{{.ID}}'                 # → container ID
docker inspect --format '{{{{.State}}}'   # → '{{.State}}'             # → map[...]

Shared Template Data

Reusable values are defined in .chezmoidata/data.yaml and accessible in all templates via dot notation:

# .chezmoidata/data.yaml
status:
  ok:      "\u001b[32mOK:\u001b[0m"
  fail:    "\u001b[31mFAIL:\u001b[0m"
  success: "\u001b[32mSUCCESS:\u001b[0m"
  warning: "\u001b[33mWarning:\u001b[0m"

Usage in templates:

echo "[{ .status.ok }] Something worked"
echo "[{ .status.fail }] Something failed"

This keeps colour codes DRY and consistent across all scripts.

Conditional Patterns

Use these patterns to handle platform differences:

# Skip on Bluefin (tool provided by system)
[{- if not .isBluefin }]
brew "starship"
[{- end }]

# Guard for Linux (e.g., font cache)
[{- if .isLinux }]
fc-cache -f
[{- end }]

# OS-specific logic
[{- if .isMacos }]
# macOS only
[{- else if .isLinux }]
# Linux only
[{- end }]

Because .isMacos, .isLinux, etc. are custom data flags (not built-in chezmoi values), they can be overridden via chezmoi execute-template --override-data — this is how the full-render test generates all platform variants from a single machine.

Externally-Modified Files (Modify Templates)

Some config files are shared between chezmoi and external tools that rewrite them at runtime. For example, ~/.docker/config.json is managed by chezmoi (to set currentContext and cliPluginsExtraDirs) but Docker also rewrites it — reordering JSON keys and stripping trailing newlines. A standard chezmoi template treats the rendered output as the complete desired state, so any external rewrite causes a perpetual diff.

The Problem

Approach Outcome
Standard template (config.json.tmpl) Perpetual diff — Docker reformats the file after every apply
create_ prefix One-shot — chezmoi never updates the file once it exists
run_onchange_ script Imperative, needs jq, invisible to chezmoi diff

The Solution: modify_ with deepEqual Guard

A modify template receives the existing file contents via .chezmoi.stdin, merges in the desired keys, and uses deepEqual to decide whether to rewrite:

{{- /* chezmoi:modify-template */ -}}
{{- $config := dict -}}
{{- if ne .chezmoi.stdin "" -}}
{{-   $config = fromJson .chezmoi.stdin -}}
{{- end -}}
{{- $desired := deepCopy $config -}}
{{- $desired = set $desired "currentContext" "colima" -}}
{{- $desired = set $desired "cliPluginsExtraDirs" (list (joinPath (lookPath "brew" | dir | dir) "lib/docker/cli-plugins")) -}}
{{- if deepEqual $config $desired -}}
{{-   .chezmoi.stdin -}}
{{- else -}}
{{-   toPrettyJson "\t" $desired }}
{{ end -}}

How it works:

  • Keys already correct: deepEqual returns true → stdin is passed through verbatim (preserving the external tool's formatting). Chezmoi sees zero diff.
  • Keys missing or wrong: deepEqual returns false → file is rewritten with toPrettyJson. Subsequent runs will then pass through verbatim.
  • File doesn't exist (fresh machine): stdin is empty → template creates the file from scratch.
  • External tool adds new keys: Those keys flow into both $config and $desired via deepCopy, so they are preserved.

When to Use

Use a modify template when:

  • Chezmoi needs to ensure specific keys in a file that external tools also modify
  • The external tool reformats the file (key reordering, whitespace changes)
  • You want chezmoi diff and chezmoi status visibility into the file's state

Do not use modify templates for files that chezmoi fully owns — a standard template is simpler and sufficient.