Skip to content
Open
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
1 change: 1 addition & 0 deletions site/src/content/docs/commands/zarf_tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Collection of additional tools to make airgap easier
### SEE ALSO

* [zarf](/commands/zarf/) - The Airgap Native Packager Manager for Kubernetes
* [zarf tools aliases](/commands/zarf_tools_aliases/) - Print POSIX shell aliases for bundled tools
* [zarf tools archiver](/commands/zarf_tools_archiver/) - Compresses/Decompresses generic archives, including Zarf packages
* [zarf tools clear-cache](/commands/zarf_tools_clear-cache/) - Clears the configured git and image cache directory
* [zarf tools download-init](/commands/zarf_tools_download-init/) - Downloads the init package for the current Zarf version into the specified directory
Expand Down
40 changes: 40 additions & 0 deletions site/src/content/docs/commands/zarf_tools_aliases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: zarf tools aliases
description: Zarf CLI command reference for <code>zarf tools aliases</code>.
tableOfContents: false
---

<!-- Page generated by Zarf; DO NOT EDIT -->

## zarf tools aliases

Print POSIX shell aliases for bundled tools

```
zarf tools aliases [flags]
```

### Options

```
-h, --help help for aliases
```

### Options inherited from parent commands

```
-a, --architecture string Architecture for OCI images and Zarf packages
--cache string Specify the location of the Zarf cache directory (default "~/.zarf-cache")
--features stringToString Provide a comma-separated list of feature names to bools to enable or disable. Ex. --features "foo=true,bar=false,baz=true" (default [])
--insecure-skip-tls-verify Skip checking server's certificate for validity. This flag should only be used if you have a specific reason and accept the reduced security posture.
--log-format string Select a logging format. Defaults to 'console'. Valid options are: 'console', 'json', 'dev'. (default "console")
-l, --log-level string Log level when running Zarf. Valid options are: warn, info, debug, trace (default "info")
--no-color Disable terminal color codes in logging and stdout prints.
--plain-http Force the connections over HTTP instead of HTTPS. This flag should only be used if you have a specific reason and accept the reduced security posture.
--tmpdir string Specify the temporary directory to use for intermediate files
```

### SEE ALSO

* [zarf tools](/commands/zarf_tools/) - Collection of additional tools to make airgap easier

