Home

tmpl @main - refs - log -
-
https://git.jolheiser.com/tmpl.git
Template automation
tree log patch
Add helpers, tests, and refactor download vs save Signed-off-by: jolheiser <john.olheiser@gmail.com>
Signature
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEgqEQpE3xoo1QwJO/uFOtpdp7v3oFAl+1WGgACgkQuFOtpdp7 v3pY8Q//TVinByPtKDvx4BUMISWT7HPSBYZ/90gODbrX1oduHb6sn0Pc7R6xMlHf 9o/JxRQ6A/40jtZ4KWzKrWF9qRECT7/5gWQSRsfQhlNu5kwkHTxhDjsXjwPdtpbO 7/pFl/vwcZw29fo/joRULSXC25Gjaa/ho4/8RYoSBhkUmy7fgTVy1SEoHfOtxOKO Vq5PJLqdfk5kE5DUqxV4aQ3s34JI+0qygUkmIsaNFz0pKirZLNXAVq6Q4NN5o+gh ISkHJK7aT3Pcx6HFOTLhY65Sfkof2mcVB9ils0OE8J3U2MlLnnZwNlxGokoOdlM2 KD3XAaRh6U9vmO1stHKSpZQ3czoa8ZJgfHqKQKrk5Y2Kvl14VXrXCPlDoGBGguqq jeUVjbik9FEULvBJ1nMnOiUCbdMl/AJkkn1W184iqVXczslY8AQhGxBUpYEjaLap 2r/QdjCv+JCtBZK+5NW5wZ+se1yYTuIxPJjob70ZZsX7u7F1eQRNeMRU3MkGfotb vkWkdG1Z5VVXNOUIhYezUkEak3l2oZk8ZiwWrRNfW1XcLZ66FBE386h5+BOv63ob pBCi3etVCHS9mLCWZjN2naMg4SUZ4Hlu0UDwyyZMgNMlwjVwY//fuzUzbjGsc0kP qWtont7rTe+kNwbmRQf9g5TRXx3IPlJPhRgeSABCaYWyevVvQDM= =VYkB -----END PGP SIGNATURE-----
jolheiser <john.olheiser@gmail.com>
4 years ago
14 changed files, 303 additions(+), 46 deletions(-)
DOCS.mdREADME.mdcmd/download.gocmd/save.gocmd/source.gocmd/update.gocmd/use.gogo.modgo.summain.goregistry/helper.goregistry/registry.goregistry/registry_test.goregistry/template.go
M DOCS.mdDOCS.md
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
diff --git a/DOCS.md b/DOCS.md
index 3dce5d7025cf9f97cd06a16fb6081224043b5046..7f9a148eb7a5ac728ddfc946d1da6e584fa83689 100644
--- a/DOCS.md
+++ b/DOCS.md
@@ -51,8 +51,6 @@ ## save
 
 Save a local template
 
-**--branch, -b**="": Branch to clone (default: main)
-
 ## source
 
 Commands for working with sources
@@ -63,11 +61,11 @@ List available sources
 
 ### add
 
-AddTemplate a source
+Add a source
 
 ### remove
 
-RemoveTemplate a source
+Remove a source
 
 ## test
 
@@ -80,3 +78,5 @@
 ## use
 
 Use a template
+
+**--defaults**: Use template defaults
M README.mdREADME.md
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
diff --git a/README.md b/README.md
index 1053560e8465238f60ba0313ab445e07d0239619..3853e8279d04d75936a9a2966657649ce8322104 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,10 @@ The two projects share many similarities, however other than general layout/structure the implementation is entirely my own.
 
 [CLI Docs](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). 
+
 ## License
 
 [MIT](LICENSE)
M cmd/download.gocmd/download.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
diff --git a/cmd/download.go b/cmd/download.go
index 2421ae8679352a1d9eae6e348cf1cf53cbd44298..c173c9e96ec0c3e241cca9cdf991f99eaa03eaac 100644
--- a/cmd/download.go
+++ b/cmd/download.go
@@ -59,7 +59,7 @@ 	if source != nil {
 		cloneURL = source.CloneURL(cloneURL)
 	}
 
-	t, err := reg.AddTemplate(ctx.Args().Get(1), cloneURL, ctx.String("branch"))
+	t, err := reg.DownloadTemplate(ctx.Args().Get(1), cloneURL, ctx.String("branch"))
 	if err != nil {
 		return err
 	}
