Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4daaacb
bug: Fixed config file not generating
XxThunderBlastxX Oct 2, 2026
fb59701
Fixed the config get command to implement all the keys
XxThunderBlastxX Oct 2, 2026
7a40885
fixed error message formatting
XxThunderBlastxX Oct 2, 2026
c7bdb2d
fixed config set command and its validation criteria
XxThunderBlastxX Oct 2, 2026
e32adf5
Added spinner in login command
XxThunderBlastxX Oct 3, 2026
3b152b2
Improved create app command TUI and changed its theme.
XxThunderBlastxX Oct 4, 2026
37fafb9
Changed the TUI layout of app list command
XxThunderBlastxX Oct 4, 2026
f248346
Changed TUI for app delete command and also added a confirmation model
XxThunderBlastxX Oct 4, 2026
5a9c56b
Removed loading animation in app list command for --json flag
XxThunderBlastxX Oct 4, 2026
4aab31d
Improved the init command tui flow and also added env-type selection
XxThunderBlastxX Oct 4, 2026
cbec464
Implemented the error conditions
XxThunderBlastxX Oct 4, 2026
fc46102
Changed TUI for switch env
XxThunderBlastxX Oct 4, 2026
82ccb24
Fixed and changed TUI interface for listing types
XxThunderBlastxX Oct 5, 2026
07efb8d
Improved the gpg keys list command
XxThunderBlastxX Oct 5, 2026
71cdbfa
Added a TUI for generate gpg key command
XxThunderBlastxX Oct 5, 2026
c727417
Removed export command
XxThunderBlastxX Oct 6, 2026
abe8d87
Implemented the logic to refresh access token with refresh token
XxThunderBlastxX Oct 6, 2026
6608a74
Improved logging of pull commmand
XxThunderBlastxX Oct 7, 2026
e472665
Formated log output for run command
XxThunderBlastxX Oct 7, 2026
aa52b6b
Improved gpg sign commmand with TUI interface
XxThunderBlastxX Oct 8, 2026
4aa5a3b
Fixed and improved the service token create command
XxThunderBlastxX Oct 15, 2026
e4f1bc2
Implemented TUI interface for service key list command
XxThunderBlastxX Oct 15, 2026
8ee9f69
Implemented TUI interface for service-token get command
XxThunderBlastxX Oct 15, 2026
1e1673a
Implemented a TUI interface for service token delete command
XxThunderBlastxX Oct 15, 2026
55170ed
Implemented TUI interface for grant app access command
XxThunderBlastxX Oct 16, 2026
e2897af
Fixed local log file and telemetry issue
XxThunderBlastxX Oct 16, 2026
bf2b0a2
Removed saml, oidc, log forwarding commands
XxThunderBlastxX Oct 26, 2026
d8cab8e
Removed create|update|delete sub commands from rotation
XxThunderBlastxX Oct 26, 2026
b1b389a
fix(cli): keep the linker backend URL for E2E
BRAVO68WEB Oct 28, 2026
0d98723
fix(cli): restore export and treat an existing CA as initialized
BRAVO68WEB Oct 28, 2026
89b6ded
fix(cli): accept an already initialized org CA
BRAVO68WEB Oct 28, 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
17 changes: 17 additions & 0 deletions packages/envsync-cli/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,23 @@ internal/
- User config: `~/.envsyncrc.toml` (auth tokens and preferences)
- Backend URL: compile-time injected via ldflags (`BACKEND_URL` make variable). Override in `~/.envsyncrc.toml`. Default local API is typically `http://localhost:4000` (origin only — no path prefix).

## Logging & telemetry

- Log file: `~/.local/envsync/logs/envsync.log` (macOS/Linux) or `~/envsync/logs/envsync.log` (Windows), JSON, rotated (10 MB, 3 backups, 7 days). There is no console core: stdout/stderr belong to command output (`--json`) and the spinner/TUI.
- Logs carry `trace_id`/`span_id` fields and are exported as OTEL logs via otelzap when telemetry is enabled, so local records and telemetry signals correlate.
- Traces: every command action runs under a `cli/<command path>` span (`internal/features/commands/root.go` `observe`); each use case spans its operation via `telemetry.StartSpan`; each service method spans `svc.<domain>.<method>`; HTTP is auto-instrumented with `otelhttp` via `telemetry.NewInstrumentedTransport` (span name `METHOD /path`, one log record per API call with method/path/status/duration).
- Attribute rule: span attributes and log fields carry identifiers only (ids, names, paths) — never secret values, tokens, key material, or request/response bodies.
- Use-case convention (named `err` result required):