17 changes: 17 additions & 0 deletions site/src/content/docs/ref/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@ TODO: finish this page for all embedded CLIs
Zarf vendors in [`yq`](https://github.com/mikefarah/yq/) to provide a way to interact with YAML files.
`yq` is a lightweight and portable command-line YAML processor. It can be used to extract specific parts of a YAML file, replace or delete values, and more.

## Shell aliases

> command: [`zarf tools aliases`](/commands/zarf_tools_aliases)

Source the generated aliases to use Zarf's bundled `kubectl`, `helm`, `yq`, `k9s`, and `syft` as standalone commands:

```sh
source <(zarf tools aliases)
```

When using Bash, enable alias expansion before sourcing aliases from a package manifest or another noninteractive context:

```sh
shopt -s expand_aliases
source <(zarf tools aliases)
```

## archiver

> command: [`zarf tools archiver`](/commands/zarf_tools_archiver)
Expand Down
1 change: 1 addition & 0 deletions src/cmd/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func newToolsCommand() *cobra.Command {
v := getViper()

cmd.AddCommand(newArchiverCommand())
cmd.AddCommand(newToolsAliasesCommand())
cmd.AddCommand(newRegistryCommand())
cmd.AddCommand(newDeprecatedCraneCommand())
cmd.AddCommand(newHelmCommand())
Expand Down
120 changes: 120 additions & 0 deletions src/cmd/tools_aliases.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package cmd contains the CLI commands for Zarf.
package cmd

import (
"errors"
"fmt"
"os"
"runtime"
"strings"
"unicode"

"github.com/spf13/cobra"
"github.com/zarf-dev/zarf/src/config"
"github.com/zarf-dev/zarf/src/config/lang"
"github.com/zarf-dev/zarf/src/pkg/utils"
)

var (
aliasedTools = []struct {
alias string
target string
}{
{"kubectl", "kubectl"},
{"helm", "helm"},
{"yq", "yq"},
{"k9s", "monitor"},
{"syft", "sbom"},
}
doubleQuoteEscaper = strings.NewReplacer(
`\`, `\\`,
`$`, `\$`,
"`", "\\`",
`"`, `\"`,
)
toolsAliasesUsage = "aliases"
)

func newToolsAliasesCommand() *cobra.Command {
return &cobra.Command{
Use: toolsAliasesUsage,
Args: cobra.NoArgs,
Short: lang.CmdToolsAliasesShort,
RunE: func(cmd *cobra.Command, _ []string) error {
if runtime.GOOS == "windows" {
return errors.New(lang.CmdToolsAliasesErrWindows)
}

executablePath, err := utils.GetFinalExecutablePath()
if err != nil {
return fmt.Errorf("resolving final executable path: %w", err)
}

_, err = fmt.Fprint(cmd.OutOrStdout(), toolsAliases(executablePath, os.Args))

return err
},
}
}

func toolsAliases(executablePath string, args []string) string {
aliasComponents := []string{escape(executablePath)}
if config.ActionsCommandZarfPrefix != "" {
aliasComponents = append(aliasComponents, config.ActionsCommandZarfPrefix)
}

for i, arg := range args {
if i == 0 {
continue
}

if arg == toolsAliasesUsage {
break
}

aliasComponents = append(aliasComponents, escape(arg))
}

aliasPrefix := strings.Join(aliasComponents, " ")

var output strings.Builder
for _, tool := range aliasedTools {
fmt.Fprintf(&output, "alias %s=%s\n", tool.alias, quote(aliasPrefix+" "+tool.target))
}

return output.String()
}

func escape(value string) string {
var escaped strings.Builder

for _, r := range value {
if r == '\n' {
escaped.WriteString("'\n'")
continue
}

if !isShellSafe(r) {
escaped.WriteByte('\\')
}

escaped.WriteRune(r)
}

return escaped.String()
}

func isShellSafe(r rune) bool {
return unicode.IsLetter(r) || unicode.IsDigit(r) || strings.ContainsRune("_@%+=:,./-", r)
}

func quote(value string) string {
if !strings.Contains(value, "'") {
return "'" + value + "'"
}

return `"` + doubleQuoteEscaper.Replace(value) + `"`
}
141 changes: 141 additions & 0 deletions src/cmd/tools_aliases_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package cmd contains the CLI commands for Zarf.
package cmd

import (
"bytes"
"runtime"
"strings"
"testing"

"github.com/stretchr/testify/require"
"github.com/zarf-dev/zarf/src/config"
"github.com/zarf-dev/zarf/src/config/lang"
)

func TestToolsAliasesIntegration(t *testing.T) {
cmd := newToolsAliasesCommand()

stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)

cmd.SetOut(stdout)
cmd.SetErr(stderr)

err := cmd.Execute()

if runtime.GOOS == "windows" {
require.ErrorContains(t, err, lang.CmdToolsAliasesErrWindows)
return
}

require.NoError(t, err)

for i, alias := range strings.Split(strings.TrimSuffix(stdout.String(), "\n"), "\n") {
parts := strings.Split(alias, " ")
cmd := parts[len(parts)-1]
require.Contains(t, cmd, aliasedTools[i].target)
}
}

func TestToolsAliasesOutput(t *testing.T) {
tests := map[string]struct {
before func() func()
executablePath string
args []string
expected string
}{
"standalone executable": {
executablePath: "/path/zarf",
args: []string{"/path/zarf", "tools", "aliases"},
expected: "" +
"alias kubectl='/path/zarf tools kubectl'\n" +
"alias helm='/path/zarf tools helm'\n" +
"alias yq='/path/zarf tools yq'\n" +
"alias k9s='/path/zarf tools monitor'\n" +
"alias syft='/path/zarf tools sbom'\n",
},
"embedded executable": {
executablePath: "/path/uds",
args: []string{"/path/uds", "zarf", "tools", "aliases"},
expected: "" +
"alias kubectl='/path/uds zarf tools kubectl'\n" +
"alias helm='/path/uds zarf tools helm'\n" +
"alias yq='/path/uds zarf tools yq'\n" +
"alias k9s='/path/uds zarf tools monitor'\n" +
"alias syft='/path/uds zarf tools sbom'\n",
},
"escapes whitespace and shell metacharacters": {
executablePath: "/path with spaces/$zarf",
args: []string{"/path with spaces/$zarf", "tools", "aliases"},
expected: "" +
`alias kubectl='/path\ with\ spaces/\$zarf tools kubectl'` + "\n" +
`alias helm='/path\ with\ spaces/\$zarf tools helm'` + "\n" +
`alias yq='/path\ with\ spaces/\$zarf tools yq'` + "\n" +
`alias k9s='/path\ with\ spaces/\$zarf tools monitor'` + "\n" +
`alias syft='/path\ with\ spaces/\$zarf tools sbom'` + "\n",
},
"escapes double-quote-unfriendly characters": {
executablePath: "/linus' $special$ k8s tools/zarf",
args: []string{"/linus' $special$ k8s tools/zarf", "tools", "aliases"},
expected: "" +
`alias kubectl="/linus\\'\\ \\\$special\\\$\\ k8s\\ tools/zarf tools kubectl"` + "\n" +
`alias helm="/linus\\'\\ \\\$special\\\$\\ k8s\\ tools/zarf tools helm"` + "\n" +
`alias yq="/linus\\'\\ \\\$special\\\$\\ k8s\\ tools/zarf tools yq"` + "\n" +
`alias k9s="/linus\\'\\ \\\$special\\\$\\ k8s\\ tools/zarf tools monitor"` + "\n" +
`alias syft="/linus\\'\\ \\\$special\\\$\\ k8s\\ tools/zarf tools sbom"` + "\n",
},
"multi-token embedded prefix": {
executablePath: "/path/uds",
args: []string{"/path/uds", "wrapper", "zarf", "tools", "aliases"},
expected: "" +
"alias kubectl='/path/uds wrapper zarf tools kubectl'\n" +
"alias helm='/path/uds wrapper zarf tools helm'\n" +
"alias yq='/path/uds wrapper zarf tools yq'\n" +
"alias k9s='/path/uds wrapper zarf tools monitor'\n" +
"alias syft='/path/uds wrapper zarf tools sbom'\n",
},
"with custom zarf prefix": {
before: func() func() {
oldPrefix := config.ActionsCommandZarfPrefix
config.ActionsCommandZarfPrefix = "thisisatest"
return func() {
config.ActionsCommandZarfPrefix = oldPrefix
}
},
executablePath: "/path/uds",
args: []string{"/path/uds", "zarf", "tools", "aliases"},
expected: "" +
"alias kubectl='/path/uds thisisatest zarf tools kubectl'\n" +
"alias helm='/path/uds thisisatest zarf tools helm'\n" +
"alias yq='/path/uds thisisatest zarf tools yq'\n" +
"alias k9s='/path/uds thisisatest zarf tools monitor'\n" +
"alias syft='/path/uds thisisatest zarf tools sbom'\n",
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
if test.before != nil {
after := test.before()
defer after()
}
require.Equal(t, test.expected, toolsAliases(test.executablePath, test.args))
})
}
}

