Skip to content

Commit

Permalink
Merge pull request #156 from commitdev/add-prompts-for-project-names
Browse files Browse the repository at this point in the history
#134 Add prompts for project names
  • Loading branch information
bmonkman committed Jun 10, 2020
2 parents a09ffcf + 514557d commit eb68ca4
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 32 deletions.
9 changes: 5 additions & 4 deletions internal/config/projectconfig/project_config.go
Expand Up @@ -9,10 +9,11 @@ import (
)

type ZeroProjectConfig struct {
Name string
Infrastructure Infrastructure // TODO simplify and flatten / rename?
Parameters map[string]string
Modules []string
Name string
ShouldPushRepositories bool
Infrastructure Infrastructure // TODO simplify and flatten / rename?
Parameters map[string]string
Modules []string
}

type Infrastructure struct {
Expand Down
87 changes: 66 additions & 21 deletions internal/context/init.go
@@ -1,8 +1,10 @@
package context

import (
"fmt"
"os"
"path"
"sync"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
Expand All @@ -28,7 +30,7 @@ func Init(outDir string) *projectconfig.ZeroProjectConfig {
projectConfig.Name = getProjectNamePrompt().GetParam(projectConfig.Parameters)

rootDir := path.Join(outDir, projectConfig.Name)
flog.Infof(":tada: Creating project")
flog.Infof(":tada: Initializing project")

err := os.MkdirAll(rootDir, os.ModePerm)
if os.IsExist(err) {
Expand All @@ -37,29 +39,45 @@ func Init(outDir string) *projectconfig.ZeroProjectConfig {
exit.Fatal("Error creating root: %v ", err)
}

prompts := getProjectPrompts(projectConfig.Name)
projectConfig.Parameters["ShouldPushRepoUpstream"] = prompts["ShouldPushRepoUpstream"].GetParam(projectConfig.Parameters)
// Prompting for push-up stream, then conditionally prompting for github
projectConfig.Parameters["GithubRootOrg"] = prompts["GithubRootOrg"].GetParam(projectConfig.Parameters)
personalToken := prompts["githubPersonalToken"].GetParam(projectConfig.Parameters)
if personalToken != "" && personalToken != globalconfig.GetUserCredentials(projectConfig.Name).AccessToken {
projectConfig.Parameters["githubPersonalToken"] = personalToken
projectCredential := globalconfig.GetUserCredentials(projectConfig.Name)
projectCredential.GithubResourceConfig.AccessToken = personalToken
globalconfig.Save(projectCredential)
}
moduleSources := chooseStack(getRegistry())
moduleConfigs := loadAllModules(moduleSources)
for _ = range moduleConfigs {
// TODO: initialize module structs inside project
}

prompts := getProjectPrompts(projectConfig.Name, moduleConfigs)

initParams := make(map[string]string)
projectConfig.ShouldPushRepositories = true
initParams["ShouldPushRepositories"] = prompts["ShouldPushRepositories"].GetParam(initParams)
if initParams["ShouldPushRepositories"] == "n" {
projectConfig.ShouldPushRepositories = false
}

// Prompting for push-up stream, then conditionally prompting for github
initParams["GithubRootOrg"] = prompts["GithubRootOrg"].GetParam(initParams)
initParams["GithubPersonalToken"] = prompts["GithubPersonalToken"].GetParam(initParams)
if initParams["GithubRootOrg"] != "" && initParams["GithubPersonalToken"] != globalconfig.GetUserCredentials(projectConfig.Name).AccessToken {
projectCredential := globalconfig.GetUserCredentials(projectConfig.Name)
projectCredential.GithubResourceConfig.AccessToken = initParams["GithubPersonalToken"]
globalconfig.Save(projectCredential)
}

projectParameters := promptAllModules(moduleConfigs)
for k, v := range projectParameters {
projectConfig.Parameters[k] = v
// TODO: Add parameters to module structs inside project
}

for moduleName, _ := range moduleConfigs {
// @TODO : Uncomment when this struct is implemented
repoName := prompts[moduleName].GetParam(initParams)
repoURL := fmt.Sprintf("%s/%s", initParams["GithubRootOrg"], repoName)
//projectConfig.Modules[moduleName].Files.Directory = prompts[moduleName].GetParam(initParams)
//projectConfig.Modules[moduleName].Files.Repository = repoURL
fmt.Println(repoURL)
}

// TODO: load ~/.zero/config.yml (or credentials)
// TODO: prompt global credentials

Expand All @@ -70,8 +88,15 @@ func Init(outDir string) *projectconfig.ZeroProjectConfig {
func loadAllModules(moduleSources []string) map[string]moduleconfig.ModuleConfig {
modules := make(map[string]moduleconfig.ModuleConfig)

wg := sync.WaitGroup{}
wg.Add(len(moduleSources))
for _, moduleSource := range moduleSources {
go module.FetchModule(moduleSource, &wg)
}
wg.Wait()

for _, moduleSource := range moduleSources {
mod, err := module.FetchModule(moduleSource)
mod, err := module.ParseModuleConfig(moduleSource)
if err != nil {
exit.Fatal("Unable to load module: %v\n", err)
}
Expand Down Expand Up @@ -103,36 +128,56 @@ func getProjectNamePrompt() PromptHandler {
Default: "",
},
NoCondition,
NoValidation,
}
}

func getProjectPrompts(projectName string) map[string]PromptHandler {
return map[string]PromptHandler{
"ShouldPushRepoUpstream": {
func getProjectPrompts(projectName string, modules map[string]moduleconfig.ModuleConfig) map[string]PromptHandler {
handlers := map[string]PromptHandler{
"ShouldPushRepositories": {
moduleconfig.Parameter{
Field: "ShouldPushRepoUpstream",
Field: "ShouldPushRepositories",
Label: "Should the created projects be checked into github automatically? (y/n)",
Default: "y",
},
NoCondition,
SpecificValueValidation("y", "n"),
},
"GithubRootOrg": {
moduleconfig.Parameter{
Field: "GithubRootOrg",
Label: "What's the root of the github org to create repositories in?",
Default: "github.com/",
},
KeyMatchCondition("ShouldPushRepoUpstream", "y"),
KeyMatchCondition("ShouldPushRepositories", "y"),
NoValidation,
},
"githubPersonalToken": {
"GithubPersonalToken": {
moduleconfig.Parameter{
Field: "githubPersonalToken",
Field: "GithubPersonalToken",
Label: "Github Personal Access Token with access to the above organization",
Default: globalconfig.GetUserCredentials(projectName).AccessToken,
},
KeyMatchCondition("ShouldPushRepoUpstream", "y"),
KeyMatchCondition("ShouldPushRepositories", "y"),
NoValidation,
},
}

for moduleName, module := range modules {
label := fmt.Sprintf("What do you want to call the %s project?", moduleName)

handlers[moduleName] = PromptHandler{
moduleconfig.Parameter{
Field: moduleName,
Label: label,
Default: module.OutputDir,
},
NoCondition,
NoValidation,
}
}

return handlers
}

func chooseCloudProvider(projectConfig *projectconfig.ZeroProjectConfig) {
Expand Down
25 changes: 23 additions & 2 deletions internal/context/prompts.go
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"regexp"
"strings"

"github.com/commitdev/zero/internal/config/moduleconfig"
"github.com/commitdev/zero/pkg/util/exit"
Expand All @@ -15,17 +16,34 @@ import (
type PromptHandler struct {
moduleconfig.Parameter
Condition func(map[string]string) bool
Validate func(string) error
}

func NoCondition(map[string]string) bool {
return true
}

func KeyMatchCondition(key string, value string) func(map[string]string) bool {
return func(param map[string]string) bool {
return param[key] == value
}
}

func NoValidation(string) error {
return nil
}

func SpecificValueValidation(values ...string) func(string) error {
return func(checkValue string) error {
for _, allowedValue := range values {
if checkValue == allowedValue {
return nil
}
}
return fmt.Errorf("Please choose one of %s", strings.Join(values, "/"))
}
}

// TODO: validation / allow prompt retry ...etc
func (p PromptHandler) GetParam(projectParams map[string]string) string {
var err error
Expand All @@ -40,7 +58,7 @@ func (p PromptHandler) GetParam(projectParams map[string]string) string {
} else if p.Parameter.Value != "" {
result = p.Parameter.Value
} else {
err, result = promptParameter(p.Parameter)
err, result = promptParameter(p)
}
if err != nil {
exit.Fatal("Exiting prompt: %v\n", err)
Expand All @@ -51,7 +69,8 @@ func (p PromptHandler) GetParam(projectParams map[string]string) string {
return ""
}

func promptParameter(param moduleconfig.Parameter) (error, string) {
func promptParameter(prompt PromptHandler) (error, string) {
param := prompt.Parameter
label := param.Label
if param.Label == "" {
label = param.Field
Expand All @@ -72,6 +91,7 @@ func promptParameter(param moduleconfig.Parameter) (error, string) {
Label: label,
Default: defaultValue,
AllowEdit: true,
Validate: prompt.Validate,
}
result, err = prompt.Run()
}
Expand Down Expand Up @@ -119,6 +139,7 @@ func PromptModuleParams(moduleConfig moduleconfig.ModuleConfig, parameters map[s
promptHandler := PromptHandler{
promptConfig,
NoCondition,
NoValidation,
}
result := promptHandler.GetParam(parameters)

Expand Down
3 changes: 3 additions & 0 deletions internal/context/prompts_test.go
Expand Up @@ -20,6 +20,7 @@ func TestGetParam(t *testing.T) {
prompt := context.PromptHandler{
param,
context.NoCondition,
context.NoValidation,
}

result := prompt.GetParam(projectParams)
Expand All @@ -35,6 +36,7 @@ func TestGetParam(t *testing.T) {
prompt := context.PromptHandler{
param,
context.NoCondition,
context.NoValidation,
}

result := prompt.GetParam(map[string]string{
Expand All @@ -52,6 +54,7 @@ func TestGetParam(t *testing.T) {
prompt := context.PromptHandler{
param,
context.NoCondition,
context.NoValidation,
}

result := prompt.GetParam(projectParams)
Expand Down
17 changes: 13 additions & 4 deletions internal/module/module.go
Expand Up @@ -7,11 +7,13 @@ import (
"log"
"path"
"regexp"
"sync"

"github.com/commitdev/zero/internal/config"
"github.com/commitdev/zero/internal/config/moduleconfig"
"github.com/commitdev/zero/internal/constants"
"github.com/commitdev/zero/internal/util"
"github.com/commitdev/zero/pkg/util/exit"
"github.com/hashicorp/go-getter"
)

Expand All @@ -21,17 +23,24 @@ type TemplateModule struct {
Config moduleconfig.ModuleConfig
}

// FetchModule downloads the remote module source (or loads the local files) and parses the module config yaml
func FetchModule(source string) (moduleconfig.ModuleConfig, error) {
config := moduleconfig.ModuleConfig{}
// FetchModule downloads the remote module source if necessary. Meant to be run in a goroutine.
func FetchModule(source string, wg *sync.WaitGroup) {
defer wg.Done()

localPath := GetSourceDir(source)
if !isLocal(source) {
err := getter.Get(localPath, source)
if err != nil {
return config, err
exit.Fatal("Failed to fetch remote module from %s: %v\n", source, err)
}
}
return
}

// ParseModuleConfig loads the local config file for a module and parses the yaml
func ParseModuleConfig(source string) (moduleconfig.ModuleConfig, error) {
localPath := GetSourceDir(source)
config := moduleconfig.ModuleConfig{}
configPath := path.Join(localPath, constants.ZeroModuleYml)
config, err := moduleconfig.LoadModuleConfig(configPath)
return config, err
Expand Down
2 changes: 1 addition & 1 deletion internal/module/module_test.go
Expand Up @@ -32,7 +32,7 @@ func TestNewTemplateModule(t *testing.T) {
var mod moduleconfig.ModuleConfig

t.Run("Loading module from source", func(t *testing.T) {
mod, _ = module.FetchModule(testModuleSource)
mod, _ = module.ParseModuleConfig(testModuleSource)

assert.Equal(t, "CI templates", mod.Name)
})
Expand Down

0 comments on commit eb68ca4

Please sign in to comment.