diff --git a/cmd/plural/crypto.go b/cmd/plural/crypto.go index a7b12adc..bf1691c0 100644 --- a/cmd/plural/crypto.go +++ b/cmd/plural/crypto.go @@ -11,6 +11,9 @@ import ( "github.com/pluralsh/plural/pkg/crypto" "github.com/pluralsh/plural/pkg/utils" "github.com/pluralsh/plural/pkg/utils/git" + "github.com/pluralsh/plural/pkg/scm" + homedir "github.com/mitchellh/go-homedir" + "github.com/AlecAivazis/survey/v2" "github.com/urfave/cli" ) @@ -81,6 +84,11 @@ func cryptoCommands() []cli.Command { }, }, }, + { + Name: "ssh-keygen", + Usage: "generate an ed5519 keypair for use in git ssh", + Action: affirmed(handleKeygen, "This command will autogenerate an ed5519 keypair, without passphrase. Sound good?"), + }, { Name: "export", Usage: "dumps the current aes key to stdout", @@ -273,3 +281,39 @@ func randString(c *cli.Context) error { fmt.Println(str) return nil } + +func handleKeygen(c *cli.Context) error { + path, err := homedir.Expand("~/.ssh") + if err != nil { + return err + } + + pub, priv, err := scm.GenerateKeys() + if err != nil { + return err + } + + filename := "" + input := &survey.Input{Message: "What do you want to name your keypair?", Default: "id_plrl"} + err = survey.AskOne(input, &filename, survey.WithValidator(func (val interface{}) error { + name, _ := val.(string) + if utils.Exists(filepath.Join(path, name)) { + return fmt.Errorf("File ~/.ssh/%s already exists", name) + } + + return nil + })) + if err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(path, filename), []byte(priv), 0600); err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(path, filename + ".pub"), []byte(pub), 0644); err != nil { + return err + } + + return nil +} \ No newline at end of file diff --git a/cmd/plural/validation.go b/cmd/plural/validation.go index 76f87d87..19c46ca8 100644 --- a/cmd/plural/validation.go +++ b/cmd/plural/validation.go @@ -59,6 +59,16 @@ func confirmed(fn func(*cli.Context) error, msg string) func(*cli.Context) error } } +func affirmed(fn func(*cli.Context) error, msg string) func(*cli.Context) error { + return func(c *cli.Context) error { + if ok := affirm(msg); !ok { + return nil + } + + return fn(c) + } +} + func tracked(fn func(*cli.Context) error, event string) func(*cli.Context) error { return func(c *cli.Context) error { event := api.UserEventAttributes{Data: "", Event: event, Status: "OK"} diff --git a/pkg/bundle/configuration.go b/pkg/bundle/configuration.go index 2ce112a5..a23168d3 100644 --- a/pkg/bundle/configuration.go +++ b/pkg/bundle/configuration.go @@ -33,9 +33,9 @@ func evaluateCondition(ctx map[string]interface{}, cond *api.Condition) bool { return true } -func configure(ctx map[string]interface{}, item *api.ConfigurationItem, context *manifest.Context, section *api.RecipeSection) error { +func configure(ctx map[string]interface{}, item *api.ConfigurationItem, context *manifest.Context, section *api.RecipeSection) (err error) { if !evaluateCondition(ctx, item.Condition) { - return nil + return } if item.Type == Function { @@ -61,36 +61,36 @@ func configure(ctx map[string]interface{}, item *api.ConfigurationItem, context case Int: var res int prompt, opts := intSurvey(def, item, proj) - survey.AskOne(prompt, &res, opts...) + err = survey.AskOne(prompt, &res, opts...) ctx[item.Name] = res case Bool: res := false prompt, opts := boolSurvey(def, item, proj) - survey.AskOne(prompt, &res, opts...) + err = survey.AskOne(prompt, &res, opts...) ctx[item.Name] = res case Domain: var res string def = prevDefault(ctx, item, def) prompt, opts := domainSurvey(def, item, proj) - survey.AskOne(prompt, &res, opts...) + err = survey.AskOne(prompt, &res, opts...) ctx[item.Name] = res case String: var res string def = prevDefault(ctx, item, def) prompt, opts := stringSurvey(def, item, proj) - survey.AskOne(prompt, &res, opts...) + err = survey.AskOne(prompt, &res, opts...) ctx[item.Name] = res case Password: var res string def = prevDefault(ctx, item, def) prompt, opts := passwordSurvey(def, item, proj) - survey.AskOne(prompt, &res, opts...) + err = survey.AskOne(prompt, &res, opts...) ctx[item.Name] = res case Bucket: var res string def = prevDefault(ctx, item, def) prompt, opts := bucketSurvey(def, item, proj, context) - survey.AskOne(prompt, &res, opts...) + err = survey.AskOne(prompt, &res, opts...) if res != def { ctx[item.Name] = bucketName(res, proj) } else { @@ -100,7 +100,9 @@ func configure(ctx map[string]interface{}, item *api.ConfigurationItem, context case File: var res string prompt, opts := fileSurvey(def, item, proj) - survey.AskOne(prompt, &res, opts...) + if err := survey.AskOne(prompt, &res, opts...); err != nil { + return err + } path, err := homedir.Expand(res) if err != nil { return err @@ -112,7 +114,7 @@ func configure(ctx map[string]interface{}, item *api.ConfigurationItem, context ctx[item.Name] = contents } - return nil + return } func prevDefault(ctx map[string]interface{}, item *api.ConfigurationItem, def string) string { diff --git a/pkg/scm/github.go b/pkg/scm/github.go index de4f510c..168ca204 100644 --- a/pkg/scm/github.go +++ b/pkg/scm/github.go @@ -69,7 +69,7 @@ func (gh *Github) Setup() (con Context, err error) { } survey.AskOne(prompt, &org, survey.WithValidator(survey.Required)) - pub, priv, err := generateKeys() + pub, priv, err := GenerateKeys() if err != nil { return } diff --git a/pkg/scm/gitlab.go b/pkg/scm/gitlab.go index 7f8a1ed4..656ebfab 100644 --- a/pkg/scm/gitlab.go +++ b/pkg/scm/gitlab.go @@ -75,7 +75,7 @@ func (gl *Gitlab) Setup() (con Context, err error) { } survey.AskOne(prompt, &org, survey.WithValidator(survey.Required)) - pub, priv, err := generateKeys() + pub, priv, err := GenerateKeys() if err != nil { return } diff --git a/pkg/scm/keys.go b/pkg/scm/keys.go index 1e4ac217..9bc3055a 100644 --- a/pkg/scm/keys.go +++ b/pkg/scm/keys.go @@ -18,7 +18,7 @@ type keys struct { priv string } -func generateKeys() (pub string, priv string, err error) { +func GenerateKeys() (pub string, priv string, err error) { pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { return diff --git a/pkg/scm/provider.go b/pkg/scm/provider.go index cd4ec8dc..02ff294d 100644 --- a/pkg/scm/provider.go +++ b/pkg/scm/provider.go @@ -42,7 +42,7 @@ func Setup() (string, error) { return "", err } - utils.Highlight("Cloning the repo locally (be sure you have git ssh auth set up)\n") + utils.Highlight("Cloning the repo locally (be sure you have git ssh auth set up, you can use `plural crypto ssh-keygen` to create your first ssh keys then upload the public key to your git provider)\n") auth, _ := git.SSHAuth("git", ctx.priv, "") if _, err := git.Clone(auth, ctx.url, ctx.repoName); err != nil { return "", err