twpayne / chezmoi

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

OS detection error - hasKey and index of .chezmoi.osRelease #2502

Closed gadams999 closed 1 year ago

gadams999 commented 1 year ago

What exactly are you trying to do?

I upgraded chezmoi to version:

chezmoi --version
chezmoi version v2.25.0, commit b3a8879e30a20134c9bef48646055e57aa78d8c5, built at 2022-10-13T14:01:32Z, built by goreleaser

and started to see my OS detection fail. I had been using this for detecting Linux variants:

{{ if (index .chezmoi.osRelease "id") -}}
{{   if eq .chezmoi.osRelease.id "raspbian" -}}
echo "Installing Raspberry Pi OS specific packages"

but then started to get errors about nil values (from the first line above):

chezmoi: template: run_once_before_10_install_packages.sh.tmpl:70:7: executing "run_once_before_10_install_packages.sh.tmpl" at <index .chezmoi.osRelease "id">: error calling index: index of untyped nil

Reading the docs, I saw the reference to using hasKey to check for id and then create an osid entry in my config file:

{{- $osid := .chezmoi.os -}}
{{- if hasKey .chezmoi.osRelease "id" -}}
{{-   $osid = printf "%s-%s" .chezmoi.os .chezmoi.osRelease.id -}}
{{- end -}}

[data]
    osid = {{ $osid | quote }}

Now when trying chezmoi init I get the error:

Below is the output from --verbose and my redacted data when running this on macOS.

Looking for guidance on why hasKey errors out (details from chezmoi data below).