func TestToolsAliasesTargetsExistInCommandTree(t *testing.T) {
rootCmd := NewZarfCommand()

for _, tool := range aliasedTools {
t.Run(tool.alias, func(t *testing.T) {
cmd, _, err := rootCmd.Find([]string{"tools", tool.target})

require.NoError(t, err)
require.Equal(t, tool.target, cmd.Name())
})
}
}
4 changes: 3 additions & 1 deletion src/config/lang/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,9 @@ $ zarf package pull oci://ghcr.io/zarf-dev/packages/dos-games:1.3.0 -a skeleton`
CmdDevLintLong = "Verifies the package schema, checks if any variables won't be evaluated, and checks for unpinned images/repos/files"

// zarf tools
CmdToolsShort = "Collection of additional tools to make airgap easier"
CmdToolsShort = "Collection of additional tools to make airgap easier"
CmdToolsAliasesShort = "Print POSIX shell aliases for bundled tools"
CmdToolsAliasesErrWindows = "zarf tools aliases is only supported on Linux and Unix systems"

CmdToolsArchiverShort = "Compresses/Decompresses generic archives, including Zarf packages"
CmdToolsArchiverCompressShort = "Compresses a collection of sources based off of the destination file extension."
Expand Down
20 changes: 20 additions & 0 deletions src/test/e2e/00_use_cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/otiai10/copy"
"github.com/sigstore/sigstore-go/pkg/root"
"github.com/stretchr/testify/require"
"github.com/zarf-dev/zarf/src/config/lang"
"github.com/zarf-dev/zarf/src/pkg/logger"
"github.com/zarf-dev/zarf/src/pkg/packager/layout"
"github.com/zarf-dev/zarf/src/test"
Expand Down Expand Up @@ -358,6 +359,25 @@ components:
})
}

func TestToolsAliases(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip(lang.CmdToolsAliasesErrWindows)
}
t.Parallel()

stdOut, stdErr, err := e2e.Zarf(t, "tools", "aliases")

require.NoError(t, err, stdOut, stdErr)

expected := "" +
"alias kubectl='" + e2e.ZarfBinPath + " tools kubectl'\n" +
"alias helm='" + e2e.ZarfBinPath + " tools helm'\n" +
"alias yq='" + e2e.ZarfBinPath + " tools yq'\n" +
"alias k9s='" + e2e.ZarfBinPath + " tools monitor'\n" +
"alias syft='" + e2e.ZarfBinPath + " tools sbom'\n"
require.Equal(t, expected, stdOut)
}

func TestBuildMachineInfo(t *testing.T) {
t.Parallel()

Expand Down