Repository: JanDeDobbeleer/oh-my-posh
Pull request #2558 (2022-07-21 08:05:49)

feat(command): execute script
### Prerequisites

- [x] I have read and understood the [contributing guide][CONTRIBUTING.md]
- [x] The commit message follows the [conventional commits][cc] guidelines
- [x] Tests for the changes have been added (for bug fixes/features)
- [x] Docs have been added/updated (for bug fixes/features)

### Description

- fix(oauth): correct expires_in data type
- feat(command): run script


<!---

Tips:

If you're not comfortable with working with Git, we're working a guide (https://ohmyposh.dev/docs/contributing_git) to help you out.
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.

-->

[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/src/segments/command.go b/src/segments/command.go
index 66dc079e..c5ece77d 100644
--- a/src/segments/command.go
+++ b/src/segments/command.go
@@ -18,6 +18,8 @@ const (
 	ExecutableShell properties.Property = "shell"
 	// Command to execute
 	Command properties.Property = "command"
+	// Command to execute
+	Script properties.Property = "script"
 )
 
 func (c *Cmd) Template() string {
@@ -29,12 +31,23 @@ func (c *Cmd) Enabled() bool {
 	if !c.env.HasCommand(shell) {
 		return false
 	}
-	command := c.props.GetString(Command, "echo no command specified")
+	command := c.props.GetString(Command, "")
+	if len(command) != 0 {
+		return c.runCommand(shell, command)
+	}
+	script := c.props.GetString(Script, "")
+	if len(script) != 0 {
+		return c.runScript(shell, script)
+	}
+	return false
+}
+
+func (c *Cmd) runCommand(shell, command string) bool {
 	if strings.Contains(command, "||") {
 		commands := strings.Split(command, "||")
 		for _, cmd := range commands {
 			output := c.env.RunShellCommand(shell, strings.TrimSpace(cmd))
-			if output != "" {
+			if len(output) != 0 {
 				c.Output = output
 				return true
 			}
@@ -47,10 +60,15 @@ func (c *Cmd) Enabled() bool {
 			output += c.env.RunShellCommand(shell, strings.TrimSpace(cmd))
 		}
 		c.Output = output
-		return c.Output != ""
+		return len(c.Output) != 0
 	}
 	c.Output = c.env.RunShellCommand(shell, strings.TrimSpace(command))
-	return c.Output != ""
+	return len(c.Output) != 0
+}
+
+func (c *Cmd) runScript(shell, script string) bool {
+	c.Output = c.env.RunShellCommand(shell, script)
+	return len(c.Output) != 0
 }
 
 func (c *Cmd) Init(props properties.Properties, env environment.Environment) {
diff --git a/src/segments/withings.go b/src/segments/withings.go
index 0a9a47b6..3f5c4ea6 100644
--- a/src/segments/withings.go
+++ b/src/segments/withings.go
@@ -9,7 +9,6 @@ import (
 	"oh-my-posh/properties"
 	"strconv"
 	"strings"
-	"sync"
 	"time"
 
 	http2 "net/http"
@@ -147,24 +146,16 @@ func (w *Withings) Template() string {
 }
 
 func (w *Withings) Enabled() bool {
-	wg := sync.WaitGroup{}
-	wg.Add(3)
-	functions := []func() bool{
-		w.getMeasures,
-		w.getActivities,
-		w.getSleep,
-	}
 	var enabled bool
-	for _, function := range functions {
-		go func(f func() bool) {
-			defer wg.Done()
-			success := f()
-			if success {
-				enabled = true
-			}
-		}(function)
+	if w.getActivities() {
+		enabled = true
+	}
+	if w.getMeasures() {
+		enabled = true
+	}
+	if w.getSleep() {
+		enabled = true
 	}
-	wg.Wait()
 	return enabled
 }
 
diff --git a/themes/schema.json b/themes/schema.json
index 7a81d6d2..2d4496b2 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -556,7 +556,13 @@
                     "type": "string",
                     "title": "Command",
                     "description": "the command(s) to run",
-                    "default": "echo no command specified"
+                    "default": ""
+                  },
+                  "script": {
+                    "type": "string",
+                    "title": "Script",
+                    "description": "A script to run",
+                    "default": ""
                   }
                 }
               }
diff --git a/website/docs/segments/command.mdx b/website/docs/segments/command.mdx
index 4fcc2701..698c9933 100644
--- a/website/docs/segments/command.mdx
+++ b/website/docs/segments/command.mdx
@@ -44,6 +44,7 @@ error). The `&&` functionality will join the output of the commands when success
 
 - shell: `string` - the shell in which to run the command in. Uses `shell -c command` under the hood.
 - command: `string` - the command(s) to run
+- script: `string` - the path to a script to run
 
 ## Template ([info][templates])
 
@@ -57,7 +58,7 @@ error). The `&&` functionality will join the output of the commands when success
 
 ### Properties
 
-- `.Output`: `string` - the output of the command.
+- `.Output`: `string` - the output of the command or script.
 
 [env]: /docs/configuration/templates#environment-variables
 [templates]: /docs/configuration/templates
diff --git a/website/src/components/Auth.js b/website/src/components/Auth.js
index 0352bdd9..6d01d50b 100644
--- a/website/src/components/Auth.js
+++ b/website/src/components/Auth.js
@@ -30,9 +30,9 @@ function Auth() {
     ...
     "properties": {
       // highlight-start
-      "access_token":"${params.access_token}",
-      "refresh_token":"${params.refresh_token}",
-      "expires_in":"${params.expires_in}"
+      "access_token": "${params.access_token}",
+      "refresh_token": "${params.refresh_token}",
+      "expires_in": ${params.expires_in}
       // highlight-end
     }
   }


Test patch:
diff --git a/src/segments/command_test.go b/src/segments/command_test.go
index 16a3eed8..f7d3f7f8 100644
--- a/src/segments/command_test.go
+++ b/src/segments/command_test.go
@@ -94,15 +94,14 @@ func TestExecuteSingleCommandEmpty(t *testing.T) {
 func TestExecuteSingleCommandNoCommandProperty(t *testing.T) {
 	env := new(mock.MockedEnvironment)
 	env.On("HasCommand", "bash").Return(true)
-	env.On("RunShellCommand", "bash", "echo no command specified").Return("no command specified")
+	env.On("RunShellCommand", "bash", "").Return("")
 	var props properties.Map
 	c := &Cmd{
 		props: props,
 		env:   env,
 	}
 	enabled := c.Enabled()
-	assert.True(t, enabled)
-	assert.Equal(t, "no command specified", c.Output)
+	assert.False(t, enabled)
 }
 
 func TestExecuteMultipleCommandsAndDisabled(t *testing.T) {
@@ -135,3 +134,42 @@ func TestExecuteMultipleCommandsOrDisabled(t *testing.T) {
 	enabled := c.Enabled()
 	assert.False(t, enabled)
 }
+
+func TestExecuteScript(t *testing.T) {
+	cases := []struct {
+		Case            string
+		Output          string
+		HasScript       bool
+		ExpectedString  string
+		ExpectedEnabled bool
+	}{
+		{
+			Case:            "Output",
+			Output:          "Hello World",
+			ExpectedString:  "Hello World",
+			ExpectedEnabled: true,
+		},
+		{
+			Case:            "No output",
+			ExpectedEnabled: false,
+		},
+	}
+	for _, tc := range cases {
+		script := "../test/script.sh"
+		env := new(mock.MockedEnvironment)
+		env.On("HasCommand", "bash").Return(true)
+		env.On("RunShellCommand", "bash", script).Return(tc.Output)
+		props := properties.Map{
+			Script: script,
+		}
+		c := &Cmd{
+			props: props,
+			env:   env,
+		}
+		enabled := c.Enabled()
+		assert.Equal(t, tc.ExpectedEnabled, enabled, tc.Case)
+		if tc.ExpectedEnabled {
+			assert.Equal(t, tc.ExpectedString, renderTemplate(env, c.Template(), c))
+		}
+	}
+}