From 2f78690f5f26726da6d5937b22478dca4cd8950a Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Thu, 31 May 2018 14:34:14 +0900 Subject: [PATCH] Ensure config is created before executing any commands This will walk the user through setting up their token unless otherwise configured, i.e. via flags Diagnostic command prints out host, and if token is found. Moved GQL call to Query command for testing. --- cmd/diagnostic.go | 55 +++++++-------------------- cmd/query.go | 64 +++++++++++++++++++++++++++++++ cmd/root.go | 96 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 167 insertions(+), 48 deletions(-) create mode 100644 cmd/query.go diff --git a/cmd/diagnostic.go b/cmd/diagnostic.go index 5df93551b..fba967c81 100644 --- a/cmd/diagnostic.go +++ b/cmd/diagnostic.go @@ -1,12 +1,8 @@ package cmd import ( - "context" - "encoding/json" "fmt" - "log" - "github.com/machinebox/graphql" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -18,46 +14,21 @@ var diagnosticCmd = &cobra.Command{ } func diagnostic(cmd *cobra.Command, args []string) { - // TODO: Pass token once figure out how api-service uses it - host := viper.GetString("host") + "/graphql" - client := graphql.NewClient(host) - query := ` - query IntrospectionQuery { - __schema { - queryType { name } - mutationType { name } - subscriptionType { name } - types { - ...FullType - } - directives { - name - description - } - } - } + host := viper.GetString("host") + token := viper.GetString("token") - fragment FullType on __Type { - kind - name - description - fields(includeDeprecated: true) { - name - } - }` + fmt.Printf("---\nCircleCI CLI Diagnostics\n---\n") + fmt.Printf("Config found: `%v`\n", viper.ConfigFileUsed()) - req := graphql.NewRequest(query) - req.Header.Set("Authorization", viper.GetString("token")) - - ctx := context.Background() - var resp map[string]interface{} - - fmt.Println("Querying", host, "with:\n", query, "\n") - if err := client.Run(ctx, req, &resp); err != nil { - log.Fatal(err) + if host == "host" || host == "" { + fmt.Println("Please set a host!") + } else { + fmt.Printf("Host is: %s\n", host) } - b, _ := json.MarshalIndent(resp, "", " ") - fmt.Println("Result: \n") - fmt.Println(string(b)) + if token == "token" || token == "" { + fmt.Println("Please set a token!") + } else { + fmt.Println("OK, got a token.") + } } diff --git a/cmd/query.go b/cmd/query.go new file mode 100644 index 000000000..018eef3dd --- /dev/null +++ b/cmd/query.go @@ -0,0 +1,64 @@ +package cmd + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "github.com/machinebox/graphql" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var queryCmd = &cobra.Command{ + Use: "query", + Short: "Query the CircleCI GraphQL API.", + Run: query, +} + +func query(cmd *cobra.Command, args []string) { + host := viper.GetString("host") + token := viper.GetString("token") + client := graphql.NewClient(host + "/graphql") + + query := ` + query IntrospectionQuery { + __schema { + queryType { name } + mutationType { name } + subscriptionType { name } + types { + ...FullType + } + directives { + name + description + } + } + } + + fragment FullType on __Type { + kind + name + description + fields(includeDeprecated: true) { + name + } + }` + + req := graphql.NewRequest(query) + req.Header.Set("Authorization", token) + + ctx := context.Background() + var resp map[string]interface{} + + fmt.Println("Querying", host, "with:\n", query, "\n") + if err := client.Run(ctx, req, &resp); err != nil { + log.Fatal(err) + } + + b, _ := json.MarshalIndent(resp, "", " ") + fmt.Println("Result: \n") + fmt.Println(string(b)) +} diff --git a/cmd/root.go b/cmd/root.go index f7c9413ae..d7ce80a08 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -14,8 +14,15 @@ var RootCmd = &cobra.Command{ Long: `Use CircleCI from the command line.`, } +var ( + cfgFile string + cfgName = "cli" + configPathDefault = fmt.Sprintf("%s/.circleci/%s.yml", os.Getenv("HOME"), cfgName) +) + func AddCommands() { RootCmd.AddCommand(diagnosticCmd) + RootCmd.AddCommand(queryCmd) } // TODO: This convention was carried over from admin-cli, do we still need it? @@ -31,6 +38,7 @@ func Execute() { func init() { cobra.OnInitialize(initConfig) + RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.circleci/cli.yml)") RootCmd.PersistentFlags().StringP("host", "H", "https://circleci.com", "the host of your CircleCI install") RootCmd.PersistentFlags().StringP("token", "t", "", "your token for using CircleCI") @@ -38,14 +46,90 @@ func init() { viper.BindPFlag("token", RootCmd.PersistentFlags().Lookup("token")) } +// TODO: move config stuff to it's own package func initConfig() { + if err := readConfig(); err != nil { + if err = createConfig(); err != nil { + fmt.Println(err.Error()) + os.Exit(-1) + } + cfgFile = configPathDefault + readConfig() // reload config after creating it + } +} + +func readConfig() (err error) { + if cfgFile != "" { + viper.SetConfigFile(cfgFile) + } + if viper.ConfigFileUsed() == "" { - viper.SetConfigName("cli") // name of config file (without extension) viper.AddConfigPath("$HOME/.circleci") - // If a config file is found, read it in. - viper.AutomaticEnv() // read in environment variables that match - if err := viper.ReadInConfig(); err != nil { - fmt.Println("Failed to load config file...") - } + viper.SetConfigName(cfgName) } + + // read in environment variables that match + // set a prefix for config, i.e. CIRCLECI_CLI_HOST + viper.SetEnvPrefix("circleci_cli") + viper.AutomaticEnv() + + // If a config file is found, read it in. + err = viper.ReadInConfig() + return err +} + +func createConfig() (err error) { + // Don't support creating config at --config flag, only default + if cfgFile != "" { + fmt.Printf("Setting up default config at: %v\n", configPathDefault) + } + + var host, token string + + path := fmt.Sprintf("%s/.circleci", os.Getenv("HOME")) + + if _, err := os.Stat(path); os.IsNotExist(err) { + os.Mkdir(path, 0777) + } + + // Create default config file + if _, err := os.Create(configPathDefault); err != nil { + return err + } + + // open file with read & write + file, err := os.OpenFile(configPathDefault, os.O_RDWR, 0644) + if err != nil { + fmt.Println(err.Error()) + os.Exit(-1) + } + + // read flag values + host = viper.GetString("host") + token = viper.GetString("token") + + if host == "host" || host == "" { + fmt.Print("Please enter the HTTP(S) host of your CircleCI installation:") + fmt.Scanln(&host) + fmt.Println("OK.") + } + + if token == "token" || token == "" { + fmt.Print("Please enter your CircleCI API token:") + fmt.Scanln(&token) + fmt.Println("OK.") + } + + // format input + configValues := fmt.Sprintf("host: %v\ntoken: %v\n", host, token) + + // write new config values to file + if _, err = file.WriteString(configValues); err != nil { + fmt.Println(err.Error()) + os.Exit(-1) + } + + fmt.Printf("Your configuration has been created in `%v`.\n", configPathDefault) + fmt.Println("It can edited manually for advanced settings.") + return err }