Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1dd6f5c
refactor: restructure packages and drop unused code
ravisuhag Apr 18, 2026
6f8fc36
feat(server): add h2c server with health checks and timeouts
ravisuhag Apr 18, 2026
e7e41ef
feat(middleware): add Connect interceptors and HTTP middleware
ravisuhag Apr 18, 2026
12a2c29
feat(app): add service bootstrap
ravisuhag Apr 18, 2026
1f44bcf
feat(cli): add CLI bootstrap and rewrite printer
ravisuhag Apr 18, 2026
15d6c36
refactor(config): upgrade deps and remove stdout printing
ravisuhag Apr 18, 2026
5f97557
fix: upgrade golangci-lint to v2 and Go to 1.24
ravisuhag Apr 18, 2026
e2df2b3
docs: add README, migration guide, and GoDoc examples
ravisuhag Apr 18, 2026
a38dd1b
refactor(cli): replace cli.Execute with cli.Init decorator pattern
ravisuhag Apr 18, 2026
6b941e9
test(cli): add tests for Init, Output, and Prompter
ravisuhag Apr 18, 2026
b3cfd31
test(app): add test for OnStart failure cleanup behavior
ravisuhag Apr 18, 2026
8a66841
feat(cli): add ConfigCommand helper for config init/list
ravisuhag Apr 18, 2026
0f6ef68
deps: upgrade cobra v1.8.1 → v1.10.2, pflag v1.0.5 → v1.0.9
ravisuhag Apr 18, 2026
71c0949
deps: upgrade all direct dependencies to latest
ravisuhag Apr 18, 2026
02cd603
feat(cli): support cobra GroupID and set error prefix
ravisuhag Apr 18, 2026
4e0d8f7
refactor(version): unexport internal functions
ravisuhag Apr 19, 2026
3239333
docs: update migration guide with GroupID, ConfigCommand, dep versions
ravisuhag Apr 19, 2026
77c342a
feat(prompt): add Password method for masked secret input
ravisuhag Apr 19, 2026
eaafbde
feat(cli): add typed error handling, remove IsCommandErr
ravisuhag Apr 19, 2026
f560a14
docs: add error handling examples and migration guide
ravisuhag Apr 19, 2026
71818f7
feat(cli): cache version checks and add TTY-aware table output
ravisuhag Apr 19, 2026
3eb7605
feat(cli): add terminal width and separate stderr for status output
ravisuhag Apr 19, 2026
8e152b6
refactor(cli): add Execute, unexport error types, modernize idioms
ravisuhag Apr 19, 2026
65c5fdd
fix: address review feedback from CodeRabbit
ravisuhag Apr 19, 2026
2eab3de
docs: add language tag to code block and note defaults.Set error
ravisuhag Apr 19, 2026
4837a4f
feat(cli): add IOStreams for centralized I/O and testability
ravisuhag Apr 19, 2026
866c776
refactor(terminal): remove functions superseded by IOStreams
ravisuhag Apr 19, 2026
90a1347
feat(cli): add --json flag with field selection for structured output
ravisuhag Apr 19, 2026
6a1eb85
fix: address review findings across packages
ravisuhag Apr 20, 2026
05a493a
fix(cli): DX improvements and comprehensive integration tests
ravisuhag Apr 20, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ jobs:
with:
go-version-file: "go.mod"
- name: Run linter
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v7
with:
version: v1.60
version: v2.11
23 changes: 10 additions & 13 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
output:
formats:
- format: line-number
version: "2"
formatters:
enable:
- goimports
- gofmt
linters:
enable-all: false
disable-all: true
disable:
- errcheck
enable:
- govet
- goimports
- thelper
- tparallel
- unconvert
- wastedassign
- revive
- unused
- gofmt
- whitespace
- misspell
linters-settings:
revive:
ignore-generated-header: true
severity: warning
severity:
default-severity: error
settings:
revive:
severity: warning
328 changes: 328 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
# Migration Guide

This guide covers migrating from the previous salt version to the new structure.

## Go version

Update `go.mod` to require Go 1.24:

```
go 1.24
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

## Packages removed

| Removed | Replacement |
|---------|-------------|
| `observability/logger` | Use `*slog.Logger` from `log/slog` directly |
| `observability/otelgrpc` | Use `connectrpc.com/otelconnect` |
| `observability/otelhttpclient` | Use `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` |
| `server/mux` | Use `github.com/raystack/salt/server` |
| `db` | Use your preferred DB library (sqlx, pgx, gorm) directly |
| `auth/oidc` | Planned as complete CLI auth solution (#86) |
| `auth/audit` | Planned with standardized schema (#87) |
| `testing/dockertestx` | Use `ory/dockertest/v3` directly |

## Packages moved

| Old | New |
|-----|-----|
| `observability` | `telemetry` |
| `cli/terminator` | `cli/terminal` |
| `cli/prompter` | `cli/prompt` |
| `cli/releaser` | `cli/version` |

## Logger

The custom `logger.Logger` interface and all backends (Zap, Logrus, Slog, Noop) are removed. Use `*slog.Logger` from the Go standard library directly.

```go
// Before
import "github.com/raystack/salt/observability/logger"
l := logger.NewZap()
l := logger.NewLogrus()
l := logger.NewNoop()

