Skip to content
Merged
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
9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
BINARY := rossoctl
PKG := github.com/rossoctl/rossoctl-cli
CMD_PKG := $(PKG)/cmd
BINARY := rossoctl
PKG := github.com/rossoctl/rossoctl-cli
CMD_PKG := $(PKG)/cmd
BUILDINFO_PKG := $(PKG)/internal/buildinfo

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit (optional, non-blocking): BUILDINFO_PKG := is longer than the column BINARY/PKG/CMD_PKG were padded to, so its := no longer lines up with the three above it, and the VERSION/COMMIT/DATE block below still uses the older narrower alignment. Purely cosmetic (make doesn't care). Either widen all of them to match BUILDINFO_PKG, or drop the manual padding entirely.


VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo none)
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)

LDFLAGS := -s -w \
-X '$(CMD_PKG).version=$(VERSION)' \
-X '$(BUILDINFO_PKG).Version=$(VERSION)' \
-X '$(CMD_PKG).commit=$(COMMIT)' \
-X '$(CMD_PKG).date=$(DATE)'

Expand Down
9 changes: 5 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ import (
"github.com/rossoctl/rossoctl-cli/internal/rossoctlclient"
)

// These are set at build time via -ldflags. See the Makefile.
// commit and date are set at build time via -ldflags. See the Makefile.
// The version itself lives in buildinfo.Version, so non-Cobra packages can
// report it too.
var (
version = "dev"
commit = "none"
date = "unknown"
commit = "none"
date = "unknown"
)

// defaultServer is the API endpoint used when --server is not supplied.
Expand Down
21 changes: 20 additions & 1 deletion cmd/version.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cmd

import (
"strings"

"github.com/spf13/cobra"

"github.com/rossoctl/rossoctl-cli/internal/buildinfo"
Expand All @@ -11,12 +13,29 @@ var versionCmd = &cobra.Command{
Short: "Print the version information",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
info := buildinfo.Info{Version: version, Commit: commit, Date: date}
info := buildinfo.Info{Version: buildinfo.Version, Commit: commit, Date: date}
cmd.Println(info.String())
cmd.Println("server version: " + serverVersion(cmd))
return nil
},
}

// serverVersion reports the connected server's version, as returned by
// GET /auth/config. It returns "unknown" whenever the server is unreachable
// or doesn't report a version, so `version` never fails just because the
// server is down.
func serverVersion(cmd *cobra.Command) string {
client, err := newClient(cmd)
if err != nil {
return "unknown"
}
cfg, err := client.GetAuthConfig(cmd.Context())
if err != nil || cfg.Version == nil || strings.TrimSpace(*cfg.Version) == "" {
return "unknown"
}
return *cfg.Version
}

func init() {
rootCmd.AddCommand(versionCmd)
}
55 changes: 55 additions & 0 deletions cmd/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package cmd

import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)

func newVersionAuthConfigServer(t *testing.T, body string) *httptest.Server {
t.Helper()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/v1/auth/config" {
t.Errorf("unexpected path %q", r.URL.Path)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(body))
}))
t.Cleanup(srv.Close)
return srv
}

func TestVersionReportsServerVersion(t *testing.T) {
srv := newVersionAuthConfigServer(t, `{"enabled": true, "version": "v0.8.0-alpha.1"}`)

out, err := execute(t, "--server", srv.URL+"/api/v1/", "version")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !strings.Contains(out, "server version: v0.8.0-alpha.1") {
t.Errorf("output = %q, want it to contain server version", out)
}
}

func TestVersionUnknownWhenServerOmitsVersion(t *testing.T) {
srv := newVersionAuthConfigServer(t, `{"enabled": false}`)

out, err := execute(t, "--server", srv.URL+"/api/v1/", "version")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !strings.Contains(out, "server version: unknown") {
t.Errorf("output = %q, want it to report unknown server version", out)
}
}

func TestVersionUnknownWhenServerUnreachable(t *testing.T) {
out, err := execute(t, "--server", "http://127.0.0.1:1/api/v1/", "version")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !strings.Contains(out, "server version: unknown") {
t.Errorf("output = %q, want it to report unknown server version", out)
}
}
1 change: 1 addition & 0 deletions internal/apiclient/apiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func (e *StatusError) Error() string {
// is distinguishable from "empty string" when rendering.
type AuthConfig struct {
Enabled bool `json:"enabled"`
Version *string `json:"version"`
KeycloakURL *string `json:"keycloak_url"`
Realm *string `json:"realm"`
ClientID *string `json:"client_id"`
Expand Down
5 changes: 5 additions & 0 deletions internal/buildinfo/buildinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ package buildinfo

import "fmt"

// Version is the CLI's build version, set at build time via -ldflags (see the
// Makefile). It lives here, rather than in cmd, so that non-Cobra packages
// such as internal/serve can report it without importing cmd.
var Version = "dev"

// Info describes how the binary was built.
type Info struct {
Version string
Expand Down
5 changes: 4 additions & 1 deletion internal/serve/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import (
"unicode/utf8"

"github.com/rossoctl/rossoctl-cli/internal/agentapi"
"github.com/rossoctl/rossoctl-cli/internal/buildinfo"
"github.com/rossoctl/rossoctl-cli/internal/instances"
)

Expand Down Expand Up @@ -210,6 +211,7 @@ func HealthRoutes() []Route { return append([]Route(nil), healthRoutes...) }
// fields are nullable and omitted while auth is disabled.
type AuthConfig struct {
Enabled bool `json:"enabled"`
Version *string `json:"version,omitempty"`
KeycloakURL *string `json:"keycloak_url,omitempty"`
Realm *string `json:"realm,omitempty"`
ClientID *string `json:"client_id,omitempty"`
Expand Down Expand Up @@ -488,7 +490,8 @@ func readyRoute(opts) http.HandlerFunc {
// answering "disabled" lets it proceed straight to the API.
func authConfigRoute(opts) http.HandlerFunc {
return func(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, http.StatusOK, AuthConfig{Enabled: false})
version := buildinfo.Version
writeJSON(w, http.StatusOK, AuthConfig{Enabled: false, Version: &version})
}
}

Expand Down
7 changes: 6 additions & 1 deletion internal/serve/serve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"slices"
"strings"
"testing"

"github.com/rossoctl/rossoctl-cli/internal/buildinfo"
)

// testNamespaces are the namespaces newTestServer serves, distinct from the
Expand Down Expand Up @@ -60,13 +62,16 @@ func TestAuthConfigReportsDisabled(t *testing.T) {
if err := json.NewDecoder(resp.Body).Decode(&got); err != nil {
t.Fatalf("decode: %v", err)
}
want := map[string]any{"enabled": false}
want := map[string]any{"enabled": false, "version": buildinfo.Version}
if len(got) != len(want) {
t.Fatalf("body = %v, want exactly %v", got, want)
}
if got["enabled"] != false {
t.Errorf("enabled = %v, want false", got["enabled"])
}
if got["version"] != buildinfo.Version {
t.Errorf("version = %v, want %v", got["version"], buildinfo.Version)
}
}

// implementedRoutes are the operations with a real implementation, excluded
Expand Down