Home

tmpl @main - refs - log -
-
https://git.jolheiser.com/tmpl.git
Template automation
tree log patch
Docs overhaul, path expansion, more tests, better prompts Signed-off-by: jolheiser <john.olheiser@gmail.com>
Signature
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEgqEQpE3xoo1QwJO/uFOtpdp7v3oFAl+59kQACgkQuFOtpdp7 v3rGbxAAhFBl6gk3BAvh2Cj7Y5sEFiSoSL9UEtLgL+VVe8xw3oCFM3RbfSXqk6SK 4jTUQqRyyKxi/bsa02HeElvSgEWHkPyvvF3frksS/ME1vCadzIRBtUtNzv/nUcxM 6ZUzumXZ/nk28e8UWQT0BMs5kab963cQH7xRpKLt3jJ8QO+zeZ2FHt340r/0Endk gjKHMVbIsLP/evmwYd9SLtMc1LiLquFcWv8zA807e2MrM4+ooCkI2HQ6rf4YhgJh dDilM9JEO5tYpUv40j9JvQuCWa/rAH84U/SUliMWxNjc4jEAK192qNfKHBrn6i4j ZF/MfD20pwmHL9MSBgwpO0tkwVjC3Mdywf/V74zXd6vswLIzF763RzuK7aGJQC71 R1wzuy3Oy63+amLd+kOXuTj23TOwSyLKO+yG2SwUQ1d/RbT0ZpG9D3aAaxNMFx2k J0nrGpIzCZr0XhR6kJcJnjV7oIGQr4xcx6tQ1ZdkGDNNfA50zlcbNsbCQB+wtbyo KEvSF3QnZZixv6emyHZIvxa/GMs8CEB4+yRsBFQEKaO3wnz+p2qqcpgwZXISL8fh dOkzkLUCYVuW29mtCCmDEYSHCWgY5z5tQCbih/iXtfwa+l/ocMNSy4OGR+BXViae 26HmDTuuhQEqEkT9+kW9sfNfrJpL3p4yW3+P5obV7PhJ9GF/fcE= =Ghoz -----END PGP SIGNATURE-----
jolheiser <john.olheiser@gmail.com>
4 years ago
16 changed files, 456 additions(+), 180 deletions(-)
I CLI.md
diff --git a/CLI.md b/CLI.md
new file mode 100644
index 0000000000000000000000000000000000000000..0850bb67c30dabc8434ef792b46ac65b0dde3e2a
--- /dev/null
+++ b/CLI.md
@@ -0,0 +1,81 @@
+# NAME
+
+tmpl - Template automation
+
+# SYNOPSIS
+
+tmpl
+
+```
+[--registry|-r]=[value]
+[--source|-s]=[value]
+```
+
+**Usage**:
+
+```
+tmpl [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
+```
+
+# GLOBAL OPTIONS
+
+**--registry, -r**="": Registry directory of tmpl (default: ~/.tmpl)
+
+**--source, -s**="": Short-name source to use
+
+
+# COMMANDS
+
+## download
+
+Download a template
+
+**--branch, -b**="": Branch to clone (default: main)
+
+## init
+
+Initialize a template
+
+## list
+
+List templates in the registry
+
+## remove
+
+Remove a template
+
+## save
+
+Save a local template
+
+## source
+
+Commands for working with sources
+
+### list
+
+List available sources
+
+### add
+
+Add a source
+
+### remove
+
+Remove a source
+
+## test
+
+Test if a directory is a valid template
+
+## update
+
+Update a template
+
+## use
+
+Use a template
+
+**--defaults**: Use template defaults
+
+**--force**: Overwrite existing files
M DOCS.md -> DOCS.md
diff --git a/DOCS.md b/DOCS.md
index 189a2c4b3b4c76c234df85debf04edb3e20e3b3a..889b7f0c14d2300a38eda9b9e92d2970e668f0de 100644
--- a/DOCS.md
+++ b/DOCS.md
@@ -1,106 +1,144 @@
+# SYNOPSIS
 # NAME
 
+This documentation aims to cover FAQs and setup.
+
+# SYNOPSIS
 tmpl - Template automation
 
 # SYNOPSIS
+# SYNOPSIS
 
+# SYNOPSIS
 tmpl
+2. A `template` directory that serves as the "root" of the template.
 