What have you tried so far?

  1. Use the hasKey approach in my .tmpl files (with the same error above from the config file when running chezmoi init.

Where else have you checked for solutions?

Output of any commands you've tried with --verbose flag

$ chezmoi --verbose apply
chezmoi: template: run_once_before_10_install_packages.sh.tmpl:70:7: executing "run_once_before_10_install_packages.sh.tmpl" at <index .chezmoi.osRelease "id">: error calling index: index of untyped nil

Output of chezmoi doctor

```console $ chezmoi doctor RESULT CHECK MESSAGE ok version v2.25.0, commit b3a8879e30a20134c9bef48646055e57aa78d8c5, built at 2022-10-13T14:01:32Z, built by goreleaser ok latest-version v2.25.0 ok os-arch darwin/amd64 ok uname Darwin redacted 21.6.0 Darwin Kernel Version 21.6.0: Mon Aug 22 20:17:10 PDT 2022; root:xnu-8020.140.49~2/RELEASE_X86_64 x86_64 ok go-version go1.19.2 (gc) ok executable ~/bin/chezmoi ok upgrade-method replace-executable ok config-file ~/.config/chezmoi/chezmoi.toml, last modified 2022-10-27T14:44:00-07:00 warning source-dir ~/.local/share/chezmoi is a git working tree (dirty) ok suspicious-entries no suspicious entries warning working-tree ~/.local/share/chezmoi is a git working tree (dirty) ok dest-dir ~ is a directory ok umask 022 ok cd-command found /bin/zsh ok cd-args /bin/zsh info diff-command not set ok edit-command found /Applications/Visual Studio Code.app/Contents/Resources/app/bin/code ok edit-args code --wait ok git-command found /usr/bin/git, version 2.37.0 ok merge-command found /usr/bin/vimdiff ok shell-command found /bin/zsh ok shell-args /bin/zsh ok age-command found /usr/local/bin/age, version 1.0.0 info gpg-command gpg not found in $PATH info pinentry-command not set info 1password-command op not found in $PATH warning bitwarden-command found /usr/local/bin/bw, cannot parse version from Could not find dir, "~/Library/Application Support/Bitwarden CLI"; creating it instead. Could not find data file, "~/Library/Application Support/Bitwarden CLI/data.json"; creating it instead. 2022.6.2 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 vault-command vault not found in $PATH info secret-command not set ```

Additional context


$ chezmoi data
{
  "chezmoi": {
    "arch": "amd64",
    "args": [
      "chezmoi",
      "data"
    ],
    "cacheDir": "/Users/xxx/.cache/chezmoi",
    "configFile": "/Users/xxx/.config/chezmoi/chezmoi.toml",
    "executable": "/Users/xxx/bin/chezmoi",
    "fqdnHostname": "redacted",
    "gid": "1896053708",
    "group": "",
    "homeDir": "/Users/xxx",
    "hostname": "redacted",
    "kernel": null,
    "os": "darwin",
    "osRelease": null,
    "sourceDir": "/Users/xxx/.local/share/chezmoi",
    "uid": "481389813",
    "username": "xxx",
    "version": {
      "builtBy": "goreleaser",
      "commit": "b3a8879e30a20134c9bef48646055e57aa78d8c5",
      "date": "2022-10-13T14:01:32Z",
      "version": "2.25.0"
    },
    "windowsVersion": null,
    "workingTree": "/Users/xxx/.local/share/chezmoi"
  },
  "email": "redacted",
  "encryption": "age",
  "environment": "work",
  "identity": "~/.age_chezmoi.txt",
  "name": "redacted",
  "recipient": "redacted"
}
bradenhilton commented 1 year ago

I think this is because .chezmoi.osRelease is null.

/etc/os-release does not exist on darwin, or at least it doesn't exist on my MBP:

❯ /etc/os-release
zsh: no such file or directory: /etc/os-release
gadams999 commented 1 year ago

Agreed that there is no os-release file. I would think since .chezmoi.osRelease is null and doesn't have the key, that shouldn't blow up? If so, it makes is challenging to have template logic that spans Windows, macOS, and Linux.

What's really weird is I had no issues a couple months ago the last time I did a chezmoi apply. Only after the update to 2.25.0 is when I saw the error. I may look to downgrade to a previous version and see that changes anything.

bradenhilton commented 1 year ago

You can just check if .chezmoi.osRelease isn't null by anding it: {{- if and (.chezmoi.osRelease) (hasKey .chezmoi.osRelease "id") -}}:

❯ chezmoi execute-template '{{- $osid := .chezmoi.os -}}{{- if hasKey .chezmoi.osRelease "id" -}}{{- $osid = printf "%s-%s" .chezmoi.os .chezmoi.osRelease.id -}}{{- end -}}{{- $osid -}}'
chezmoi: template: arg1:1:50: executing "arg1" at <.chezmoi.osRelease>: wrong type for value; expected map[string]interface {}; got interface {}

❯ chezmoi execute-template '{{- $osid := .chezmoi.os -}}{{- if and (.chezmoi.osRelease) (hasKey .chezmoi.osRelease "id") -}}{{- $osid = printf "%s-%s" .chezmoi.os .chezmoi.osRelease.id -}}{{- end -}}{{- $osid -}}'
windows

@twpayne We should probably add something like this to the template. I can do this tomorrow if you want. (Assuming this isn't a bug)

halostatue commented 1 year ago

@twpayne, @bradenhilton this looks like a regression from 2.24.0 to 2.25.0.

❯ for d in 3 4 5
      echo 2.2$d.0 && ./chezmoi-2.2$d.0 execute-template '{{ .chezmoi.osRelease | toJson }}' -W . | jq .
  end
2.23.0
{}
2.24.0
{}
2.25.0
null

I think that the current behaviour is more or less correct, but it’s a regression. That said, we might want to consider options to enhance the osRelease data on macOS in any case, since there may be value in knowing the difference between macOS 12 and macOS 13.

bradenhilton commented 1 year ago

@halostatue Interesting. Would you be willing to try a bisect?

Re: osRelease, it's populated by reading /etc/os-release or /usr/lib/os-release:

https://github.com/twpayne/chezmoi/blob/3b95c7e8d8add5001db98c2a2aa38774c522b681/pkg/chezmoi/data.go#L53-L73

Are you suggesting we manually populate it with equivalent data if this isn't possible?

halostatue commented 1 year ago

I’m doing a code comparison now to investigate.

I am suggesting manually populating it with equivalent data. There are alternate keys for some of these (e.g., windowsVersion), but it feels like we could probably shift some of this into interesting places on a more regular basis. I have a bunch of functions I’ve written for fish that do this for macOS.

halostatue commented 1 year ago

I know which commit is causing this, but I can’t quite figure out why the previous commit returns {} and the suspect commit returns null.

halostatue commented 1 year ago

Found it…and it’s a doozy. Specifically, this is because of the JSON roundtrip in func (c *Config) getTemplateDataMap() map[string]any.

bradenhilton commented 1 year ago

Specifically https://github.com/twpayne/chezmoi/commit/964810aa28319a720fe800b6e6b0a1934e2c8056 if I'm not mistaken.