twpayne / chezmoi

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

Nesting conditionals in .chezmoiignore #1886

Closed skyfaller closed 2 years ago

skyfaller commented 2 years ago

What exactly are you trying to do?

I have a Mac and several Linux computers. I find that I generally cannot test for specific Linux distributions unless the conditionals are nested within a conditional testing whether we're running Linux, because Mac does not have .chezmoi.osRelease.id and Chezmoi will error out.

I want to have some files that appear on certain Linux distributions and not others (for example, configuration files for window managers like KDE or Sway that I only use on specific distros / computers).

What have you tried so far?

Here is the .chezmoiignore file I'm trying to use:

{{- if ne .chezmoi.os "darwin" }}
Library # only manage Library folder on macOS
{{- else if ne .chezmoi.os "linux" }}
.config/Code/ # VS Code settings live here on Linux
  {{- if ne .chezmoi.osrelease.id "fedora" }}
.config/fish/functions/theme-konsole.fish # KDE only on Linux
.config/mako/
.config/sway/
  {{- end}}
{{- end}}

I don't use Sway or KDE on Fedora, so don't want those config files. But this code does not seem to work. Chezmoi wants to create those files on Fedora anyway when I run chezmoi apply. This code previously worked fine for differentiating between Mac and Linux, before I tried nesting conditionals in .chezmoiignore.

Where else have you checked for solutions?

Output of chezmoi doctor

```console > chezmoi doctor RESULT CHECK MESSAGE ok version v2.11.2, commit 13de45fdd4d2eddd3447d80c129754090fde80ac, built at 2022-02-03T13:31:23Z, built by Homebrew ok os-arch linux/amd64 (Fedora Linux 35 (Workstation Edition)) ok uname Linux bobbie 5.16.8-200.fc35.x86_64 #1 SMP PREEMPT Tue Feb 8 20:58:59 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux ok go-version go1.17.6 (gc) ok executable /home/linuxbrew/.linuxbrew/Cellar/chezmoi/2.11.2/bin/chezmoi ok upgrade-method brew-upgrade ok config-file ~/.config/chezmoi/chezmoi.toml ok source-dir ~/.local/share/chezmoi is a directory ok suspicious-entries no suspicious entries ok working-tree ~/.local/share/chezmoi is a directory ok dest-dir ~ is a directory ok shell found /usr/bin/fish ok edit-command found /home/linuxbrew/.linuxbrew/bin/micro ok umask 022 ok git-command found /usr/bin/git, version 2.34.1 warning merge-command vimdiff not found in $PATH info age-command age not found in $PATH ok gpg-command found /usr/bin/gpg, version 2.3.4 info pinentry-command not set info 1password-command op not found in $PATH info bitwarden-command bw 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 lastpass-command lpass not found in $PATH info pass-command pass not found in $PATH info vault-command vault not found in $PATH info secret-command not set ```
twpayne commented 2 years ago

Nested conditionals work. I think what's happening in your case is that the {{- template tags are consuming whitespace (including the previous newline) and so you'll ending up with corrupted patterns in your .chezmoiignore file.

