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
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
go-version: "1.25.9"
cache: true

- name: Add Go bin to PATH
Expand All @@ -47,7 +47,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
go-version: "1.25.9"
cache: true

- name: Run unit tests
Expand All @@ -63,7 +63,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
go-version: "1.25.9"
cache: true

- name: Build
Expand All @@ -79,7 +79,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
go-version: "1.25.9"
cache: true

- name: Add Go bin to PATH
Expand Down Expand Up @@ -107,7 +107,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
go-version: "1.25.9"
cache: true

- name: Check go mod tidy
Expand All @@ -129,7 +129,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
go-version: "1.25.9"
cache: true

- name: Add Go bin to PATH
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
go-version: "1.25.9"
cache: true

- name: Install git-cliff
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
go-version: "1.25.9"
cache: true

- name: Add Go bin to PATH
Expand All @@ -55,7 +55,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
go-version: "1.25.9"
cache: false

- name: Install gitleaks
Expand Down
6 changes: 5 additions & 1 deletion cmd/verda/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"errors"
"fmt"
"os"
"strings"
Expand All @@ -11,7 +12,10 @@ import (

func main() {
root, opts := cmd.NewRootCommand(cmdutil.NewStdIOStreams())
if err := root.Execute(); err != nil {
if err := root.Execute(); errors.Is(err, cmd.ErrVersionRequested) {
// --version flag was handled; exit cleanly.
return
} else if err != nil {
// In agent mode, always emit structured JSON errors.
if opts.Agent || cmdutil.IsAgentError(err) {
ae := cmdutil.ClassifyError(err)
Expand Down
36 changes: 35 additions & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,38 @@ verda cost balance
| `verda auth show` | Show active profile and credentials path |
| `verda auth use PROFILE` | Switch active auth profile |

### Credential Resolution Order

Credentials are resolved from multiple sources in order of precedence:

| Priority | Source | Example |
|----------|--------|---------|
| 1 | CLI flags (highest) | `--auth.client-id=xxx` |
| 2 | Config file | `auth.client-id` in `~/.verda/config.yaml` |
| 3 | Environment variables | `VERDA_CLIENT_ID`, `VERDA_CLIENT_SECRET` |
| 4 | Credentials file | `[default]` in `~/.verda/credentials` |

> **Note:** When `--auth.profile` is passed explicitly, the credentials file
> values for that profile override env vars — but CLI flags still win.

### Environment Variables

| Variable | Description |
|----------|-------------|
| `VERDA_CLIENT_ID` | API client ID |
| `VERDA_CLIENT_SECRET` | API client secret |
| `VERDA_PROFILE` | Credentials profile name |
| `VERDA_SHARED_CREDENTIALS_FILE` | Path to credentials file |
| `VERDA_AGENT` | Enable agent mode (`1` or `true`) |
| `VERDA_HOME` | Base directory for config (default `~/.verda`) |

```bash
# Use env vars for CI/CD pipelines
export VERDA_CLIENT_ID=your-client-id
export VERDA_CLIENT_SECRET=your-client-secret
verda vm list
```

## Settings

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

## Shell Completion

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

| Flag | Description |
|------|-------------|
| `--version, -v` | Print version information |
| `--output, -o` | Output format: `table`, `json`, `yaml` (default: table) |
| `--agent` | Agent mode: JSON output, no prompts, structured errors |
| `--debug` | Enable debug output (API request/response details) |
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github/verda-cloud/verda-cli

go 1.25.8
go 1.25.9

require (
charm.land/lipgloss/v2 v2.0.2
Expand Down
47 changes: 45 additions & 2 deletions internal/verda-cli/cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package cmd

import (
"errors"
"fmt"
"runtime/debug"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/verda-cloud/verdagostack/pkg/log"
"github.com/verda-cloud/verdagostack/pkg/tui/bubbletea"
"github.com/verda-cloud/verdagostack/pkg/version"

"github/verda-cloud/verda-cli/internal/verda-cli/cmd/auth"
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/availability"
Expand All @@ -20,7 +25,6 @@ import (
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/startupscript"
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/update"
cmdutil "github/verda-cloud/verda-cli/internal/verda-cli/cmd/util"
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/version"
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/vm"
"github/verda-cloud/verda-cli/internal/verda-cli/cmd/volume"
clioptions "github/verda-cloud/verda-cli/internal/verda-cli/options"
Expand All @@ -31,6 +35,7 @@ import (
// callers (e.g. main) can annotate errors with profile context.
func NewRootCommand(ioStreams cmdutil.IOStreams) (*cobra.Command, *clioptions.Options) {
opts := clioptions.NewOptions()
var showVersion bool

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

// --version / -v flag: print rich version info.
cmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "Print version information")

opts.AddFlags(cmd.PersistentFlags())
_ = viper.BindPFlags(cmd.PersistentFlags())

Expand Down Expand Up @@ -119,7 +134,6 @@ func NewRootCommand(ioStreams cmdutil.IOStreams) (*cobra.Command, *clioptions.Op
completion.NewCmdCompletion(ioStreams),
settings.NewCmdSettings(f, ioStreams),
update.NewCmdUpdate(f, ioStreams),
version.NewCmdVersion(f, ioStreams),
},
},
}
Expand Down Expand Up @@ -148,3 +162,32 @@ func skipCredentialResolution(cmd *cobra.Command) bool {
}
return false
}

// ErrVersionRequested is returned by PersistentPreRunE when --version is set.
// Callers should check for this error and exit 0 instead of printing it.
var ErrVersionRequested = errors.New("version requested")

// versionOutput returns the formatted version string.
func versionOutput() string {
info := version.Get()
sdkVer := depVersion("github.com/verda-cloud/verdacloud-sdk-go")
stackVer := depVersion("github.com/verda-cloud/verdagostack")
return fmt.Sprintf(" Version: %s\n Platform: %s\n SDK: %s\n Verdagostack: %s\n",
info.GitVersion, info.Platform, sdkVer, stackVer)
}

func depVersion(modulePath string) string {
bi, ok := debug.ReadBuildInfo()
if !ok {
return "unknown"
}
for _, dep := range bi.Deps {
if dep.Path == modulePath {
if dep.Replace != nil {
return dep.Replace.Version + " (replaced)"
}
return dep.Version
}
}
return "unknown"
}
4 changes: 2 additions & 2 deletions internal/verda-cli/cmd/update/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

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

## Domain-Specific Logic

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

### Asset Matching
Expand Down
8 changes: 4 additions & 4 deletions internal/verda-cli/cmd/update/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This is a single command (no subcommands).

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

## Usage Examples

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

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

# List available versions (marks current with *)
verda update --list
Expand All @@ -25,7 +25,7 @@ verda update --list

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

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

### Update Flow
1. Resolve target version (latest via API, or from `--version` flag)
1. Resolve target version (latest via API, or from `--target` flag)
2. Compare with current version from `version.Get().GitVersion`
3. Download platform-specific archive asset from GitHub Releases
4. Extract binary from tar.gz (Linux/macOS) or zip (Windows)
Expand Down
12 changes: 9 additions & 3 deletions internal/verda-cli/cmd/update/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const (
func NewCmdUpdate(f cmdutil.Factory, ioStreams cmdutil.IOStreams) *cobra.Command {
var targetVersion string
var listVersions bool
var verify bool

cmd := &cobra.Command{
Use: "update",
Expand All @@ -46,15 +47,15 @@ func NewCmdUpdate(f cmdutil.Factory, ioStreams cmdutil.IOStreams) *cobra.Command
The binary is installed to ~/.verda/bin/ (no sudo required).

Without flags, updates to the latest version.
Use --version to install a specific version (upgrade or downgrade).
Use --target to install a specific version (upgrade or downgrade).
Use --list to show available versions.
`),
Example: cmdutil.Examples(`
# Update to latest
verda update

# Install specific version
verda update --version v1.0.0
verda update --target v1.0.0

# List available versions
verda update --list
Expand All @@ -64,12 +65,17 @@ func NewCmdUpdate(f cmdutil.Factory, ioStreams cmdutil.IOStreams) *cobra.Command
if listVersions {
return runList(cmd.Context(), ioStreams)
}
if verify {
info := version.Get()
return runVerify(ioStreams.Out, ioStreams.ErrOut, f.OutputFormat(), f.HTTPClient(), info.GitVersion, runtime.GOOS, runtime.GOARCH)
}
return runUpdate(cmd.Context(), f, ioStreams, targetVersion)
},
}

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

return cmd
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package version
package update

import (
"context"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package version
package update

import (
"bytes"
Expand Down
22 changes: 0 additions & 22 deletions internal/verda-cli/cmd/version/CLAUDE.md

This file was deleted.

Loading
Loading