Skip to content

Commit 2fbf4a0

Browse files
authored
Fix/refactor version sub command to --version/-v pararms (#23)
* update sub command for user * refactor(update): move verify logic from version to update package * feat(update): add --verify flag to verify binary checksum * feat(cli): add --version/-v flag to root command, remove version subcommand * refactor(cli): delete version subcommand package * docs: update commands.md for --version flag and update --verify * fix(cli): resolve --version flag conflict with update --version - Use manual PersistentFlag instead of cobra's cmd.Version to avoid flag type conflict - Rename update command's --version flag to --target - Add ErrVersionRequested sentinel for clean exit handling - Remove stale "version" from hidden flags list
1 parent 6852d07 commit 2fbf4a0

16 files changed

Lines changed: 113 additions & 142 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
- name: Set up Go
2626
uses: actions/setup-go@v6
2727
with:
28-
go-version: "1.25"
28+
go-version: "1.25.9"
2929
cache: true
3030

3131
- name: Add Go bin to PATH
@@ -47,7 +47,7 @@ jobs:
4747
- name: Set up Go
4848
uses: actions/setup-go@v6
4949
with:
50-
go-version: "1.25"
50+
go-version: "1.25.9"
5151
cache: true
5252

5353
- name: Run unit tests
@@ -63,7 +63,7 @@ jobs:
6363
- name: Set up Go
6464
uses: actions/setup-go@v6
6565
with:
66-
go-version: "1.25"
66+
go-version: "1.25.9"
6767
cache: true
6868

6969
- name: Build
@@ -79,7 +79,7 @@ jobs:
7979
- name: Set up Go
8080
uses: actions/setup-go@v6
8181
with:
82-
go-version: "1.25"
82+
go-version: "1.25.9"
8383
cache: true
8484

8585
- name: Add Go bin to PATH
@@ -107,7 +107,7 @@ jobs:
107107
- name: Set up Go
108108
uses: actions/setup-go@v6
109109
with:
110-
go-version: "1.25"
110+
go-version: "1.25.9"
111111
cache: true
112112

113113
- name: Check go mod tidy
@@ -129,7 +129,7 @@ jobs:
129129
- name: Set up Go
130130
uses: actions/setup-go@v6
131131
with:
132-
go-version: "1.25"
132+
go-version: "1.25.9"
133133
cache: true
134134

135135
- name: Add Go bin to PATH

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- name: Set up Go
3535
uses: actions/setup-go@v6
3636
with:
37-
go-version: "1.25"
37+
go-version: "1.25.9"
3838
cache: true
3939

4040
- name: Install git-cliff

.github/workflows/security.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
- name: Set up Go
3232
uses: actions/setup-go@v6
3333
with:
34-
go-version: "1.25"
34+
go-version: "1.25.9"
3535
cache: true
3636

3737
- name: Add Go bin to PATH
@@ -55,7 +55,7 @@ jobs:
5555
- name: Set up Go
5656
uses: actions/setup-go@v6
5757
with:
58-
go-version: "1.25"
58+
go-version: "1.25.9"
5959
cache: false
6060

6161
- name: Install gitleaks

cmd/verda/main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
67
"strings"
@@ -11,7 +12,10 @@ import (
1112

1213
func main() {
1314
root, opts := cmd.NewRootCommand(cmdutil.NewStdIOStreams())
14-
if err := root.Execute(); err != nil {
15+
if err := root.Execute(); errors.Is(err, cmd.ErrVersionRequested) {
16+
// --version flag was handled; exit cleanly.
17+
return
18+
} else if err != nil {
1519
// In agent mode, always emit structured JSON errors.
1620
if opts.Agent || cmdutil.IsAgentError(err) {
1721
ae := cmdutil.ClassifyError(err)

docs/commands.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,38 @@ verda cost balance
8888
| `verda auth show` | Show active profile and credentials path |
8989
| `verda auth use PROFILE` | Switch active auth profile |
9090

91+
### Credential Resolution Order
92+
93+
Credentials are resolved from multiple sources in order of precedence:
94+
95+
| Priority | Source | Example |
96+
|----------|--------|---------|
97+
| 1 | CLI flags (highest) | `--auth.client-id=xxx` |
98+
| 2 | Config file | `auth.client-id` in `~/.verda/config.yaml` |
99+
| 3 | Environment variables | `VERDA_CLIENT_ID`, `VERDA_CLIENT_SECRET` |
100+
| 4 | Credentials file | `[default]` in `~/.verda/credentials` |
101+
102+
> **Note:** When `--auth.profile` is passed explicitly, the credentials file
103+
> values for that profile override env vars — but CLI flags still win.
104+
105+
### Environment Variables
106+
107+
| Variable | Description |
108+
|----------|-------------|
109+
| `VERDA_CLIENT_ID` | API client ID |
110+
| `VERDA_CLIENT_SECRET` | API client secret |
111+
| `VERDA_PROFILE` | Credentials profile name |
112+
| `VERDA_SHARED_CREDENTIALS_FILE` | Path to credentials file |
113+
| `VERDA_AGENT` | Enable agent mode (`1` or `true`) |
114+
| `VERDA_HOME` | Base directory for config (default `~/.verda`) |
115+
116+
```bash
117+
# Use env vars for CI/CD pipelines
118+
export VERDA_CLIENT_ID=your-client-id
119+
export VERDA_CLIENT_SECRET=your-client-secret
120+
verda vm list
121+
```
122+
91123
## Settings
92124

93125
| Command | Description |
@@ -102,8 +134,9 @@ Available themes: `default`, `dracula`, `catppuccin`, `catppuccin-latte`, `nord`
102134
| Command | Description |
103135
|---------|-------------|
104136
| `verda update` | Update to the latest version |
105-
| `verda update --version v1.0.0` | Install a specific version (upgrade or downgrade) |
137+
| `verda update --target v1.0.0` | Install a specific version (upgrade or downgrade) |
106138
| `verda update --list` | List available versions |
139+
| `verda update --verify` | Verify the binary checksum against the GitHub release |
107140

108141
## Shell Completion
109142

@@ -122,6 +155,7 @@ verda completion fish | source
122155

123156
| Flag | Description |
124157
|------|-------------|
158+
| `--version, -v` | Print version information |
125159
| `--output, -o` | Output format: `table`, `json`, `yaml` (default: table) |
126160
| `--agent` | Agent mode: JSON output, no prompts, structured errors |
127161
| `--debug` | Enable debug output (API request/response details) |

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github/verda-cloud/verda-cli
22

3-
go 1.25.8
3+
go 1.25.9
44

55
require (
66
charm.land/lipgloss/v2 v2.0.2

internal/verda-cli/cmd/cmd.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package cmd
22

33
import (
4+
"errors"
5+
"fmt"
6+
"runtime/debug"
7+
48
"github.com/spf13/cobra"
59
"github.com/spf13/viper"
610
"github.com/verda-cloud/verdagostack/pkg/log"
711
"github.com/verda-cloud/verdagostack/pkg/tui/bubbletea"
12+
"github.com/verda-cloud/verdagostack/pkg/version"
813

914
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/auth"
1015
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/availability"
@@ -20,7 +25,6 @@ import (
2025
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/startupscript"
2126
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/update"
2227
cmdutil "github/verda-cloud/verda-cli/internal/verda-cli/cmd/util"
23-
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/version"
2428
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/vm"
2529
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/volume"
2630
clioptions "github/verda-cloud/verda-cli/internal/verda-cli/options"
@@ -31,6 +35,7 @@ import (
3135
// callers (e.g. main) can annotate errors with profile context.
3236
func NewRootCommand(ioStreams cmdutil.IOStreams) (*cobra.Command, *clioptions.Options) {
3337
opts := clioptions.NewOptions()
38+
var showVersion bool
3439

3540
cmd := &cobra.Command{
3641
Use: "verda",
@@ -39,6 +44,13 @@ func NewRootCommand(ioStreams cmdutil.IOStreams) (*cobra.Command, *clioptions.Op
3944
Command-line interface for Verda Cloud.`),
4045
SilenceUsage: true,
4146
SilenceErrors: true,
47+
RunE: func(cmd *cobra.Command, args []string) error {
48+
if showVersion {
49+
_, _ = fmt.Fprint(cmd.OutOrStdout(), versionOutput())
50+
return ErrVersionRequested
51+
}
52+
return cmd.Help()
53+
},
4254
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
4355
// Skip heavy credential resolution for commands that don't need it:
4456
// - mcp serve: defers auth to the first tool call
@@ -61,6 +73,9 @@ func NewRootCommand(ioStreams cmdutil.IOStreams) (*cobra.Command, *clioptions.Op
6173
},
6274
}
6375

76+
// --version / -v flag: print rich version info.
77+
cmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "Print version information")
78+
6479
opts.AddFlags(cmd.PersistentFlags())
6580
_ = viper.BindPFlags(cmd.PersistentFlags())
6681

@@ -119,7 +134,6 @@ func NewRootCommand(ioStreams cmdutil.IOStreams) (*cobra.Command, *clioptions.Op
119134
completion.NewCmdCompletion(ioStreams),
120135
settings.NewCmdSettings(f, ioStreams),
121136
update.NewCmdUpdate(f, ioStreams),
122-
version.NewCmdVersion(f, ioStreams),
123137
},
124138
},
125139
}
@@ -148,3 +162,32 @@ func skipCredentialResolution(cmd *cobra.Command) bool {
148162
}
149163
return false
150164
}
165+
166+
// ErrVersionRequested is returned by PersistentPreRunE when --version is set.
167+
// Callers should check for this error and exit 0 instead of printing it.
168+
var ErrVersionRequested = errors.New("version requested")
169+
170+
// versionOutput returns the formatted version string.
171+
func versionOutput() string {
172+
info := version.Get()
173+
sdkVer := depVersion("github.com/verda-cloud/verdacloud-sdk-go")
174+
stackVer := depVersion("github.com/verda-cloud/verdagostack")
175+
return fmt.Sprintf(" Version: %s\n Platform: %s\n SDK: %s\n Verdagostack: %s\n",
176+
info.GitVersion, info.Platform, sdkVer, stackVer)
177+
}
178+
179+
func depVersion(modulePath string) string {
180+
bi, ok := debug.ReadBuildInfo()
181+
if !ok {
182+
return "unknown"
183+
}
184+
for _, dep := range bi.Deps {
185+
if dep.Path == modulePath {
186+
if dep.Replace != nil {
187+
return dep.Replace.Version + " (replaced)"
188+
}
189+
return dep.Version
190+
}
191+
}
192+
return "unknown"
193+
}

internal/verda-cli/cmd/update/CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
## Quick Reference
44
- Parent: `verda update` (no aliases)
5-
- Subcommands: none (single command with `--list` and `--version` flags)
5+
- Subcommands: none (single command with `--list` and `--target` flags)
66
- Files:
77
- `update.go` -- All logic: command def, GitHub API, archive extraction, binary replacement
88

99
## Domain-Specific Logic
1010

1111
### Version Resolution
1212
- Current version from `version.Get().GitVersion` (from `verdagostack/pkg/version`)
13-
- Auto-prepends `v` prefix if missing from `--version` flag
13+
- Auto-prepends `v` prefix if missing from `--target` flag
1414
- Skips update if target == current
1515

1616
### Asset Matching

internal/verda-cli/cmd/update/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This is a single command (no subcommands).
66

77
| Command | Description | Key Flags |
88
|---------|-------------|-----------|
9-
| `verda update` | Update CLI binary in-place from GitHub Releases | `--version`, `--list` |
9+
| `verda update` | Update CLI binary in-place from GitHub Releases | `--target`, `--list` |
1010

1111
## Usage Examples
1212

@@ -15,7 +15,7 @@ This is a single command (no subcommands).
1515
verda update
1616

1717
# Install a specific version (upgrade or downgrade)
18-
verda update --version v1.0.0
18+
verda update --target v1.0.0
1919

2020
# List available versions (marks current with *)
2121
verda update --list
@@ -25,7 +25,7 @@ verda update --list
2525

2626
This command is entirely non-interactive. No prompts are used. Behavior is controlled by flags:
2727
- No flags: fetches and installs the latest release.
28-
- `--version <tag>`: installs the specified version. Accepts with or without `v` prefix.
28+
- `--target <tag>`: installs the specified version. Accepts with or without `v` prefix.
2929
- `--list`: prints up to 20 available versions and exits. The current version is marked with `*`.
3030

3131
If already at the target version, it prints "Already at vX.Y.Z" and exits.
@@ -35,7 +35,7 @@ If already at the target version, it prints "Already at vX.Y.Z" and exits.
3535
- **update.go** -- Single file containing all logic: command definition, GitHub API interaction, archive extraction, and binary replacement.
3636

3737
### Update Flow
38-
1. Resolve target version (latest via API, or from `--version` flag)
38+
1. Resolve target version (latest via API, or from `--target` flag)
3939
2. Compare with current version from `version.Get().GitVersion`
4040
3. Download platform-specific archive asset from GitHub Releases
4141
4. Extract binary from tar.gz (Linux/macOS) or zip (Windows)

internal/verda-cli/cmd/update/update.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const (
3535
func NewCmdUpdate(f cmdutil.Factory, ioStreams cmdutil.IOStreams) *cobra.Command {
3636
var targetVersion string
3737
var listVersions bool
38+
var verify bool
3839

3940
cmd := &cobra.Command{
4041
Use: "update",
@@ -46,15 +47,15 @@ func NewCmdUpdate(f cmdutil.Factory, ioStreams cmdutil.IOStreams) *cobra.Command
4647
The binary is installed to ~/.verda/bin/ (no sudo required).
4748
4849
Without flags, updates to the latest version.
49-
Use --version to install a specific version (upgrade or downgrade).
50+
Use --target to install a specific version (upgrade or downgrade).
5051
Use --list to show available versions.
5152
`),
5253
Example: cmdutil.Examples(`
5354
# Update to latest
5455
verda update
5556
5657
# Install specific version
57-
verda update --version v1.0.0
58+
verda update --target v1.0.0
5859
5960
# List available versions
6061
verda update --list
@@ -64,12 +65,17 @@ func NewCmdUpdate(f cmdutil.Factory, ioStreams cmdutil.IOStreams) *cobra.Command
6465
if listVersions {
6566
return runList(cmd.Context(), ioStreams)
6667
}
68+
if verify {
69+
info := version.Get()
70+
return runVerify(ioStreams.Out, ioStreams.ErrOut, f.OutputFormat(), f.HTTPClient(), info.GitVersion, runtime.GOOS, runtime.GOARCH)
71+
}
6772
return runUpdate(cmd.Context(), f, ioStreams, targetVersion)
6873
},
6974
}
7075

71-
cmd.Flags().StringVar(&targetVersion, "version", "", "Version to install (e.g. v1.0.0)")
76+
cmd.Flags().StringVar(&targetVersion, "target", "", "Version to install (e.g. v1.0.0)")
7277
cmd.Flags().BoolVar(&listVersions, "list", false, "List available versions")
78+
cmd.Flags().BoolVar(&verify, "verify", false, "Verify the binary checksum against the GitHub release")
7379

7480
return cmd
7581
}

0 commit comments

Comments
 (0)