Repository: JanDeDobbeleer/oh-my-posh
Pull request #5623 (2024-09-15 21:35:29)

chore(lint): enhance commitlint rules
### Prerequisites

- [x] I have read and understood the [contributing guide][CONTRIBUTING.md].
- [x] The commit message follows the [conventional commits][cc] guidelines.
- [ ] Tests for the changes have been added (for bug fixes / features).
- [x] Docs have been added/updated (for bug fixes / features).

### Description


- Updated commitlint rules to adjust allowed types and line length.
- Added 'style' to the list of commit types.
- Sorted commit types alphabetically.

[CONTRIBUTING.md]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md
[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 8cb93a33..07e182df 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -2758,6 +2758,25 @@
       "contributions": [
         "doc"
       ]
+    },
+    {
+      "login": "oliviaBahr",
+      "name": "Olivia Bahr",
+      "avatar_url": "https://avatars.githubusercontent.com/u/98684296?v=4",
+      "profile": "https://github.com/oliviaBahr",
+      "contributions": [
+        "code",
+        "doc"
+      ]
+    },
+    {
+      "login": "garysassano",
+      "name": "Gary Sassano",
+      "avatar_url": "https://avatars.githubusercontent.com/u/10464497?v=4",
+      "profile": "https://github.com/garysassano",
+      "contributions": [
+        "design"
+      ]
     }
   ],
   "contributorsPerLine": 7,
diff --git a/.commitlintrc.yml b/.commitlintrc.yml
index 91f74a58..6a2c7ea6 100644
--- a/.commitlintrc.yml
+++ b/.commitlintrc.yml
@@ -2,32 +2,21 @@
 extends:
   - '@commitlint/config-conventional'
 rules:
-  type-enum:
-  - 2
-  - always
-  - - chore
-    - ci
-    - feat
-    - fix
-    - docs
-    - theme
-    - refactor
-    - perf
-    - test
-    - revert
   body-max-line-length:
     - 2
     - always
     - 200
-help: |
-  **Possible types**:
-  `chore`:    Change build process, tooling or dependencies.
-  `ci`:       Changes to our CI configuration files and scripts
-  `feat`:     Adds a new feature.
-  `fix`:      Solves a bug.
-  `docs`:     Adds or alters documentation.
-  `theme`:    Work on or add a theme.
-  `refactor`: Rewrites code without feature, performance or bug changes.
-  `perf`:     Improves performance.
-  `test`:     Adds or modifies tests.
-  `revert`:   Changes that reverting other changes
+  type-enum:
+    - 2
+    - always
+    - - chore
+      - ci
+      - docs
+      - feat
+      - fix
+      - perf
+      - refactor
+      - revert
+      - style
+      - test
+      - theme
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index f1cec202..26aab8b6 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -27,10 +27,10 @@ jobs:
     - name: Checkout code
       uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@8214744c546c1e5c8f03dde8fab3a7353211988d
+      uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3
       with:
         languages: go
     - name: Autobuild
-      uses: github/codeql-action/autobuild@8214744c546c1e5c8f03dde8fab3a7353211988d
+      uses: github/codeql-action/autobuild@294a9d92911152fe08befb9ec03e240add280cb3
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@8214744c546c1e5c8f03dde8fab3a7353211988d
+      uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 5f61b90c..7b1e0ef6 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -27,7 +27,7 @@ jobs:
         with:
           go-version: 1.21
           cache-dependency-path: src/go.sum
-      - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b
+      - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6
         with:
           node-version: 20.9.0
         # Create Kind cluster to have a Kubernetes context for cloud-native-azure theme
diff --git a/src/runtime/battery/battery_windows.go b/src/runtime/battery/battery_windows.go
index 838f8c80..a5d4b294 100644
--- a/src/runtime/battery/battery_windows.go
+++ b/src/runtime/battery/battery_windows.go
@@ -185,6 +185,10 @@ func systemGet(idx int) (*battery, error) {
 		return nil, errno
 	}
 
+	if cbRequired == 0 {
+		return nil, errors.New("no buffer information returned")
+	}
+
 	// The god damn struct with ANYSIZE_ARRAY of utf16 in it is crazy.
 	// So... let's emulate it with array of uint16 ;-D.
 	// Keep in mind that the first two elements are actually cbSize.
@@ -250,7 +254,7 @@ func systemGet(idx int) (*battery, error) {
 	}
 
 	if bqi.BatteryTag == 0 {
-		return nil, errors.New("BatteryTag not returned")
+		return nil, errors.New("battery tag not returned")
 	}
 
 	b := &battery{}
