@@ -5,8 +5,10 @@ import (
55 "fmt"
66 "os"
77 "path/filepath"
8+ "time"
89
910 "github.com/localstack/lstk/internal/api"
11+ "github.com/localstack/lstk/internal/auth"
1012 "github.com/localstack/lstk/internal/config"
1113 "github.com/localstack/lstk/internal/container"
1214 "github.com/localstack/lstk/internal/env"
@@ -17,6 +19,7 @@ import (
1719 "github.com/localstack/lstk/internal/ui"
1820 "github.com/localstack/lstk/internal/version"
1921 "github.com/spf13/cobra"
22+ "github.com/spf13/pflag"
2023)
2124
2225func NewRootCmd (cfg * env.Env , tel * telemetry.Client , logger log.Logger ) * cobra.Command {
@@ -30,7 +33,7 @@ func NewRootCmd(cfg *env.Env, tel *telemetry.Client, logger log.Logger) *cobra.C
3033 if err != nil {
3134 return err
3235 }
33- return runStart (cmd .Context (), rt , cfg , tel , logger )
36+ return runStart (cmd .Context (), cmd . Flags (), rt , cfg , tel , logger )
3437 },
3538 }
3639
@@ -50,13 +53,13 @@ func NewRootCmd(cfg *env.Env, tel *telemetry.Client, logger log.Logger) *cobra.C
5053
5154 root .AddCommand (
5255 newStartCmd (cfg , tel , logger ),
53- newStopCmd (cfg ),
54- newLoginCmd (cfg , logger ),
55- newLogoutCmd (cfg , logger ),
56- newStatusCmd (cfg ),
57- newLogsCmd (cfg ),
58- newConfigCmd (),
59- newUpdateCmd (cfg ),
56+ newStopCmd (cfg , tel ),
57+ newLoginCmd (cfg , tel , logger ),
58+ newLogoutCmd (cfg , tel , logger ),
59+ newStatusCmd (cfg , tel ),
60+ newLogsCmd (cfg , tel ),
61+ newConfigCmd (cfg , tel ),
62+ newUpdateCmd (cfg , tel ),
6063 )
6164
6265 return root
@@ -74,6 +77,16 @@ func Execute(ctx context.Context) error {
7477 defer cleanup ()
7578 logger .Info ("lstk %s starting" , version .Version ())
7679
80+ // Resolve auth token for telemetry: keyring first, then env var.
81+ resolvedToken := cfg .AuthToken
82+ if tokenStorage , err := auth .NewTokenStorage (cfg .ForceFileKeyring , logger ); err == nil {
83+ if token , err := tokenStorage .GetAuthToken (); err == nil && token != "" {
84+ resolvedToken = token
85+ }
86+ }
87+ cfg .AuthToken = resolvedToken
88+ tel .SetAuthToken (resolvedToken )
89+
7790 root := NewRootCmd (cfg , tel , logger )
7891 root .SilenceErrors = true
7992 root .SilenceUsage = true
@@ -87,9 +100,7 @@ func Execute(ctx context.Context) error {
87100 return nil
88101}
89102
90- func runStart (ctx context.Context , rt runtime.Runtime , cfg * env.Env , tel * telemetry.Client , logger log.Logger ) error {
91- // TODO: replace map with a typed payload struct once event schema is finalised
92- tel .Emit (ctx , "cli_cmd" , map [string ]any {"cmd" : "lstk start" , "params" : []string {}})
103+ func startEmulator (ctx context.Context , rt runtime.Runtime , cfg * env.Env , tel * telemetry.Client , logger log.Logger ) error {
93104
94105 appConfig , err := config .Get ()
95106 if err != nil {
@@ -105,6 +116,7 @@ func runStart(ctx context.Context, rt runtime.Runtime, cfg *env.Env, tel *teleme
105116 Containers : appConfig .Containers ,
106117 Env : appConfig .Env ,
107118 Logger : logger ,
119+ Telemetry : tel ,
108120 }
109121
110122 if isInteractiveMode (cfg ) {
@@ -113,6 +125,51 @@ func runStart(ctx context.Context, rt runtime.Runtime, cfg *env.Env, tel *teleme
113125 return container .Start (ctx , rt , output .NewPlainSink (os .Stdout ), opts , false )
114126}
115127
128+ func runStart (ctx context.Context , cmdFlags * pflag.FlagSet , rt runtime.Runtime , cfg * env.Env , tel * telemetry.Client , logger log.Logger ) error {
129+ startTime := time .Now ()
130+
131+ var flags []string
132+ cmdFlags .Visit (func (f * pflag.Flag ) {
133+ flags = append (flags , "--" + f .Name )
134+ })
135+
136+ runErr := startEmulator (ctx , rt , cfg , tel , logger )
137+
138+ exitCode := 0
139+ errorMsg := ""
140+ if runErr != nil {
141+ exitCode = 1
142+ errorMsg = runErr .Error ()
143+ }
144+ tel .EmitCommand (ctx , "start" , flags , time .Since (startTime ).Milliseconds (), exitCode , errorMsg )
145+
146+ return runErr
147+ }
148+
149+ // wraps a RunE function so that an lstk_command event is emitted after every invocation
150+ // used for commands that do not emit lstk_lifecycle events (i.e. status, logs, config path, etc)
151+ func commandWithTelemetry (name string , tel * telemetry.Client , fn func (* cobra.Command , []string ) error ) func (* cobra.Command , []string ) error {
152+ return func (cmd * cobra.Command , args []string ) error {
153+ startTime := time .Now ()
154+ runErr := fn (cmd , args )
155+
156+ var flags []string
157+ cmd .Flags ().Visit (func (f * pflag.Flag ) {
158+ flags = append (flags , "--" + f .Name )
159+ })
160+
161+ exitCode := 0
162+ errorMsg := ""
163+ if runErr != nil {
164+ exitCode = 1
165+ errorMsg = runErr .Error ()
166+ }
167+ tel .EmitCommand (cmd .Context (), name , flags , time .Since (startTime ).Milliseconds (), exitCode , errorMsg )
168+
169+ return runErr
170+ }
171+ }
172+
116173func isInteractiveMode (cfg * env.Env ) bool {
117174 return ! cfg .NonInteractive && ui .IsInteractive ()
118175}
0 commit comments