diff --git a/.github/actions/save-logs/action.yaml b/.github/actions/save-logs/action.yaml index 2cefc70cca..770b2b52ba 100644 --- a/.github/actions/save-logs/action.yaml +++ b/.github/actions/save-logs/action.yaml @@ -6,7 +6,7 @@ runs: steps: - name: Fix log permissions run: | - stat -t /tmp/zarf-*.log >/dev/null 2>&1 && sudo chown $USER /tmp/zarf-*.log + sudo chown $USER /tmp/zarf-*.log || echo "" shell: bash - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 diff --git a/docs/2-the-zarf-cli/100-cli-commands/zarf_version.md b/docs/2-the-zarf-cli/100-cli-commands/zarf_version.md index b17b2e1e37..ac4f65bd63 100644 --- a/docs/2-the-zarf-cli/100-cli-commands/zarf_version.md +++ b/docs/2-the-zarf-cli/100-cli-commands/zarf_version.md @@ -14,7 +14,8 @@ zarf version [flags] ## Options ``` - -h, --help help for version + -h, --help help for version + -o, --output string Output format (yaml|json) ``` ## Options inherited from parent commands diff --git a/src/cmd/version.go b/src/cmd/version.go index 8b3aa30e30..d436222a10 100644 --- a/src/cmd/version.go +++ b/src/cmd/version.go @@ -5,27 +5,87 @@ package cmd import ( + "encoding/json" "fmt" + "os" + "runtime" + "github.com/Masterminds/semver/v3" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/spf13/cobra" + + "runtime/debug" + + goyaml "github.com/goccy/go-yaml" ) +var outputFormat string + var versionCmd = &cobra.Command{ Use: "version", Aliases: []string{"v"}, PersistentPreRun: func(cmd *cobra.Command, args []string) { config.SkipLogFile = true - cliSetup() }, Short: lang.CmdVersionShort, Long: lang.CmdVersionLong, Run: func(cmd *cobra.Command, args []string) { - fmt.Println(config.CLIVersion) + output := make(map[string]interface{}) + + buildInfo, ok := debug.ReadBuildInfo() + if !ok && outputFormat != "" { + fmt.Println("Failed to get build info") + return + } + depMap := map[string]string{} + for _, dep := range buildInfo.Deps { + if dep.Replace != nil { + depMap[dep.Path] = fmt.Sprintf("%s -> %s %s", dep.Version, dep.Replace.Path, dep.Replace.Version) + } else { + depMap[dep.Path] = dep.Version + } + } + output["dependencies"] = depMap + + buildMap := make(map[string]interface{}) + buildMap["platform"] = runtime.GOOS + "/" + runtime.GOARCH + buildMap["goVersion"] = runtime.Version() + ver, err := semver.NewVersion(config.CLIVersion) + if err != nil { + buildMap["minor"] = "" + buildMap["patch"] = "" + buildMap["prerelease"] = "" + } else { + buildMap["major"] = ver.Major() + buildMap["minor"] = ver.Minor() + buildMap["patch"] = ver.Patch() + buildMap["prerelease"] = ver.Prerelease() + } + + output["version"] = config.CLIVersion + + output["build"] = buildMap + + switch outputFormat { + case "yaml": + text, _ := goyaml.Marshal(output) + fmt.Println(string(text)) + case "json": + text, _ := json.Marshal(output) + fmt.Println(string(text)) + default: + fmt.Println(config.CLIVersion) + } }, } +func isVersionCmd() bool { + args := os.Args + return len(args) > 1 && (args[1] == "version" || args[1] == "v") +} + func init() { + versionCmd.Flags().StringVarP(&outputFormat, "output", "o", "", "Output format (yaml|json)") rootCmd.AddCommand(versionCmd) } diff --git a/src/cmd/viper.go b/src/cmd/viper.go index b9ae36a7c6..d157438c57 100644 --- a/src/cmd/viper.go +++ b/src/cmd/viper.go @@ -93,6 +93,11 @@ func initViper() { return } + // Skip for the version command + if isVersionCmd() { + return + } + // Specify an alternate config file cfgFile := os.Getenv("ZARF_CONFIG") diff --git a/src/test/e2e/00_use_cli_test.go b/src/test/e2e/00_use_cli_test.go index 7265acc1b8..ccae6019b6 100644 --- a/src/test/e2e/00_use_cli_test.go +++ b/src/test/e2e/00_use_cli_test.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "runtime" + "strings" "testing" "github.com/stretchr/testify/require" @@ -50,10 +51,22 @@ func TestUseCLI(t *testing.T) { t.Run("zarf version", func(t *testing.T) { t.Parallel() // Test `zarf version` - stdOut, _, err := e2e.Zarf("version") + version, _, err := e2e.Zarf("version") require.NoError(t, err) - require.NotEqual(t, len(stdOut), 0, "Zarf version should not be an empty string") - require.NotEqual(t, stdOut, "UnknownVersion", "Zarf version should not be the default value") + require.NotEqual(t, len(version), 0, "Zarf version should not be an empty string") + version = strings.Trim(version, "\n") + + // test `zarf version --output=json` + stdOut, _, err := e2e.Zarf("version", "--output=json") + require.NoError(t, err) + jsonVersion := fmt.Sprintf(",\"version\":\"%s\"}", version) + require.Contains(t, stdOut, jsonVersion, "Zarf version should be the same in all formats") + + // test `zarf version --output=yaml` + stdOut, _, err = e2e.Zarf("version", "--output=yaml") + require.NoError(t, err) + yamlVersion := fmt.Sprintf("version: %s", version) + require.Contains(t, stdOut, yamlVersion, "Zarf version should be the same in all formats") }) t.Run("zarf prepare find-images", func(t *testing.T) { @@ -91,7 +104,7 @@ func TestUseCLI(t *testing.T) { t.Run("changing log level", func(t *testing.T) { t.Parallel() // Test that changing the log level actually applies the requested level - _, stdErr, _ := e2e.Zarf("version", "--log-level=debug") + _, stdErr, _ := e2e.Zarf("internal", "crc32", "zarf", "--log-level=debug") expectedOutString := "Log level set to debug" require.Contains(t, stdErr, expectedOutString, "The log level should be changed to 'debug'") })