Repository: twpayne/chezmoi
Pull request #528 (2019-12-22 21:42:07)

Added kernel information to data
The main purpose of this is to have a way to detect WSL (windows subsystem for linux).

From what I can gather, the best way is currently to look in `/proc/sys/kernel/osrelease` and look for "microsoft" in the kernel name: https://github.com/microsoft/WSL/issues/423



twpayne: Thank you for this very nice contribution!

Please can you:
* Use build flags so this is only built when `GOOS=linux` (you can create a `getKernelInfo` function that returns nothing on other OSs, only set `data["kernel"]` if the return value is non-`nil`).
* Add documentation of the variable to `docs/REFERENCE.md`.
ptxmac: Sure, I'll get on that.

I based the implementation on `osrelease.go` since that's also a linux specific thing, but that isn't using build tags. Are there any reason for that?
twpayne: Thank you. `osrelease.go` not doing this is an oversight on my part. I'll fix it.

Patch:
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index f39a43b4a..c0526dcbc 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -137,7 +137,7 @@ brews:
     name: Tom Payne
     email: twpayne@gmail.com
   folder: Formula
-  homepage: "https://chezmoi.io"
+  homepage: "https://github.com/twpayne/chezmoi"
   install: |
     bin.install "chezmoi"
     bash_completion.install "completions/chezmoi-completion.bash"
@@ -152,7 +152,7 @@ scoop:
   commit_author:
     name: Tom Payne
     email: twpayne@gmail.com
-  homepage: "https://chezmoi.io"
+  homepage: "https://github.com/twpayne/chezmoi"
   description: "Manage your dotfiles across multiple machines, securely."
   license: MIT
 
diff --git a/cmd/config.go b/cmd/config.go
index e9ef77ec0..effa05031 100644
--- a/cmd/config.go
+++ b/cmd/config.go
@@ -315,13 +315,17 @@ func (c *Config) getDefaultData() (map[string]interface{}, error) {
 
 	osRelease, err := getOSRelease(c.fs)
 	if err == nil {
-		if osRelease != nil {
-			data["osRelease"] = upperSnakeCaseToCamelCaseMap(osRelease)
-		}
+		data["osRelease"] = upperSnakeCaseToCamelCaseMap(osRelease)
 	} else if !os.IsNotExist(err) {
 		return nil, err
 	}
 
+	kernelInfo, err := getKernelInfo(c.fs)
+	if err == nil && kernelInfo != nil {
+		data["kernel"] = kernelInfo
+	} else if err != nil && !os.IsNotExist(err) {
+		return nil, err
+	}
 	return data, nil
 }
 
diff --git a/cmd/kernelinfo_linux.go b/cmd/kernelinfo_linux.go
new file mode 100644
index 000000000..3af842842
--- /dev/null
+++ b/cmd/kernelinfo_linux.go
@@ -0,0 +1,43 @@
+package cmd
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/twpayne/go-vfs"
+)
+
+func getKernelInfo(fs vfs.FS) (map[string]string, error) {
+	const procKernel = "/proc/sys/kernel"
+	files := []string{"version", "ostype", "osrelease"}
+
+	stat, err := fs.Stat(procKernel)
+	if err != nil {
+		return nil, err
+	}
+	if !stat.IsDir() {
+		return nil, fmt.Errorf("expected %q to be a directory", procKernel)
+	}
+
+	res := map[string]string{}
+	for _, file := range files {
+		p := filepath.Join(procKernel, file)
+		f, err := fs.Open(p)
+		if os.IsNotExist(err) {
+			continue
+		} else if err != nil {
+			return nil, err
+		}
+		defer f.Close()
+		data, err := ioutil.ReadAll(f)
+		if err != nil {
+			return nil, err
+		}
+		res[file] = strings.TrimSpace(string(data))
+	}
+
+	return res, nil
+}
diff --git a/cmd/kernelinfo_notlinux.go b/cmd/kernelinfo_notlinux.go
new file mode 100644
index 000000000..aab568f42
--- /dev/null
+++ b/cmd/kernelinfo_notlinux.go
@@ -0,0 +1,9 @@
+// +build !linux
+
+package cmd
+
+import "github.com/twpayne/go-vfs"
+
+func getKernelInfo(fs vfs.FS) (map[string]string, error) {
+	return nil, nil
+}
diff --git a/cmd/osrelease_linux.go b/cmd/osrelease.go
similarity index 100%
rename from cmd/osrelease_linux.go
rename to cmd/osrelease.go
diff --git a/cmd/osrelease_notlinux.go b/cmd/osrelease_notlinux.go
deleted file mode 100644
index 59dc59568..000000000
--- a/cmd/osrelease_notlinux.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// +build !linux
-
-package cmd
-
-import vfs "github.com/twpayne/go-vfs"
-
-func getOSRelease(fs vfs.FS) (map[string]string, error) {
-	return nil, nil
-}
diff --git a/cmd/osrelease_linux_test.go b/cmd/osrelease_test.go
similarity index 100%
rename from cmd/osrelease_linux_test.go
rename to cmd/osrelease_test.go
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 1417b5654..a494ba354 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -152,7 +152,7 @@ If you're packaging chezmoi for an operating system or distribution:
 
 ## Updating the website
 
