feat(cli): add --output={text,json} to version cmd (#7010)

* feat(cliui): add TextFormat
* feat(cli): add --format={text,json} to version cmd
This commit is contained in:
Cian Johnston 2023-04-05 13:16:05 +01:00 committed by GitHub
parent 9c4ccd76a0
commit 00d468b964
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 192 additions and 33 deletions

View File

@ -3,6 +3,7 @@ package cliui
import (
"context"
"encoding/json"
"fmt"
"reflect"
"strings"
@ -171,3 +172,23 @@ func (jsonFormat) Format(_ context.Context, data any) (string, error) {
return string(outBytes), nil
}
type textFormat struct{}
var _ OutputFormat = textFormat{}
// TextFormat is a formatter that just outputs unstructured text.
// It uses fmt.Sprintf under the hood.
func TextFormat() OutputFormat {
return textFormat{}
}
func (textFormat) ID() string {
return "text"
}
func (textFormat) AttachOptions(_ *clibase.OptionSet) {}
func (textFormat) Format(_ context.Context, data any) (string, error) {
return fmt.Sprintf("%s", data), nil
}

View File

@ -50,6 +50,9 @@ func Test_OutputFormatter(t *testing.T) {
require.Panics(t, func() {
cliui.NewOutputFormatter(cliui.JSONFormat())
})
require.NotPanics(t, func() {
cliui.NewOutputFormatter(cliui.JSONFormat(), cliui.TextFormat())
})
})
t.Run("NoMissingFormatID", func(t *testing.T) {

View File

@ -82,7 +82,7 @@ func (r *RootCmd) Core() []*clibase.Cmd {
r.templates(),
r.users(),
r.tokens(),
r.version(),
r.version(defaultVersionInfo),
// Workspace Commands
r.configSSH(),
@ -370,36 +370,6 @@ func LoggerFromContext(ctx context.Context) (slog.Logger, bool) {
return l, ok
}
// version prints the coder version
func (*RootCmd) version() *clibase.Cmd {
return &clibase.Cmd{
Use: "version",
Short: "Show coder version",
Handler: func(inv *clibase.Invocation) error {
var str strings.Builder
_, _ = str.WriteString("Coder ")
if buildinfo.IsAGPL() {
_, _ = str.WriteString("(AGPL) ")
}
_, _ = str.WriteString(buildinfo.Version())
buildTime, valid := buildinfo.Time()
if valid {
_, _ = str.WriteString(" " + buildTime.Format(time.UnixDate))
}
_, _ = str.WriteString("\r\n" + buildinfo.ExternalURL() + "\r\n\r\n")
if buildinfo.IsSlim() {
_, _ = str.WriteString(fmt.Sprintf("Slim build of Coder, does not support the %s subcommand.\n", cliui.Styles.Code.Render("server")))
} else {
_, _ = str.WriteString(fmt.Sprintf("Full build of Coder, supports the %s subcommand.\n", cliui.Styles.Code.Render("server")))
}
_, _ = fmt.Fprint(inv.Stdout, str.String())
return nil
},
}
}
func isTest() bool {
return flag.Lookup("test.v") != nil
}

View File

@ -1,6 +1,10 @@
Usage: coder version
Usage: coder version [flags]
Show coder version
Options
-o, --output string (default: text)
Output format. Available formats: text, json.
---
Run `coder --help` for a list of global options.

84
cli/version.go Normal file
View File

@ -0,0 +1,84 @@
package cli
import (
"fmt"
"strings"
"time"
"github.com/coder/coder/buildinfo"
"github.com/coder/coder/cli/clibase"
"github.com/coder/coder/cli/cliui"
)
// versionInfo wraps the stuff we get from buildinfo so that it's
// easier to emit in different formats.
type versionInfo struct {
Version string `json:"version"`
BuildTime time.Time `json:"build_time"`
ExternalURL string `json:"external_url"`
Slim bool `json:"slim"`
AGPL bool `json:"agpl"`
}
// String() implements Stringer
func (vi versionInfo) String() string {
var str strings.Builder
_, _ = str.WriteString("Coder ")
if vi.AGPL {
_, _ = str.WriteString("(AGPL) ")
}
_, _ = str.WriteString(vi.Version)
if !vi.BuildTime.IsZero() {
_, _ = str.WriteString(" " + vi.BuildTime.Format(time.UnixDate))
}
_, _ = str.WriteString("\r\n" + vi.ExternalURL + "\r\n\r\n")
if vi.Slim {
_, _ = str.WriteString(fmt.Sprintf("Slim build of Coder, does not support the %s subcommand.", cliui.Styles.Code.Render("server")))
} else {
_, _ = str.WriteString(fmt.Sprintf("Full build of Coder, supports the %s subcommand.", cliui.Styles.Code.Render("server")))
}
return str.String()
}
func defaultVersionInfo() *versionInfo {
buildTime, _ := buildinfo.Time()
return &versionInfo{
Version: buildinfo.Version(),
BuildTime: buildTime,
ExternalURL: buildinfo.ExternalURL(),
Slim: buildinfo.IsSlim(),
AGPL: buildinfo.IsAGPL(),
}
}
// version prints the coder version
func (*RootCmd) version(versionInfo func() *versionInfo) *clibase.Cmd {
var (
formatter = cliui.NewOutputFormatter(
cliui.TextFormat(),
cliui.JSONFormat(),
)
vi = versionInfo()
)
cmd := &clibase.Cmd{
Use: "version",
Short: "Show coder version",
Options: clibase.OptionSet{},
Handler: func(inv *clibase.Invocation) error {
out, err := formatter.Format(inv.Context(), vi)
if err != nil {
return err
}
_, err = fmt.Fprintln(inv.Stdout, out)
return err
},
}
formatter.AttachOptions(&cmd.Options)
return cmd
}

66
cli/version_test.go Normal file
View File

@ -0,0 +1,66 @@
package cli_test
import (
"bytes"
"context"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/testutil"
)
func TestVersion(t *testing.T) {
t.Parallel()
expectedText := `Coder v0.0.0-devel
https://github.com/coder/coder
Full build of Coder, supports the server subcommand.
`
expectedJSON := `{
"version": "v0.0.0-devel",
"build_time": "0001-01-01T00:00:00Z",
"external_url": "https://github.com/coder/coder",
"slim": false,
"agpl": false
}
`
for _, tt := range []struct {
Name string
Args []string
Expected string
}{
{
Name: "Defaults to human-readable output",
Args: []string{"version"},
Expected: expectedText,
},
{
Name: "JSON output",
Args: []string{"version", "--output=json"},
Expected: expectedJSON,
},
{
Name: "Text output",
Args: []string{"version", "--output=text"},
Expected: expectedText,
},
} {
tt := tt
t.Run(tt.Name, func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
t.Cleanup(cancel)
inv, _ := clitest.New(t, tt.Args...)
buf := new(bytes.Buffer)
inv.Stdout = buf
err := inv.WithContext(ctx).Run()
require.NoError(t, err)
actual := buf.String()
actual = strings.ReplaceAll(actual, "\r\n", "\n")
require.Equal(t, tt.Expected, actual)
})
}
}

View File

@ -7,5 +7,16 @@ Show coder version
## Usage
```console
coder version
coder version [flags]
```
## Options
### -o, --output
| | |
| ------- | ------------------- |
| Type | <code>string</code> |
| Default | <code>text</code> |
Output format. Available formats: text, json.