-
Notifications
You must be signed in to change notification settings - Fork 2
/
internal.go
219 lines (193 loc) · 5.57 KB
/
internal.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package internal
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"github.com/Samasource/jen/src/internal/exec"
"github.com/Samasource/jen/src/internal/helpers"
"github.com/Samasource/jen/src/internal/home"
"github.com/Samasource/jen/src/internal/logging"
"github.com/Samasource/jen/src/internal/project"
"github.com/Samasource/jen/src/internal/spec"
)
// Options represents all command line configurations
type Options struct {
TemplateName string
SkipConfirm bool
VarOverrides []string
}
// NewContext creates a context to be used for executing executables
func (o Options) NewContext() (exec.Context, error) {
_, err := home.GetOrCloneRepo()
if err != nil {
return nil, err
}
proj, err := project.LoadOrCreate(o.TemplateName, o.SkipConfirm, o.VarOverrides)
if err != nil {
return nil, err
}
templateDir, err := proj.GetTemplateDir()
if err != nil {
return nil, err
}
specification, err := spec.Load(templateDir)
if err != nil {
return nil, err
}
cloneSubDir, err := home.GetCloneSubDir()
if err != nil {
return nil, err
}
return context{
cloneSubDir: cloneSubDir,
templateDir: templateDir,
project: proj,
spec: *specification,
}, nil
}
// context contains all the information for implementing both the
// exec.context and evaluation.context interfaces
type context struct {
cloneSubDir string
templateDir string
project *project.Project
spec spec.Spec
}
// GetVars returns a dictionary of the project's variable names mapped to
// their corresponding values. It does not include the process' env var.
// Whenever you alter this map, you are responsible for later calling
// SetVars() to save your changes back to the project file.
func (c context) GetVars() map[string]interface{} {
clone := make(map[string]interface{})
for k, v := range c.project.Vars {
clone[k] = v
}
return clone
}
// SetVars saves given variables in project file.
func (c context) SetVars(vars map[string]interface{}) error {
c.project.Vars = vars
return c.project.Save()
}
// IsVarOverriden returns whether given variable has been overriden via command
// line. This is used to skip prompting for those variables.
func (c context) IsVarOverriden(name string) bool {
for _, x := range c.project.OverridenVars {
if x == name {
return true
}
}
return false
}
// GetPlaceholders returns a map of special placeholders that can be used instead
// of go template expressions, for more lightweight templating, especially for the
// project's name, which appears everywhere.
func (c context) GetPlaceholders() map[string]string {
return c.spec.Placeholders
}
// GetEvalVars returns a dictionary of the project's variable names mapped to
// their corresponding values for evaluation purposes. It does not include the
// process' env var.
func (c context) GetEvalVars() map[string]interface{} {
vars := c.GetVars()
absProjectDir, err := filepath.Abs(c.project.Dir)
if err != nil {
panic(fmt.Errorf("failed to determine project's absolute dir: %w", err))
}
vars["projectDirName"] = filepath.Base(absProjectDir)
return vars
}
// getBinDirs returns the list of bin dirs that actually exist
func (c context) getBinDirs() []string {
binDirs := []string{
filepath.Join(c.cloneSubDir, "bin"),
filepath.Join(c.project.Dir, "bin"),
}
// Add bin dirs to PATH env var
var validBinDirs []string
for _, dir := range binDirs {
if helpers.PathExists(dir) {
validBinDirs = append(validBinDirs, dir)
}
}
return validBinDirs
}
// GetScripts returns the list of executable scripts in bin dirs
func (c context) GetScripts() ([]string, error) {
var scripts []string
binDirs := c.getBinDirs()
for _, dir := range binDirs {
infos, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
for _, info := range infos {
// Is it executable by owner, group or any?
if info.Mode()&0111 != 0 {
scripts = append(scripts, info.Name())
}
}
}
return scripts, nil
}
// GetShellVars returns all env vars to be used when invoking shell commands,
// including the current process' env vars, the project's vars and an augmented
// PATH var including extra bin dirs.
func (c context) GetShellVars(includeProcessVars bool) []string {
// Add bin dirs to PATH env var
binDirs := c.getBinDirs()
pathVar := os.Getenv("PATH")
for _, dir := range binDirs {
pathVar = dir + ":" + pathVar
}
// Collect all current process env vars, except PATH
var env []string
if includeProcessVars {
for _, entry := range os.Environ() {
if !strings.HasPrefix(entry, "PATH=") {
env = append(env, entry)
}
}
}
// Override PATH env var
entry := fmt.Sprintf("PATH=%v", pathVar)
env = append(env, entry)
logging.Log(entry)
// Then values env vars
logging.Log("Environment variables:")
for key, value := range c.project.Vars {
entry := fmt.Sprintf("%s=%v", key, value)
env = append(env, entry)
logging.Log(entry)
}
return env
}
// GetAction returns action with given name within same
// spec file or nil if not found.
func (c context) GetAction(name string) exec.Executable {
action, ok := c.spec.Actions[name]
if !ok {
return nil
}
return action
}
// GetActionNames returns the names of all actions available in template.
func (c context) GetActionNames() []string {
names := make([]string, 0, len(c.spec.Actions))
for name := range c.spec.Actions {
names = append(names, name)
}
sort.Strings(names)
return names
}
// GetTemplateDir returns the current template's dir
func (c context) GetTemplateDir() string {
return c.templateDir
}
// GetProjectDir returns the current project's dir
func (c context) GetProjectDir() string {
return c.project.Dir
}