```go
ctx, end := telemetry.StartSpan(ctx, "app.create", trace.WithAttributes(attribute.String("app.name", app.Name)))
defer end(&err)
```

`end` records failures on the span (exception + error status), logs them (`logger.FromContext(ctx)`), and ends the span. Logging elsewhere: `logger.FromContext(ctx)`.
- Telemetry config: `ENVSYNC_TELEMETRY_URL` (collector base or concrete signal URL), `ENVSYNC_OTEL_SERVICE`, `ENVSYNC_OTEL_DISABLED=true`; fallback `telemetry_url` in `~/.envsyncrc.toml`. Graceful no-op when unset.
- Failures exit non-zero: `FormatError`/`FormatJSONError` return `formatters.RenderedError` (message already displayed); `main` exits 1 without re-printing. `FormatWarning` stays non-fatal.

## Release

GoReleaser config in `.goreleaser.yml`. Release workflow in `.github/workflows/release.yml`.
Expand Down
114 changes: 38 additions & 76 deletions packages/envsync-cli/cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"errors"
"log"
"os"

Expand All @@ -16,13 +17,11 @@ import (
genpem "github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/features/usecases/gen_pem"
gpgUseCases "github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/features/usecases/gpg_key"
inituc "github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/features/usecases/init"
lfUseCases "github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/features/usecases/log_forwarding"
oidcUseCases "github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/features/usecases/oidc"
"github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/features/usecases/run"
samlUseCases "github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/features/usecases/saml"
serviceTokenUseCases "github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/features/usecases/service_token"
syncUseCase "github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/features/usecases/sync"
"github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/presentation/formatters"
"github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/presentation/tui/factory"
"github.com/EnvSync-Cloud/envsync/packages/envsync-cli/internal/services"
)

Expand All @@ -47,44 +46,44 @@ func main() {
container.AccessHandler,
container.RequestHandler,
container.ServiceTokenHandler,
container.OidcHandler,
container.SamlHandler,
container.RotationHandler,
container.DynamicSecretHandler,
container.LogForwardingHandler,
)

// Build CLI app
app := registry.RegisterCLI()

// Run the application
if err := app.Run(context.Background(), os.Args); err != nil {
var rendered formatters.RenderedError
if errors.As(err, &rendered) {
// The formatter already displayed the failure; exit non-zero
// without printing the message a second time.
os.Exit(1)
}
log.Fatal(err)
}
}

// Container holds the handler dependencies
type Container struct {
AppHandler *handlers.AppHandler
AuthHandler *handlers.AuthHandler
ConfigHandler *handlers.ConfigHandler
EnvironmentHandler *handlers.EnvironmentHandler
SyncHandler *handlers.SyncHandler
ExportHandler *handlers.ExportHandler
InitHandler *handlers.InitHandler
RunHandler *handlers.RunHandler
GenPEMKeyHandler *handlers.GenPEMKeyHandler
GpgKeyHandler *handlers.GpgKeyHandler
CertificateHandler *handlers.CertificateHandler
TeamHandler *handlers.TeamHandler
AccessHandler *handlers.AccessHandler
RequestHandler *handlers.RequestHandler
ServiceTokenHandler *handlers.ServiceTokenHandler
OidcHandler *handlers.OidcHandler
SamlHandler *handlers.SamlHandler
RotationHandler *handlers.RotationHandler
DynamicSecretHandler *handlers.DynamicSecretHandler
LogForwardingHandler *handlers.LogForwardingHandler
AppHandler *handlers.AppHandler
AuthHandler *handlers.AuthHandler
ConfigHandler *handlers.ConfigHandler
EnvironmentHandler *handlers.EnvironmentHandler
SyncHandler *handlers.SyncHandler
ExportHandler *handlers.ExportHandler
InitHandler *handlers.InitHandler
RunHandler *handlers.RunHandler
GenPEMKeyHandler *handlers.GenPEMKeyHandler
GpgKeyHandler *handlers.GpgKeyHandler
CertificateHandler *handlers.CertificateHandler
TeamHandler *handlers.TeamHandler
AccessHandler *handlers.AccessHandler
RequestHandler *handlers.RequestHandler
ServiceTokenHandler *handlers.ServiceTokenHandler
RotationHandler *handlers.RotationHandler
DynamicSecretHandler *handlers.DynamicSecretHandler
}

