Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the selection between CMD and Powershell and prefer CMD for dataInjections #1581

Merged
merged 14 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions docs/4-user-guide/3-zarf-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,89 @@ Must be one of:
</blockquote>
</details>

<details open>
<summary>
<strong> <a name="components_items_actions_onCreate_defaults_shell"></a>shell</strong>
</summary>
&nbsp;
<blockquote>

## components > actions > onCreate > defaults > shell

**Description:** (cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems

| | |
| ------------------------- | -------------------------------------------------------------------------------------------------------- |
| **Type** | `object` |
| **Additional properties** | [![Not allowed](https://img.shields.io/badge/Not%20allowed-red)](# "Additional Properties not allowed.") |
| **Defined in** | #/definitions/ZarfComponentActionShell |

<details>
<summary>
<strong> <a name="components_items_actions_onCreate_defaults_shell_windows"></a>windows</strong>
</summary>
&nbsp;
<blockquote>

**Description:** (default 'powershell') Indicates a preference for the shell to use on Windows systems (note that choosing 'cmd' will turn off migrations like touch -> New-Item)

| | |
| -------- | -------- |
| **Type** | `string` |

**Examples:**

<code>
"powershell", "cmd", "pwsh", "sh", "bash", "gsh"</code>

</blockquote>
</details>

<details>
<summary>
<strong> <a name="components_items_actions_onCreate_defaults_shell_linux"></a>linux</strong>
</summary>
&nbsp;
<blockquote>

**Description:** (default 'sh') Indicates a preference for the shell to use on Linux systems

| | |
| -------- | -------- |
| **Type** | `string` |

**Examples:**

<code>
"sh", "bash", "fish", "zsh", "pwsh"</code>

</blockquote>
</details>

<details>
<summary>
<strong> <a name="components_items_actions_onCreate_defaults_shell_darwin"></a>darwin</strong>
</summary>
&nbsp;
<blockquote>

**Description:** (default 'sh') Indicates a preference for the shell to use on macOS systems

| | |
| -------- | -------- |
| **Type** | `string` |

**Examples:**

<code>
"sh", "bash", "fish", "zsh", "pwsh"</code>

</blockquote>
</details>

</blockquote>
</details>

</blockquote>
</details>

Expand Down Expand Up @@ -990,6 +1073,26 @@ Must be one of:
</blockquote>
</details>

<details open>
<summary>
<strong> <a name="components_items_actions_onCreate_before_items_shell"></a>shell</strong>
</summary>
&nbsp;
<blockquote>

## components > actions > onCreate > before > shell

**Description:** (cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems

| | |
| ------------------------- | -------------------------------------------------------------------------------------------------------- |
| **Type** | `object` |
| **Additional properties** | [![Not allowed](https://img.shields.io/badge/Not%20allowed-red)](# "Additional Properties not allowed.") |
| **Same definition as** | [shell](#components_items_actions_onCreate_defaults_shell) |

</blockquote>
</details>

<details open>
<summary>
<strong> <a name="components_items_actions_onCreate_before_items_setVariables"></a>setVariables</strong>
Expand Down
3 changes: 3 additions & 0 deletions examples/component-actions/zarf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ components:
- cmd: touch test-create-after.txt
- cmd: sleep 1
- cmd: echo "I can print!"
# prefer to run the above command in "cmd" instead of "powershell" on Windows
shell:
windows: cmd
- cmd: sleep 1
- cmd: |
echo "multiline!"
Expand Down
8 changes: 4 additions & 4 deletions src/extensions/bigbang/bigbang.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func Run(tmpPaths types.ComponentPaths, c types.ZarfComponent) (types.ZarfCompon
cfg := c.Extensions.BigBang
manifests := []types.ZarfManifest{}

err, validVersionResponse := isValidVersion(cfg.Version)
validVersionResponse, err := isValidVersion(cfg.Version)

if err != nil {
return c, fmt.Errorf("invalid Big Bang version: %s, parsing issue %s", cfg.Version, err)
Expand Down Expand Up @@ -233,11 +233,11 @@ func Run(tmpPaths types.ComponentPaths, c types.ZarfComponent) (types.ZarfCompon
}

// isValidVersion check if the version is 1.54.0 or greater.
func isValidVersion(version string) (error, bool) {
func isValidVersion(version string) (bool, error) {
specifiedVersion, err := semver.NewVersion(version)

if err != nil {
return err, false
return false, err
}

minRequiredVersion, _ := semver.NewVersion(bbMinRequiredVersion)
Expand All @@ -246,7 +246,7 @@ func isValidVersion(version string) (error, bool) {
c, _ := semver.NewConstraint(fmt.Sprintf(">= %s-0", minRequiredVersion))

// This extension requires BB 1.54.0 or greater.
return nil, c.Check(specifiedVersion)
return c.Check(specifiedVersion), nil
}

// findBBResources takes a list of yaml objects (as a string) and
Expand Down
13 changes: 7 additions & 6 deletions src/extensions/bigbang/bigbang_test.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
package bigbang

import (
"github.com/stretchr/testify/assert"
"testing"

"github.com/stretchr/testify/assert"
)

func TestRequiredBigBangVersions(t *testing.T) {
// Support 1.54.0 and beyond
err, vv := isValidVersion("1.54.0")
vv, err := isValidVersion("1.54.0")
assert.Equal(t, err, nil)
assert.Equal(t, vv, true)

// Do not support earlier than 1.54.0
err, vv = isValidVersion("1.53.0")
vv, err = isValidVersion("1.53.0")
assert.Equal(t, err, nil)
assert.Equal(t, vv, false)

// Support for Big Bang release candidates
err, vv = isValidVersion("1.57.0-rc.0")
vv, err = isValidVersion("1.57.0-rc.0")
assert.Equal(t, err, nil)
assert.Equal(t, vv, true)

// Support for Big Bang 2.0.0
err, vv = isValidVersion("2.0.0")
vv, err = isValidVersion("2.0.0")
assert.Equal(t, err, nil)
assert.Equal(t, vv, true)

// Fail on non-semantic versions
err, vv = isValidVersion("1.57b")
vv, err = isValidVersion("1.57b")
Expected := "Invalid Semantic Version"
if err.Error() != Expected {
t.Errorf("Error actual = %v, and Expected = %v.", err, Expected)
Expand Down
2 changes: 1 addition & 1 deletion src/internal/cluster/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (c *Cluster) HandleDataInjection(wg *sync.WaitGroup, data types.ZarfDataInj
}

// Get the OS shell to execute commands in
shell, shellArgs := exec.GetOSShell()
shell, shellArgs := exec.GetOSShell(types.ZarfComponentActionShell{Windows: "cmd"})

if _, _, err := exec.Cmd(shell, shellArgs, "tar --version"); err != nil {
message.Error(err, "Unable to execute tar on this system. Please ensure it is installed and on your $PATH.")
Expand Down
18 changes: 11 additions & 7 deletions src/pkg/packager/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (p *Packager) runAction(defaultCfg types.ZarfComponentActionDefaults, actio

cfg := actionGetCfg(defaultCfg, action, vars)

if cmd, err = actionCmdMutation(cmd); err != nil {
if cmd, err = actionCmdMutation(cmd, cfg.Shell); err != nil {
spinner.Errorf(err, "Error mutating command: %s", cmdEscaped)
}

Expand All @@ -102,7 +102,7 @@ func (p *Packager) runAction(defaultCfg types.ZarfComponentActionDefaults, actio
// Perform the action run.
tryCmd := func(ctx context.Context) error {
// Try running the command and continue the retry loop if it fails.
if out, err = actionRun(ctx, cfg, cmd, spinner); err != nil {
if out, err = actionRun(ctx, cfg, cmd, cfg.Shell, spinner); err != nil {
return err
}

Expand Down Expand Up @@ -193,7 +193,7 @@ func convertWaitToCmd(wait types.ZarfComponentActionWait, timeout *int) (string,
}

// Perform some basic string mutations to make commands more useful.
func actionCmdMutation(cmd string) (string, error) {
func actionCmdMutation(cmd string, shellPref types.ZarfComponentActionShell) (string, error) {
binaryPath, err := os.Executable()
if err != nil {
return cmd, err
Expand All @@ -203,7 +203,7 @@ func actionCmdMutation(cmd string) (string, error) {
cmd = strings.ReplaceAll(cmd, "./zarf ", binaryPath+" ")

// Make commands 'more' compatible with Windows OS PowerShell
if runtime.GOOS == "windows" {
if runtime.GOOS == "windows" && (exec.IsPowershell(shellPref.Windows) || shellPref.Windows == "") {
// Replace "touch" with "New-Item" on Windows as it's a common command, but not POSIX so not aliased by M$.
// See https://mathieubuisson.github.io/powershell-linux-bash/ &
// http://web.cs.ucla.edu/~miryung/teaching/EE461L-Spring2012/labs/posix.html for more details.
Expand Down Expand Up @@ -246,6 +246,10 @@ func actionGetCfg(cfg types.ZarfComponentActionDefaults, a types.ZarfComponentAc
cfg.Env = append(cfg.Env, a.Env...)
}

if a.Shell != nil {
cfg.Shell = *a.Shell
}

// Add variables to the environment.
for k, v := range vars {
// Remove # from env variable name.
Expand All @@ -259,10 +263,10 @@ func actionGetCfg(cfg types.ZarfComponentActionDefaults, a types.ZarfComponentAc
return cfg
}

func actionRun(ctx context.Context, cfg types.ZarfComponentActionDefaults, cmd string, spinner *message.Spinner) (string, error) {
shell, shellArgs := exec.GetOSShell()
func actionRun(ctx context.Context, cfg types.ZarfComponentActionDefaults, cmd string, shellPref types.ZarfComponentActionShell, spinner *message.Spinner) (string, error) {
shell, shellArgs := exec.GetOSShell(shellPref)

message.Debug("Running command in %s: %s", shell, cmd)
message.Debugf("Running command in %s: %s", shell, cmd)

execCfg := exec.Config{
Env: cfg.Env,
Expand Down
47 changes: 44 additions & 3 deletions src/pkg/utils/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"os/exec"
"runtime"
"sync"

"github.com/defenseunicorns/zarf/src/types"
)

// Change terminal colors.
Expand Down Expand Up @@ -152,17 +154,56 @@ func LaunchURL(url string) error {
}

// GetOSShell returns the shell and shellArgs based on the current OS
func GetOSShell() (string, string) {
func GetOSShell(shellPref types.ZarfComponentActionShell) (string, string) {
var shell string
var shellArgs string

if runtime.GOOS == "windows" {
switch runtime.GOOS {
case "windows":
shell = "powershell"
if shellPref.Windows != "" {
shell = shellPref.Windows
}

shellArgs = "-Command"
} else {
if shell == "cmd" {
// Change shellArgs to /c if cmd is chosen
shellArgs = "/c"
} else if !IsPowershell(shell) {
// Change shellArgs to -c if a real shell is chosen
shellArgs = "-c"
}
case "darwin":
shell = "sh"
if shellPref.Darwin != "" {
shell = shellPref.Darwin
}

shellArgs = "-c"
if IsPowershell(shell) {
// Change shellArgs to -Command if pwsh is chosen
shellArgs = "-Command"
}
case "linux":
shell = "sh"
if shellPref.Linux != "" {
shell = shellPref.Linux
}

shellArgs = "-c"
if IsPowershell(shell) {
// Change shellArgs to -Command if pwsh is chosen
shellArgs = "-Command"
}
default:
shell = "sh"
shellArgs = "-c"
}

return shell, shellArgs
}

// IsPowershell returns whether a shell name is powershell
func IsPowershell(shellName string) bool {
return shellName == "powershell" || shellName == "pwsh"
}
19 changes: 14 additions & 5 deletions src/types/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,19 @@ type ZarfComponentActionSet struct {

// ZarfComponentActionDefaults sets the default configs for child actions
type ZarfComponentActionDefaults struct {
Mute bool `json:"mute,omitempty" jsonschema:"description=Hide the output of commands during execution (default false)"`
MaxTotalSeconds int `json:"maxTotalSeconds,omitempty" jsonschema:"description=Default timeout in seconds for commands (default to 0, no timeout)"`
MaxRetries int `json:"maxRetries,omitempty" jsonschema:"description=Retry commands given number of times if they fail (default 0)"`
Dir string `json:"dir,omitempty" jsonschema:"description=Working directory for commands (default CWD)"`
Env []string `json:"env,omitempty" jsonschema:"description=Additional environment variables for commands"`
Mute bool `json:"mute,omitempty" jsonschema:"description=Hide the output of commands during execution (default false)"`
MaxTotalSeconds int `json:"maxTotalSeconds,omitempty" jsonschema:"description=Default timeout in seconds for commands (default to 0, no timeout)"`
MaxRetries int `json:"maxRetries,omitempty" jsonschema:"description=Retry commands given number of times if they fail (default 0)"`
Dir string `json:"dir,omitempty" jsonschema:"description=Working directory for commands (default CWD)"`
Env []string `json:"env,omitempty" jsonschema:"description=Additional environment variables for commands"`
Shell ZarfComponentActionShell `json:"shell,omitempty" jsonschema:"description=(cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems"`
}

// ZarfComponentActionShell represents the desired shell to use for a given command
type ZarfComponentActionShell struct {
Windows string `json:"windows,omitempty" jsonschema:"description=(default 'powershell') Indicates a preference for the shell to use on Windows systems (note that choosing 'cmd' will turn off migrations like touch -> New-Item),example=powershell,example=cmd,example=pwsh,example=sh,example=bash,example=gsh"`
Linux string `json:"linux,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on Linux systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"`
Darwin string `json:"darwin,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on macOS systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"`
}

// ZarfComponentAction represents a single action to run during a zarf package operation
Expand All @@ -148,6 +156,7 @@ type ZarfComponentAction struct {
Dir *string `json:"dir,omitempty" jsonschema:"description=The working directory to run the command in (default is CWD)"`
Env []string `json:"env,omitempty" jsonschema:"description=Additional environment variables to set for the command"`
Cmd string `json:"cmd,omitempty" jsonschema:"description=The command to run. Must specify either cmd or wait for the action to do anything."`
Shell *ZarfComponentActionShell `json:"shell,omitempty" jsonschema:"description=(cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems"`
DeprecatedSetVariable string `json:"setVariable,omitempty" jsonschema:"description=[Deprecated] (replaced by setVariables) (onDeploy/cmd only) The name of a variable to update with the output of the command. This variable will be available to all remaining actions and components in the package.,pattern=^[A-Z0-9_]+$"`
SetVariables []ZarfComponentActionSetVariable `json:"setVariables,omitempty" jsonschema:"description=(onDeploy/cmd only) An array of variables to update with the output of the command. These variables will be available to all remaining actions and components in the package."`
Description string `json:"description,omitempty" jsonschema:"description=Description of the action to be displayed during package execution instead of the command"`
Expand Down
Loading