// After
import "log/slog"
l := slog.Default()
l := slog.New(slog.NewJSONHandler(os.Stderr, nil))
l := slog.New(slog.DiscardHandler) // noop
```

All salt packages that previously accepted `logger.Logger` now accept `*slog.Logger`.

## Server

The dual-port `server/mux` package is replaced by a single-port `server` package with h2c support.

```go
// Before
import "github.com/raystack/salt/server/mux"
mux.Serve(ctx,
mux.WithHTTPTarget(":8080", httpServer),
mux.WithGRPCTarget(":8081", grpcServer),
)

// After
import "github.com/raystack/salt/server"
srv := server.New(
server.WithAddr(":8080"),
server.WithHandler("/api/", connectHandler),
)
srv.Start(ctx)
```

H2C and health check (`/ping`) are enabled by default. Use `server.WithoutH2C()` or `server.WithHealthCheck("")` to disable.

## App bootstrap

New `app.Run()` for service bootstrap:

```go
import "github.com/raystack/salt/app"

app.Run(
app.WithConfig(&cfg, config.WithFile("config.yaml")),
app.WithLogger(slog.Default()),
app.WithHTTPMiddleware(middleware.DefaultHTTP(slog.Default())),
app.WithHandler("/api/", handler),
app.WithAddr(cfg.Addr),
Comment thread
ravisuhag marked this conversation as resolved.
)
```

HTTP middleware is explicit — use `middleware.DefaultHTTP(logger)` for the standard chain or compose your own. Database connections are managed via `app.WithOnStart` / `app.WithOnStop` hooks.

## CLI bootstrap

`cli.Init()` enhances your root command with standard features and `cli.Execute()` runs it with proper error handling:

```go
// Before
rootCmd := &cobra.Command{Use: "frontier", Short: "identity management"}
mgr := commander.New(rootCmd, commander.WithTopics(topics))
mgr.Init()
rootCmd.AddCommand(serverCmd, configCmd)

cmd, err := rootCmd.ExecuteC()
if err != nil {
if commander.IsCommandErr(err) {
fmt.Println(cmd.UsageString())
}
fmt.Println(err)
os.Exit(1)
}

// After
import "github.com/raystack/salt/cli"

rootCmd := &cobra.Command{Use: "frontier", Short: "identity management"}
rootCmd.PersistentFlags().StringP("host", "h", "", "API host")
rootCmd.AddCommand(serverCmd, configCmd)

cli.Init(rootCmd,
cli.Version("0.1.0", "raystack/frontier"),
cli.Topics(topics...),
)

cli.Execute(rootCmd)
```

Config command helper replaces boilerplate:

```go
// Before (50 lines of config init/list commands)
cmd.AddCommand(configInitCommand())
cmd.AddCommand(configListCommand())

// After (1 line)
rootCmd.AddCommand(cli.ConfigCommand("frontier", &Config{}))
```

Command grouping uses cobra's native GroupID instead of annotations:

```go
// Before
cmd.Annotations = map[string]string{"group": "core"}