-[The website](https://chezmoi.io), is generated with [Hugo](https://gohugo.io/)
+The website, https://chezmoi.io, is generated with [Hugo](https://gohugo.io/)
 and served with [GitHub pages](https://pages.github.com/) from the [`gh-pages`
 branch](https://github.com/twpayne/chezmoi/tree/gh-pages) to GitHub.
 
diff --git a/docs/REFERENCE.md b/docs/REFERENCE.md
index 8757825eb..4241a2a75 100644
--- a/docs/REFERENCE.md
+++ b/docs/REFERENCE.md
@@ -713,17 +713,18 @@ chezmoi's configuration file.
 
 chezmoi provides the following automatically populated variables:
 
-| Variable                | Value                                                                                                                  |
-| ----------------------- | ---------------------------------------------------------------------------------------------------------------------- |
-| `.chezmoi.arch`         | Architecture, e.g. `amd64`, `arm`, etc. as returned by [runtime.GOARCH](https://godoc.org/runtime#pkg-constants).      |
-| `.chezmoi.fullHostname` | The full hostname of the machine chezmoi is running on.                                                                |
-| `.chezmoi.group`        | The group of the user running chezmoi.                                                                                 |
-| `.chezmoi.homedir`      | The home directory of the user running chezmoi.                                                                        |
-| `.chezmoi.hostname`     | The hostname of the machine chezmoi is running on, up to the first `.`.                                                |
-| `.chezmoi.os`           | Operating system, e.g. `darwin`, `linux`, etc. as returned by [runtime.GOOS](https://godoc.org/runtime#pkg-constants). |
-| `.chezmoi.osRelease`    | The information from `/etc/os-release`, Linux only, run `chezmoi data` to see its output.                              |
-| `.chezmoi.sourceDir`    | The source directory.                                                                                                  |
-| `.chezmoi.username`     | The username of the user running chezmoi.                                                                              |
+| Variable                | Value                                                                                                                          |
+| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
+| `.chezmoi.arch`         | Architecture, e.g. `amd64`, `arm`, etc. as returned by [runtime.GOARCH](https://godoc.org/runtime#pkg-constants).              |
+| `.chezmoi.fullHostname` | The full hostname of the machine chezmoi is running on.                                                                        |
+| `.chezmoi.group`        | The group of the user running chezmoi.                                                                                         |
+| `.chezmoi.homedir`      | The home directory of the user running chezmoi.                                                                                |
+| `.chezmoi.hostname`     | The hostname of the machine chezmoi is running on, up to the first `.`.                                                        |
+| `.chezmoi.kernel`       | Contains information from `/proc/sys/kernel`. Linux only, useful for detecting specific kernels (i.e. Microsoft's WSL kernel). |
+| `.chezmoi.os`           | Operating system, e.g. `darwin`, `linux`, etc. as returned by [runtime.GOOS](https://godoc.org/runtime#pkg-constants).         |
+| `.chezmoi.osRelease`    | The information from `/etc/os-release`, Linux only, run `chezmoi data` to see its output.                                      |
+| `.chezmoi.sourceDir`    | The source directory.                                                                                                          |
+| `.chezmoi.username`     | The username of the user running chezmoi.                                                                                      |
 
 Additional variables can be defined in the config file in the `data` section.
 


Test patch:
diff --git a/cmd/kernelinfo_linux_test.go b/cmd/kernelinfo_linux_test.go
new file mode 100644
index 000000000..ee0d032b5
--- /dev/null
+++ b/cmd/kernelinfo_linux_test.go
@@ -0,0 +1,27 @@
+package cmd
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"github.com/twpayne/go-vfs/vfst"
+)
+
+func TestGetKernelInfo(t *testing.T) {
+	root := map[string]interface{}{
+		"/proc/sys/kernel/version":   "#1 SMP Fri Nov 1 14:28:19 UTC 2019",
+		"/proc/sys/kernel/ostype":    "Linux",
+		"/proc/sys/kernel/osrelease": "4.19.81-microsoft-standard",
+	}
+	fs, cleanup, err := vfst.NewTestFS(root)
+	require.NoError(t, err)
+	defer cleanup()
+	info, err := getKernelInfo(fs)
+	assert.NoError(t, err)
+	assert.Equal(t, map[string]string{
+		"version":   "#1 SMP Fri Nov 1 14:28:19 UTC 2019",
+		"ostype":    "Linux",
+		"osrelease": "4.19.81-microsoft-standard",
+	}, info)
+}