From ead2815edb76d2869b062993f7694668c0740831 Mon Sep 17 00:00:00 2001 From: Lewis Burgess Date: Mon, 21 Nov 2022 11:19:17 -0500 Subject: [PATCH] 1.1.3 --- .gitignore | 3 + Makefile | 6 +- boilr.rb.tmpl | 12 +++ pkg/boilr/configuration.go | 2 +- pkg/cmd/root.go | 2 + pkg/cmd/use.go | 62 +++++++++++++ pkg/prompt/prompt.go | 22 ++++- pkg/template/template.go | 112 ++++++++++++++++-------- pkg/util/templateinput/templateinput.go | 23 +++++ 9 files changed, 202 insertions(+), 42 deletions(-) create mode 100644 boilr.rb.tmpl create mode 100644 pkg/util/templateinput/templateinput.go diff --git a/.gitignore b/.gitignore index 7739ec7..c4609bb 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ vendor # don't accidentally commit a build of main boilr +boilr-mac +*.tgz +boilr.rb diff --git a/Makefile b/Makefile index 30fce21..3f335e6 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,8 @@ .PHONY: help .DEFAULT_GOAL := help -VERSION := 1.1.2 -SHA256SUM_MAC=$(shell sha256sum boilr-${VERSION}-darwin_amd6.tgz | cut -d' ' -f1 2>&1) +VERSION := 1.1.3 +SHA256SUM_MAC=$(shell sha256sum boilr-${VERSION}-darwin_amd64.tgz | cut -d' ' -f1 2>&1) define FORMULA class Boilr < Formula @@ -39,7 +39,7 @@ precommit-run: ## run all pre-commit hooks build-mac: ## build the boilr executable @go mod tidy @go build - @tar czf boilr-${VERSION}-darwin_amd6.tgz ./boilr + @tar czf boilr-${VERSION}-darwin_amd64.tgz ./boilr @mv boilr boilr-mac build-linux: ## build the boilr executable diff --git a/boilr.rb.tmpl b/boilr.rb.tmpl new file mode 100644 index 0000000..b6850e9 --- /dev/null +++ b/boilr.rb.tmpl @@ -0,0 +1,12 @@ +class Boilr < Formula + desc "Boilerplate template manager that generates files or directories from template repositories" + homepage "https://github.com/solaegis/boilr" + url "https://github.com/solaegis/boilr/releases/download/v${VERIONS}/boilr-${VERSION}-darwin_amd64.tgz" + version "1.1.1" + sha256 "$(sha256sum boilr-mac | cut -d' ' -f1)" + + def install + bin.install "boilr" + end +end + diff --git a/pkg/boilr/configuration.go b/pkg/boilr/configuration.go index 54a2e78..6dd25a1 100644 --- a/pkg/boilr/configuration.go +++ b/pkg/boilr/configuration.go @@ -16,7 +16,7 @@ const ( AppName = "boilr" // Version of the application - Version = "1.1.2" + Version = "1.1.3" // ConfigDirPath is the configuration directory of the application ConfigDirPath = ".config/boilr" diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index ed42596..56284f5 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -35,6 +35,8 @@ func Run() { Use.PersistentFlags().BoolP("use-defaults", "f", false, "Uses default values in project.json instead of prompting the user") Use.PersistentFlags().StringP("log-level", "l", "error", "log-level for output") + Use.PersistentFlags().StringP("use-file", "i", "", "Json file used to get custom values") + Use.PersistentFlags().StringP("json-file", "j", "", "Json file to create using prompt inputs") Template.AddCommand(Use) Template.AddCommand(Validate) diff --git a/pkg/cmd/use.go b/pkg/cmd/use.go index d1668bd..ed2b991 100644 --- a/pkg/cmd/use.go +++ b/pkg/cmd/use.go @@ -1,6 +1,7 @@ package cmd import ( + "encoding/json" "fmt" "io/ioutil" "os" @@ -12,6 +13,7 @@ import ( "github.com/solaegis/boilr/pkg/template" "github.com/solaegis/boilr/pkg/util/exit" "github.com/solaegis/boilr/pkg/util/osutil" + "github.com/solaegis/boilr/pkg/util/templateinput" "github.com/solaegis/boilr/pkg/util/validate" ) @@ -64,6 +66,16 @@ var Use = &cli.Command{ exit.Fatal(fmt.Errorf("use: %s", err)) } + contextFile := GetStringFlag(cmd, "use-file") + if contextFile != "" { + + err := tmpl.CachaedValuesFromJson(contextFile) + if err != nil { + exit.Fatal(fmt.Errorf("error reading values value frrom %s", contextFile)) + } + tmpl.UseDefaultValues() + } + if shouldUseDefaults := GetBoolFlag(cmd, "use-defaults"); shouldUseDefaults { tmpl.UseDefaultValues() } @@ -98,6 +110,56 @@ var Use = &cli.Command{ exit.Fatal(fmt.Errorf("use: %v", err)) } + // store promted inputs in a json file + jsonFile := GetStringFlag(cmd, "json-file") + if jsonFile != "" { + file, err := json.MarshalIndent(templateinput.UserInput, "", " ") + if err != nil { + exit.Fatal(fmt.Errorf("use: %v", err)) + } + if err := ioutil.WriteFile(jsonFile, file, 0644); err != nil { + exit.Fatal(fmt.Errorf("use: %v", err)) + } + } + + if contextFile != "" { + f, err := os.Open(contextFile) + if err != nil { + if os.IsNotExist(err) { + fmt.Println("could not find ", contextFile, " Please fix the error and run the boilr again.") + exit.Fatal(fmt.Errorf("use: %v", err)) + } + fmt.Println("could not read ", contextFile, " Please fix the error and run the boilr again.") + exit.Fatal(fmt.Errorf("use: %v", err)) + } + defer f.Close() + buf, err := ioutil.ReadAll(f) + if err != nil { + fmt.Println("could not read ", contextFile, " Please fix the error and run the boilr again.") + exit.Fatal(fmt.Errorf("use: %v", err)) + } + var contextFileJson map[string]interface{} + if err := json.Unmarshal(buf, &contextFileJson); err != nil { + fmt.Println("could not read ", contextFile, " as a Joson. Please fix the error and run the boilr again.") + exit.Fatal(fmt.Errorf("use: %v", err)) + } + returnError := false + for k, _ := range templateinput.UsedKeys { + if _, ok := contextFileJson[k]; !ok { + returnError = true + fmt.Printf(` +******************************************************************************************************************** +boilr used project.json value for key %s. Please define the key and value in %s +********************************************************************************************************************`, + k, contextFile) + } + } + if returnError { + fmt.Println() + exit.Fatal(fmt.Errorf("Missing values in %s, please review the file", contextFile)) + } + } + exit.OK("Successfully executed the project template %v in %v", tmplName, targetDir) }, } diff --git a/pkg/prompt/prompt.go b/pkg/prompt/prompt.go index 051b3a4..c283145 100644 --- a/pkg/prompt/prompt.go +++ b/pkg/prompt/prompt.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" + "github.com/solaegis/boilr/pkg/util/templateinput" "github.com/solaegis/boilr/pkg/util/tlog" ) @@ -136,13 +137,32 @@ func New(fieldName string, defval interface{}) func() interface{} { if err != nil { tlog.Warn(err.Error()) } - cachedValue, err = prompt.EvaluateChoice(choice) if err != nil { tlog.Warn(err.Error()) } + templateinput.UserInput[fieldName] = cachedValue } return cachedValue } } + +func CachedValue(value string, defval interface{}, key string) func() interface{} { + prompt := Func(defval) + + var cachedValue interface{} + return func() interface{} { + if cachedValue == nil { + msg := prompt.PromptMessage(value) + tlog.Prompt(msg, defval) + var err error + cachedValue, err = prompt.EvaluateChoice(value) + if err != nil { + tlog.Warn(err.Error()) + } + } + templateinput.UsedKeys[key] = cachedValue + return cachedValue + } +} diff --git a/pkg/template/template.go b/pkg/template/template.go index 5ec0827..ba7a4a1 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -24,6 +24,9 @@ type Interface interface { // If used, the template will execute using default values. UseDefaultValues() + // If used, the template will execute using CachaedValuesFromJson. + CachaedValuesFromJson(string) error + // Returns the metadata of the template. Info() Metadata } @@ -38,7 +41,6 @@ func Get(path string) (Interface, error) { if err != nil { return nil, err } - // TODO make context optional ctxt, err := func(fname string) (map[string]interface{}, error) { f, err := os.Open(fname) @@ -88,71 +90,107 @@ func Get(path string) (Interface, error) { }() return &dirTemplate{ - Context: ctxt, - FuncMap: FuncMap, - Path: filepath.Join(absPath, boilr.TemplateDirName), + Context: ctxt, + FuncMap: FuncMap, + Path: filepath.Join(absPath, boilr.TemplateDirName), + Metadata: md, }, err } type dirTemplate struct { - Path string - Context map[string]interface{} - FuncMap template.FuncMap - Metadata Metadata + Path string + Context map[string]interface{} + StoredContext map[string]interface{} + FuncMap template.FuncMap + Metadata Metadata alignment string ShouldUseDefaults bool } +func (t *dirTemplate) CachaedValuesFromJson(path string) error { + absPath, err := filepath.Abs(path) + + if err != nil { + return err + } + fmt.Println(absPath) + // TODO make context optional + ctxt, err := func(fname string) (map[string]interface{}, error) { + f, err := os.Open(fname) + if err != nil { + if os.IsNotExist(err) { + fmt.Println("Not Found ->", fname) + return nil, nil + } + return nil, err + } + defer f.Close() + buf, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + var metadata map[string]interface{} + if err := json.Unmarshal(buf, &metadata); err != nil { + return nil, err + } + return metadata, nil + }(absPath) + t.StoredContext = ctxt + return nil +} + func (t *dirTemplate) UseDefaultValues() { t.ShouldUseDefaults = true } func (t *dirTemplate) BindPrompts() { - for s, v := range t.Context { - if m, ok := v.(map[string]interface{}); ok { - advancedMode := prompt.New(s, false) - - for k, v2 := range m { - if t.ShouldUseDefaults { - t.FuncMap[k] = func() interface{} { + // load all variables from project.json and stored.json. + // order of list is importanat as we want to use variable functions from t.StoredContext + // it is importanat that all variable functions should be avaliable before template execution + // otherwise you will get an error like - panic: template: : function "" not defined + + contextList := []map[string]interface{}{t.Context, t.StoredContext} + for _, ctx := range contextList { + for s, v := range ctx { + if m, ok := v.(map[string]interface{}); ok { + advancedMode := prompt.New(s, false) + + for k, v2 := range m { + if t.ShouldUseDefaults { switch v2 := v2.(type) { - // First is the default value if it's a slice case []interface{}: - return v2[0] + t.FuncMap[k] = prompt.CachedValue(fmt.Sprintf("%v", v2[0]), v2[0], k) + default: + t.FuncMap[k] = prompt.CachedValue(fmt.Sprintf("%v", v2), v2, k) } + } else { + v, p := v2, prompt.New(k, v2) - return v2 - } - } else { - v, p := v2, prompt.New(k, v2) + t.FuncMap[k] = func() interface{} { + if val := advancedMode().(bool); val { + return p() + } - t.FuncMap[k] = func() interface{} { - if val := advancedMode().(bool); val { - return p() + return v } - - return v } } - } - continue - } + continue + } - if t.ShouldUseDefaults { - t.FuncMap[s] = func() interface{} { + if t.ShouldUseDefaults { switch v := v.(type) { - // First is the default value if it's a slice case []interface{}: - return v[0] + t.FuncMap[s] = prompt.CachedValue(fmt.Sprintf("%v", v[0]), v[0], s) + default: + t.FuncMap[s] = prompt.CachedValue(fmt.Sprintf("%v", v), v, s) } - - return v + } else { + t.FuncMap[s] = prompt.New(s, v) } - } else { - t.FuncMap[s] = prompt.New(s, v) } } } diff --git a/pkg/util/templateinput/templateinput.go b/pkg/util/templateinput/templateinput.go new file mode 100644 index 0000000..6d2e43f --- /dev/null +++ b/pkg/util/templateinput/templateinput.go @@ -0,0 +1,23 @@ +package templateinput + +func init() { + UserInput.Init() + UsedKeys.Init() +} + +type set map[string]interface{} + +var UserInput set + +func (s *set) Init() { + *s = make(map[string]interface{}) +} + +// store keys used during boilr execution +type s map[string]interface{} + +var UsedKeys s + +func (s *s) Init() { + *s = make(map[string]interface{}) +}