M cmd/save.gocmd/save.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
diff --git a/cmd/save.go b/cmd/save.go
index 0d4c4c1e6d95bd881ce4e7a7ca6087b5abdf958e..c7e50dbd99084298695434c6f7d67c9f8c33b53f 100644
--- a/cmd/save.go
+++ b/cmd/save.go
@@ -3,7 +3,6 @@
 import (
 	"errors"
 	"path/filepath"
-	"strings"
 
 	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
@@ -16,16 +15,7 @@ var Save = &cli.Command{
 	Name:        "save",
 	Usage:       "Save a local template",
 	Description: "Save a local template to the registry",
-	Flags: []cli.Flag{
-		&cli.StringFlag{
-			Name:    "branch",
-			Aliases: []string{"b"},
-			Usage:   "Branch to clone",
-			Value:   "main",
-			EnvVars: []string{"TMPL_BRANCH"},
-		},
-	},
-	Action: runSave,
+	Action:      runSave,
 }
 
 func runSave(ctx *cli.Context) error {
@@ -38,17 +28,13 @@ 	if err != nil {
 		return err
 	}
 
-	// Did the user give us the root path, or the .git directory?
 	localPath := ctx.Args().First()
-	if !strings.HasSuffix(localPath, ".git") {
-		localPath = filepath.Join(localPath, ".git")
-	}
 	localPath, err = filepath.Abs(localPath)
 	if err != nil {
 		return err
 	}
 
-	t, err := reg.AddTemplate(ctx.Args().Get(1), localPath, ctx.String("branch"))
+	t, err := reg.SaveTemplate(ctx.Args().Get(1), localPath)
 	if err != nil {
 		return err
 	}
M cmd/source.gocmd/source.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
diff --git a/cmd/source.go b/cmd/source.go
index efb532f20b16306aac62b05616a3cde0343f1d19..4b341791eef4c4e306e8d6f89c114321a6800601 100644
--- a/cmd/source.go
+++ b/cmd/source.go
@@ -35,15 +35,15 @@ 	}
 
 	SourceAdd = &cli.Command{
 		Name:        "add",
-		Usage:       "AddTemplate a source",
-		Description: "AddTemplate a new source to the registry",
+		Usage:       "Add a source",
+		Description: "Add a new source to the registry",
 		Action:      runSourceAdd,
 	}
 
 	SourceRemove = &cli.Command{
 		Name:        "remove",
-		Usage:       "RemoveTemplate a source",
-		Description: "RemoveTemplate a source from the registry",
+		Usage:       "Remove a source",
+		Description: "Remove a source from the registry",
 		Action:      runSourceRemove,
 	}
 )
M cmd/update.gocmd/update.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
diff --git a/cmd/update.go b/cmd/update.go
index 03cdcba714c4940a7a6419a040d626e07d35cc80..00536464d7f774ff260a8de775ed8022e0dd0fa8 100644
--- a/cmd/update.go
+++ b/cmd/update.go
@@ -36,7 +36,12 @@ 	if err := reg.RemoveTemplate(tmpl.Name); err != nil {
 		return err
 	}
 