// After
rootCmd.AddGroup(&cobra.Group{ID: "manage", Title: "Management:"})
cmd.GroupID = "manage"
```

Access shared output and prompting in commands:

```go
func newListCmd() *cobra.Command {
return &cobra.Command{
Use: "list",
RunE: func(cmd *cobra.Command, args []string) error {
out := cli.Output(cmd)
out.Table(rows)
return nil
},
}
}
```

## Error handling

`commander.IsCommandErr` (string matching) and manual error handling are replaced by `cli.Execute`:

```go
// Before
if err := rootCmd.Execute(); err != nil {
if commander.IsCommandErr(err) {
// show usage
}
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

// After
cli.Execute(rootCmd) // handles all errors with proper exit codes
```

`cli.Execute` uses `ExecuteC` internally and handles all error types:

| Error | Behavior |
|-------|----------|
| `cli.ErrSilent` | Exit 1, no output (command already printed the error) |
| `cli.ErrCancel` | Exit 0, no output (user cancelled) |
| Flag errors | Prints error + failing command's usage, exit 1 |
| Other errors | Prints "Error: \<message\>", exit 1 |

In commands, return sentinel errors to control exit behavior:

```go
// Command already printed a rich error — exit 1, no extra output
out.Error("connection failed: timeout")
return cli.ErrSilent

// User cancelled (ctrl-c, declined prompt) — exit 0
return cli.ErrCancel
```

The following exports are removed — their functionality is now internal to `cli.Execute`:

| Removed | Replacement |
|---------|-------------|
| `cli.HandleError(err)` | `cli.Execute(rootCmd)` handles errors automatically |
| `cli.NewFlagError(err)` | `cli.Init` wraps flag errors automatically via `SetFlagErrorFunc` |
| `cli.FlagError` (type) | Unexported; flag errors are handled internally by `Execute` |
| `commander.IsCommandErr(err)` | Removed; `Execute` detects and handles flag/command errors |

## Printer

Package-level functions replaced by `Output` type:

```go
// Before
printer.Success("done")
printer.Table(os.Stdout, rows)
printer.JSON(data)
spinner := printer.Spin("loading")

// After
out := printer.NewOutput(os.Stdout)
// or inside a command: out := cli.Output(cmd)

out.Success("done")
out.Table(rows)
out.JSON(data)
spinner := out.Spin("loading")
```

Color formatting functions remain as package-level helpers returning styled strings:

```go
printer.Green("text")
printer.Greenf("count: %d", n)
printer.Icon("success") // ✔
printer.Italic("note")
```

## Telemetry

```go
// Before
import "github.com/raystack/salt/observability"
observability.Init(ctx, cfg, logger)

// After
import "github.com/raystack/salt/telemetry"
telemetry.Init(ctx, cfg, slogLogger)
```

## Middleware

New package for ConnectRPC and HTTP middleware:

```go
import "github.com/raystack/salt/middleware"

// Connect interceptors for your handler
interceptors := middleware.Default(slog.Default())
handler := myv1connect.NewServiceHandler(svc, connect.WithInterceptors(interceptors...))

// HTTP middleware
httpMW := middleware.DefaultHTTP(slog.Default())
```

## Config

```go
// Import path for validator changed
// Before: "github.com/go-playground/validator"
// After: "github.com/go-playground/validator/v10"

// If you imported go-defaults directly:
// Before: "github.com/mcuadros/go-defaults"
// After: "github.com/creasty/defaults"
// API change: defaults.SetDefaults(cfg) → defaults.Set(cfg)
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

The config package no longer prints warnings to stdout when a config file is missing.

## Version package

`cli/version` now exports only `CheckForUpdate`. The functions `FetchInfo`, `CompareVersions`, and types `Info`, `Timeout`, `APIFormat` are no longer exported — they were internal implementation details.

## Dependency changes

| Removed (direct) | Replacement |
|-------------------|-------------|
| `go.uber.org/zap` | `log/slog` (stdlib) |
| `sirupsen/logrus` | `log/slog` (stdlib) |
| `AlecAivazis/survey/v2` | `charmbracelet/huh` |
| `olekukonko/tablewriter` | `text/tabwriter` (stdlib) |
| `oklog/run` | Removed with `server/mux` |
| `cli/safeexec` | `exec.LookPath` (stdlib) |
| `pkg/errors` | `fmt.Errorf` with `%w` (stdlib) |
| `mcuadros/go-defaults` | `creasty/defaults` |
| `go-playground/validator` v9 | `go-playground/validator/v10` |
| `jmoiron/sqlx` | Use directly if needed |
| `golang-migrate` | Use directly if needed |
| `ory/dockertest` | Use directly if needed |

| Added | Purpose |
|-------|---------|
| `connectrpc.com/connect` | Middleware interceptors |
| `charmbracelet/huh` | Interactive prompts |
| `creasty/defaults` | Struct default values |

| Upgraded | From → To |
|----------|-----------|
| `spf13/cobra` | v1.8.1 → v1.10.2 |
| `spf13/pflag` | v1.0.5 → v1.0.10 |
| `spf13/viper` | v1.19.0 → v1.21.0 |
| `go-playground/validator` | v9 → v10 |
| `charmbracelet/glamour` | v0.3 → v1.0.0 |
| `muesli/termenv` | v0.11 → v0.16.0 |
| `briandowns/spinner` | v1.18 → v1.23.2 |
| `schollz/progressbar` | v3.8 → v3.19.0 |
| `mattn/go-isatty` | v0.0.19 → v0.0.21 |
| `opentelemetry/otel` | v1.31.0 → v1.43.0 |
| `google.golang.org/grpc` | v1.67.1 → v1.80.0 |
| `stretchr/testify` | v1.9.0 → v1.11.1 |
| `hashicorp/go-version` | v1.3.0 → v1.9.0 |
Loading
Loading