Template Conversion

haven uses Tera (Jinja2-compatible) instead of Go templates. The importer converts most constructs automatically, but complex expressions may need manual attention.

Variable mapping

chezmoi / Go haven / Tera
{{ .chezmoi.hostname }} {{ hostname }}
{{ .chezmoi.username }} {{ username }}
{{ .chezmoi.os }} {{ os }}
{{ .chezmoi.arch }} {{ arch }}
{{ .chezmoi.homeDir }} {{ home_dir }}
{{ .chezmoi.sourceDir }} {{ source_dir }}
{{ .someCustomVar }} {{ data.someCustomVar }}
{{ (index . "someVar") }} {{ data.someVar }}
{{ env "VAR" }} {{ get_env(name="VAR") }}

Custom data namespacing

chezmoi accesses custom data as .key. haven namespaces it under data.key to avoid collisions with built-in variables. The importer rewrites these automatically and migrates values from .chezmoidata.* into [data] in haven.toml.

OS name

chezmoi uses "darwin" for macOS; haven uses "macos". The importer rewrites these automatically.

Control flow

chezmoi / Go haven / Tera
{{- if eq .chezmoi.os "darwin" -}} {% if os == "macos" %}
{{- if eq .chezmoi.os "linux" -}} {% if os == "linux" %}
{{- else if eq .chezmoi.os "linux" -}} {% elif os == "linux" %}
{{- else -}} {% else %}
{{- end -}} {% endif %}
{{ if .someVar }} {% if data.someVar %}
{{ range .someList }} {% for item in data.someList %}
{{ end }} (range) {% endfor %}

Comments

chezmoi / Go haven / Tera
{{/* comment */}} {# comment #}

1Password integration

chezmoi haven
{{ onepasswordField "Personal" "GitHub" "token" }} {{ op(path="Personal/GitHub/token") }}
{{ onepassword "op://Personal/GitHub/token" }} {{ op(path="op://Personal/GitHub/token") }}

Before and after example

chezmoi (Go templates):

[user]
  email = {{ if eq .chezmoi.os "darwin" }}{{ .work_email }}{{ else }}{{ .personal_email }}{{ end }}
  name = {{ .chezmoi.username }}

[core]
  editor = {{ env "EDITOR" }}

haven (Tera):

[user]
{% if os == "macos" %}
  email = {{ data.work_email }}
{% else %}
  email = {{ data.personal_email }}
{% endif %}
  name = {{ username }}

[core]
  editor = {{ get_env(name="EDITOR") }}

Constructs that need manual conversion

The importer marks unconvertible constructs with # haven: TODO:

# haven: TODO: complex pipeline: {{ .someList | join "," }}
{{ .someList | join "," }}

Find them after importing:

grep -r "haven: TODO" ~/.local/share/haven/source/

Common manual conversions

String join:

# Go:   {{ .list | join "," }}
# Tera: {{ data.list | join(sep=",") }}

Default value:

# Go:   {{ .value | default "fallback" }}
# Tera: {{ data.value | default(value="fallback") }}

String contains:

# Go:   {{ contains .str "substr" }}
# Tera: {% if str is containing("substr") %}

Custom functions: Go template custom functions registered by chezmoi (like bitwarden, lastpass, vault) have no Tera equivalent. Replace these with {{ op(path="...") }} calls if you use 1Password, or migrate the secret to an environment variable.

Checking variables in scope

After migration, run:

haven data

To see all variables available in templates on this machine:

os         = macos
hostname   = my-laptop
username   = alice
home_dir   = /Users/alice
source_dir = /Users/alice/.local/share/haven

data.work_email  = alice@corp.example
data.kanata_path = /usr/local/bin/kanata