// buildDependencyContainer creates and wires all handler dependencies
Expand All @@ -98,10 +97,12 @@ func buildDependencyContainer() *Container {
envFormatter := formatters.NewEnvFormatter()
initFormatter := formatters.NewInitFormatter()
syncFormatter := formatters.NewSyncFormatter()
runFormatter := formatters.NewRunFormatter()

// Initialize use cases
createAppUseCase := appUseCases.NewCreateAppUseCase()
deleteAppUseCase := appUseCases.NewDeleteAppUseCase()
tuiFactory := factory.NewAppFactory()
deleteAppUseCase := appUseCases.NewDeleteAppUseCase(tuiFactory)
listAppsUseCase := appUseCases.NewListAppsUseCase()

loginUseCase := authUseCases.NewLoginUseCase()
Expand All @@ -120,7 +121,8 @@ func buildDependencyContainer() *Container {
pushUseCase := syncUseCase.NewPushUseCase()
exportUseCase := exportUseCases.NewExportUseCase()

initUC := inituc.NewInitUseCase()
initFactory := factory.NewInitFactory()
initUC := inituc.NewInitUseCase(initFactory)

injectUseCase := run.NewInjectEnv()
injectSecretUseCase := run.NewInjectSecretUseCase()
Expand Down Expand Up @@ -157,6 +159,7 @@ func buildDependencyContainer() *Container {
deleteAppUseCase,
listAppsUseCase,
appFormatter,
tuiFactory,
)

c.AuthHandler = handlers.NewAuthHandler(
Expand All @@ -178,6 +181,7 @@ func buildDependencyContainer() *Container {
switchEnvironmentUseCase,
deleteEnvironmentUseCase,
envFormatter,
factory.NewEnvFactory(),
)

c.SyncHandler = handlers.NewSyncHandler(
Expand All @@ -201,6 +205,7 @@ func buildDependencyContainer() *Container {
injectSecretUseCase,
fetchAppUseCase,
readConfigUseCase,
runFormatter,
)

c.GenPEMKeyHandler = handlers.NewGenPEMKeyHandler(
Expand All @@ -218,6 +223,7 @@ func buildDependencyContainer() *Container {
gpgDeleteKeyUseCase,
gpgService,
gpgKeyFormatter,
factory.NewGpgKeyFactory(),
)

certFormatter := formatters.NewCertificateFormatter()
Expand All @@ -235,7 +241,7 @@ func buildDependencyContainer() *Container {
)

c.TeamHandler = handlers.NewTeamHandler()
c.AccessHandler = handlers.NewAccessHandler()
c.AccessHandler = handlers.NewAccessHandler(factory.NewAccessFactory())
c.RequestHandler = handlers.NewRequestHandler()

stCreateUseCase := serviceTokenUseCases.NewCreateServiceTokenUseCase()
Expand All @@ -248,58 +254,14 @@ func buildDependencyContainer() *Container {
stListUseCase,
stGetUseCase,
stDeleteUseCase,
listAppsUseCase,
getEnvironmentUseCase,
stFormatter,
)

oidcCreateUseCase := oidcUseCases.NewCreateOidcProviderUseCase()
oidcListUseCase := oidcUseCases.NewListOidcProvidersUseCase()
oidcGetUseCase := oidcUseCases.NewGetOidcProviderUseCase()
oidcUpdateUseCase := oidcUseCases.NewUpdateOidcProviderUseCase()
oidcDeleteUseCase := oidcUseCases.NewDeleteOidcProviderUseCase()
oidcFormatter := formatters.NewOidcFormatter()
c.OidcHandler = handlers.NewOidcHandler(
oidcCreateUseCase,
oidcListUseCase,
oidcGetUseCase,
oidcUpdateUseCase,
oidcDeleteUseCase,
oidcFormatter,
)

samlCreateUseCase := samlUseCases.NewCreateSamlProviderUseCase()
samlListUseCase := samlUseCases.NewListSamlProvidersUseCase()
samlGetUseCase := samlUseCases.NewGetSamlProviderUseCase()
samlUpdateUseCase := samlUseCases.NewUpdateSamlProviderUseCase()
samlDeleteUseCase := samlUseCases.NewDeleteSamlProviderUseCase()
samlMetadataUseCase := samlUseCases.NewGetSamlMetadataUseCase()
samlSsoUseCase := samlUseCases.NewInitiateSamlSsoUseCase()
samlFormatter := formatters.NewSamlFormatter()
c.SamlHandler = handlers.NewSamlHandler(
samlCreateUseCase,
samlListUseCase,
samlGetUseCase,
samlUpdateUseCase,
samlDeleteUseCase,
samlMetadataUseCase,
samlSsoUseCase,
samlFormatter,
factory.NewServiceTokenFactory(),
)

c.RotationHandler = handlers.NewRotationHandler()
c.DynamicSecretHandler = handlers.NewDynamicSecretHandler()

lfCreateUseCase := lfUseCases.NewCreateLogForwardingConfigUseCase()
lfListUseCase := lfUseCases.NewListLogForwardingConfigsUseCase()
lfGetUseCase := lfUseCases.NewGetLogForwardingConfigUseCase()
lfDeleteUseCase := lfUseCases.NewDeleteLogForwardingConfigUseCase()
lfFormatter := formatters.NewLogForwardingFormatter()
c.LogForwardingHandler = handlers.NewLogForwardingHandler(
lfCreateUseCase,
lfListUseCase,
lfGetUseCase,
lfDeleteUseCase,
lfFormatter,
)

return c
}
92 changes: 47 additions & 45 deletions packages/envsync-cli/go.mod
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
module github.com/EnvSync-Cloud/envsync/packages/envsync-cli

go 1.25.0
go 1.26.0

require (
github.com/BurntSushi/toml v1.5.0
github.com/EnvSync-Cloud/envsync/sdks/envsync-go-sdk v0.0.0-00010101000000-000000000000
github.com/aymanbagabas/go-pty v0.2.2
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.5
github.com/charmbracelet/huh v0.7.0
github.com/BurntSushi/toml v1.6.0
github.com/EnvSync-Cloud/envsync/sdks/envsync-go-sdk v0.0.0-20260926232912-24fa54a7c668
github.com/aymanbagabas/go-pty v0.2.3
github.com/charmbracelet/bubbles v1.0.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/huh v1.0.0
github.com/charmbracelet/lipgloss v1.1.0
github.com/joho/godotenv v1.5.1
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/savioxavier/termlink v1.4.3
github.com/urfave/cli/v3 v3.3.8
go.opentelemetry.io/contrib/bridges/otelzap v0.15.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
go.opentelemetry.io/otel v1.45.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0
go.opentelemetry.io/otel/sdk v1.45.0
go.opentelemetry.io/otel/sdk/log v0.19.0
go.opentelemetry.io/otel/trace v1.45.0
go.uber.org/zap v1.27.1
github.com/urfave/cli/v3 v3.14.0
go.opentelemetry.io/contrib/bridges/otelzap v0.21.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0
go.opentelemetry.io/otel v1.47.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.23.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0
go.opentelemetry.io/otel/sdk v1.47.0
go.opentelemetry.io/otel/sdk/log v1.47.0
go.opentelemetry.io/otel/trace v1.47.0
go.uber.org/zap v1.28.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
resty.dev/v3 v3.0.0-beta.3
resty.dev/v3 v3.0.0-rc.4
)

replace github.com/EnvSync-Cloud/envsync/sdks/envsync-go-sdk => ../../sdks/envsync-go-sdk
Expand All @@ -35,44 +35,46 @@ require (
github.com/catppuccin/go v0.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/colorprofile v0.3.1 // indirect
github.com/charmbracelet/x/ansi v0.9.3 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20250629123816-066ae234febc // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
github.com/charmbracelet/x/ansi v0.11.8 // indirect
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
github.com/charmbracelet/x/exp/strings v0.1.0 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/creack/pty v1.1.24 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dustin/go-humanize v1.1.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/felixge/httpsnoop v1.1.0 // indirect
github.com/go-logr/logr v1.4.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0 // indirect
github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
github.com/mattn/go-isatty v0.0.24 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-runewidth v0.0.30 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/u-root/u-root v0.14.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/sahilm/fuzzy v0.1.3 // indirect
github.com/u-root/u-root v0.16.0 // indirect
github.com/xo/terminfo v1.2.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 // indirect
go.opentelemetry.io/otel/log v0.19.0 // indirect
go.opentelemetry.io/otel/metric v1.45.0 // indirect
go.opentelemetry.io/proto/otlp v1.11.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 // indirect
go.opentelemetry.io/otel/log v1.47.0 // indirect
go.opentelemetry.io/otel/metric v1.47.0 // indirect
go.opentelemetry.io/proto/otlp v1.11.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.41.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d // indirect
google.golang.org/grpc v1.83.2 // indirect
google.golang.org/protobuf v1.36.11 // indirect
golang.org/x/crypto v0.57.0 // indirect
golang.org/x/net v0.59.0 // indirect
golang.org/x/oauth2 v0.37.0 // indirect
golang.org/x/sys v0.48.0 // indirect
golang.org/x/text v0.42.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc // indirect
google.golang.org/grpc v1.84.0 // indirect
google.golang.org/protobuf v1.36.12 // indirect
)
Loading
Loading