@@ -274,6 +278,7 @@ func systemGet(idx int) (*battery, error) {
 	b.Full = float64(bi.FullChargedCapacity)
 
 	bws := batteryWaitStatus{BatteryTag: bqi.BatteryTag}
+
 	var bs batteryStatus
 	err = windows.DeviceIoControl(
 		handle,
diff --git a/src/template/func_map.go b/src/template/func_map.go
index e1eed05d..57cba57b 100644
--- a/src/template/func_map.go
+++ b/src/template/func_map.go
@@ -17,6 +17,7 @@ func funcMap() template.FuncMap {
 		"replaceP":     replaceP,
 		"gt":           gt,
 		"lt":           lt,
+		"random":       random,
 		"reason":       GetReasonFromStatus,
 		"hresult":      hresult,
 		"trunc":        trunc,
diff --git a/src/template/random.go b/src/template/random.go
new file mode 100644
index 00000000..9fc3cced
--- /dev/null
+++ b/src/template/random.go
@@ -0,0 +1,22 @@
+package template
+
+import (
+	"errors"
+	"fmt"
+	"math/rand"
+	"reflect"
+)
+
+func random(list interface{}) (string, error) {
+	v := reflect.ValueOf(list)
+
+	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+		return "", errors.New("input must be a slice or array")
+	}
+
+	if v.Len() == 0 {
+		return "", errors.New("input slice or array is empty")
+	}
+
+	return fmt.Sprintf("%v", v.Index(rand.Intn(v.Len()))), nil
+}
diff --git a/themes/multiverse-neon.omp.json b/themes/multiverse-neon.omp.json
index dfab405c..9282e5d4 100644
--- a/themes/multiverse-neon.omp.json
+++ b/themes/multiverse-neon.omp.json
@@ -50,19 +50,19 @@
      {
       "alignment": "right",
       "segments": [
-
         {
-          "foreground": "#5fb157",
+          "type": "node",
+          "style": "plain",
+          "foreground": "#88e570",
           "properties": {
             "display_mode": "files",
             "fetch_package_manager": true,
             "fetch_version": true,
-            "npm_icon": "<#FE4A49>\ue71e npm</> ",
-            "yarn_icon":"<#44FFD2>\uf487 yarn</> "
+            "npm_icon": "<#e5272d>\ue616 npm</> ",
+            "yarn_icon": "<#37aee5>\ue6a7 yarn</> ",
+            "pnpm_icon": "<#e5a100>\udb80\udec1 pnpm</> "
           },
-          "style": "plain",
-          "template": "{{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}\ue718 {{ .Full }}",
-          "type": "node"
+          "template": "{{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}\ue718 {{ .Full }}"
         },
         {
           "foreground": "#4063D8",
diff --git a/website/docs/configuration/templates.mdx b/website/docs/configuration/templates.mdx
index 38261a4d..4e435dfb 100644
--- a/website/docs/configuration/templates.mdx
+++ b/website/docs/configuration/templates.mdx
@@ -182,6 +182,7 @@ use them.
 | `{{ replaceP "c.t" "cut code cat" "dog" }}`                        | Exposes [regexp.ReplaceAllString][regexpra] as a string template function.                                                 |
 | <code>\{\{ .Code &vert; hresult \}\}</code>                        | Transform a status code to its HRESULT value for easy troubleshooting. For example `-1978335212` becomes `0x8A150014`.     |
 | `{{ readFile ".version.json" }}`                                   | Read a file in the current directory. Returns a string.                                                                    |
+| `{{ random (list \"a\" 2 .MyThirdItem) }}`                         | Selects a random element from a list. The list can be an array or slice containing any types (use sprig's `list`).         |
 
 <!-- markdownlint-enable MD013 -->
 
diff --git a/website/docs/contributors.md b/website/docs/contributors.md
index 0a2131ea..d040c252 100644
--- a/website/docs/contributors.md
+++ b/website/docs/contributors.md
@@ -389,6 +389,10 @@ Thanks goes to these wonderful people ([emoji key][acek]):
       <td align="center" valign="top" width="14.28%"><a href="https://kizivat.eu"><img src="https://avatars.githubusercontent.com/u/3535926?v=4?s=100" width="100px;" alt="David Kizivat"/><br /><sub><b>David Kizivat</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=kizivat" title="Code">💻</a> <a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=kizivat" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="14.28%"><a href="https://github.com/mgrubb"><img src="https://avatars.githubusercontent.com/u/351301?v=4?s=100" width="100px;" alt="Michael Grubb"/><br /><sub><b>Michael Grubb</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=mgrubb" title="Documentation">📖</a></td>
     </tr>
+    <tr>
+      <td align="center" valign="top" width="14.28%"><a href="https://github.com/oliviaBahr"><img src="https://avatars.githubusercontent.com/u/98684296?v=4?s=100" width="100px;" alt="Olivia Bahr"/><br /><sub><b>Olivia Bahr</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=oliviaBahr" title="Code">💻</a> <a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=oliviaBahr" title="Documentation">📖</a></td>
+      <td align="center" valign="top" width="14.28%"><a href="https://github.com/garysassano"><img src="https://avatars.githubusercontent.com/u/10464497?v=4?s=100" width="100px;" alt="Gary Sassano"/><br /><sub><b>Gary Sassano</b></sub></a><br /><a href="#design-garysassano" title="Design">🎨</a></td>
+    </tr>
   </tbody>
 </table>
 
diff --git a/website/docs/installation/prompt.mdx b/website/docs/installation/prompt.mdx
index 869c3c30..a94fdc79 100644
--- a/website/docs/installation/prompt.mdx
+++ b/website/docs/installation/prompt.mdx
@@ -32,7 +32,7 @@ oh-my-posh get shell
 }>
 <TabItem value="bash">
 
-Add the following to `~/.bashrc` (could be `~/.profile` or `~/.bash_profile` depending on your environment):
+Add the following snippet as the last line to `~/.bashrc` (could be `~/.profile` or `~/.bash_profile` depending on your environment):
 
 ```bash
 eval "$(oh-my-posh init bash)"
@@ -69,7 +69,7 @@ Once added, restart cmd for the changes to take effect.
 </TabItem>
 <TabItem value="elvish">
 
-Add the following at the end of `~/.elvish/rc.elv`:
+Add the following snippet as the last line to `~/.elvish/rc.elv`:
 
 ```bash
 eval (oh-my-posh init elvish)
@@ -88,7 +88,7 @@ exec elvish
 It is recommended to use the latest version of Fish. Versions below 3.1.2 have issues displaying the prompt.
 :::
 
-Initialize Oh My Posh in `~/.config/fish/config.fish`:
+Add the following snippet as the last line to `~/.config/fish/config.fish`:
 
 ```bash
 oh-my-posh init fish | source
@@ -107,7 +107,7 @@ exec fish
 Oh My Posh requires Nushell v0.93.0 or higher.
 :::
 
-Add the following line to the Nushell env file (`$nu.env-path`):
+Add the following snippet as the last line to the Nushell env file (`$nu.env-path`):
 
 ```bash
 oh-my-posh init nu
@@ -154,7 +154,7 @@ In this scenario, it can also be that PowerShell blocks running local scripts. T
 to only require remote scripts to be signed using `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine`, or [sign the profile][sign].
 :::
 
-Then add the following line.
+Add the following snippet as the last line to your PowerShell profile script:
 
 ```powershell
 oh-my-posh init pwsh | Invoke-Expression
@@ -180,7 +180,7 @@ Once added, reload your profile for the changes to take effect.
 </TabItem>
 <TabItem value="tcsh">
 
-Add the following at the end of `~/.tcshrc`:
+Add the following snippet as the last line to `~/.tcshrc`:
 
 ```bash
 eval "`oh-my-posh init tcsh`"
@@ -195,7 +195,7 @@ exec tcsh
 </TabItem>
 <TabItem value="xonsh">
 
-Add the following line at the end of `~/.xonshrc`:
+Add the following snippet as the last line to `~/.xonshrc`:
 
 ```bash
 execx($(oh-my-posh init xonsh))
@@ -210,7 +210,7 @@ exec xonsh
 </TabItem>
 <TabItem value="zsh">
 
-Add the following to `~/.zshrc`:
+Add the following snippet as the last line to `~/.zshrc`:
 
 ```bash
 eval "$(oh-my-posh init zsh)"


Test patch:
diff --git a/src/template/random_test.go b/src/template/random_test.go
new file mode 100644
index 00000000..da70e37e
--- /dev/null
+++ b/src/template/random_test.go
@@ -0,0 +1,65 @@
+package template
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRandom(t *testing.T) {
+	cases := []struct {
+		Case        string
+		Input       interface{}
+		ShouldError bool
+	}{
+		{
+			Case:  "valid slice",
+			Input: []int{1, 2, 3, 4, 5},
+		},
+		{
+			Case:  "valid array",
+			Input: [5]int{1, 2, 3, 4, 5},
+		},
+		{
+			Case:        "empty slice",
+			Input:       []int{},
+			ShouldError: true,
+		},
+		{
+			Case:        "not a slice or array",
+			Input:       "not a slice",
+			ShouldError: true,
+		},
+		{
+			Case:  "valid string slice",
+			Input: []string{"a", "b", "c"},
+		},
+		{
+			Case:  "valid float slice",
+			Input: []float64{1.1, 2.2, 3.3},
+		},
+		{
+			Case: "interface with multiple types",
+			Input: []interface{}{
+				"a",
+				1,
+				true,
+			},
+		},
+		{
+			Case:  "valid struct slice",
+			Input: []struct{ Name string }{{Name: "Alice"}, {Name: "Bob"}},
+		},
+	}
+
+	for _, tc := range cases {
+		result, err := random(tc.Input)
+		if tc.ShouldError {
+			assert.Error(t, err, tc.Case)
+		} else {
+			assert.NoError(t, err, tc.Case)
+			assert.Contains(t, fmt.Sprintf("%v", tc.Input), result, tc.Case)
+		}
+	}
+}