fuzzyworld

template / windows / files / main.go

Branch windows · default branch · all branches

main.go

8.7 KB · history · blame

1package main
2
3import (
4 "fmt"
5 "os"
6 "os/user"
7 "strconv"
8 "strings"
9
10 log "github.com/sirupsen/logrus"
11 "github.com/urfave/cli/v2" // imports as package "cli"
12 "gopkg.in/yaml.v3"
13)
14
15const defaultUserConfigPath = "~/.template.user.yaml"
16const defaultConfigPath = "~/.template.yaml"
17const defaultLogLevel = "info"
18
19type ArgumentValues struct {
20 profile string
21 projectName string
22 configFilePath string
23 userConfigPath string
24 extrasFilePath string
25 logLevel string
26}
27type EnvVarValues struct {
28 useDefaultConfig bool
29}
30type Settings struct {
31 ArgumentValues
32 EnvVarValues
33 extras map[string]string
34 fullConfigPath string
35 config Config
36 usr *user.User
37}
38
39func main() {
40 // var err error
41 // var usr *user.User
42 argumentValues := ArgumentValues{}
43
44 app := &cli.App{
45 Name: "template",
46 Usage: "create file or directory from a template",
47 EnableBashCompletion: true,
48 Suggest: true,
49 Flags: []cli.Flag{
50 &cli.StringFlag{
51 Name: "profile",
52 Aliases: []string{"p"},
53 Value: "",
54 Usage: "`PROFILE` to use for the template",
55 Destination: &argumentValues.profile,
56 // Value: defaultProfileName,
57 // Required: true,
58 // EnvVars: []string{"TEMPLATE_PROFILE"},
59 },
60 &cli.StringFlag{
61 Name: "name",
62 Aliases: []string{"n"},
63 Usage: "name of the project to create",
64 Value: "",
65 Destination: &argumentValues.projectName,
66 },
67 &cli.PathFlag{
68 Name: "config-yaml",
69 Aliases: []string{"c"},
70 Usage: "path to the config yaml file",
71 Value: defaultConfigPath,
72 Destination: &argumentValues.configFilePath,
73 },
74 &cli.PathFlag{
75 Name: "user-config-yaml",
76 Aliases: []string{"u"},
77 Usage: "path to the user config yaml file",
78 Value: defaultUserConfigPath,
79 Destination: &argumentValues.userConfigPath,
80 },
81 &cli.PathFlag{
82 Name: "extras-yaml",
83 Aliases: []string{"e"},
84 Usage: "path to the extras yaml file",
85 Value: "~/.template-extras.yaml",
86 Destination: &argumentValues.extrasFilePath,
87 },
88 &cli.StringFlag{
89 Name: "log-level",
90 Aliases: []string{"l"},
91 Usage: "log level",
92 Value: defaultLogLevel,
93 Destination: &argumentValues.logLevel,
94 },
95 },
96 Commands: []*cli.Command{
97 {
98 Name: "list",
99 Usage: "list available profiles",
100 Action: func(c *cli.Context) error {
101 settings, err := createSettings(argumentValues, c)
102 if err != nil {
103 return fmt.Errorf("error creating settings: %v", err)
104 }
105
106 config := settings.config
107 // list profiles
108 fmt.Println("Available profiles:")
109 for _, p := range config.Profiles {
110 fmt.Printf(" %v: %v\n", p.Name, p.Description)
111 }
112 return nil
113 },
114 },
115 {
116 Name: "get",
117 Usage: "get a profile",
118 Action: func(c *cli.Context) error {
119 settings, err := createSettings(argumentValues, c)
120 if err != nil {
121 return fmt.Errorf("error creating settings: %v", err)
122 }
123 config := settings.config
124
125 // get profile information
126 index, err := findProfileIndex(config.Profiles, settings.profile)
127 if err != nil {
128 return fmt.Errorf("profile '%v' not found", settings.profile)
129 }
130 profile := config.Profiles[index]
131
132 writer := strings.Builder{}
133 encoder := yaml.NewEncoder(&writer)
134 err = encoder.Encode(profile)
135 if err != nil {
136 return fmt.Errorf("error encoding profile: %v", err)
137 }
138
139 yamlText := writer.String()
140 fmt.Printf("Profile '%v':\n", settings.profile)
141 fmt.Println(yamlText)
142 return nil
143 },
144 },
145 {
146 Name: "reset",
147 Usage: "reset the global template config to default profiles",
148 Action: func(c *cli.Context) error {
149 settings, err := createSettings(argumentValues, c)
150 if err != nil {
151 return fmt.Errorf("error creating settings: %v", err)
152 }
153 fullConfigPath := settings.fullConfigPath
154
155 fmt.Printf("Resetting config at '%v' to default\n", fullConfigPath)
156 err = saveConfig(fullConfigPath, defaultConfig())
157 if err != nil {
158 return fmt.Errorf("error saving default config: %v", err)
159 }
160 return nil
161 },
162 },
163 },
164 Action: func(context *cli.Context) error {
165 settings, err := createSettings(argumentValues, context)
166 if err != nil {
167 return fmt.Errorf("error parsing arguments: %w", err)
168 }
169 err = mainLogic(settings)
170 if err != nil {
171 return fmt.Errorf("error in main logic: %w", err)
172 }
173 return nil
174 },
175 }
176
177 if err := app.Run(os.Args); err != nil {
178 log.Fatal(err)
179 }
180}
181
182func createSettings(
183 argumentValues ArgumentValues,
184 context *cli.Context,
185) (Settings, error) {
186 envVarValues, err := readEnvVars()
187 if err != nil {
188 return Settings{}, fmt.Errorf("error getting env var values: %w", err)
189 }
190 settings := Settings{
191 ArgumentValues: argumentValues,
192 EnvVarValues: envVarValues,
193 }
194
195 // read args
196 args := context.Args()
197
198 // configure logging
199 switch settings.logLevel {
200 case "debug":
201 log.SetLevel(log.DebugLevel)
202 case "info":
203 log.SetLevel(log.InfoLevel)
204 case "warn":
205 log.SetLevel(log.WarnLevel)
206 case "error":
207 log.SetLevel(log.ErrorLevel)
208 case "fatal":
209 log.SetLevel(log.FatalLevel)
210 case "panic":
211 log.SetLevel(log.PanicLevel)
212 default:
213 fmt.Printf("invalid log level specified %v, using info", settings.logLevel)
214 log.SetLevel(log.InfoLevel)
215 }
216 log.SetOutput(os.Stdout)
217 // log.SetFormatter(&log.JSONFormatter{})
218
219 log.WithFields(log.Fields{"args": args}).Debug("Args parsed")
220
221 // get profile name from args
222 // if not set explicitly, use first arg, if present
223 if settings.profile == "" && args.Len() > 0 {
224 settings.profile = args.Get(0)
225 }
226 fmt.Println("Profile:", settings.profile)
227
228 // get project name from args
229 // if not set explicitly, use second arg, if present
230 if settings.projectName == "" && args.Len() > 1 {
231 settings.projectName = args.Get(1)
232 }
233 fmt.Println("Project name:", settings.projectName)
234
235 // extras strings
236 _, err = os.Stat(settings.extrasFilePath)
237 if err == nil {
238 extras, err := loadExtras(settings.extrasFilePath)
239 if err != nil {
240 return settings, fmt.Errorf("error loading extras from %v: %w", settings.extrasFilePath, err)
241 }
242 settings.extras = extras
243 }
244
245 // get user
246 usr, err := user.Current()
247 if err != nil {
248 log.Fatal(err)
249 }
250 settings.usr = usr
251
252 // get config from default path
253 fullConfigPath := getFullPath(settings.configFilePath, *usr)
254 settings.fullConfigPath = fullConfigPath
255
256 var config Config
257 if settings.useDefaultConfig {
258 config = defaultConfig()
259 } else {
260 config, err = loadConfig(fullConfigPath)
261 if err != nil {
262 return settings, fmt.Errorf("error loading config from %v: %w", fullConfigPath, err)
263 } else {
264 fmt.Printf("Loaded config from %v\n", fullConfigPath)
265 }
266 // load user config, if present
267 fullUserConfigPath := getFullPath(settings.userConfigPath, *usr)
268 _, err = os.Stat(fullUserConfigPath)
269 if err == nil {
270 userConfig, err := loadConfig(fullUserConfigPath)
271 if err != nil {
272 return settings, fmt.Errorf("error loading user config from %v: %w", fullUserConfigPath, err)
273 } else {
274 fmt.Printf("Loaded user config from %v, combining with global config\n", fullUserConfigPath)
275 config, err = mergeConfigs(config, userConfig)
276 if err != nil {
277 return settings, fmt.Errorf("error merging configs: %w", err)
278 }
279 }
280 }
281 }
282 settings.config = config
283
284 return settings, nil
285}
286
287func readEnvVars() (EnvVarValues, error) {
288 envVarValues := EnvVarValues{}
289
290 useDefaultConfigText := os.Getenv("TEMPLATE_USE_DEFAULT_CONFIG")
291 if useDefaultConfigText != "" {
292 useDefaultConfig, err := strconv.ParseBool(useDefaultConfigText)
293 if err != nil {
294 return envVarValues, fmt.Errorf("error parsing env var TEMPLATE_USE_DEFAULT_CONFIG: \"%s\" %w", useDefaultConfigText, err)
295 }
296 envVarValues.useDefaultConfig = useDefaultConfig
297 }
298
299 return envVarValues, nil
300}
301
302func findProfile(settings Settings) (Profile, error) {
303 var profile Profile = Profile{}
304 var err error
305 config := settings.config
306 if settings.profile == "" {
307 profile, err = findDefaultProfile(config.Profiles)
308 if err != nil {
309 return profile, fmt.Errorf("no profile specified and no default profile found")
310 }
311 } else {
312 index, err := findProfileIndex(config.Profiles, settings.profile)
313 if err != nil {
314 return profile, fmt.Errorf("error finding profile: %v", err)
315 }
316 profile = config.Profiles[index]
317 }
318 return profile, nil
319}
320
321func mainLogic(settings Settings) error {
322 // find profile
323 profile, err := findProfile(settings)
324 if err != nil {
325 return fmt.Errorf("error finding profile: %v", err)
326 }
327
328 // process profile
329 errors := processProfile(profile, settings.projectName, settings.extras, *settings.usr)
330 if len(errors) > 0 {
331 return fmt.Errorf("error processing profile: %v", errors)
332 }
333
334 return nil
335}