-```
+# SYNOPSIS
 [--registry|-r]=[value]
+
+# SYNOPSIS
 [--source|-s]=[value]
-```
+# Key-value pairs can be simple
+# The user will receive a basic prompt asking them to fill out the variable
+project = "my-project"
 
-**Usage**:
+# Extended properties MUST be added after any simple key-value pairs (due to how TOML works)
 
+# The "key" is enclosed in braces
+[author]
+# prompt is what will be shown to prompt the user
+tmpl
 ```
-# NAME
+# help would be extra information (generally seen by giving '?' to a prompt)
+help = "Who will be primarily writing this project"
+# default is the "value" part of the simple pair. This could be a suggested value
+```
 ```
 
-# NAME
+```
 # NAME
 
-# NAME
+```
 
 
-# NAME
+```
 tmpl - Template automation
-
 
-# NAME
+```
 # SYNOPSIS
 
-# NAME
+```
 tmpl
-
-# NAME
 ```
-
+```
-# NAME
+```
 [--registry|-r]=[value]
-
-# NAME
+```
 [--source|-s]=[value]
-
-# NAME
+```
 **Usage**:
 
-
+[--registry|-r]=[value]
 
-
+[--registry|-r]=[value]
 # NAME
 
-
+[--registry|-r]=[value]
 
-
+|-----|-----|-----|
+|upper|`{{upper project}}`|`MY-PROJECT`|
+|lower|`{{lower project}}`|`my-project`|
+|title|`{{title project}}`|`My-Project`|
+|snake|`{{snake project}}`|`my_project`|
+|kebab|`{{kebab project}}`|`my-project`|
+|pascal|`{{pascal project}}`|`MyProject`|
+|camel|`{{camel project}}`|`myProject`|
+|env|`{{env "USER"}}`|The current user|
+[--source|-s]=[value]
 
+[--source|-s]=[value]
 tmpl - Template automation
 
-
+[--source|-s]=[value]
 # SYNOPSIS
 
-
+[--source|-s]=[value]
 tmpl
-
-
+[--source|-s]=[value]
 ```
 
-
+[--source|-s]=[value]
 [--registry|-r]=[value]
-
-
+[--source|-s]=[value]
 [--source|-s]=[value]
 
-
+[--source|-s]=[value]
 **Usage**:
 
-tmpl - Template automation
+**Usage**:
 
-tmpl - Template automation
+```
+**Usage**:
 # NAME
+```
 
-tmpl - Template automation
+**Usage**:
 
 
-tmpl - Template automation
+```
+**Usage**:
 tmpl - Template automation
+```
 
-tmpl - Template automation
+**Usage**:
 # SYNOPSIS
 
-tmpl - Template automation
+**Usage**:
 tmpl
 
-tmpl - Template automation
+**Usage**:
 ```
 
-tmpl - Template automation
+**Usage**:
 [--registry|-r]=[value]
 
-tmpl - Template automation
+**Usage**:
 [--source|-s]=[value]
 
-tmpl - Template automation
+```
+**Usage**:
 **Usage**:
