diff --git a/.goreleaser.yml b/.goreleaser.yml index c80c512..4a987fc 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -25,6 +25,7 @@ builds: ldflags: - -s -w -X github.com/fabric8-analytics/cli-tools/pkg/version.version={{.Version}} - "-X 'github.com/fabric8-analytics/cli-tools/pkg/version.vendorInfo=Red Hat'" + - -X 'github.com/fabric8-analytics/cli-tools/pkg/segment.writeKey=XVVwgAtJHhr4YiuoFURmycI2oKVD8mxV - -X github.com/fabric8-analytics/cli-tools/pkg/version.timestamp={{ .Timestamp }} - -X github.com/fabric8-analytics/cli-tools/pkg/version.commitHash={{ .ShortCommit }} id: crda diff --git a/analyses/stackanalyses/controller.go b/analyses/stackanalyses/controller.go index 9c8f71c..ab03c4a 100644 --- a/analyses/stackanalyses/controller.go +++ b/analyses/stackanalyses/controller.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/fabric8-analytics/cli-tools/pkg/telemetry" "io" "mime/multipart" "net/http" @@ -64,7 +65,7 @@ func StackAnalyses(ctx context.Context, requestParams driver.RequestType, jsonOu } else { hasVul = summary.ProcessSummary(ctx, getResponse, jsonOut, showVerboseMsg) } - + telemetry.SetEcosystem(ctx, mc.fileStats.Ecosystem) log.Debug().Msgf("Success StackAnalyses.") return hasVul, nil } diff --git a/cmd/analyse.go b/cmd/analyse.go index 0c58256..4b14bfe 100644 --- a/cmd/analyse.go +++ b/cmd/analyse.go @@ -3,15 +3,14 @@ package cmd import ( "errors" "fmt" - "os" - "path/filepath" - "github.com/fabric8-analytics/cli-tools/analyses/driver" sa "github.com/fabric8-analytics/cli-tools/analyses/stackanalyses" "github.com/fabric8-analytics/cli-tools/pkg/telemetry" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" + "os" + "path/filepath" ) var jsonOut bool @@ -62,6 +61,8 @@ func destructor(_ *cobra.Command, _ []string) error { //runAnalyse is controller func for analyses cmd. func runAnalyse(cmd *cobra.Command, args []string) error { + log.Debug().Msgf("Executing Analyse command.") + askTelemetryConsent() telemetry.SetFlag(cmd.Context(), "json", jsonOut) telemetry.SetFlag(cmd.Context(), "verbose", verboseOut) if !viper.IsSet("crda_key") { diff --git a/cmd/auth.go b/cmd/auth.go index 4d38083..ccd8202 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -45,6 +45,7 @@ func init() { // runAuth is controller for Auth command. func runAuth(cmd *cobra.Command, _ []string) error { log.Debug().Msgf("Executing Auth command.") + askTelemetryConsent() var err error if snykToken == "" { snykToken, err = promptForToken() diff --git a/cmd/config.go b/cmd/config.go new file mode 100644 index 0000000..52c0384 --- /dev/null +++ b/cmd/config.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +// configCmd represents the config command +var configCmd = &cobra.Command{ + Use: "config SUBCOMMAND [flags]", + Short: "Modify crda configuration", + Long: `Modify crda configuration`, + Run: func(cmd *cobra.Command, args []string) { + _ = cmd.Help() + }, +} + +func init() { + rootCmd.AddCommand(configCmd) +} diff --git a/cmd/get.go b/cmd/get.go new file mode 100644 index 0000000..8326e76 --- /dev/null +++ b/cmd/get.go @@ -0,0 +1,40 @@ +package cmd + +import ( + "fmt" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "gopkg.in/yaml.v2" +) + +// getCmd represents the get command +var getCmd = &cobra.Command{ + Use: "get CONFIG-KEY", + Short: "Get active crda configurations", + Long: `Get active crda configurations`, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + c := viper.AllSettings() + bs, err := yaml.Marshal(c) + if err != nil { + log.Error().Msgf("unable to marshal config to YAML") + return err + } + fmt.Println(string(bs)) + return nil + } + key := args[0] + v := viper.IsSet(args[0]) + if !v { + return fmt.Errorf("configuration property '%s' does not exist", key) + } + value := viper.Get(args[0]) + fmt.Println(key, ":", value) + return nil + }, +} + +func init() { + configCmd.AddCommand(getCmd) +} diff --git a/cmd/root.go b/cmd/root.go index 0410f97..235c75c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "strconv" "strings" "time" @@ -43,7 +44,6 @@ var rootCmd = &cobra.Command{ Authenticated token can be used as Auth Token to interact with CRDA Platform.`, Args: cobra.ExactValidArgs(1), - SilenceUsage: true, SilenceErrors: true, } @@ -52,7 +52,6 @@ var rootCmd = &cobra.Command{ func Execute() { attachMiddleware([]string{}, rootCmd) ctx = telemetry.NewContext(context.Background()) - if err := rootCmd.ExecuteContext(ctx); err != nil { // CLI Errors _, _ = fmt.Fprintln(os.Stderr, err.Error()) @@ -161,10 +160,6 @@ func initConfig() { if !viper.IsSet("auth_token") { viper.Set("auth_token", utils.AuthToken) } - if !viper.IsSet("consent_telemetry") { - response := telemetryConsent() - viper.Set("consent_telemetry", response) - } err = viper.WriteConfig() if err != nil { @@ -181,13 +176,10 @@ func initConfig() { log.Debug().Msgf("Successfully configured config files %s.", viper.ConfigFileUsed()) } -func telemetryConsent() bool { - fmt.Println("CRDA CLI is constantly improving and we would like to know more about usage") - response := telemetry.GetTelemetryConsent() - if response { - fmt.Printf("Thanks for helping us! You can disable telemetry by editing %s \n", viper.ConfigFileUsed()) - } else { - fmt.Printf("No worry, you can still enable telemetry manually by editing %s \n", viper.ConfigFileUsed()) +// askTelemetryConsent fires Telemetry Consent Prompt +func askTelemetryConsent() { + if !viper.IsSet("consent_telemetry") { + response := telemetry.GetTelemetryConsent() + viper.Set("consent_telemetry", strconv.FormatBool(response)) } - return response } diff --git a/cmd/set.go b/cmd/set.go new file mode 100644 index 0000000..7fd6a86 --- /dev/null +++ b/cmd/set.go @@ -0,0 +1,40 @@ +package cmd + +import ( + "fmt" + "github.com/fabric8-analytics/cli-tools/pkg/config" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// setCmd represents the set command +var setCmd = &cobra.Command{ + Use: "set CONFIG-KEY VALUE", + Short: "Set a crda configuration property", + Long: `Sets a crda configuration property`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) < 2 { + log.Error().Msgf("please provide a configuration property and its value as in 'crda config set CONFIG-KEY VALUE'") + return + } + viper.Set(args[0], args[1]) + err := viper.WriteConfig() + if err != nil { + log.Error().Msgf("unable to write config health") + return + } + value := viper.Get(args[0]) + config.ViperUnMarshal() + if value != args[1] { + log.Error().Msgf("unable to set configuration value") + return + } + fmt.Println("successfully set configuration value") + return + }, +} + +func init() { + configCmd.AddCommand(setCmd) +} diff --git a/docs/cli_README.md b/docs/cli_README.md index a8ed1e5..2f996c2 100644 --- a/docs/cli_README.md +++ b/docs/cli_README.md @@ -10,6 +10,17 @@ As of now, CLI supports following stacks: ![screenshot of summary](https://github.com/fabric8-analytics/cli-tools/blob/b407d2a7c595a47e3126ad62a816dc107bd148d2/summary.png) ![screenshot of analyse](https://github.com/fabric8-analytics/cli-tools/blob/71198735d0dee3173ed3082a5ab1dee41dfa9ce8/analyse.png) +### Usage data + +The first time CRDA CLI is run, you will be asked to opt-in to Red Hat’s telemetry collection program. + +With your approval, CRDA CLI collects pseudonymized usage data and sends it to Red Hat servers to help improve our products and services. To learn more, Please visit https://developers.redhat.com/article/tool-data-collection + +Manually configuring usage data collection + +You can manually change your preference about usage data collection by running in `crda config set consent_telemetry false/true`. + + ### Installation: - Select, Download and Install latest binary from [Releases](https://github.com/fabric8-analytics/cli-tools/releases) @@ -17,22 +28,22 @@ As of now, CLI supports following stacks: - ##### For Linux ``` -$ curl -s -L https://github.com/fabric8-analytics/cli-tools/releases/download/v0.0.1/crda_0.0.1_Linux_64bit.tar.gz | tar xvz -C . +$ curl -s -L https://github.com/fabric8-analytics/cli-tools/releases/download/v0.1.2/crda_0.1.2_Linux_64bit.tar.gz | tar xvz -C . ``` - ##### For Linux - Fedora/CentOS/RHEL ``` -$ curl -s -L https://github.com/fabric8-analytics/cli-tools/releases/download/v0.0.1/crda_0.0.1_Linux-64bit.rpm +$ curl -s -L https://github.com/fabric8-analytics/cli-tools/releases/download/v0.1.2/crda_0.1.2_Linux-64bit.rpm ``` - ##### For MacOS ``` -$ curl -s -L https://github.com/fabric8-analytics/cli-tools/releases/download/v0.0.1/crda_0.0.1_macOS_64bit.tar.gz | tar xvz -C . +$ curl -s -L https://github.com/fabric8-analytics/cli-tools/releases/download/v0.1.2/crda_0.1.2_macOS_64bit.tar.gz | tar xvz -C . ``` - ##### For MacOS - Apple Silicon ``` -$ curl -s -L https://github.com/fabric8-analytics/cli-tools/releases/download/v0.0.1/crda_0.0.1_macOS_ARM64.tar.gz | tar xvz -C . +$ curl -s -L https://github.com/fabric8-analytics/cli-tools/releases/download/v0.1.2/crda_0.1.2_macOS_ARM64.tar.gz | tar xvz -C . ``` - ##### For Windows -Click [here](https://github.com/fabric8-analytics/cli-tools/releases/download/v0.0.1/crda_0.0.1_Windows_64bit.tar.gz) to start download. +Click [here](https://github.com/fabric8-analytics/cli-tools/releases/download/v0.1.2/crda_0.1.2_Windows_64bit.tar.gz) to start download. ### Usage: Executable supports following commands: @@ -55,6 +66,10 @@ Executable supports following commands: - `crda version`: This outputs version details of Binary. +- `crda config set $CONFIG-KEY $VALUE`: Sets configuration values + +- `crda config get $CONFIG-KEY`: Gets configuration values + #### Global Flags: - `--debug`: (bool) (Optional): Debug Flag. Enables Debug Logs diff --git a/go.mod b/go.mod index 7e41e4c..43c8d04 100644 --- a/go.mod +++ b/go.mod @@ -19,4 +19,5 @@ require ( github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect + gopkg.in/yaml.v2 v2.4.0 ) diff --git a/pkg/segment/segment.go b/pkg/segment/segment.go index 4494470..3062399 100644 --- a/pkg/segment/segment.go +++ b/pkg/segment/segment.go @@ -22,7 +22,7 @@ import ( "github.com/segmentio/analytics-go" ) -// writeKey is segment project key +// writeKey is segment project key var writeKey = "MW6rAYP7Q6AAiSAZ3Ussk6eMebbVcchD" // test // Client is a segment client struct @@ -70,7 +70,7 @@ func (c *Client) Close() error { // Upload sends telemetry data to segment func (c *Client) Upload(ctx context.Context, action string, duration time.Duration, err error) error { - if config.ActiveConfigValues.ConsentTelemetry != "1" { + if config.ActiveConfigValues.ConsentTelemetry != "true" { return nil } log.Debug().Msgf("Sending Segment Info") diff --git a/pkg/telemetry/telemetry.go b/pkg/telemetry/telemetry.go index 83da2ed..647887a 100644 --- a/pkg/telemetry/telemetry.go +++ b/pkg/telemetry/telemetry.go @@ -21,8 +21,21 @@ type Properties struct { storage map[string]interface{} } -// GetTelemetryConsent fires telemetry consent popup. func GetTelemetryConsent() bool { + fmt.Println("CRDA CLI is constantly improving and we would like to know more about usage (more details at https://developers.redhat.com/article/tool-data-collection)") + fmt.Println("Your preference can be changed manually if desired using 'crda config set consent_telemetry '") + + response := telemetryConsent() + if response { + fmt.Printf("Thanks for helping us! You can disable telemetry by `crda config set consent_telemetry false` \n\n") + } else { + fmt.Printf("No worry, you can still enable telemetry by `crda config set consent_telemetry true` \n\n") + } + return response +} + +// telemetryConsent fires telemetry consent popup. +func telemetryConsent() bool { prompt := promptui.Prompt{ Label: "Would you like to contribute anonymous usage statistics [y/n]", HideEntered: true, @@ -138,6 +151,11 @@ func SetVulnerability(ctx context.Context, value int) { setContextProperty(ctx, "total-vulnerabilities", value) } +// SetEcosystem sets ecosystem property +func SetEcosystem(ctx context.Context, value string) { + setContextProperty(ctx, "ecosystem", value) +} + // SetSnykTokenAssociation sets synk-token-associated property func SetSnykTokenAssociation(ctx context.Context, value bool) { setContextProperty(ctx, "snyk-token-associated", value)