2020-11-17 05:41:34 +00:00
|
|
|
package registry
|
|
|
|
|
|
|
|
import (
|
2020-11-22 05:25:24 +00:00
|
|
|
"bytes"
|
2020-11-17 05:41:34 +00:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2020-11-18 05:28:07 +00:00
|
|
|
"text/template"
|
2020-11-17 05:41:34 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/mholt/archiver/v3"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Template is a tmpl project
|
|
|
|
type Template struct {
|
|
|
|
reg *Registry `toml:"-"`
|
|
|
|
Name string `toml:"name"`
|
2020-11-18 05:28:07 +00:00
|
|
|
Path string `toml:"path"`
|
2020-11-17 05:41:34 +00:00
|
|
|
Repository string `toml:"repository"`
|
|
|
|
Branch string `toml:"branch"`
|
2020-11-30 05:16:25 +00:00
|
|
|
LastUpdate time.Time `toml:"last_update"`
|
2020-11-17 05:41:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ArchiveName is the name given to the archive for this Template
|
|
|
|
func (t *Template) ArchiveName() string {
|
|
|
|
return fmt.Sprintf("%s.tar.gz", t.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ArchivePath is the full path to the archive for this Template within the Registry
|
|
|
|
func (t *Template) ArchivePath() string {
|
|
|
|
return filepath.Join(t.reg.dir, t.ArchiveName())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute runs the Template and copies to dest
|
2020-11-22 05:25:24 +00:00
|
|
|
func (t *Template) Execute(dest string, defaults, overwrite bool) error {
|
2020-11-17 05:41:34 +00:00
|
|
|
tmp, err := ioutil.TempDir(os.TempDir(), "tmpl")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(tmp)
|
|
|
|
|
|
|
|
if err := archiver.Unarchive(t.ArchivePath(), tmp); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-22 05:25:24 +00:00
|
|
|
prompts, err := prompt(tmp, defaults)
|
2020-11-17 05:41:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-22 05:25:24 +00:00
|
|
|
funcs := mergeMaps(funcMap, prompts.ToFuncMap())
|
|
|
|
|
2020-11-17 05:41:34 +00:00
|
|
|
base := filepath.Join(tmp, "template")
|
|
|
|
return filepath.Walk(base, func(walkPath string, walkInfo os.FileInfo, walkErr error) error {
|
|
|
|
if walkErr != nil {
|
|
|
|
return walkErr
|
|
|
|
}
|
|
|
|
|
|
|
|
if walkInfo.IsDir() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
contents, err := ioutil.ReadFile(walkPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-12-29 03:21:41 +00:00
|
|
|
newDest := strings.TrimPrefix(walkPath, base+string(filepath.Separator))
|
2020-11-22 05:25:24 +00:00
|
|
|
newDest = filepath.Join(dest, newDest)
|
|
|
|
|
|
|
|
tmplDest, err := template.New("dest").Funcs(funcs).Parse(newDest)
|
2020-11-17 05:41:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-22 05:25:24 +00:00
|
|
|
var buf bytes.Buffer
|
|
|
|
if err := tmplDest.Execute(&buf, prompts.ToMap()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
newDest = buf.String()
|
2020-11-17 05:41:34 +00:00
|
|
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(newDest), os.ModePerm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-03 03:06:32 +00:00
|
|
|
// Skip .tmplkeep files, after creating the directory structure
|
|
|
|
if strings.EqualFold(walkInfo.Name(), ".tmplkeep") {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-17 05:41:34 +00:00
|
|
|
oldFi, err := os.Lstat(walkPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-11-22 05:25:24 +00:00
|
|
|
|
|
|
|
// Check if new file exists. If it does, only skip if not overwriting
|
|
|
|
if _, err := os.Lstat(newDest); err == nil && !overwrite {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-17 05:41:34 +00:00
|
|
|
newFi, err := os.OpenFile(newDest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, oldFi.Mode())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-22 05:25:24 +00:00
|
|
|
tmplContents, err := template.New("tmpl").Funcs(funcs).Parse(string(contents))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := tmplContents.Execute(newFi, prompts.ToMap()); err != nil {
|
2020-11-17 05:41:34 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return newFi.Close()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-11-18 05:28:07 +00:00
|
|
|
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
|
|
|
|
}
|