Skip to content

Commit

Permalink
Introduces --json flag for k6 version sub-command
Browse files Browse the repository at this point in the history
  • Loading branch information
olegbespalov committed Dec 5, 2024
1 parent ec1f601 commit dfe1539
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 13 deletions.
55 changes: 47 additions & 8 deletions cmd/version.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"encoding/json"
"fmt"
"strings"

Expand All @@ -24,16 +25,54 @@ func versionString() string {
return v
}

func getCmdVersion(_ *state.GlobalState) *cobra.Command {
// versionCmd represents the version command.
return &cobra.Command{
type versionCmd struct {
gs *state.GlobalState
isJSON bool
}

func (c *versionCmd) run(cmd *cobra.Command, _ []string) error {
if !c.isJSON {
root := cmd.Root()
root.SetArgs([]string{"--version"})
_ = root.Execute()
return nil
}

details := consts.VersionDetails()
if exts := ext.GetAll(); len(exts) > 0 {
ext := make([]map[string]string, 0, len(exts))
for _, e := range exts {
ext = append(ext, map[string]string{
"name": e.Name,
"type": e.Type.String(),
"version": e.Version,
"path": e.Path,
})
}

details["extensions"] = ext
}

jsonDetails, err := json.Marshal(details)
if err != nil {
return fmt.Errorf("failed produce a JSON version details: %w", err)
}

_, err = fmt.Fprintln(c.gs.Stdout, string(jsonDetails))
return err
}

func getCmdVersion(gs *state.GlobalState) *cobra.Command {
versionCmd := &versionCmd{gs: gs}

cmd := &cobra.Command{
Use: "version",
Short: "Show application version",
Long: `Show the application version and exit.`,
Run: func(cmd *cobra.Command, _ []string) {
root := cmd.Root()
root.SetArgs([]string{"--version"})
_ = root.Execute()
},
RunE: versionCmd.run,
}

cmd.Flags().BoolVar(&versionCmd.isJSON, "json", false, "if set, output version information will be in JSON format")

return cmd
}
82 changes: 82 additions & 0 deletions cmd/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cmd

import (
"encoding/json"
"runtime"
"testing"

"github.com/stretchr/testify/assert"
"go.k6.io/k6/cmd/tests"
"go.k6.io/k6/lib/consts"
)

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

ts := tests.NewGlobalTestState(t)
ts.ExpectedExitCode = 0
ts.CmdArgs = []string{"k6", "--version"}

ExecuteWithGlobalState(ts.GlobalState)

stdout := ts.Stdout.String()
t.Log(stdout)
assert.NotEmpty(t, stdout)

// Check that the version/format string is correct
assert.Contains(t, stdout, "k6 v")
assert.Contains(t, stdout, consts.Version)
assert.Contains(t, stdout, runtime.Version())
assert.Contains(t, stdout, runtime.GOOS)
assert.Contains(t, stdout, runtime.GOARCH)
}

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

ts := tests.NewGlobalTestState(t)
ts.ExpectedExitCode = 0
ts.CmdArgs = []string{"k6", "version"}

ExecuteWithGlobalState(ts.GlobalState)

stdout := ts.Stdout.String()
t.Log(stdout)
assert.NotEmpty(t, stdout)

// Check that the version/format string is correct
assert.Contains(t, stdout, "k6 v")
assert.Contains(t, stdout, consts.Version)
assert.Contains(t, stdout, runtime.Version())
assert.Contains(t, stdout, runtime.GOOS)
assert.Contains(t, stdout, runtime.GOARCH)
}

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

ts := tests.NewGlobalTestState(t)
ts.ExpectedExitCode = 0
ts.CmdArgs = []string{"k6", "version", "--json"}

ExecuteWithGlobalState(ts.GlobalState)

stdout := ts.Stdout.String()
t.Log(stdout)
assert.NotEmpty(t, stdout)

// try to unmarshal the JSON output
var details map[string]interface{}
err := json.Unmarshal([]byte(stdout), &details)
assert.NoError(t, err)

// Check that details are correct
assert.Contains(t, details, "version")
assert.Contains(t, details, "go_version")
assert.Contains(t, details, "go_os")
assert.Contains(t, details, "go_arch")
assert.Equal(t, "v"+consts.Version, details["version"])
assert.Equal(t, runtime.Version(), details["go_version"])
assert.Equal(t, runtime.GOOS, details["go_os"])
assert.Equal(t, runtime.GOARCH, details["go_arch"])
}
50 changes: 45 additions & 5 deletions lib/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,53 @@ import (
// Version contains the current semantic version of k6.
const Version = "0.55.0"

const (
commitKey = "commit"
commitDirtyKey = "commit_dirty"
)

// FullVersion returns the maximally full version and build information for
// the currently running k6 executable.
func FullVersion() string {
goVersionArch := fmt.Sprintf("%s, %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH)
details := VersionDetails()

goVersionArch := fmt.Sprintf("%s, %s/%s", details["go_version"], details["go_os"], details["go_arch"])

k6version := fmt.Sprintf("%s", details["version"])
// for the fallback case when the version is not in the expected format
// cobra adds a "v" prefix to the version
k6version = strings.TrimLeft(k6version, "v")

commit, ok := details[commitKey].(string)
if !ok || commit == "" {
return fmt.Sprintf("%s (%s)", k6version, goVersionArch)
}

isDirty, ok := details[commitDirtyKey].(bool)
if ok && isDirty {
commit += "-dirty"
}

return fmt.Sprintf("%s (commit/%s, %s)", k6version, commit, goVersionArch)
}

// VersionDetails returns the structured details about version
func VersionDetails() map[string]interface{} {
v := Version
if !strings.HasPrefix(v, "v") {
v = "v" + v
}

details := map[string]interface{}{
"version": v,
"go_version": runtime.Version(),
"go_os": runtime.GOOS,
"go_arch": runtime.GOARCH,
}

buildInfo, ok := debug.ReadBuildInfo()
if !ok {
return fmt.Sprintf("%s (%s)", Version, goVersionArch)
return details
}

var (
Expand All @@ -42,14 +81,15 @@ func FullVersion() string {
}

if commit == "" {
return fmt.Sprintf("%s (%s)", Version, goVersionArch)
return details
}

details[commitKey] = commit
if dirty {
commit += "-dirty"
details[commitDirtyKey] = true
}

return fmt.Sprintf("%s (commit/%s, %s)", Version, commit, goVersionArch)
return details
}

// Banner returns the ASCII-art banner with the k6 logo
Expand Down

0 comments on commit dfe1539

Please sign in to comment.