fuzzyworld

template / windows / files / main.go

Branch windows · default branch · all branches

main.go

Blame: the commit that last changed each line · file · history

f819f801 2022-09-21 Pavel Safronov1package main
2
3import (
4 "fmt"
5 "os"
05f9e8ef 2022-10-06 Pavel Safronov6 "os/user"
9573ca02 2022-11-16 Pavel Safronov7 "strconv"
4a7759db 2022-09-24 Pavel Safronov8 "strings"
f819f801 2022-09-21 Pavel Safronov9
886f7fa2 2022-11-02 Pavel Safronov10 log "github.com/sirupsen/logrus"
f819f801 2022-09-21 Pavel Safronov11 "github.com/urfave/cli/v2" // imports as package "cli"
4a7759db 2022-09-24 Pavel Safronov12 "gopkg.in/yaml.v3"
f819f801 2022-09-21 Pavel Safronov13)
14
ae89a42a 2022-11-02 Pavel Safronov15const defaultUserConfigPath = "~/.template.user.yaml"
16const defaultConfigPath = "~/.template.yaml"
886f7fa2 2022-11-02 Pavel Safronov17const defaultLogLevel = "info"
ae89a42a 2022-11-02 Pavel Safronov18
b1ded4b4 2022-10-30 Pavel Safronov19type ArgumentValues struct {
ae89a42a 2022-11-02 Pavel Safronov20 profile string
21 projectName string
22 configFilePath string
23 userConfigPath string
24 extrasFilePath string
886f7fa2 2022-11-02 Pavel Safronov25 logLevel string
9573ca02 2022-11-16 Pavel Safronov26}
27type EnvVarValues struct {
28 useDefaultConfig bool
b1ded4b4 2022-10-30 Pavel Safronov29}
30type Settings struct {
31 ArgumentValues
9573ca02 2022-11-16 Pavel Safronov32 EnvVarValues
8c15a714 2022-11-04 Pavel Safronov33 extras map[string]string
34 fullConfigPath string
35 config Config
36 usr *user.User
b1ded4b4 2022-10-30 Pavel Safronov37}
38
f819f801 2022-09-21 Pavel Safronov39func main() {
b1ded4b4 2022-10-30 Pavel Safronov40 // var err error
41 // var usr *user.User
42 argumentValues := ArgumentValues{}
f819f801 2022-09-21 Pavel Safronov43
44 app := &cli.App{
45 Name: "template",
46 Usage: "create file or directory from a template",
47 EnableBashCompletion: true,
185f54e8 2022-10-05 Pavel Safronov48 Suggest: true,
f819f801 2022-09-21 Pavel Safronov49 Flags: []cli.Flag{
50 &cli.StringFlag{
ea896f47 2022-09-23 Pavel Safronov51 Name: "profile",
52 Aliases: []string{"p"},
53 Value: "",
f819f801 2022-09-21 Pavel Safronov54 Usage: "`PROFILE` to use for the template",
b1ded4b4 2022-10-30 Pavel Safronov55 Destination: &argumentValues.profile,
ea896f47 2022-09-23 Pavel Safronov56 // Value: defaultProfileName,
f819f801 2022-09-21 Pavel Safronov57 // Required: true,
58 // EnvVars: []string{"TEMPLATE_PROFILE"},
59 },
d026838a 2022-09-22 Pavel Safronov60 &cli.StringFlag{
61 Name: "name",
62 Aliases: []string{"n"},
63 Usage: "name of the project to create",
64 Value: "",
b1ded4b4 2022-10-30 Pavel Safronov65 Destination: &argumentValues.projectName,
d026838a 2022-09-22 Pavel Safronov66 },
185f54e8 2022-10-05 Pavel Safronov67 &cli.PathFlag{
68 Name: "config-yaml",
69 Aliases: []string{"c"},
70 Usage: "path to the config yaml file",
ae89a42a 2022-11-02 Pavel Safronov71 Value: defaultConfigPath,
b1ded4b4 2022-10-30 Pavel Safronov72 Destination: &argumentValues.configFilePath,
185f54e8 2022-10-05 Pavel Safronov73 },
ae89a42a 2022-11-02 Pavel Safronov74 &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 },
185f54e8 2022-10-05 Pavel Safronov81 &cli.PathFlag{
55c36f6a 2022-10-05 Pavel Safronov82 Name: "extras-yaml",
83 Aliases: []string{"e"},
84 Usage: "path to the extras yaml file",
85 Value: "~/.template-extras.yaml",
b1ded4b4 2022-10-30 Pavel Safronov86 Destination: &argumentValues.extrasFilePath,
185f54e8 2022-10-05 Pavel Safronov87 },
886f7fa2 2022-11-02 Pavel Safronov88 &cli.StringFlag{
89 Name: "log-level",
90 Aliases: []string{"l"},
91 Usage: "log level",
92 Value: defaultLogLevel,
93 Destination: &argumentValues.logLevel,
94 },
f819f801 2022-09-21 Pavel Safronov95 },
8c15a714 2022-11-04 Pavel Safronov96 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()
10676da3 2022-11-06 Pavel Safronov140 fmt.Printf("Profile '%v':\n", settings.profile)
8c15a714 2022-11-04 Pavel Safronov141 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 },
f819f801 2022-09-21 Pavel Safronov164 Action: func(context *cli.Context) error {
8c15a714 2022-11-04 Pavel Safronov165 settings, err := createSettings(argumentValues, context)
05f9e8ef 2022-10-06 Pavel Safronov166 if err != nil {
b1ded4b4 2022-10-30 Pavel Safronov167 return fmt.Errorf("error parsing arguments: %w", err)
05f9e8ef 2022-10-06 Pavel Safronov168 }
b1ded4b4 2022-10-30 Pavel Safronov169 err = mainLogic(settings)
170 if err != nil {
171 return fmt.Errorf("error in main logic: %w", err)
55d96f9c 2022-09-23 Pavel Safronov172 }
b1ded4b4 2022-10-30 Pavel Safronov173 return nil
174 },
175 }
55d96f9c 2022-09-23 Pavel Safronov176
b1ded4b4 2022-10-30 Pavel Safronov177 if err := app.Run(os.Args); err != nil {
178 log.Fatal(err)
179 }
180}
319b8b3f 2022-09-22 Pavel Safronov181
8c15a714 2022-11-04 Pavel Safronov182func createSettings(
b1ded4b4 2022-10-30 Pavel Safronov183 argumentValues ArgumentValues,
184 context *cli.Context,
185) (Settings, error) {
9573ca02 2022-11-16 Pavel Safronov186 envVarValues, err := readEnvVars()
187 if err != nil {
188 return Settings{}, fmt.Errorf("error getting env var values: %w", err)
189 }
b1ded4b4 2022-10-30 Pavel Safronov190 settings := Settings{
191 ArgumentValues: argumentValues,
9573ca02 2022-11-16 Pavel Safronov192 EnvVarValues: envVarValues,
b1ded4b4 2022-10-30 Pavel Safronov193 }
319b8b3f 2022-09-22 Pavel Safronov194
b1ded4b4 2022-10-30 Pavel Safronov195 // read args
196 args := context.Args()
886f7fa2 2022-11-02 Pavel Safronov197
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")
319b8b3f 2022-09-22 Pavel Safronov220
b1ded4b4 2022-10-30 Pavel Safronov221 // 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)
4a7759db 2022-09-24 Pavel Safronov227
b1ded4b4 2022-10-30 Pavel Safronov228 // 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)
8179bb1c 2022-10-26 Pavel Safronov234
ae89a42a 2022-11-02 Pavel Safronov235 // extras strings
9573ca02 2022-11-16 Pavel Safronov236 _, err = os.Stat(settings.extrasFilePath)
ae89a42a 2022-11-02 Pavel Safronov237 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
b1ded4b4 2022-10-30 Pavel Safronov243 }
4a7759db 2022-09-24 Pavel Safronov244
8c15a714 2022-11-04 Pavel Safronov245 // get user
b1ded4b4 2022-10-30 Pavel Safronov246 usr, err := user.Current()
247 if err != nil {
248 log.Fatal(err)
249 }
8c15a714 2022-11-04 Pavel Safronov250 settings.usr = usr
4a7361b0 2022-09-21 Pavel Safronov251
ae89a42a 2022-11-02 Pavel Safronov252 // get config from default path
b1ded4b4 2022-10-30 Pavel Safronov253 fullConfigPath := getFullPath(settings.configFilePath, *usr)
8c15a714 2022-11-04 Pavel Safronov254 settings.fullConfigPath = fullConfigPath
255
5e424f1b 2022-11-12 Pavel Safronov256 var config Config
9573ca02 2022-11-16 Pavel Safronov257 if settings.useDefaultConfig {
5e424f1b 2022-11-12 Pavel Safronov258 config = defaultConfig()
b1ded4b4 2022-10-30 Pavel Safronov259 } else {
5e424f1b 2022-11-12 Pavel Safronov260 config, err = loadConfig(fullConfigPath)
b1ded4b4 2022-10-30 Pavel Safronov261 if err != nil {
5e424f1b 2022-11-12 Pavel Safronov262 return settings, fmt.Errorf("error loading config from %v: %w", fullConfigPath, err)
ae89a42a 2022-11-02 Pavel Safronov263 } else {
5e424f1b 2022-11-12 Pavel Safronov264 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)
ae89a42a 2022-11-02 Pavel Safronov271 if err != nil {
5e424f1b 2022-11-12 Pavel Safronov272 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 }
ae89a42a 2022-11-02 Pavel Safronov279 }
b1ded4b4 2022-10-30 Pavel Safronov280 }
281 }
8c15a714 2022-11-04 Pavel Safronov282 settings.config = config
5e424f1b 2022-11-12 Pavel Safronov283
284 return settings, nil
8c15a714 2022-11-04 Pavel Safronov285}
b1ded4b4 2022-10-30 Pavel Safronov286
9573ca02 2022-11-16 Pavel Safronov287func 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
8c15a714 2022-11-04 Pavel Safronov302func findProfile(settings Settings) (Profile, error) {
303 var profile Profile = Profile{}
304 var err error
305 config := settings.config
ae89a42a 2022-11-02 Pavel Safronov306 if settings.profile == "" {
307 profile, err = findDefaultProfile(config.Profiles)
308 if err != nil {
8c15a714 2022-11-04 Pavel Safronov309 return profile, fmt.Errorf("no profile specified and no default profile found")
ae89a42a 2022-11-02 Pavel Safronov310 }
311 } else {
312 index, err := findProfileIndex(config.Profiles, settings.profile)
313 if err != nil {
8c15a714 2022-11-04 Pavel Safronov314 return profile, fmt.Errorf("error finding profile: %v", err)
ae89a42a 2022-11-02 Pavel Safronov315 }
316 profile = config.Profiles[index]
b1ded4b4 2022-10-30 Pavel Safronov317 }
8c15a714 2022-11-04 Pavel Safronov318 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 }
b1ded4b4 2022-10-30 Pavel Safronov327
328 // process profile
8c15a714 2022-11-04 Pavel Safronov329 errors := processProfile(profile, settings.projectName, settings.extras, *settings.usr)
b1ded4b4 2022-10-30 Pavel Safronov330 if len(errors) > 0 {
331 return fmt.Errorf("error processing profile: %v", errors)
332 }
333
334 return nil
f819f801 2022-09-21 Pavel Safronov335}