Try replacing the {{-s with {{s. You can use chezmoi execute-template to check the template is correct.

skyfaller commented 2 years ago

Hm, nesting doesn't work the way I thought it did in .chezmoiignore. Here's what I get:

> chezmoi execute-template < .chezmoiignore

Library # only manage Library folder on macOS

None of the Linux stuff shows up at all, because it's all in Linux. All that gets ignored is the Mac stuff. I think I need a different approach to selectively ignore some Linux stuff.

Replacing the {{- with {{ seems to have no effect, aside from an extra line of whitespace at the end of the file:

{{ if ne .chezmoi.os "darwin" }}
Library # only manage Library folder on macOS
{{ else if ne .chezmoi.os "linux" }}
.config/Code/ # VS Code settings live here on Linux
  {{ if ne .chezmoi.osrelease.id "fedora" }}
.config/fish/functions/theme-konsole.fish # KDE only on Linux
.config/mako/
.config/sway/
  {{ end }}
{{ end }}
> chezmoi execute-template < .chezmoiignore

Library # only manage Library folder on macOS
dragonmaus commented 2 years ago

None of the Linux stuff shows up at all, because it's all in Linux. All that gets ignored is the Mac stuff. I think I need a different approach to selectively ignore some Linux stuff.

You're on the right track. Your if-else statement is testing for .chezmoi.os not equal to the specified values, so all Linux distros will activate the first branch only. They will never see the nested conditional at all.

Also, due to the negative logic, the second conditional (ne .chezmoi.os "linux") is redundant; that is, if the OS is darwin (i.e. the first conditional evaluates to false), it will inherently not be linux (i.e. the second conditional will always evaluate to true).

Finally, you're trying to test if the OS is not Linux, and only then checking to see if it's not Fedora. This will always be true, as any OS that isn't Linux is also guaranteed to not be Fedora.

Long story short, you need to put the "fedora" check inside the "not Darwin" branch:

{{- if ne .chezmoi.os "darwin" }}
darwin-only stuff goes here
{{-   if eq .chezmoi.os "linux" }}
{{-     if ne .chezmoi.osRelease.id "fedora" }}
fedora-only stuff goes here
{{-     end }}
{{-   end }}
{{- end }}
skyfaller commented 2 years ago

Long story short, you need to put the "fedora" check inside the "not Darwin" branch:

Thank you for writing out a solution! It seems inelegant somehow, but the whole negating the thing you want to ignore is confusing as heck anyway, this is just another counterintuitive wrinkle.

At any rate, your suggestion worked for me. When run on Fedora, it gives the desired result:

{{ if ne .chezmoi.os "darwin" -}}
Library # only manage Library folder on macOS
  {{- if eq .chezmoi.os "linux" }}
.config/Code/ # VS Code settings live here on Linux
    {{- if eq .chezmoi.osRelease.id "fedora" }}
.config/fish/functions/theme-konsole.fish # KDE only on Linux
.config/mako/
.config/sway/
    {{- end }}
  {{- end }}
{{- end }}
> chezmoi execute-template < .chezmoiignore
Library # only manage Library folder on macOS
.config/Code/ # VS Code settings live here on Linux
.config/fish/functions/theme-konsole.fish # KDE only on Linux
.config/mako/
.config/sway/
skyfaller commented 2 years ago

Wait, no, that's not what I want. Now both the Mac (stored in the Library) and Linux copies of my VS Code config files are ignored. I guess I have to do a separate branch for the generic (non-Fedora) Linux stuff:

{{ if ne .chezmoi.os "darwin" -}}
Library # only manage Library folder on macOS
  {{- if eq .chezmoi.os "linux" }}
    {{- if eq .chezmoi.osRelease.id "fedora" }}
.config/fish/functions/theme-konsole.fish # KDE on Linux but not Fedora
.config/mako/
.config/sway/
    {{- end }}
  {{- end }}
{{- end }}
{{- if ne .chezmoi.os "linux" }}
.config/Code/ # VS Code settings live here on Linux
{{- end }}
> chezmoi execute-template < .chezmoiignore
Library # only manage Library folder on macOS
.config/fish/functions/theme-konsole.fish # KDE on Linux but not Fedora
.config/mako/
.config/sway/

This is getting really confusing and inelegant, but I think it actually works now. Good enough.

skyfaller commented 2 years ago

I just tested it on Mac and found a further wrinkle: now the stuff I want ignored on Fedora doesn't get ignored on Mac.

> chezmoi execute-template < .chezmoiignore

.config/Code/ # VS Code settings live here on Linux

I guess I have to copy and paste it to the stuff I want Mac to ignore? I don't like duplicating code. I'm leaving this issue open because there must be a better way to do this. That said, here's my current solution:

{{- if ne .chezmoi.os "darwin" -}}
Library # only manage Library folder on macOS
  {{- if eq .chezmoi.os "linux" }}
    {{- if eq .chezmoi.osRelease.id "fedora" }}
.config/fish/functions/theme-konsole.fish # KDE on Linux but not Fedora
.config/mako/
.config/sway/
    {{- end }}
  {{- end }}
{{- end }}
{{- if ne .chezmoi.os "linux" -}}
.config/Code/ # VS Code settings live here on Linux
.config/fish/functions/theme-konsole.fish # KDE only on Linux
.config/mako/
.config/sway/
{{- end }}
> chezmoi execute-template < .chezmoiignore
.config/Code/ # VS Code settings live here on Linux
.config/fish/functions/theme-konsole.fish # KDE only on Linux
.config/mako/
.config/sway/
dragonmaus commented 2 years ago

Ah, I think I see where I wasn't understanding what you're after. In order to avoid duplication of ignore lines, you must be willing to accept some redundancy of logic blocks. (As a convenience I use a custom variable in chezmoi.conf [I call it .system.id, but the name doesn't matter] that contains both .chezmoi.os and .chezmoi.osRelease.id [if it exists]. You can reference my .chezmoiignore for a more involved example.)

{{- if ne .chezmoi.os "darwin" }}
Library/ # only manage Library folder on macOS
{{- end }}

{{- if ne .chezmoi.os "linux" }}
.config/Code/ # VS Code settings live here on Linux
{{- end }}

{{- if or (ne .chezmoi.os "linux") (eq .system.id "linux-fedora") }}
.config/fish/functions/theme-konsole.fish # KDE only on Linux but not Fedora
.config/mako/
.config/sway/
{{- end }}
twpayne commented 2 years ago

Thank you @dragonmaus for all your help here.

skyfaller commented 2 years ago

(As a convenience I use a custom variable in chezmoi.conf [I call it .system.id, but the name doesn't matter] that contains both .chezmoi.os and .chezmoi.osRelease.id [if it exists]. You can reference my .chezmoiignore for a more involved example.)

This is a stroke of genius. Thank you so much for the examples! This will make my code much more compact and readable, I can stop nesting .chezmoi.osRelease.id inside of tests for Linux everywhere. I am using your code verbatim.

I think this abstraction ought to be in Chezmoi itself, so long as testing for specific Linux distros fails on any OS like macOS that does not have distro IDs: https://github.com/twpayne/chezmoi/issues/969

But I suppose I can open another feature request for that.

twpayne commented 2 years ago

Thanks, this is a great trick. For now I've documented it in dca459b22754ddaab30bda152ed4d07119488cb2.