-	if _, err := reg.AddTemplate(tmpl.Name, tmpl.Repository, tmpl.Branch); err != nil {
+	if tmpl.Path != "" {
+		_, err = reg.SaveTemplate(tmpl.Name, tmpl.Path)
+	} else {
+		_, err = reg.DownloadTemplate(tmpl.Name, tmpl.Repository, tmpl.Branch)
+	}
+	if err != nil {
 		return err
 	}
 
M cmd/use.gocmd/use.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
diff --git a/cmd/use.go b/cmd/use.go
index 3024f859a61e716be5a85d26c90baa45ec47cba4..6654f4dcc7cee4dceda144e173fe834dbdec8c0a 100644
--- a/cmd/use.go
+++ b/cmd/use.go
@@ -14,7 +14,13 @@ var Use = &cli.Command{
 	Name:        "use",
 	Usage:       "Use a template",
 	Description: "Use (execute) a template from the registry",
-	Action:      runUse,
+	Flags: []cli.Flag{
+		&cli.BoolFlag{
+			Name:  "defaults",
+			Usage: "Use template defaults",
+		},
+	},
+	Action: runUse,
 }
 
 func runUse(ctx *cli.Context) error {
@@ -32,7 +38,7 @@ 	if err != nil {
 		return err
 	}
 
-	if err := tmpl.Execute(ctx.Args().Get(1)); err != nil {
+	if err := tmpl.Execute(ctx.Args().Get(1), ctx.Bool("defaults")); err != nil {
 		return err
 	}
 
M go.modgo.mod
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/go.mod b/go.mod
index 0c6ccbf2d44faba69e4a6b9f69b7e3cf805cd161..44fa718b817a5f059f80a7fe83d0597fe7dadd5f 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@
 require (
 	github.com/AlecAivazis/survey/v2 v2.2.2
 	github.com/go-git/go-git/v5 v5.2.0
+	github.com/huandu/xstrings v1.3.2
 	github.com/mholt/archiver/v3 v3.5.0
 	github.com/pelletier/go-toml v1.8.1
 	github.com/urfave/cli/v2 v2.3.0
M go.sumgo.sum
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
diff --git a/go.sum b/go.sum
index 31ff46eeb4e2800049c77deb9060fb07d79b8b64..701e1fa2afa39765e2b3c157a68b1776199a7ea0 100644
--- a/go.sum
+++ b/go.sum
@@ -1,38 +1,47 @@
-github.com/AlecAivazis/survey v1.8.8 h1:Y4yypp763E8cbqb5RBqZhGgkCFLRFnbRBHrxnpMMsgQ=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
 github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
 github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
 github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-git v1.0.0 h1:YcN9iDGDoXuIw0vHls6rINwV416HYa0EB2X+RBsyYp4=
-github.com/go-git/go-git v4.7.0+incompatible h1:+W9rgGY4DOKKdX2x6HxSR7HNeTxqiKrOvKnuittYVdA=
+github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
 github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
 github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
 github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
 github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
 github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
+github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
+github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
 github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@@ -50,8 +59,10 @@ github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A=
 github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
 github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -59,12 +70,11 @@ github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
-github.com/mholt/archiver v1.1.2 h1:xukR55YIrnhDHp10lrNtRSsAK5THpWrOCuviweNSBw4=
-github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU=
 github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
 github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
 github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
@@ -72,7 +82,9 @@ github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
 github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
 github.com/pierrec/lz4/v4 v4.0.3 h1:vNQKSVZNYUEAvRY9FaUXAF1XPbSOHJtDTiP41kzDz2E=
 github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -82,11 +94,11 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4=
 github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
-github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
 github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
 github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
@@ -118,9 +130,11 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
M main.gomain.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
diff --git a/main.go b/main.go
index f07d7c2d4dff04617be272cc8d8b0b35a33477cf..fe9da6f44fc7ec4cd40b5cbff541fd3c2dfad4b0 100644
--- a/main.go
+++ b/main.go
@@ -6,11 +6,12 @@
 	"go.jolheiser.com/tmpl/cmd"
 
 	"go.jolheiser.com/beaver"
+	"go.jolheiser.com/beaver/color"
 )
 
 func main() {
 	app := cmd.NewApp()
-
+	color.Fatal = color.Error // Easier to read, doesn't need to stand out as much in a CLI
 	if err := app.Run(os.Args); err != nil {
 		beaver.Fatal(err)
 	}
I registry/helper.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
diff --git a/registry/helper.go b/registry/helper.go
new file mode 100644
index 0000000000000000000000000000000000000000..2ce93cb0cc6ec2b8975c8b6b34c8cf6ef05d1eab
--- /dev/null
+++ b/registry/helper.go
@@ -0,0 +1,33 @@
+package registry
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/huandu/xstrings"
+)
+
+var funcMap = map[string]interface{}{
+
+	// String conversions
+	"upper":  strings.ToUpper,
+	"lower":  strings.ToLower,
+	"title":  strings.Title,
+	"snake":  xstrings.ToSnakeCase,
+	"kebab":  xstrings.ToKebabCase,
+	"pascal": xstrings.ToCamelCase,
+	"camel": func(in string) string {
+		return xstrings.FirstRuneToLower(xstrings.ToCamelCase(in))
+	},
+
+	// Other
+	"env": os.Getenv,
+	"sep": func() string {
+		return string(filepath.Separator)
+	},
+	"time": func(fmt string) string {
+		return time.Now().Format(fmt)
+	},
+}
M registry/registry.goregistry/registry.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
diff --git a/registry/registry.go b/registry/registry.go
index 9303e0255de3aa49bab47456f9a25fe6cd1cb3d8..f685756b969d2c7da935d730511c3e3488c20a19 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -48,8 +48,8 @@ 	}
 	return nil, ErrTemplateNotFound{Name: name}
 }
 
-// AddTemplate downloads and adds a new Template to the Registry
-func (r *Registry) AddTemplate(name, repo, branch string) (*Template, error) {
+// DownloadTemplate downloads and adds a new Template to the Registry
+func (r *Registry) DownloadTemplate(name, repo, branch string) (*Template, error) {
 	t := &Template{
 		reg:        r,
 		Name:       name,
@@ -60,6 +60,23 @@ 	}
 	r.Templates = append(r.Templates, t)
 
 	if err := download(repo, branch, t.ArchivePath()); err != nil {
+		return nil, err
+	}
+
+	return t, r.save()
+}
+
+// SaveTemplate saves a local Template to the Registry
+func (r *Registry) SaveTemplate(name, path string) (*Template, error) {
+	t := &Template{
+		reg:     r,
+		Name:    name,
+		Path:    path,
+		Created: time.Now(),
+	}
+	r.Templates = append(r.Templates, t)
+
+	if err := save(path, t.ArchivePath()); err != nil {
 		return nil, err
 	}
 
@@ -178,11 +195,21 @@ 	if err := os.RemoveAll(filepath.Join(tmp, ".git")); err != nil {
 		return err
 	}
 
+	// Save the template
+	if err := save(tmp, dest); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func save(source, dest string) error {
+
 	// Make sure it's a valid template
-	if _, err := os.Lstat(filepath.Join(tmp, "template.toml")); err != nil {
+	if _, err := os.Lstat(filepath.Join(source, "template.toml")); err != nil {
 		return err
 	}
-	fi, err := os.Lstat(filepath.Join(tmp, "template"))
+	fi, err := os.Lstat(filepath.Join(source, "template"))
 	if err != nil {
 		return err
 	}
@@ -191,7 +218,7 @@ 		return errors.New("template found, expected directory")
 	}
 
 	// Create archive
-	glob, err := filepath.Glob(filepath.Join(tmp, "*"))
+	glob, err := filepath.Glob(filepath.Join(source, "*"))
 	if err != nil {
 		return err
 	}
I registry/registry_test.go
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
diff --git a/registry/registry_test.go b/registry/registry_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f05711157df667084b8848cfdb9f7a989c259080
--- /dev/null
+++ b/registry/registry_test.go
@@ -0,0 +1,153 @@
+package registry
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+var (
+	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) {
+	var err error
+	destDir, err = ioutil.TempDir(os.TempDir(), "tmpl")
+	if err != nil {
+		panic(err)
+	}
+
+	// Set up template
+	setupTemplate()
+
+	// Set up registry
+	setupRegistry()
+
+	status := m.Run()
+
+	if err = os.RemoveAll(destDir); err != nil {
+		fmt.Printf("could not clean up temp directory %s\n", destDir)
+	}
+	if err = os.RemoveAll(tmplDir); err != nil {
+		fmt.Printf("could not clean up temp directory %s\n", tmplDir)
+	}
+	if err = os.RemoveAll(regDir); err != nil {
+		fmt.Printf("could not clean up temp directory %s\n", regDir)
+	}
+
+	os.Exit(status)
+}
+
+func TestTemplate(t *testing.T) {
+	t.Run("save", testSave)
+	t.Run("get", testGet)
+	t.Run("get-fail", testGetFail)
+	t.Run("execute", testExecute)
+}
+
+func testSave(t *testing.T) {
+	if _, err := reg.SaveTemplate("test", tmplDir); err != nil {
+		t.Log("could not save template")
+		t.FailNow()
+	}
+}
+
+func testGet(t *testing.T) {
+	_, err := reg.GetTemplate("test")
+	if err != nil {
+		t.Logf("could not get template")
+		t.FailNow()
+	}
+}
+
+func testGetFail(t *testing.T) {
+	_, err := reg.GetTemplate("fail")
+	if !IsErrTemplateNotFound(err) {
+		t.Logf("template should not exist")
+		t.FailNow()
+	}
+}
+
+func testExecute(t *testing.T) {
+	tmpl, err := reg.GetTemplate("test")
+	if err != nil {
+		t.Logf("could not get template")
+		t.FailNow()
+	}
+
+	if err := tmpl.Execute(destDir, true); err != nil {
+		t.Logf("could not execute template: %v\n", err)
+		t.FailNow()
+	}
+
+	contents, err := ioutil.ReadFile(filepath.Join(destDir, "TEST"))
+	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()
+	}
+}
+
+func setupTemplate() {
+	var err error
+	tmplDir, err = ioutil.TempDir(os.TempDir(), "tmpl")
+	if err != nil {
+		panic(err)
+	}
+
+	// Template config
+	fi, err := os.Create(filepath.Join(tmplDir, "template.toml"))
+	if err != nil {
+		panic(err)
+	}
+	_, err = fi.WriteString(tmplTemplate)
+	if err != nil {
+		panic(err)
+	}
+	if err := fi.Close(); err != nil {
+		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)
+	}
+	_, err = fi.WriteString(tmplContents)
+	if err != nil {
+		panic(err)
+	}
+	if err := fi.Close(); err != nil {
+		panic(err)
+	}
+}
+
+func setupRegistry() {
+	var err error
+	regDir, err = ioutil.TempDir(os.TempDir(), "tmpl")
+	if err != nil {
+		panic(err)
+	}
+
+	reg, err = Open(regDir)
+	if err != nil {
+		panic(err)
+	}
+}
M registry/template.goregistry/template.go
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
diff --git a/registry/template.go b/registry/template.go
index 19e1d71c0fce9cbe948e83a4b7536825b437e980..5bf0c98b418f766856804f2986e2d73f37fc5ae4 100644
--- a/registry/template.go
+++ b/registry/template.go
@@ -2,12 +2,12 @@ package registry
 
 import (
 	"fmt"
-	"html/template"
 	"io/ioutil"
 	"os"
 	"path/filepath"
 	"sort"
 	"strings"
+	"text/template"
 	"time"
 
 	"github.com/AlecAivazis/survey/v2"
@@ -19,6 +19,7 @@ // Template is a tmpl project
 type Template struct {
 	reg        *Registry `toml:"-"`
 	Name       string    `toml:"name"`
+	Path       string    `toml:"path"`
 	Repository string    `toml:"repository"`
 	Branch     string    `toml:"branch"`
 	Created    time.Time `toml:"created"`
@@ -35,7 +36,7 @@ 	return filepath.Join(t.reg.dir, t.ArchiveName())
 }
 
 // Execute runs the Template and copies to dest
-func (t *Template) Execute(dest string) error {
+func (t *Template) Execute(dest string, defaults bool) error {
 	tmp, err := ioutil.TempDir(os.TempDir(), "tmpl")
 	if err != nil {
 		return err
@@ -46,7 +47,7 @@ 	if err := archiver.Unarchive(t.ArchivePath(), tmp); err != nil {
 		return err
 	}
 
-	vars, err := prompt(tmp)
+	vars, err := prompt(tmp, defaults)
 	if err != nil {
 		return err
 	}
@@ -66,7 +67,7 @@ 		if err != nil {
 			return err
 		}
 
-		tmpl, err := template.New("tmpl").Parse(string(contents))
+		tmpl, err := template.New("tmpl").Funcs(mergeMaps(funcMap, convertMap(vars))).Parse(string(contents))
 		if err != nil {
 			return err
 		}
@@ -95,7 +96,7 @@ 		return newFi.Close()
 	})
 }
 
-func prompt(dir string) (map[string]interface{}, error) {
+func prompt(dir string, defaults bool) (map[string]interface{}, error) {
 	templatePath := filepath.Join(dir, "template.toml")
 	if _, err := os.Lstat(templatePath); err != nil {
 		return nil, err
@@ -105,9 +106,14 @@ 	tree, err := toml.LoadFile(templatePath)
 	if err != nil {
 		return nil, err
 	}
+	vars := tree.ToMap()
+
+	// Return early if we only want defaults
+	if defaults {
+		return vars, nil
+	}
 
 	// Sort the map keys so they are consistent
-	vars := tree.ToMap()
 	sorted := make([]string, 0, len(vars))
 	for k := range vars {
 		sorted = append(sorted, k)
@@ -147,3 +153,24 @@ 	}
 
 	return vars, nil
 }
+
+func convertMap(m map[string]interface{}) template.FuncMap {
+	mm := make(template.FuncMap)
+	for k, v := range m {
+		vv := v // Enclosures in a loop
+		mm[k] = func() interface{} {
+			return fmt.Sprintf("%v", vv)
+		}
+	}
+	return mm
+}
+
+func mergeMaps(maps ...map[string]interface{}) map[string]interface{} {
+	m := make(map[string]interface{})
+	for _, mm := range maps {
+		for k, v := range mm {
+			m[k] = v
+		}
+	}
+	return m
+}