Home

tmpl @6ffc50dc06e46255334863e33156b8e0e82fafcb - refs - log -
-
https://git.jolheiser.com/tmpl.git
Template automation
tmpl / registry / template.go
- raw
  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
package registry

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/mholt/archiver/v3"
)

// 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"`
}

// 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
func (t *Template) Execute(dest string, defaults, overwrite bool) error {
	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
	}

	prompts, err := prompt(tmp, defaults)
	if err != nil {
		return err
	}

	funcs := mergeMaps(funcMap, prompts.ToFuncMap())

	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
		}

		newDest := strings.TrimPrefix(walkPath, base+"/")
		newDest = filepath.Join(dest, newDest)

		tmplDest, err := template.New("dest").Funcs(funcs).Parse(newDest)
		if err != nil {
			return err
		}

		var buf bytes.Buffer
		if err := tmplDest.Execute(&buf, prompts.ToMap()); err != nil {
			return err
		}
		newDest = buf.String()

		if err := os.MkdirAll(filepath.Dir(newDest), os.ModePerm); err != nil {
			return err
		}

		oldFi, err := os.Lstat(walkPath)
		if err != nil {
			return err
		}

		// Check if new file exists. If it does, only skip if not overwriting
		if _, err := os.Lstat(newDest); err == nil && !overwrite {
			return nil
		}

		newFi, err := os.OpenFile(newDest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, oldFi.Mode())
		if err != nil {
			return err
		}

		tmplContents, err := template.New("tmpl").Funcs(funcs).Parse(string(contents))
		if err != nil {
			return err
		}
		if err := tmplContents.Execute(newFi, prompts.ToMap()); err != nil {
			return err
		}

		return newFi.Close()
	})
}

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
}