Skip to content

Commit 2f15915

Browse files
committed
feat(version): replace --version with 'version' command to print build information for uc and uncloudd
1 parent 28c88e4 commit 2f15915

8 files changed

Lines changed: 194 additions & 17 deletions

File tree

‎cmd/uncloud/art.txt‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
_ _ ___
2+
| | | |/ __|
3+
| |_| | (__
4+
\__,_|\___|

‎cmd/uncloud/main.go‎

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"os"
99
"strings"
1010

11-
"charm.land/lipgloss/v2"
1211
"github.com/psviderski/uncloud/cmd/uncloud/caddy"
1312
cmdcontext "github.com/psviderski/uncloud/cmd/uncloud/context"
1413
"github.com/psviderski/uncloud/cmd/uncloud/dns"
@@ -19,6 +18,7 @@ import (
1918
"github.com/psviderski/uncloud/cmd/uncloud/wg"
2019
"github.com/psviderski/uncloud/internal/cli"
2120
"github.com/psviderski/uncloud/internal/cli/config"
21+
"github.com/psviderski/uncloud/internal/cli/tui"
2222
"github.com/psviderski/uncloud/internal/fs"
2323
"github.com/psviderski/uncloud/internal/log"
2424
"github.com/psviderski/uncloud/internal/machine"
@@ -39,7 +39,6 @@ func main() {
3939
cmd := &cobra.Command{
4040
Use: "uc",
4141
Short: "A CLI tool for managing Uncloud resources such as machines, services, and volumes.",
42-
Version: version.String(),
4342
SilenceUsage: true,
4443
SilenceErrors: true,
4544
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
@@ -113,15 +112,11 @@ func main() {
113112
defaultHelpFunc(c, args)
114113
// Only show links for the root 'uc' command.
115114
if c.Name() == "uc" {
116-
urlStyle := lipgloss.NewStyle().
117-
Underline(true).
118-
Foreground(lipgloss.Color("12")) // light blue
119-
120115
fmt.Fprintln(c.OutOrStdout())
121116
fmt.Fprintf(c.OutOrStdout(), "Learn more about Uncloud: %s\n",
122-
urlStyle.Render("https://uncloud.run/docs"))
117+
tui.URLStyle.Render(version.DocsURL))
123118
fmt.Fprintf(c.OutOrStdout(), "Join our Discord community: %s\n",
124-
urlStyle.Render("https://uncloud.run/discord"))
119+
tui.URLStyle.Render(version.DiscordURL))
125120
}
126121
})
127122

@@ -151,6 +146,7 @@ func main() {
151146
service.NewScaleCommand("service"),
152147
service.NewStartCommand("service"),
153148
service.NewStopCommand("service"),
149+
NewVersionCommand(),
154150
volume.NewRootCommand(),
155151
wg.NewRootCommand(),
156152
)

‎cmd/uncloud/version.go‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
_ "embed"
6+
"fmt"
7+
"strings"
8+
"text/template"
9+
10+
"github.com/psviderski/uncloud/internal/cli/tui"
11+
"github.com/psviderski/uncloud/internal/version"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
//go:embed art.txt
16+
var asciiArt string
17+
18+
// NewVersionCommand creates a new command to print the version and build information for the binary.
19+
func NewVersionCommand() *cobra.Command {
20+
var output string
21+
22+
cmd := &cobra.Command{
23+
Use: "version",
24+
Short: "Show version and build information.",
25+
Args: cobra.NoArgs,
26+
RunE: func(cmd *cobra.Command, _ []string) error {
27+
info := version.GetInfo()
28+
w := cmd.OutOrStdout()
29+
30+
switch output {
31+
case "":
32+
fmt.Fprint(w, humanVersion(info))
33+
case "json":
34+
s, err := info.JSONString()
35+
if err != nil {
36+
return err
37+
}
38+
fmt.Fprintln(w, s)
39+
default:
40+
s, err := templateVersion(output, info)
41+
if err != nil {
42+
return err
43+
}
44+
fmt.Fprint(w, s)
45+
}
46+
47+
return nil
48+
},
49+
}
50+
51+
cmd.Flags().StringVarP(&output, "output", "o", "",
52+
"Output format: 'json' or a Go template (e.g. '{{.Version}}').\n"+
53+
"Run with '-o json' to discover the field names available to the template.\n"+
54+
"(default is human-readable)")
55+
56+
return cmd
57+
}
58+
59+
func humanVersion(info version.Info) string {
60+
var b strings.Builder
61+
b.WriteString(asciiArt)
62+
b.WriteString("\n")
63+
b.WriteString(fmt.Sprintf("uc: Uncloud CLI tool for deploying apps and managing resources (%s)",
64+
tui.URLStyle.Render(version.WebsiteURL)))
65+
b.WriteString("\n\n")
66+
b.WriteString(info.String())
67+
return b.String()
68+
}
69+
70+
// templateVersion renders the version info using the provided Go template.
71+
func templateVersion(tmpl string, info version.Info) (string, error) {
72+
t, err := template.New("version").Parse(tmpl)
73+
if err != nil {
74+
return "", fmt.Errorf("parse template: %w", err)
75+
}
76+
77+
var buf bytes.Buffer
78+
if err = t.Execute(&buf, info); err != nil {
79+
return "", fmt.Errorf("execute template: %w", err)
80+
}
81+
return buf.String(), nil
82+
}

‎cmd/uncloudd/art.txt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
_ _ _
2+
_ _ _ __ ___| | ___ _ _ __| | __| |
3+
| | | | '_ \ / __| |/ _ \| | | |/ _` |/ _` |
4+
| |_| | | | | (__| | (_) | |_| | (_| | (_| |
5+
\__,_|_| |_|\___|_|\___/ \__,_|\__,_|\__,_|

‎cmd/uncloudd/main.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os/signal"
88
"syscall"
99

10+
"github.com/psviderski/uncloud/internal/cli/tui"
1011
"github.com/psviderski/uncloud/internal/daemon"
1112
"github.com/psviderski/uncloud/internal/log"
1213
"github.com/psviderski/uncloud/internal/machine"
@@ -24,7 +25,7 @@ func main() {
2425
cmd := &cobra.Command{
2526
Use: "uncloudd",
2627
Short: "Uncloud machine daemon.",
27-
Version: version.String(),
28+
Long: "Uncloud machine daemon.\n" + tui.URLStyle.Render(version.WebsiteURL),
2829
SilenceUsage: true,
2930
SilenceErrors: true,
3031
RunE: func(cmd *cobra.Command, args []string) error {
@@ -39,11 +40,11 @@ func main() {
3940
},
4041
}
4142
cmd.PersistentFlags().StringVarP(&dataDir, "data-dir", "d", machine.DefaultDataDir,
42-
"Directory for storing persistent machine state")
43+
"Directory for storing persistent machine state.")
4344
_ = cmd.MarkFlagDirname("data-dir")
4445

45-
// Add dial-stdio subcommand.
4646
cmd.AddCommand(newDialStdioCommand())
47+
cmd.AddCommand(newVersionCommand())
4748

4849
// ctx is canceled when the daemon command is interrupted.
4950
ctx, cancel := context.WithCancel(context.Background())

‎cmd/uncloudd/version.go‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
_ "embed"
6+
"fmt"
7+
"strings"
8+
"text/template"
9+
10+
"github.com/psviderski/uncloud/internal/cli/tui"
11+
"github.com/psviderski/uncloud/internal/version"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
//go:embed art.txt
16+
var asciiArt string
17+
18+
// newVersionCommand creates a new command to print the version and build information for the binary.
19+
func newVersionCommand() *cobra.Command {
20+
var output string
21+
22+
cmd := &cobra.Command{
23+
Use: "version",
24+
Short: "Show version and build information.",
25+
Args: cobra.NoArgs,
26+
RunE: func(cmd *cobra.Command, _ []string) error {
27+
info := version.GetInfo()
28+
w := cmd.OutOrStdout()
29+
30+
switch output {
31+
case "":
32+
fmt.Fprint(w, humanVersion(info))
33+
case "json":
34+
s, err := info.JSONString()
35+
if err != nil {
36+
return err
37+
}
38+
fmt.Fprintln(w, s)
39+
default:
40+
s, err := templateVersion(output, info)
41+
if err != nil {
42+
return err
43+
}
44+
fmt.Fprint(w, s)
45+
}
46+
47+
return nil
48+
},
49+
}
50+
51+
cmd.Flags().StringVarP(&output, "output", "o", "",
52+
"Output format: 'json' or a Go template (e.g., '{{.Version}}').\n"+
53+
"Run with '-o json' to discover the field names available to the template.\n"+
54+
"(default is human-readable)")
55+
56+
return cmd
57+
}
58+
59+
func humanVersion(info version.Info) string {
60+
var b strings.Builder
61+
b.WriteString(asciiArt)
62+
b.WriteString("\n")
63+
b.WriteString(fmt.Sprintf("uncloudd: Uncloud machine daemon (%s)",
64+
tui.URLStyle.Render(version.WebsiteURL)))
65+
b.WriteString("\n\n")
66+
b.WriteString(info.String())
67+
return b.String()
68+
}
69+
70+
// templateVersion renders the version info using the provided Go template.
71+
func templateVersion(tmpl string, info version.Info) (string, error) {
72+
t, err := template.New("version").Parse(tmpl)
73+
if err != nil {
74+
return "", fmt.Errorf("parse template: %w", err)
75+
}
76+
77+
var buf bytes.Buffer
78+
if err = t.Execute(&buf, info); err != nil {
79+
return "", fmt.Errorf("execute template: %w", err)
80+
}
81+
return buf.String(), nil
82+
}

‎internal/cli/tui/style.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ var (
1919
BoldYellow = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Yellow)
2020

2121
NameStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("152"))
22+
URLStyle = lipgloss.NewStyle().Underline(true).Foreground(lipgloss.BrightBlue)
2223
)
2324

2425
// FormatImage renders an image reference with the given style, using a faint colon separator for tagged images.

‎internal/version/version.go‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ import (
99
goversion "github.com/caarlos0/go-version"
1010
)
1111

12-
func String() string {
13-
if version == "" {
14-
return "999.0.0-dev"
15-
}
16-
return version
17-
}
12+
const (
13+
WebsiteURL = "https://uncloud.run"
14+
DocsURL = "https://uncloud.run/docs"
15+
DiscordURL = "https://uncloud.run/discord"
16+
)
1817

1918
// Build-time metadata injected via -ldflags by GoReleaser (see .goreleaser.yaml).
2019
// These are package-level so the same paths can be referenced from any binary's ldflags.
@@ -26,6 +25,13 @@ var (
2625
builtBy string
2726
)
2827

28+
func String() string {
29+
if version == "" {
30+
return "999.0.0-dev"
31+
}
32+
return version
33+
}
34+
2935
// Info holds version and build metadata for an Uncloud binary.
3036
type Info struct {
3137
Version string

0 commit comments

Comments
 (0)