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
|
package registry
import (
"bytes"
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
"time"
"github.com/Masterminds/sprig/v3"
"github.com/mholt/archiver/v3"
)
// Template is a tmpl project
type Template struct {
reg *Registry `yaml:"-"`
Name string `yaml:"name"`
Path string `yaml:"path"`
Repository string `yaml:"repository"`
Branch string `yaml:"branch"`
LastUpdate time.Time `yaml:"last_update"`
}
// 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 := os.MkdirTemp(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(), sprig.TxtFuncMap())
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 := os.ReadFile(walkPath)
if err != nil {
return err
}
newDest := strings.TrimPrefix(walkPath, base+string(filepath.Separator))
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
}
// 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
}
// 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]any) map[string]any {
m := make(map[string]any)
for _, mm := range maps {
for k, v := range mm {
m[k] = v
}
}
return m
}
|