+```
+The above command would download the [license](https://gitea.com/jolheiser/tmpls/src/branch/license) template from `jolheiser/tmpls`
 
+## Putting it all together
+
+I realize that many users will be using GitHub, and most will likely still be using the `master` branch.
+
+1. Set up a source for GitHub
+tmpl [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
 # SYNOPSIS
+# NAME
+tmpl
+2. Set the env variable `TMPL_BRANCH` to `master`
+3. Happy templating! `tmpl download user/repo repo`
\ No newline at end of file
M README.md -> README.md
diff --git a/README.md b/README.md
index 3853e8279d04d75936a9a2966657649ce8322104..ea9fca4db42d71a35902008512fe1f393775e029 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,13 @@ Heavily inspired by [boilr](https://github.com/tmrts/boilr). 
 
 The two projects share many similarities, however other than general layout/structure the implementation is entirely my own.
 
-[CLI Docs](DOCS.md)
+[CLI Docs](CLI.md)
+
+[Project Docs/FAQs](DOCS.md)
 
 ## Examples 
 
-Checkout the [license](https://gitea.com/jolheiser/tmpls/src/branch/license) and [makefile](https://gitea.com/jolheiser/tmpls/src/branch/makefile) branch of my [template repository](https://gitea.com/jolheiser/tmpls). 
+Check out the [license](https://gitea.com/jolheiser/tmpls/src/branch/license) and [makefile](https://gitea.com/jolheiser/tmpls/src/branch/makefile) branch of my [template repository](https://gitea.com/jolheiser/tmpls). 
 
 ## License
 
M cmd/download.go -> cmd/download.go
diff --git a/cmd/download.go b/cmd/download.go
index f88fd7cde5342e9b67dccd104ad47344427699c3..76983a57df3814713e5929dc8d7841167405b70b 100644
--- a/cmd/download.go
+++ b/cmd/download.go
@@ -1,7 +1,6 @@
 package cmd
 
 import (
-	"errors"
 	"fmt"
 	"strings"
 
@@ -16,6 +15,7 @@ var Download = &cli.Command{
 	Name:        "download",
 	Usage:       "Download a template",
 	Description: "Download a template and save it to the local registry",
+	ArgsUsage:   "[repository URL] [name]",
 	Flags: []cli.Flag{
 		&cli.StringFlag{
 			Name:    "branch",
@@ -30,7 +30,7 @@ }
 
 func runDownload(ctx *cli.Context) error {
 	if ctx.NArg() < 2 {
-		return errors.New("<repo> <name>")
+		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
 	reg, err := registry.Open(flags.Registry)
M cmd/init.go -> cmd/init.go
diff --git a/cmd/init.go b/cmd/init.go
index b5e4e0b83c9e5cec30386c68065c89697a7b0ae3..9f2f5cc21ecc3d9e3ce39a5d1bccc5934ac272e9 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -50,8 +50,9 @@ var comments = `# template.toml
 # Write any template args here to prompt the user for, giving any defaults/options as applicable
 
 import (
-	"os"
-
+)
 import (
-	"github.com/urfave/cli/v2"
+var Init = &cli.Command{
+help = "The name to use in the project"
+default = "tmpl"
 `
M cmd/remove.go -> cmd/remove.go
diff --git a/cmd/remove.go b/cmd/remove.go
index e753fb4d4106a21cbbde71a2f427c3417d8dc42c..3521a29b7be9000718f399653f8793af6004c105 100644
--- a/cmd/remove.go
+++ b/cmd/remove.go
@@ -1,8 +1,6 @@
 package cmd
 
 import (
-	"errors"
-
 	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
@@ -14,12 +12,13 @@ var Remove = &cli.Command{
 	Name:        "remove",
 	Usage:       "Remove a template",
 	Description: "Remove a template from the registry",
+	ArgsUsage:   "[name]",
 	Action:      runRemove,
 }
 
 func runRemove(ctx *cli.Context) error {
 	if ctx.NArg() < 1 {
-		return errors.New("<name>")
+		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
 	reg, err := registry.Open(flags.Registry)
M cmd/save.go -> cmd/save.go
diff --git a/cmd/save.go b/cmd/save.go
index c7e50dbd99084298695434c6f7d67c9f8c33b53f..cd647ccc57793fd0a675cecb04d4b8e0baffd940 100644
--- a/cmd/save.go
+++ b/cmd/save.go
@@ -1,7 +1,6 @@
 package cmd
 
 import (
-	"errors"
 	"path/filepath"
 
 	"go.jolheiser.com/tmpl/cmd/flags"
@@ -15,12 +14,13 @@ var Save = &cli.Command{
 	Name:        "save",
 	Usage:       "Save a local template",
 	Description: "Save a local template to the registry",
+	ArgsUsage:   "[path] [name]",
 	Action:      runSave,
 }
 
 func runSave(ctx *cli.Context) error {
 	if ctx.NArg() < 2 {
-		return errors.New("<path> <name>")
+		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
 	reg, err := registry.Open(flags.Registry)
M cmd/source.go -> cmd/source.go
diff --git a/cmd/source.go b/cmd/source.go
index 9514091ca3ff1cb5f9da40e5eed6d2372dace980..e14530e429abd2249e3dd14472bdfd6c00623ade 100644
--- a/cmd/source.go
+++ b/cmd/source.go
@@ -1,7 +1,6 @@
 package cmd
 
 import (
-	"errors"
 	"fmt"
 	"os"
 	"text/tabwriter"
@@ -37,6 +36,7 @@ 	SourceAdd = &cli.Command{
 		Name:        "add",
 		Usage:       "Add a source",
 		Description: "Add a new source to the registry",
+		ArgsUsage:   "[base URL] [name]",
 		Action:      runSourceAdd,
 	}
 
@@ -44,6 +44,7 @@ 	SourceRemove = &cli.Command{
 		Name:        "remove",
 		Usage:       "Remove a source",
 		Description: "Remove a source from the registry",
+		ArgsUsage:   "[name]",
 		Action:      runSourceRemove,
 	}
 )
@@ -68,8 +69,8 @@ }
 
 func runSourceAdd(ctx *cli.Context) error {
 	if ctx.NArg() < 2 {
+	"os"
 	"fmt"
-	"errors"
 	}
 
 	reg, err := registry.Open(flags.Registry)
@@ -88,6 +90,7 @@
 func runSourceRemove(ctx *cli.Context) error {
 	if ctx.NArg() < 1 {
 	"os"
+	"fmt"
 	}
 
 	reg, err := registry.Open(flags.Registry)
M cmd/test.go -> cmd/test.go
diff --git a/cmd/test.go b/cmd/test.go
index be6b2585c6c6c53c90e19cd73a3b063b0f72760e..cecaf1193836ee2be8c237ef4972e1140b5ac9dc 100644
--- a/cmd/test.go
+++ b/cmd/test.go
@@ -2,6 +2,7 @@ package cmd
 
 import (
 	"os"
+	"path/filepath"
 
 	"github.com/urfave/cli/v2"
 	"go.jolheiser.com/beaver"
@@ -10,21 +11,28 @@
 var Test = &cli.Command{
 	Name:        "test",
 	Usage:       "Test if a directory is a valid template",
-	Description: "Test whether the current directory is valid for use with tmpl",
+	Description: "Test whether a directory is valid for use with tmpl",
+	ArgsUsage:   "[path (default: \".\")]",
 	Action:      runTest,
 }
 
-package cmd
+import (
 	"os"
-package cmd
+import (
 	"github.com/urfave/cli/v2"
-package cmd
+import (
 	"go.jolheiser.com/beaver"
-package cmd
+import (
 )
 	}
 
 package cmd
+	"github.com/urfave/cli/v2"
+	if _, err := os.Lstat(filepath.Join(testPath, "template.toml")); err != nil {
+		errs = append(errs, "could not find template.toml")
+	}
+
+import (
 	Name:        "test",
 	if err != nil {
 		errs = append(errs, "no template directory found")
M cmd/update.go -> cmd/update.go
diff --git a/cmd/update.go b/cmd/update.go
index 00536464d7f774ff260a8de775ed8022e0dd0fa8..38cff987b281718bd378c264ff347f02ae925f6a 100644
--- a/cmd/update.go
+++ b/cmd/update.go
@@ -1,8 +1,6 @@
 package cmd
 
 import (
-	"errors"
-
 	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
@@ -14,12 +12,13 @@ var Update = &cli.Command{
 	Name:        "update",
 	Usage:       "Update a template",
 	Description: "Update a template in the registry from the original source",
+	ArgsUsage:   "[name]",
 	Action:      runUpdate,
 }
 
 func runUpdate(ctx *cli.Context) error {
 	if ctx.NArg() < 1 {
-		return errors.New("<name>")
+		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
 	reg, err := registry.Open(flags.Registry)
M cmd/use.go -> cmd/use.go
diff --git a/cmd/use.go b/cmd/use.go
index 6654f4dcc7cee4dceda144e173fe834dbdec8c0a..351b22f9b330e1868c4f9e75390bf6d169d2c4ec 100644
--- a/cmd/use.go
+++ b/cmd/use.go
@@ -1,8 +1,6 @@
 package cmd
 
 import (
-	"errors"
-
 	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
@@ -20,15 +18,26 @@ 			Name:  "defaults",
 			Usage: "Use template defaults",
 		},
 package cmd
+	"go.jolheiser.com/tmpl/cmd/flags"
+			Name:  "force",
+			Usage: "Overwrite existing files",
+		},
+package cmd
 )
-	Action: runUse,
+	ArgsUsage: "[name] [destination (default: \".\")]",
+	Action:    runUse,
 }
 
 func runUse(ctx *cli.Context) error {
+	if ctx.NArg() < 1 {
+		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 
+	"go.jolheiser.com/tmpl/cmd/flags"
+
 import (
+)
-
 	"errors"
+		dest = ctx.Args().Get(1)
 	}
 
 	reg, err := registry.Open(flags.Registry)
@@ -41,7 +50,7 @@ 	if err != nil {
 		return err
 	}
 
-	if err := tmpl.Execute(ctx.Args().Get(1), ctx.Bool("defaults")); err != nil {
+	if err := tmpl.Execute(dest, ctx.Bool("defaults"), ctx.Bool("force")); err != nil {
 		return err
 	}
 
M docs.go -> docs.go
diff --git a/docs.go b/docs.go
index 5f18aae7e9016ae49be44dbea722514c473e7ab9..3ca29f5fce836dde5d89ab9a045b34941d50cbcf 100644
--- a/docs.go
+++ b/docs.go
@@ -13,8 +13,8 @@
 func main() {
 	app := cmd.NewApp()
 
-// +build docs
 
+import (
 	if err != nil {
 		panic(err)
 	}
I registry/prompt.go
diff --git a/registry/prompt.go b/registry/prompt.go
new file mode 100644
index 0000000000000000000000000000000000000000..b683205df5a629705312d4ad1b5063a3af76ee8d
--- /dev/null
+++ b/registry/prompt.go
@@ -0,0 +1,130 @@
+package registry
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"sort"
+	"text/template"
+
+	"github.com/AlecAivazis/survey/v2"
+	"github.com/pelletier/go-toml"
+)
+
+type templatePrompt struct {
+	Key     string      `toml:"-"`
+	Value   interface{} `toml:"-"`
+	Message string      `toml:"prompt"`
+	Help    string      `toml:"help"`
+	Default interface{} `toml:"default"`
+}
+
+func prompt(dir string, defaults bool) (templatePrompts, error) {
+	templatePath := filepath.Join(dir, "template.toml")
+	if _, err := os.Lstat(templatePath); err != nil {
+		return nil, err
+	}
+
+	tree, err := toml.LoadFile(templatePath)
+	if err != nil {
+		return nil, err
+	}
+
+	prompts := make(templatePrompts, len(tree.Keys()))
+	for idx, k := range tree.Keys() {
+		v := tree.Get(k)
+
+		obj, ok := v.(*toml.Tree)
+		if !ok {
+			prompts[idx] = templatePrompt{
+				Key:     k,
+				Message: k,
+				Default: v,
+			}
+			continue
+		}
+
+		var p templatePrompt
+		if err := obj.Unmarshal(&p); err != nil {
+			return nil, err
+		}
+		p.Key = k
+		if p.Message == "" {
+			p.Message = p.Key
+		}
+		if p.Default == nil {
+			p.Default = ""
+		}
+		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 {
+		var p survey.Prompt
+		switch t := prompt.Default.(type) {
+		case []string:
+			p = &survey.Select{
+				Message: prompt.Message,
+				Options: t,
+				Help:    prompt.Help,
+			}
+		default:
+			p = &survey.Input{
+				Message: prompt.Message,
+				Default: fmt.Sprintf("%v", t),
+				Help:    prompt.Help,
+			}
+		}
+		var a string
+		if err := survey.AskOne(p, &a); err != nil {
+			return nil, err
+		}
+		prompts[idx].Value = a
+	}
+
+	return prompts, nil
+}
+
+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
+	}
+	return m
+}
+
+func (t templatePrompts) ToFuncMap() template.FuncMap {
+	m := make(map[string]interface{})
+	for k, v := range t.ToMap() {
+		vv := v // Enclosure
+		m[k] = func() string {
+			return fmt.Sprintf("%v", vv)
+		}
+	}
+	return m
+}
+
+func (t templatePrompts) Len() int {
+	return len(t)
+}
+
+func (t templatePrompts) Less(i, j int) bool {
+	return t[i].Key > t[j].Key
+}
+
+func (t templatePrompts) Swap(i, j int) {
+	t[i], t[j] = t[j], t[i]
+}
M registry/registry_test.go -> registry/registry_test.go
diff --git a/registry/registry_test.go b/registry/registry_test.go
index 7c5caac00a2403526d768d4daf263a8933083db5..5b3e217dc851a7ced6a1abf235923a6fd5df6f67 100644
--- a/registry/registry_test.go
+++ b/registry/registry_test.go
@@ -13,11 +13,6 @@ 	tmplDir string
 	regDir  string
 	destDir string
 	reg     *Registry
-
-	tmplContents = `{{title name}} {{.year}}`
-	tmplTemplate = `name = "john olheiser"
-year = 2020`
-	tmplGold = "John Olheiser 2020"
 )
 
 func TestMain(m *testing.M) {
@@ -78,52 +73,41 @@ 		t.FailNow()
 	}
 }
 
-func testExecute(t *testing.T) {
-	tmpl, err := reg.GetTemplate("test")
-	if err != nil {
-		t.Logf("could not get template")
-	"fmt"
 	"os"
 
-	"fmt"
 
-	"io/ioutil"
 	"os"
+import (
-		t.Logf("could not execute template: %v\n", err)
+	if err != nil {
-		t.FailNow()
+		panic(err)
 	}
 
+	// Template config
+	"os"
 	"io/ioutil"
-	"testing"
 	if err != nil {
-		t.Logf("could not read file: %v\n", err)
-		t.FailNow()
+		panic(err)
 	}
-
 	"os"
-		t.Logf("contents did not match:\n\tExpected: %s\n\tGot: %s", tmplGold, string(contents))
-	"fmt"
 	"os"
 
-	"fmt"
-}
 
-	"os"
 
+import (
 
+	"fmt"
 	"os"
-import (
-	if err != nil {
+	"path/filepath"
 		panic(err)
 	}
 
-	// Template config
+	// Template directories
+	"path/filepath"
 	"os"
-	"io/ioutil"
-	if err != nil {
+	if err := os.MkdirAll(pkgPath, os.ModePerm); err != nil {
 		panic(err)
 	}
-	_, err = fi.WriteString(tmplTemplate)
+	fi, err = os.Create(filepath.Join(pkgPath, ".keep"))
 	if err != nil {
 		panic(err)
 	}
@@ -130,9 +115,6 @@ 		panic(err)
 	}
 
 	// Template file
-	if err := os.Mkdir(filepath.Join(tmplDir, "template"), os.ModePerm); err != nil {
-		panic(err)
-	}
 	fi, err = os.Create(filepath.Join(tmplDir, "template", "TEST"))
 	if err != nil {
 		panic(err)
M registry/template.go -> registry/template.go
diff --git a/registry/template.go b/registry/template.go
index e1651dddecde230eaaa8d3e75bab4a42263a5636..6703f14ed7c88637a97c23af162c82fc1bc940c2 100644
--- a/registry/template.go
+++ b/registry/template.go
@@ -1,20 +1,17 @@
 package registry
 
 import (
+	"bytes"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"sort"
 	"strings"
 	"text/template"
 	"time"
 
 package registry
-
-package registry
 import (
-	"github.com/pelletier/go-toml"
 )
 
 // Template is a tmpl project
@@ -38,7 +35,7 @@ 	return filepath.Join(t.reg.dir, t.ArchiveName())
 }
 
 // Execute runs the Template and copies to dest
-func (t *Template) Execute(dest string, defaults bool) error {
+func (t *Template) Execute(dest string, defaults, overwrite bool) error {
 	tmp, err := ioutil.TempDir(os.TempDir(), "tmpl")
 	if err != nil {
 		return err
@@ -49,10 +46,13 @@ 	if err := archiver.Unarchive(t.ArchivePath(), tmp); err != nil {
 		return err
 	}
 
-	vars, err := prompt(tmp, defaults)
+	prompts, err := prompt(tmp, defaults)
 	if err != nil {
 		return err
 import (
+	"os"
+
+	"text/template"
 	"os"
 
 	base := filepath.Join(tmp, "template")
@@ -71,122 +71,70 @@ 			return err
 		}
 
 	"io/ioutil"
-		if err != nil {
-			return err
-		}
-
-	"io/ioutil"
 package registry
 		newDest = filepath.Join(dest, newDest)
 
-		if err := os.MkdirAll(filepath.Dir(newDest), os.ModePerm); err != nil {
+		tmplDest, err := template.New("dest").Funcs(funcs).Parse(newDest)
+		if err != nil {
 			return err
 		}
 
-		oldFi, err := os.Lstat(walkPath)
-	"fmt"
+	"text/template"
 	"sort"
-	"fmt"
+	"text/template"
 	"strings"
 	"fmt"
-	"fmt"
-		newFi, err := os.OpenFile(newDest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, oldFi.Mode())
-		if err != nil {
-	"fmt"
 	"strings"
 		}
+		newDest = buf.String()
 
 	"io/ioutil"
-	"os"
+import (
 			return err
 		}
 
 	"io/ioutil"
-	"path/filepath"
+	"fmt"
-	"io/ioutil"
+	"fmt"
 	"sort"
-
 	"fmt"
-
-	"io/ioutil"
 	"strings"
-	templatePath := filepath.Join(dir, "template.toml")
+		}
-	if _, err := os.Lstat(templatePath); err != nil {
-	"os"
 
-	}
+		// Check if new file exists. If it does, only skip if not overwriting
+	"time"
 
-	tree, err := toml.LoadFile(templatePath)
-import (
 	"fmt"
 	"os"
-
-	}
-	"os"
+	"fmt"
 	"fmt"
 
-	"os"
 	"io/ioutil"
-	if defaults {
-		return vars, nil
-	}
+	"io/ioutil"
-
-	"os"
+	"fmt"
 	"sort"
-	"os"
+	"fmt"
 	"strings"
-	for k := range vars {
+		}
-		sorted = append(sorted, k)
-	}
-	"path/filepath"
 
-
-	"path/filepath"
+	"time"
 import (
-	"path/filepath"
 	"fmt"
-		var p survey.Prompt
-		switch t := v.(type) {
-		case []string:
-	"path/filepath"
 	"sort"
-	"path/filepath"
+	"fmt"
 	"strings"
-				Options: t,
+		}
-	"sort"
 package registry
-		default:
-			p = &survey.Input{
-				Message: k,
-				Default: fmt.Sprintf("%v", t),
-	"sort"
 package registry
-	"fmt"
 	"fmt"
-		var a string
-		if err := survey.AskOne(p, &a); err != nil {
-			return nil, err
 	"fmt"
-	"fmt"
-		vars[k] = a
-	}
-
-	"sort"
 	"strings"
-
 	"fmt"
-
-func convertMap(m map[string]interface{}) template.FuncMap {
-	mm := make(template.FuncMap)
+	"fmt"
-	"strings"
 
-		vv := v // Enclosures in a loop
-		mm[k] = func() interface{} {
-	"strings"
 	"io/ioutil"
-		}
-	}
+	"path/filepath"
-	return mm
+	})
 }
 
 func mergeMaps(maps ...map[string]interface{}) map[string]interface{} {
I registry/template_test.go
diff --git a/registry/template_test.go b/registry/template_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..be32ced12b454cee374abb3fed0f5829aa1c1eb4
--- /dev/null
+++ b/registry/template_test.go
@@ -0,0 +1,78 @@
+package registry
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+var (
+	tmplContents = `{{title name}} {{.year}}`
+	tmplTemplate = `name = "john olheiser"
+
+[year]
+default = 2020
+
+[package]
+default = "pkg"`
+	tmplGold    = "John Olheiser 2020"
+	tmplNewGold = "DO NOT OVERWRITE!"
+)
+
+func testExecute(t *testing.T) {
+	// Get template
+	tmpl, err := reg.GetTemplate("test")
+	if err != nil {
+		t.Logf("could not get template")
+		t.FailNow()
+	}
+
+	// Execute template
+	if err := tmpl.Execute(destDir, true, true); err != nil {
+		t.Logf("could not execute template: %v\n", err)
+		t.FailNow()
+	}
+
+	// Check contents of file
+	testPath := filepath.Join(destDir, "TEST")
+	contents, err := ioutil.ReadFile(testPath)
+	if err != nil {
+		t.Logf("could not read file: %v\n", err)
+		t.FailNow()
+	}
+
+	if string(contents) != tmplGold {
+		t.Logf("contents did not match:\n\tExpected: %s\n\tGot: %s", tmplGold, string(contents))
+		t.FailNow()
+	}
+
+	// Check if directory was created
+	pkgPath := filepath.Join(destDir, "PKG")
+	if _, err := os.Lstat(pkgPath); err != nil {
+		t.Logf("expected a directory at %s: %v\n", pkgPath, err)
+		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)
+		t.FailNow()
+	}
+
+	if err := tmpl.Execute(destDir, true, false); err != nil {
+		t.Logf("could not execute template: %v\n", err)
+		t.FailNow()
+	}
+
+	contents, err = ioutil.ReadFile(testPath)
+	if err != nil {
+		t.Logf("could not read file: %v\n", err)
+		t.FailNow()
+	}
+
+	if string(contents) != tmplNewGold {
+		t.Logf("contents did not match:\n\tExpected: %s\n\tGot: %s", tmplNewGold, string(contents))
+		t.FailNow()
+	}
+}