twpayne / chezmoi

Manage your dotfiles across multiple diverse machines, securely.
https://www.chezmoi.io/
MIT License
13.36k stars 493 forks source link

Unable to call templates in `.chezmoi.toml.tmpl` #3418

Closed davidolrik closed 10 months ago

davidolrik commented 10 months ago

When calling the template function in .chezmoi.toml.tmpl a template "template name" not defined error is thrown.

I have created a sample repo with just two files that show the error:

Simply use the regular installer like so:

% sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply https://davidolrik@github.com/davidolrik/chezmoi-template-bug.git

Expected behavior

The .chezmoi.toml is generated using the templateTest template.

Output of command with the --verbose flag

I tried calling it with --verbose but it didn't yield any more output.

Output of chezmoi doctor

```console % ./bin/chezmoi doctor RESULT CHECK MESSAGE ok version v2.42.3, commit 43a4f5790ef1b709de881d36afb0a61745efa34f, built at 2023-12-16T23:26:32Z, built by goreleaser ok latest-version v2.42.3 ok os-arch linux/amd64 (Ubuntu 22.04.3 LTS (Jammy Jellyfish)) ok uname Linux first.hq.olrik.cloud 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux ok go-version go1.21.5 (gc) ok executable ~/bin/chezmoi ok upgrade-method replace-executable ok config-file no config file found ok source-dir ~/.local/share/chezmoi is a git working tree (clean) ok suspicious-entries no suspicious entries ok working-tree ~/.local/share/chezmoi is a git working tree (clean) ok dest-dir ~ is a directory ok umask 002 ok cd-command found /usr/bin/zsh ok cd-args /usr/bin/zsh info diff-command not set ok edit-command found /usr/bin/vi ok edit-args /usr/bin/vi ok git-command found /usr/bin/git, version 2.34.1 ok merge-command found /usr/bin/vimdiff ok shell-command found /usr/bin/zsh ok shell-args /usr/bin/zsh info age-command age not found in $PATH ok gpg-command found /usr/bin/gpg, version 2.2.27 info pinentry-command not set info 1password-command op not found in $PATH info bitwarden-command bw not found in $PATH info bitwarden-secrets-command bws not found in $PATH info dashlane-command dcli not found in $PATH info doppler-command doppler not found in $PATH info gopass-command gopass not found in $PATH info keepassxc-command keepassxc-cli not found in $PATH info keepassxc-db not set info keeper-command keeper not found in $PATH info lastpass-command lpass not found in $PATH info pass-command pass not found in $PATH info passhole-command ph not found in $PATH info rbw-command rbw not found in $PATH info vault-command vault not found in $PATH info vlt-command vlt not found in $PATH info secret-command not set ```
twpayne commented 10 months ago

Thanks for the excellent test case to demonstrate the problem!

The current behavior is actually the intended behavior. The config file template (.chezmoi.toml.tmpl) is executed before the source state is read, so templates in .chezmoitemplates are not yet available. This is why it works with chezmoi execute-template but not chezmoi init.

I'll update the docs to say this and update chezmoi execute-template --init to not read .chezmoitemplates.

davidolrik commented 10 months ago

Is there another way to do dynamic configuration? - I tried using .chezmoidata but it doesn't support templating.

twpayne commented 10 months ago

What exactly do you mean by dynamic configuration? What exactly do you want to do?

davidolrik commented 10 months ago

I want to install specific files from releases on GitHub.

Given a GitHub url, I'm fetching the latest version dynamically and then installing one or more files and directories from the release archive.

Usually binaries goes into ~/bin and completion files goes into ~/.zfunc. Sometimes there are also full directories that needs to be copied somewhere.

To accomplish this I generate one or more entries in .chezmoiexternal.toml for files and, for directories I need to create a .chezmoiexternal.toml in the parent directory of the target directory. So most of the external config is the same for each release archive, or can be extrapolated from the url and a few arguments to the templates.

Right now I have it working with 2 templates, one for files, and one for directories. But given a release with x files, my template will do x "version lookups" and x "archive exist calls" to GitHub, making it quite slow as I have about 10 packages I want to install.

So for me, being able to execute templates in .chezmoi.toml.tmpl would have been ideal as then all the dynamic stuff would only be done once at "init" time.

twpayne commented 10 months ago

So for me, being able to execute templates in .chezmoi.toml.tmpl would have been ideal as then all the dynamic stuff would only be done once at "init" time.

This approach also means that you have to run chezmoi init to re-generate .config/chezmoi/chezmoi.toml whenever you want to check for latest releases. I suspect that this is not the behavior you want. The config file is for configuration data specific to the local machine, and determining the latest release us not specific to the local machine.

The gitHubLatestRelease template function returns the latest release and caches the result. Furthermore, results are cached for the entire run of chezmoi, meaning you can call the function with the same arguments in different templates and chezmoi will only make one API call to GitHub. Furthermore, the result of the API call is cached on disk for the duration specified in the github.refreshPeriod config variable and defaults to 1m (1 minute). If you increase this to, say 1 hour (1h), then chezmoi apply will be much faster while you're iterating on your templates, while still giving you reasonably up-to-date information.

This is an example of using gitHubLatestRelease:

{{- $ezaVersionTag := (gitHubLatestRelease "eza-community/eza").TagName }}
[".local/bin/eza"]
    type = "archive-file"
    url = "https://github.com/eza-community/eza/releases/download/{{ $ezaVersionTag }}/eza_x86_64-unknown-linux-gnu.tar.gz"
    executable = true
    path = "./eza"