From df63b5eccc924fd3daf9799c5db51b0f99c8c15d Mon Sep 17 00:00:00 2001 From: Ray Ozzie Date: Thu, 23 Oct 2025 16:10:56 -0700 Subject: [PATCH] fix: regression for unauthenticated argument handling A core function of the notehub CLI is to do things like find out what version of the utility you're running, or setting or clearing out of the "current notehub". Since adding the browser-based functionality, the CLI couldn't do these functions without validating credentials: notehub -version notehub -hub - notehub -hub ray.blues.tools This PR fixes the regression. --- go.mod | 1 + go.sum | 4 + lib/config.go | 14 +++- notehub/main.go | 216 ++++++++++++++++++++++++++++-------------------- 4 files changed, 144 insertions(+), 91 deletions(-) diff --git a/go.mod b/go.mod index fc3e96b..103df33 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/shirou/gopsutil/v3 v3.24.4 // indirect + github.com/shoenig/go-m1cpu v0.1.7 // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect go.bug.st/serial v1.6.2 golang.org/x/sys v0.20.0 // indirect diff --git a/go.sum b/go.sum index 8d970de..06c2e8e 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,12 @@ github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRB github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/go-m1cpu v0.1.7 h1:C76Yd0ObKR82W4vhfjZiCp0HxcSZ8Nqd84v+HZ0qyI0= +github.com/shoenig/go-m1cpu v0.1.7/go.mod h1:KkDOw6m3ZJQAPHbrzkZki4hnx+pDRR1Lo+ldA56wD5w= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= +github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/lib/config.go b/lib/config.go index 354079d..853d2ff 100644 --- a/lib/config.go +++ b/lib/config.go @@ -251,6 +251,12 @@ func GetConfig() (*ConfigSettings, error) { config = defaultConfig() } } + if config.IPort == nil { + config.IPort = make(map[string]ConfigPort) + } + if config.HubCreds == nil { + config.HubCreds = make(map[string]ConfigCreds) + } return config, nil } @@ -278,6 +284,12 @@ func ConfigRead() error { return fmt.Errorf("can't parse %s: %s", configPath, err) } config = &newConfig + if config.IPort == nil { + config.IPort = make(map[string]ConfigPort) + } + if config.HubCreds == nil { + config.HubCreds = make(map[string]ConfigCreds) + } return nil } @@ -378,8 +390,6 @@ func FlagParse(notecardFlags bool, notehubFlags bool) (err error) { if err := config.Write(); err != nil { return fmt.Errorf("could not write config file: %w", err) } - fmt.Printf("configuration file saved\n\n") - config.Print() } // Override, just for this session, with env vars diff --git a/notehub/main.go b/notehub/main.go index a53813d..9ef6101 100644 --- a/notehub/main.go +++ b/notehub/main.go @@ -100,6 +100,16 @@ func getFlagGroups() []lib.FlagGroup { } } +// withCreds validates credentials and then calls the provided function +func withCreds(credentials *lib.ConfigCreds, fn func() error) error { + if err := credentials.Validate(); err != nil { + config, _ := lib.GetConfig() + fmt.Printf("invalid credentials for %s: %s\n", config.Hub, err) + return fmt.Errorf("please use 'notehub -signin' or 'notehub -signin-token' to sign into Notehub") + } + return fn() +} + // Main entry point func main() { @@ -162,14 +172,14 @@ func main() { // Parse these flags and also the note tool config flags err := lib.FlagParse(false, true) if err != nil { - fmt.Printf("%s\n", err) + fmt.Printf("flags: %s\n", err) os.Exit(exitFail) } // after flags are parsed, get the resulting configuration config, err := lib.GetConfig() if err != nil { - fmt.Printf("%s\n", err) + fmt.Printf("config: %s\n", err) os.Exit(exitFail) } @@ -184,7 +194,7 @@ func main() { if flagSignIn { err = authSignIn() if err != nil { - fmt.Printf("%s\n", err) + fmt.Printf("sign-in: %s\n", err) os.Exit(exitFail) } } @@ -193,7 +203,7 @@ func main() { if flagSignInToken != "" { err = authSignInToken(flagSignInToken) if err != nil { - fmt.Printf("%s\n", err) + fmt.Printf("sign-in-token: %s\n", err) os.Exit(exitFail) } } @@ -204,7 +214,7 @@ func main() { // Process the sign-out if flagSignOut { if err := config.RemoveDefaultCredentials(); err != nil { - fmt.Printf("%s\n", err) + fmt.Printf("sign-out: %s\n", err) os.Exit(exitFail) } os.Exit(exitOk) @@ -222,11 +232,6 @@ func main() { } // Past this point, we need valid credentials, so validate them here - if err := credentials.Validate(); err != nil { - fmt.Printf("invalid credentials for %s: %s\n\n", config.Hub, err) - fmt.Printf("please use 'notehub -signin' or 'notehub -signin-token' to sign into Notehub\n") - os.Exit(exitFail) - } // See if we did something didSomething := false @@ -261,10 +266,19 @@ func main() { } // Process requests + if err == nil && flagVersion { + didSomething = true + fmt.Printf("Notehub CLI Version: %s\n", version) + } + if flagReq != "" || flagUpload != "" { - var rsp []byte - rsp, err = reqHubV0JSON(flagVerbose, lib.ConfigAPIHub(), []byte(flagReq), flagUpload, flagType, flagTags, flagNotes, flagOverwrite, flagJson, nil) - if err == nil { + didSomething = true + err = withCreds(credentials, func() (err error) { + var rsp []byte + rsp, err = reqHubV0JSON(flagVerbose, lib.ConfigAPIHub(), []byte(flagReq), flagUpload, flagType, flagTags, flagNotes, flagOverwrite, flagJson, nil) + if err != nil { + return err + } if flagOut == "" { if flagPretty { var rspo map[string]interface{} @@ -279,103 +293,115 @@ func main() { fmt.Printf("%s", rsp) } } else { - outfile, err2 := os.Create(flagOut) - if err2 != nil { - fmt.Printf("Can't create output file: %s\n", err) - os.Exit(exitFail) + var outfile *os.File + outfile, err = os.Create(flagOut) + if err != nil { + return err } outfile.Write(rsp) outfile.Close() } - didSomething = true - } + return nil + }) } // Explore the contents of the device if err == nil && flagExplore && flagScope == "" { - err = explore(flagReserved, flagVerbose, flagPretty) didSomething = true + err = withCreds(credentials, func() error { + return explore(flagReserved, flagVerbose, flagPretty) + }) } // Enter trace mode if err == nil && flagTrace { - err = trace() - didSomething = true - } - - if err == nil && flagVersion { - fmt.Printf("Notehub CLI Version: %s\n", version) didSomething = true + err = withCreds(credentials, func() error { + return trace() + }) } // Determine the scope of a later request var scopeDevices, scopeFleets []string var appMetadata AppMetadata if err == nil && flagScope != "" { - appMetadata, scopeDevices, scopeFleets, err = appGetScope(flagScope, flagVerbose) didSomething = true - if err == nil { - if len(scopeDevices) != 0 && len(scopeFleets) != 0 { - err = fmt.Errorf("'from' scope may include devices or fleets but not both") - fmt.Printf("%d devices and %d fleets\n%v\n%v\n", len(scopeDevices), len(scopeFleets), scopeDevices, scopeFleets) - } - if len(scopeDevices) == 0 && len(scopeFleets) == 0 { - err = fmt.Errorf("no devices or fleets found within the specified scope") + err = withCreds(credentials, func() (err error) { + appMetadata, scopeDevices, scopeFleets, err = appGetScope(flagScope, flagVerbose) + if err == nil { + if len(scopeDevices) != 0 && len(scopeFleets) != 0 { + err = fmt.Errorf("'from' scope may include devices or fleets but not both") + fmt.Printf("%d devices and %d fleets\n%v\n%v\n", len(scopeDevices), len(scopeFleets), scopeDevices, scopeFleets) + } + if len(scopeDevices) == 0 && len(scopeFleets) == 0 { + err = fmt.Errorf("no devices or fleets found within the specified scope") + } } - } + return err + }) } // Provision devices before doing get or set if err == nil && flagProvision { - if flagScope == "" { - err = fmt.Errorf("use -scope to specify device(s) to be provisioned") - } else { + didSomething = true + err = withCreds(credentials, func() error { + if flagScope == "" { + return fmt.Errorf("use -scope to specify device(s) to be provisioned") + } if flagProduct == "" { - err = fmt.Errorf("productUID must be specified") - } else { - if len(scopeDevices) != 0 { - err = varsProvisionDevices(appMetadata, scopeDevices, flagProduct, flagSn, flagVerbose) - } else { - err = fmt.Errorf("no devices to provision") - } + return fmt.Errorf("productUID must be specified") } - } + if len(scopeDevices) != 0 { + return varsProvisionDevices(appMetadata, scopeDevices, flagProduct, flagSn, flagVerbose) + } + return fmt.Errorf("no devices to provision") + }) } // Perform VarsGet actions based on scope if err == nil && flagScope != "" && flagVarsGet { - var vars map[string]Vars - var varsJSON []byte - if len(scopeDevices) != 0 { - vars, err = varsGetFromDevices(appMetadata, scopeDevices, flagVerbose) - } else if len(scopeFleets) != 0 { - vars, err = varsGetFromFleets(appMetadata, scopeFleets, flagVerbose) - } - if err == nil { + didSomething = true + err = withCreds(credentials, func() (err error) { + var vars map[string]Vars + var varsJSON []byte + if len(scopeDevices) != 0 { + vars, err = varsGetFromDevices(appMetadata, scopeDevices, flagVerbose) + } else if len(scopeFleets) != 0 { + vars, err = varsGetFromFleets(appMetadata, scopeFleets, flagVerbose) + } + if err != nil { + return err + } if flagPretty { varsJSON, err = note.JSONMarshalIndent(vars, "", " ") } else { varsJSON, err = note.JSONMarshal(vars) } - if err == nil { - fmt.Printf("%s\n", varsJSON) + if err != nil { + return err } - } + fmt.Printf("%s\n", varsJSON) + return nil + }) } // Perform VarsSet actions based on scope if err == nil && flagScope != "" && flagVarsSet != "" { - template := Vars{} - if strings.HasPrefix(flagVarsSet, "@") { - var templateJSON []byte - templateJSON, err = os.ReadFile(strings.TrimPrefix(flagVarsSet, "@")) - if err == nil { - err = note.JSONUnmarshal(templateJSON, &template) + didSomething = true + err = withCreds(credentials, func() (err error) { + template := Vars{} + if strings.HasPrefix(flagVarsSet, "@") { + var templateJSON []byte + templateJSON, err = os.ReadFile(strings.TrimPrefix(flagVarsSet, "@")) + if err == nil { + err = note.JSONUnmarshal(templateJSON, &template) + } + } else { + err = note.JSONUnmarshal([]byte(flagVarsSet), &template) + } + if err != nil { + return err } - } else { - err = note.JSONUnmarshal([]byte(flagVarsSet), &template) - } - if err == nil { var vars map[string]Vars var varsJSON []byte if len(scopeDevices) != 0 { @@ -383,48 +409,60 @@ func main() { } else if len(scopeFleets) != 0 { vars, err = varsSetFromFleets(appMetadata, scopeFleets, template, flagVerbose) } - if err == nil { - if flagPretty { - varsJSON, err = note.JSONMarshalIndent(vars, "", " ") - } else { - varsJSON, err = note.JSONMarshal(vars) - } - if err == nil { - fmt.Printf("%s\n", varsJSON) - } + if err != nil { + return err } - } + if flagPretty { + varsJSON, err = note.JSONMarshalIndent(vars, "", " ") + } else { + varsJSON, err = note.JSONMarshal(vars) + } + if err != nil { + return err + } + fmt.Printf("%s\n", varsJSON) + return nil + }) } // Explore the contents of the device if err == nil && len(scopeDevices) != 0 && flagExplore { didSomething = true - for _, deviceUID := range scopeDevices { - flagDevice = deviceUID - err = explore(flagReserved, flagVerbose, flagPretty) - if err != nil { - break + err = withCreds(credentials, func() (err error) { + for _, deviceUID := range scopeDevices { + flagDevice = deviceUID + err = explore(flagReserved, flagVerbose, flagPretty) + if err != nil { + return err + } } - } + return nil + }) } // If we didn't do anything and we're just asking about an app, do it if err == nil && !didSomething && (flagApp != "" || flagProduct != "") { - appMetadata, err = appGetMetadata(flagVerbose, flagVarsGet) - if err == nil { + didSomething = true + err = withCreds(credentials, func() (err error) { + appMetadata, err = appGetMetadata(flagVerbose, flagVarsGet) + if err != nil { + return err + } var metaJSON []byte if flagPretty { metaJSON, err = note.JSONMarshalIndent(appMetadata, "", " ") } else { metaJSON, err = note.JSONMarshal(appMetadata) } - if err == nil { - fmt.Printf("%s\n", metaJSON) + if err != nil { + return err } - } + fmt.Printf("%s\n", metaJSON) + return nil + }) } - // Success + // Exit if err != nil { fmt.Printf("%s\n", err) os.Exit(exitFail)