diff --git a/cmd/version.go b/cmd/version.go index 135e38b..b2f42a0 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,6 +1,7 @@ package cmd import ( + "encoding/json" "fmt" "github.com/jongio/grut/internal/config" @@ -8,13 +9,23 @@ import ( "github.com/spf13/cobra" ) +type versionInfo struct { + Version string `json:"version"` +} + // newVersionCmd creates the version command. func newVersionCmd() *cobra.Command { - return &cobra.Command{ + var jsonOutput bool + cmd := &cobra.Command{ Use: cmdVersion, Short: "Print the version of grut", Run: func(cmd *cobra.Command, args []string) { - fmt.Println(config.AppVersion) + if jsonOutput { + _ = json.NewEncoder(cmd.OutOrStdout()).Encode(versionInfo{Version: config.AppVersion}) + return + } + + fmt.Fprintln(cmd.OutOrStdout(), config.AppVersion) // Show update notification inline. if info := update.CheckForUpdate(config.AppVersion); info != nil { fmt.Fprintf(cmd.ErrOrStderr(), @@ -23,4 +34,6 @@ func newVersionCmd() *cobra.Command { } }, } + cmd.Flags().BoolVar(&jsonOutput, "json", false, "Print version information as JSON") + return cmd } diff --git a/cmd/version_test.go b/cmd/version_test.go index 8afbcee..c6d2023 100644 --- a/cmd/version_test.go +++ b/cmd/version_test.go @@ -1,6 +1,8 @@ package cmd import ( + "bytes" + "encoding/json" "testing" "github.com/jongio/grut/internal/config" @@ -13,17 +15,15 @@ import ( // --------------------------------------------------------------------------- func TestVersionCmd_OutputContainsAppVersion(t *testing.T) { - // The version command uses fmt.Println which writes to os.Stdout, not - // cmd.OutOrStdout(). We verify it runs without error and check the - // version string is set correctly via the root command's Version field. root, cleanup := buildRootCommand() defer cleanup() + var out bytes.Buffer + root.SetOut(&out) root.SetArgs([]string{"version"}) err := root.Execute() require.NoError(t, err) - // The root command's Version field is what --version and the version - // subcommand use. Verify it's set correctly. + assert.Contains(t, out.String(), config.AppVersion) assert.Equal(t, config.AppVersion, root.Version) } @@ -43,6 +43,28 @@ func TestVersionCmd_StructureHasCorrectUse(t *testing.T) { assert.Equal(t, "version", cmd.Use) } +func TestVersionCmd_JSONOutput(t *testing.T) { + cmd := newVersionCmd() + var out bytes.Buffer + cmd.SetOut(&out) + cmd.SetArgs([]string{"--json"}) + + err := cmd.Execute() + + require.NoError(t, err) + var got versionInfo + require.NoError(t, json.Unmarshal(out.Bytes(), &got)) + assert.Equal(t, config.AppVersion, got.Version) +} + +func TestVersionCmd_JSONFlagRegistered(t *testing.T) { + cmd := newVersionCmd() + flag := cmd.Flags().Lookup("json") + require.NotNil(t, flag) + assert.Equal(t, "bool", flag.Value.Type()) + assert.Equal(t, "false", flag.DefValue) +} + func TestVersionCmd_HasRunNotRunE(t *testing.T) { // version uses Run (not RunE) because it always succeeds. cmd := newVersionCmd() diff --git a/docs/version-json.md b/docs/version-json.md new file mode 100644 index 0000000..791d39d --- /dev/null +++ b/docs/version-json.md @@ -0,0 +1,9 @@ +# Version JSON + +Use JSON output when scripts need the installed grut version: + +```bash +grut version --json +``` + +The output contains a stable `version` field and does not include update notices.