From aa463ac99ccd3d9ec4a6b39a070123fbefd15715 Mon Sep 17 00:00:00 2001 From: hemarina Date: Mon, 3 Mar 2025 12:04:21 -0800 Subject: [PATCH 1/9] check powershell --- cli/azd/pkg/tools/powershell/powershell.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 659dac724b9..09f39f4f081 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -6,6 +6,7 @@ package powershell import ( "context" + "github.com/azure/azure-dev/cli/azd/internal" "github.com/azure/azure-dev/cli/azd/pkg/exec" "github.com/azure/azure-dev/cli/azd/pkg/tools" ) @@ -25,9 +26,20 @@ type powershellScript struct { envVars []string } +func checkPath(options tools.ExecOptions) (err error) { + return tools.ToolInPath(options.UserPwsh) +} + // Executes the specified powershell script // When interactive is true will attach to stdin, stdout & stderr func (bs *powershellScript) Execute(ctx context.Context, path string, options tools.ExecOptions) (exec.RunResult, error) { + if err := checkPath(options); err != nil { + return exec.RunResult{}, &internal.ErrorWithSuggestion{ + Err: err, + Suggestion: "PowerShell is not installed or not in the path. To install PowerShell, visit https://learn.microsoft.com/powershell/scripting/install/installing-powershell", + } + } + runArgs := exec.NewRunArgs(options.UserPwsh, path). WithCwd(bs.cwd). WithEnv(bs.envVars). From 0c02ffc2ecf6ff36f985520f67a917046451dcba Mon Sep 17 00:00:00 2001 From: hemarina Date: Mon, 3 Mar 2025 12:14:33 -0800 Subject: [PATCH 2/9] lll --- cli/azd/pkg/tools/powershell/powershell.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 09f39f4f081..c55f8d9aa37 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -35,8 +35,9 @@ func checkPath(options tools.ExecOptions) (err error) { func (bs *powershellScript) Execute(ctx context.Context, path string, options tools.ExecOptions) (exec.RunResult, error) { if err := checkPath(options); err != nil { return exec.RunResult{}, &internal.ErrorWithSuggestion{ - Err: err, - Suggestion: "PowerShell is not installed or not in the path. To install PowerShell, visit https://learn.microsoft.com/powershell/scripting/install/installing-powershell", + Err: err, + Suggestion: "PowerShell is not installed or not in the path. " + + "To install PowerShell, visit https://learn.microsoft.com/powershell/scripting/install/installing-powershell", } } From 2e0c3da45dc1543c160f2639315306c71b8d3aaa Mon Sep 17 00:00:00 2001 From: hemarina Date: Mon, 3 Mar 2025 13:28:59 -0800 Subject: [PATCH 3/9] update function name --- cli/azd/pkg/tools/powershell/powershell.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index c55f8d9aa37..97898beda0f 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -26,18 +26,18 @@ type powershellScript struct { envVars []string } -func checkPath(options tools.ExecOptions) (err error) { +func checkInstalled(options tools.ExecOptions) (err error) { return tools.ToolInPath(options.UserPwsh) } // Executes the specified powershell script // When interactive is true will attach to stdin, stdout & stderr func (bs *powershellScript) Execute(ctx context.Context, path string, options tools.ExecOptions) (exec.RunResult, error) { - if err := checkPath(options); err != nil { + if err := checkInstalled(options); err != nil { return exec.RunResult{}, &internal.ErrorWithSuggestion{ Err: err, - Suggestion: "PowerShell is not installed or not in the path. " + - "To install PowerShell, visit https://learn.microsoft.com/powershell/scripting/install/installing-powershell", + Suggestion: "PowerShell is not installed or not in the path. To install PowerShell, " + + "visit https://learn.microsoft.com/powershell/scripting/install/installing-powershell", } } From 9e1141e069a02d3ea8f48903a8efaa15b1a6d199 Mon Sep 17 00:00:00 2001 From: hemarina Date: Tue, 4 Mar 2025 08:54:29 -0800 Subject: [PATCH 4/9] address feedback (add pwsh 7 and use linkformat) --- cli/azd/pkg/tools/powershell/powershell.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 97898beda0f..f5d51870c99 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -5,9 +5,11 @@ package powershell import ( "context" + "fmt" "github.com/azure/azure-dev/cli/azd/internal" "github.com/azure/azure-dev/cli/azd/pkg/exec" + "github.com/azure/azure-dev/cli/azd/pkg/output" "github.com/azure/azure-dev/cli/azd/pkg/tools" ) @@ -36,8 +38,8 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to if err := checkInstalled(options); err != nil { return exec.RunResult{}, &internal.ErrorWithSuggestion{ Err: err, - Suggestion: "PowerShell is not installed or not in the path. To install PowerShell, " + - "visit https://learn.microsoft.com/powershell/scripting/install/installing-powershell", + Suggestion: fmt.Sprintf("PowerShell is not installed or not in the path. To install PowerShell 7, visit %s", + output.WithLinkFormat("https://learn.microsoft.com/powershell/scripting/install/installing-powershell")), } } From f3a5a61282e28119a974742dc36d450c46a8795e Mon Sep 17 00:00:00 2001 From: hemarina Date: Tue, 4 Mar 2025 08:59:19 -0800 Subject: [PATCH 5/9] pwsh 7 --- cli/azd/pkg/tools/powershell/powershell.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index f5d51870c99..d3941adf8e4 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -38,7 +38,7 @@ func (bs *powershellScript) Execute(ctx context.Context, path string, options to if err := checkInstalled(options); err != nil { return exec.RunResult{}, &internal.ErrorWithSuggestion{ Err: err, - Suggestion: fmt.Sprintf("PowerShell is not installed or not in the path. To install PowerShell 7, visit %s", + Suggestion: fmt.Sprintf("PowerShell 7 is not installed or not in the path. To install PowerShell 7, visit %s", output.WithLinkFormat("https://learn.microsoft.com/powershell/scripting/install/installing-powershell")), } } From edb2e93081176bdd6b121fa7178e1f13c2f60e11 Mon Sep 17 00:00:00 2001 From: hemarina Date: Tue, 4 Mar 2025 12:06:39 -0800 Subject: [PATCH 6/9] address feedback --- cli/azd/pkg/tools/powershell/powershell.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index d3941adf8e4..515b96943ff 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -6,6 +6,7 @@ package powershell import ( "context" "fmt" + "strings" "github.com/azure/azure-dev/cli/azd/internal" "github.com/azure/azure-dev/cli/azd/pkg/exec" @@ -29,7 +30,7 @@ type powershellScript struct { } func checkInstalled(options tools.ExecOptions) (err error) { - return tools.ToolInPath(options.UserPwsh) + return tools.ToolInPath(strings.Split(options.UserPwsh, " ")[0]) } // Executes the specified powershell script From 28f3779e82207b0b8980397a27e02fe1dffa2611 Mon Sep 17 00:00:00 2001 From: hemarina Date: Tue, 4 Mar 2025 13:30:11 -0800 Subject: [PATCH 7/9] add tests and fix pipeline error --- cli/azd/pkg/tools/powershell/powershell.go | 30 ++++++++++++++----- .../pkg/tools/powershell/powershell_test.go | 28 ++++++++++++++--- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 515b96943ff..f37a6de3488 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -17,26 +17,40 @@ import ( // Creates a new PowershellScript command runner func NewPowershellScript(commandRunner exec.CommandRunner, cwd string, envVars []string) tools.Script { return &powershellScript{ - commandRunner: commandRunner, - cwd: cwd, - envVars: envVars, + commandRunner: commandRunner, + cwd: cwd, + envVars: envVars, + checkInstalled: checkPath, + } +} + +// for testing +func NewPowershellScriptWithMockCheckPath(commandRunner exec.CommandRunner, cwd string, envVars []string, mockCheckPath checkInstalled) tools.Script { + return &powershellScript{ + commandRunner: commandRunner, + cwd: cwd, + envVars: envVars, + checkInstalled: mockCheckPath, } } type powershellScript struct { - commandRunner exec.CommandRunner - cwd string - envVars []string + commandRunner exec.CommandRunner + cwd string + envVars []string + checkInstalled checkInstalled } -func checkInstalled(options tools.ExecOptions) (err error) { +type checkInstalled func(options tools.ExecOptions) error + +func checkPath(options tools.ExecOptions) (err error) { return tools.ToolInPath(strings.Split(options.UserPwsh, " ")[0]) } // Executes the specified powershell script // When interactive is true will attach to stdin, stdout & stderr func (bs *powershellScript) Execute(ctx context.Context, path string, options tools.ExecOptions) (exec.RunResult, error) { - if err := checkInstalled(options); err != nil { + if err := bs.checkInstalled(options); err != nil { return exec.RunResult{}, &internal.ErrorWithSuggestion{ Err: err, Suggestion: fmt.Sprintf("PowerShell 7 is not installed or not in the path. To install PowerShell 7, visit %s", diff --git a/cli/azd/pkg/tools/powershell/powershell_test.go b/cli/azd/pkg/tools/powershell/powershell_test.go index d0c7d2ddc5a..54bb2427069 100644 --- a/cli/azd/pkg/tools/powershell/powershell_test.go +++ b/cli/azd/pkg/tools/powershell/powershell_test.go @@ -6,6 +6,7 @@ package powershell import ( "context" "errors" + "strings" "testing" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" @@ -29,7 +30,7 @@ func Test_Powershell_Execute(t *testing.T) { // #nosec G101 userPwsh := "pwsh -NoProfile" mockContext.CommandRunner.When(func(args exec.RunArgs, command string) bool { - return true + return strings.Contains(args.Cmd, userPwsh) }).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) { require.Equal(t, userPwsh, args.Cmd) require.Equal(t, workingDir, args.Cwd) @@ -39,7 +40,9 @@ func Test_Powershell_Execute(t *testing.T) { return exec.NewRunResult(0, "", ""), nil }) - PowershellScript := NewPowershellScript(mockContext.CommandRunner, workingDir, env) + PowershellScript := NewPowershellScriptWithMockCheckPath(mockContext.CommandRunner, workingDir, env, func(options tools.ExecOptions) error { + return nil + }) runResult, err := PowershellScript.Execute( *mockContext.Context, scriptPath, @@ -59,7 +62,9 @@ func Test_Powershell_Execute(t *testing.T) { return exec.NewRunResult(1, "", "error message"), errors.New("error message") }) - PowershellScript := NewPowershellScript(mockContext.CommandRunner, workingDir, env) + PowershellScript := NewPowershellScriptWithMockCheckPath(mockContext.CommandRunner, workingDir, env, func(options tools.ExecOptions) error { + return nil + }) runResult, err := PowershellScript.Execute( *mockContext.Context, scriptPath, @@ -70,6 +75,19 @@ func Test_Powershell_Execute(t *testing.T) { require.Error(t, err) }) + t.Run("NoPowerShellInstalled", func(t *testing.T) { + mockContext := mocks.NewMockContext(context.Background()) + + PowershellScript := NewPowershellScript(mockContext.CommandRunner, workingDir, env) + _, err := PowershellScript.Execute( + *mockContext.Context, + scriptPath, + tools.ExecOptions{Interactive: to.Ptr(true)}, + ) + + require.Error(t, err) + }) + tests := []struct { name string value tools.ExecOptions @@ -89,7 +107,9 @@ func Test_Powershell_Execute(t *testing.T) { return exec.NewRunResult(0, "", ""), nil }) - PowershellScript := NewPowershellScript(mockContext.CommandRunner, workingDir, env) + PowershellScript := NewPowershellScriptWithMockCheckPath(mockContext.CommandRunner, workingDir, env, func(options tools.ExecOptions) error { + return nil + }) runResult, err := PowershellScript.Execute(*mockContext.Context, scriptPath, test.value) require.NotNil(t, runResult) From e40347d34299be9cab994ab88a696576f6bacd9b Mon Sep 17 00:00:00 2001 From: hemarina Date: Tue, 4 Mar 2025 13:46:11 -0800 Subject: [PATCH 8/9] lll --- cli/azd/pkg/tools/powershell/powershell.go | 6 +++- .../pkg/tools/powershell/powershell_test.go | 30 +++++++++++++------ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index f37a6de3488..942b584bc0f 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -25,7 +25,11 @@ func NewPowershellScript(commandRunner exec.CommandRunner, cwd string, envVars [ } // for testing -func NewPowershellScriptWithMockCheckPath(commandRunner exec.CommandRunner, cwd string, envVars []string, mockCheckPath checkInstalled) tools.Script { +func NewPowershellScriptWithMockCheckPath( + commandRunner exec.CommandRunner, + cwd string, + envVars []string, + mockCheckPath checkInstalled) tools.Script { return &powershellScript{ commandRunner: commandRunner, cwd: cwd, diff --git a/cli/azd/pkg/tools/powershell/powershell_test.go b/cli/azd/pkg/tools/powershell/powershell_test.go index 54bb2427069..317fac4f4ae 100644 --- a/cli/azd/pkg/tools/powershell/powershell_test.go +++ b/cli/azd/pkg/tools/powershell/powershell_test.go @@ -40,9 +40,13 @@ func Test_Powershell_Execute(t *testing.T) { return exec.NewRunResult(0, "", ""), nil }) - PowershellScript := NewPowershellScriptWithMockCheckPath(mockContext.CommandRunner, workingDir, env, func(options tools.ExecOptions) error { - return nil - }) + PowershellScript := NewPowershellScriptWithMockCheckPath( + mockContext.CommandRunner, + workingDir, + env, + func(options tools.ExecOptions) error { + return nil + }) runResult, err := PowershellScript.Execute( *mockContext.Context, scriptPath, @@ -62,9 +66,13 @@ func Test_Powershell_Execute(t *testing.T) { return exec.NewRunResult(1, "", "error message"), errors.New("error message") }) - PowershellScript := NewPowershellScriptWithMockCheckPath(mockContext.CommandRunner, workingDir, env, func(options tools.ExecOptions) error { - return nil - }) + PowershellScript := NewPowershellScriptWithMockCheckPath( + mockContext.CommandRunner, + workingDir, + env, + func(options tools.ExecOptions) error { + return nil + }) runResult, err := PowershellScript.Execute( *mockContext.Context, scriptPath, @@ -107,9 +115,13 @@ func Test_Powershell_Execute(t *testing.T) { return exec.NewRunResult(0, "", ""), nil }) - PowershellScript := NewPowershellScriptWithMockCheckPath(mockContext.CommandRunner, workingDir, env, func(options tools.ExecOptions) error { - return nil - }) + PowershellScript := NewPowershellScriptWithMockCheckPath( + mockContext.CommandRunner, + workingDir, + env, + func(options tools.ExecOptions) error { + return nil + }) runResult, err := PowershellScript.Execute(*mockContext.Context, scriptPath, test.value) require.NotNil(t, runResult) From ada2f2ea293c198b3c85d852930a1dd6f070828e Mon Sep 17 00:00:00 2001 From: hemarina Date: Tue, 4 Mar 2025 15:31:08 -0800 Subject: [PATCH 9/9] gofmt --- cli/azd/pkg/tools/powershell/powershell.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/azd/pkg/tools/powershell/powershell.go b/cli/azd/pkg/tools/powershell/powershell.go index 942b584bc0f..9a5c43c6043 100644 --- a/cli/azd/pkg/tools/powershell/powershell.go +++ b/cli/azd/pkg/tools/powershell/powershell.go @@ -26,9 +26,9 @@ func NewPowershellScript(commandRunner exec.CommandRunner, cwd string, envVars [ // for testing func NewPowershellScriptWithMockCheckPath( - commandRunner exec.CommandRunner, - cwd string, - envVars []string, + commandRunner exec.CommandRunner, + cwd string, + envVars []string, mockCheckPath checkInstalled) tools.Script { return &powershellScript{ commandRunner: commandRunner,