From 979bdf16983e90c7789ad295a79527569c212b82 Mon Sep 17 00:00:00 2001 From: jolheiser Date: Mon, 28 Dec 2020 21:14:28 -0600 Subject: [PATCH 1/4] Allow sourcing prompt answers from env variables Signed-off-by: jolheiser --- DOCS.md | 4 ++++ registry/prompt.go | 24 ++++++++++++++---------- registry/registry_test.go | 6 +++--- registry/template.go | 2 +- registry/template_test.go | 11 +++++++++-- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/DOCS.md b/DOCS.md index 5d70838..638b13b 100644 --- a/DOCS.md +++ b/DOCS.md @@ -15,6 +15,10 @@ A "valid" tmpl template only requires two things The template.toml file will only expand environment variables with syntax `$USER` or `${USER}`. For full documentation on the syntax, see [os.ExpandEnv](https://golang.org/pkg/os/#ExpandEnv). +When using the `--defaults` flag, no prompts will be shown and only default values will be used. +As another alternative, any environment variable that matches a key will bypass the prompt. +For example, `author` would have the corresponding environment variable `TMPL_VAR_AUTHOR`. + ```toml # Key-value pairs can be simple # The user will receive a basic prompt asking them to fill out the variable diff --git a/registry/prompt.go b/registry/prompt.go index 62317c2..d0d34cf 100644 --- a/registry/prompt.go +++ b/registry/prompt.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "sort" + "strings" "text/template" "github.com/AlecAivazis/survey/v2" @@ -67,15 +68,22 @@ func prompt(dir string, defaults bool) (templatePrompts, error) { prompts[idx] = p } - // Return early if we only want defaults - if defaults { - return prompts, nil - } - // Sort the prompts so they are consistent sort.Sort(prompts) for idx, prompt := range prompts { + // Check for env variable + if e, ok := os.LookupEnv(fmt.Sprintf("TMPL_VAR_%s", strings.ToUpper(prompt.Key))); ok { + prompts[idx].Value = e + continue + } + + // Check if we are using defaults + if defaults { + prompts[idx].Value = prompt.Default + continue + } + var p survey.Prompt switch t := prompt.Default.(type) { case []string: @@ -119,11 +127,7 @@ type templatePrompts []templatePrompt func (t templatePrompts) ToMap() map[string]interface{} { m := make(map[string]interface{}) for _, p := range t { - if p.Value != nil { - m[p.Key] = p.Value - continue - } - m[p.Key] = p.Default + m[p.Key] = p.Value } return m } diff --git a/registry/registry_test.go b/registry/registry_test.go index 5b3e217..8adc7ca 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -17,7 +17,7 @@ var ( func TestMain(m *testing.M) { var err error - destDir, err = ioutil.TempDir(os.TempDir(), "tmpl") + destDir, err = ioutil.TempDir(os.TempDir(), "tmpl-dest") if err != nil { panic(err) } @@ -75,7 +75,7 @@ func testGetFail(t *testing.T) { func setupTemplate() { var err error - tmplDir, err = ioutil.TempDir(os.TempDir(), "tmpl") + tmplDir, err = ioutil.TempDir(os.TempDir(), "tmpl-setup") if err != nil { panic(err) } @@ -122,7 +122,7 @@ func setupTemplate() { func setupRegistry() { var err error - regDir, err = ioutil.TempDir(os.TempDir(), "tmpl") + regDir, err = ioutil.TempDir(os.TempDir(), "tmpl-reg") if err != nil { panic(err) } diff --git a/registry/template.go b/registry/template.go index 9f37184..938eff0 100644 --- a/registry/template.go +++ b/registry/template.go @@ -67,7 +67,7 @@ func (t *Template) Execute(dest string, defaults, overwrite bool) error { return err } - newDest := strings.TrimPrefix(walkPath, base+"/") + newDest := strings.TrimPrefix(walkPath, base+string(filepath.Separator)) newDest = filepath.Join(dest, newDest) tmplDest, err := template.New("dest").Funcs(funcs).Parse(newDest) diff --git a/registry/template_test.go b/registry/template_test.go index 019d3b1..dd8c317 100644 --- a/registry/template_test.go +++ b/registry/template_test.go @@ -8,7 +8,7 @@ import ( ) var ( - tmplContents = `{{title name}} {{if .bool}}{{.year}}{{end}}` + tmplContents = `{{title name}} (@{{username}}) {{if .bool}}{{.year}}{{end}}` tmplTemplate = ` name = "john olheiser" @@ -20,8 +20,11 @@ default = "pkg" [bool] default = true + +[username] +default = "username" ` - tmplGold = "John Olheiser 2020" + tmplGold = "John Olheiser (@jolheiser) 2020" tmplNewGold = "DO NOT OVERWRITE!" ) @@ -31,6 +34,10 @@ func testExecute(t *testing.T) { t.Logf("could not set environment: %v", err) t.FailNow() } + if err := os.Setenv("TMPL_VAR_USERNAME", "jolheiser"); err != nil { + t.Logf("could not set environment: %v", err) + t.FailNow() + } // Get template tmpl, err := reg.GetTemplate("test") -- 2.41.0 From d4c47101ab93639c8fca37aad97250dbeae577a6 Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Tue, 29 Dec 2020 11:21:41 +0800 Subject: [PATCH 2/4] Allow sourcing prompt answers from env variables (#14) Fixes #12 Co-authored-by: jolheiser Reviewed-on: https://gitea.com/jolheiser/tmpl/pulls/14 Co-authored-by: John Olheiser Co-committed-by: John Olheiser --- DOCS.md | 4 ++++ registry/prompt.go | 24 ++++++++++++++---------- registry/registry_test.go | 6 +++--- registry/template.go | 2 +- registry/template_test.go | 11 +++++++++-- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/DOCS.md b/DOCS.md index 5d70838..638b13b 100644 --- a/DOCS.md +++ b/DOCS.md @@ -15,6 +15,10 @@ A "valid" tmpl template only requires two things The template.toml file will only expand environment variables with syntax `$USER` or `${USER}`. For full documentation on the syntax, see [os.ExpandEnv](https://golang.org/pkg/os/#ExpandEnv). +When using the `--defaults` flag, no prompts will be shown and only default values will be used. +As another alternative, any environment variable that matches a key will bypass the prompt. +For example, `author` would have the corresponding environment variable `TMPL_VAR_AUTHOR`. + ```toml # Key-value pairs can be simple # The user will receive a basic prompt asking them to fill out the variable diff --git a/registry/prompt.go b/registry/prompt.go index 62317c2..d0d34cf 100644 --- a/registry/prompt.go +++ b/registry/prompt.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "sort" + "strings" "text/template" "github.com/AlecAivazis/survey/v2" @@ -67,15 +68,22 @@ func prompt(dir string, defaults bool) (templatePrompts, error) { prompts[idx] = p } - // Return early if we only want defaults - if defaults { - return prompts, nil - } - // Sort the prompts so they are consistent sort.Sort(prompts) for idx, prompt := range prompts { + // Check for env variable + if e, ok := os.LookupEnv(fmt.Sprintf("TMPL_VAR_%s", strings.ToUpper(prompt.Key))); ok { + prompts[idx].Value = e + continue + } + + // Check if we are using defaults + if defaults { + prompts[idx].Value = prompt.Default + continue + } + var p survey.Prompt switch t := prompt.Default.(type) { case []string: @@ -119,11 +127,7 @@ type templatePrompts []templatePrompt func (t templatePrompts) ToMap() map[string]interface{} { m := make(map[string]interface{}) for _, p := range t { - if p.Value != nil { - m[p.Key] = p.Value - continue - } - m[p.Key] = p.Default + m[p.Key] = p.Value } return m } diff --git a/registry/registry_test.go b/registry/registry_test.go index 5b3e217..8adc7ca 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -17,7 +17,7 @@ var ( func TestMain(m *testing.M) { var err error - destDir, err = ioutil.TempDir(os.TempDir(), "tmpl") + destDir, err = ioutil.TempDir(os.TempDir(), "tmpl-dest") if err != nil { panic(err) } @@ -75,7 +75,7 @@ func testGetFail(t *testing.T) { func setupTemplate() { var err error - tmplDir, err = ioutil.TempDir(os.TempDir(), "tmpl") + tmplDir, err = ioutil.TempDir(os.TempDir(), "tmpl-setup") if err != nil { panic(err) } @@ -122,7 +122,7 @@ func setupTemplate() { func setupRegistry() { var err error - regDir, err = ioutil.TempDir(os.TempDir(), "tmpl") + regDir, err = ioutil.TempDir(os.TempDir(), "tmpl-reg") if err != nil { panic(err) } diff --git a/registry/template.go b/registry/template.go index 9f37184..938eff0 100644 --- a/registry/template.go +++ b/registry/template.go @@ -67,7 +67,7 @@ func (t *Template) Execute(dest string, defaults, overwrite bool) error { return err } - newDest := strings.TrimPrefix(walkPath, base+"/") + newDest := strings.TrimPrefix(walkPath, base+string(filepath.Separator)) newDest = filepath.Join(dest, newDest) tmplDest, err := template.New("dest").Funcs(funcs).Parse(newDest) diff --git a/registry/template_test.go b/registry/template_test.go index 019d3b1..dd8c317 100644 --- a/registry/template_test.go +++ b/registry/template_test.go @@ -8,7 +8,7 @@ import ( ) var ( - tmplContents = `{{title name}} {{if .bool}}{{.year}}{{end}}` + tmplContents = `{{title name}} (@{{username}}) {{if .bool}}{{.year}}{{end}}` tmplTemplate = ` name = "john olheiser" @@ -20,8 +20,11 @@ default = "pkg" [bool] default = true + +[username] +default = "username" ` - tmplGold = "John Olheiser 2020" + tmplGold = "John Olheiser (@jolheiser) 2020" tmplNewGold = "DO NOT OVERWRITE!" ) @@ -31,6 +34,10 @@ func testExecute(t *testing.T) { t.Logf("could not set environment: %v", err) t.FailNow() } + if err := os.Setenv("TMPL_VAR_USERNAME", "jolheiser"); err != nil { + t.Logf("could not set environment: %v", err) + t.FailNow() + } // Get template tmpl, err := reg.GetTemplate("test") -- 2.41.0 From c497431a5205cddb6e151f9b6c0e11280b2a7519 Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Sun, 3 Jan 2021 11:06:32 +0800 Subject: [PATCH 3/4] Add .tmplkeep (#16) Resolves #15 Co-authored-by: jolheiser Reviewed-on: https://gitea.com/jolheiser/tmpl/pulls/16 Co-authored-by: John Olheiser Co-committed-by: John Olheiser --- registry/registry_test.go | 3 ++- registry/template.go | 5 +++++ registry/template_test.go | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/registry/registry_test.go b/registry/registry_test.go index 8adc7ca..628e29d 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -98,7 +98,8 @@ func setupTemplate() { if err := os.MkdirAll(pkgPath, os.ModePerm); err != nil { panic(err) } - fi, err = os.Create(filepath.Join(pkgPath, ".keep")) + // .tmplkeep file + fi, err = os.Create(filepath.Join(pkgPath, ".tmplkeep")) if err != nil { panic(err) } diff --git a/registry/template.go b/registry/template.go index 938eff0..76bf84c 100644 --- a/registry/template.go +++ b/registry/template.go @@ -85,6 +85,11 @@ func (t *Template) Execute(dest string, defaults, overwrite bool) error { return err } + // Skip .tmplkeep files, after creating the directory structure + if strings.EqualFold(walkInfo.Name(), ".tmplkeep") { + return nil + } + oldFi, err := os.Lstat(walkPath) if err != nil { return err diff --git a/registry/template_test.go b/registry/template_test.go index dd8c317..a51717b 100644 --- a/registry/template_test.go +++ b/registry/template_test.go @@ -72,6 +72,13 @@ func testExecute(t *testing.T) { t.FailNow() } + // Check for .tmplkeep + tmplKeep := filepath.Join(pkgPath, ".tmplkeep") + if _, err := os.Lstat(tmplKeep); err == nil { + t.Logf(".tmplkeep files should NOT be retained upon execution: %s\n", tmplKeep) + t.FailNow() + } + // Change file to test non-overwrite if err := ioutil.WriteFile(testPath, []byte(tmplNewGold), os.ModePerm); err != nil { t.Logf("could not write file: %v\n", err) -- 2.41.0 From c173eee38c48424abe470b8ed2b9ef0dd48f65f8 Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Sun, 3 Jan 2021 11:29:37 +0800 Subject: [PATCH 4/4] Explain .tmplkeep (#17) Co-authored-by: jolheiser Reviewed-on: https://gitea.com/jolheiser/tmpl/pulls/17 Co-authored-by: John Olheiser Co-committed-by: John Olheiser --- .gitea/pull_request_template.md | 6 ++++++ DOCS.md | 11 ++++++++++- docs.go | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 .gitea/pull_request_template.md diff --git a/.gitea/pull_request_template.md b/.gitea/pull_request_template.md new file mode 100644 index 0000000..f4def42 --- /dev/null +++ b/.gitea/pull_request_template.md @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/DOCS.md b/DOCS.md index 638b13b..7a3f2b9 100644 --- a/DOCS.md +++ b/DOCS.md @@ -119,4 +119,13 @@ I realize that many users will be using GitHub, and most will likely still be us 1. The simplest solution is to make a copy of your `registry.toml` (default: `~/.tmpl/registry.toml`). * Once in the new location, you will need to use `tmpl restore`. -2. Alternatively, you can copy/paste the entire registry (default: `~/.tmpl`) and skip the restore step. \ No newline at end of file +2. Alternatively, you can copy/paste the entire registry (default: `~/.tmpl`) and skip the restore step. + +## `.tmplkeep` + +Perhaps you are familiar with `.gitkeep` and its unofficial status in git. Git does not like empty directories, so usually +a `.gitkeep` (or just `.keep`) file is added to retain the directory while keeping it effectively empty. + +tmpl instead uses `.tmplkeep` files for this purpose. The difference is, tmpl will **not** create the `.tmplkeep` file +when the template is executed. This allows you to set up directory structures (for staging, examples, etc.) that +will *actually* be empty after execution. \ No newline at end of file diff --git a/docs.go b/docs.go index 3ca29f5..aa13bf6 100644 --- a/docs.go +++ b/docs.go @@ -28,7 +28,7 @@ func main() { md = md[strings.Index(md, "#"):] // CLI is using real default instead of DefaultText - md = regexp.MustCompile(`[\/\w]+\.tmpl`).ReplaceAllString(md, "~/.tmpl") + md = regexp.MustCompile(`[\/\\:\w]+\.tmpl`).ReplaceAllString(md, "~/.tmpl") if _, err := fi.WriteString(md); err != nil { panic(err) -- 2.41.0