fuzzyworld

template / files / internal / core / app.go

app.go

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

37160b89 2026-05-01 Pavel Safronov1package core
f819f801 2022-09-21 Pavel Safronov2
3import (
bbd011d0 2022-12-10 Pavel Safronov4 "bufio"
f819f801 2022-09-21 Pavel Safronov5 "fmt"
6962cd0b 2023-09-14 Pavel Safronov6 "io/fs"
f819f801 2022-09-21 Pavel Safronov7 "os"
05f9e8ef 2022-10-06 Pavel Safronov8 "os/user"
6962cd0b 2023-09-14 Pavel Safronov9 "path"
10 "path/filepath"
9573ca02 2022-11-16 Pavel Safronov11 "strconv"
4a7759db 2022-09-24 Pavel Safronov12 "strings"
f819f801 2022-09-21 Pavel Safronov13
886f7fa2 2022-11-02 Pavel Safronov14 log "github.com/sirupsen/logrus"
37160b89 2026-05-01 Pavel Safronov15 "github.com/urfave/cli/v2"
4a7759db 2022-09-24 Pavel Safronov16 "gopkg.in/yaml.v3"
f819f801 2022-09-21 Pavel Safronov17)
18
ae89a42a 2022-11-02 Pavel Safronov19const defaultUserConfigPath = "~/.template.user.yaml"
20const defaultConfigPath = "~/.template.yaml"
886f7fa2 2022-11-02 Pavel Safronov21const defaultLogLevel = "info"
ae89a42a 2022-11-02 Pavel Safronov22
37160b89 2026-05-01 Pavel Safronov23type argumentValues struct {
ae89a42a 2022-11-02 Pavel Safronov24 profile string
25 projectName string
8fc7c452 2023-03-29 Pavel Safronov26 fileTemplate string
27 dirTemplate string
fd0a4446 2023-04-03 Pavel Safronov28 scriptTemplate string
ae89a42a 2022-11-02 Pavel Safronov29 configFilePath string
30 userConfigPath string
31 extrasFilePath string
886f7fa2 2022-11-02 Pavel Safronov32 logLevel string
9573ca02 2022-11-16 Pavel Safronov33}
37160b89 2026-05-01 Pavel Safronov34type envVarValues struct {
9573ca02 2022-11-16 Pavel Safronov35 useDefaultConfig bool
b1ded4b4 2022-10-30 Pavel Safronov36}
37160b89 2026-05-01 Pavel Safronov37type settings struct {
38 argumentValues
39 envVarValues
bbd011d0 2022-12-10 Pavel Safronov40 extras map[string]string
41 fullConfigPath string
42 config Config
43 usr *user.User
b1ded4b4 2022-10-30 Pavel Safronov44}
45
37160b89 2026-05-01 Pavel Safronov46// Run is the entry point for the CLI application.
47func Run(args []string, version string) error {
48 av := argumentValues{}
f819f801 2022-09-21 Pavel Safronov49
50 app := &cli.App{
51 Name: "template",
52 Usage: "create file or directory from a template",
53 EnableBashCompletion: true,
185f54e8 2022-10-05 Pavel Safronov54 Suggest: true,
f819f801 2022-09-21 Pavel Safronov55 Flags: []cli.Flag{
56 &cli.StringFlag{
ea896f47 2022-09-23 Pavel Safronov57 Name: "profile",
58 Aliases: []string{"p"},
59 Value: "",
f819f801 2022-09-21 Pavel Safronov60 Usage: "`PROFILE` to use for the template",
37160b89 2026-05-01 Pavel Safronov61 Destination: &av.profile,
f819f801 2022-09-21 Pavel Safronov62 },
8fc7c452 2023-03-29 Pavel Safronov63 &cli.StringFlag{
64 Name: "file",
65 Aliases: []string{"f"},
66 Value: "",
67 Usage: "file template to use to create a file",
37160b89 2026-05-01 Pavel Safronov68 Destination: &av.fileTemplate,
8fc7c452 2023-03-29 Pavel Safronov69 },
70 &cli.StringFlag{
71 Name: "dir",
72 Aliases: []string{"d"},
73 Value: "",
74 Usage: "folder pattern to use to create a directory",
37160b89 2026-05-01 Pavel Safronov75 Destination: &av.dirTemplate,
8fc7c452 2023-03-29 Pavel Safronov76 },
fd0a4446 2023-04-03 Pavel Safronov77 &cli.StringFlag{
78 Name: "script",
79 Aliases: []string{"s"},
80 Value: "",
81 Usage: "templated script to run in the shell",
37160b89 2026-05-01 Pavel Safronov82 Destination: &av.scriptTemplate,
fd0a4446 2023-04-03 Pavel Safronov83 },
d026838a 2022-09-22 Pavel Safronov84 &cli.StringFlag{
85 Name: "name",
86 Aliases: []string{"n"},
87 Usage: "name of the project to create",
88 Value: "",
37160b89 2026-05-01 Pavel Safronov89 Destination: &av.projectName,
d026838a 2022-09-22 Pavel Safronov90 },
185f54e8 2022-10-05 Pavel Safronov91 &cli.PathFlag{
92 Name: "config-yaml",
93 Aliases: []string{"c"},
94 Usage: "path to the config yaml file",
ae89a42a 2022-11-02 Pavel Safronov95 Value: defaultConfigPath,
37160b89 2026-05-01 Pavel Safronov96 Destination: &av.configFilePath,
185f54e8 2022-10-05 Pavel Safronov97 },
ae89a42a 2022-11-02 Pavel Safronov98 &cli.PathFlag{
99 Name: "user-config-yaml",
100 Aliases: []string{"u"},
101 Usage: "path to the user config yaml file",
102 Value: defaultUserConfigPath,
37160b89 2026-05-01 Pavel Safronov103 Destination: &av.userConfigPath,
ae89a42a 2022-11-02 Pavel Safronov104 },
185f54e8 2022-10-05 Pavel Safronov105 &cli.PathFlag{
55c36f6a 2022-10-05 Pavel Safronov106 Name: "extras-yaml",
107 Aliases: []string{"e"},
108 Usage: "path to the extras yaml file",
109 Value: "~/.template-extras.yaml",
37160b89 2026-05-01 Pavel Safronov110 Destination: &av.extrasFilePath,
185f54e8 2022-10-05 Pavel Safronov111 },
886f7fa2 2022-11-02 Pavel Safronov112 &cli.StringFlag{
113 Name: "log-level",
114 Aliases: []string{"l"},
115 Usage: "log level",
116 Value: defaultLogLevel,
37160b89 2026-05-01 Pavel Safronov117 Destination: &av.logLevel,
886f7fa2 2022-11-02 Pavel Safronov118 },
f819f801 2022-09-21 Pavel Safronov119 },
8c15a714 2022-11-04 Pavel Safronov120 Commands: []*cli.Command{
121 {
122 Name: "list",
123 Usage: "list available profiles",
124 Action: func(c *cli.Context) error {
37160b89 2026-05-01 Pavel Safronov125 s, err := createSettings(av, c)
8c15a714 2022-11-04 Pavel Safronov126 if err != nil {
127 return fmt.Errorf("error creating settings: %v", err)
128 }
129
37160b89 2026-05-01 Pavel Safronov130 cfg := s.config
8c15a714 2022-11-04 Pavel Safronov131 fmt.Println("Available profiles:")
37160b89 2026-05-01 Pavel Safronov132 for i, p := range cfg.Profiles {
7add4991 2026-01-31 Pavel Safronov133 fmt.Printf(" %d: %v\n", i+1, p.Name)
83f12389 2026-01-15 Pavel Safronov134 fmt.Printf(" %v\n", p.Description)
8c15a714 2022-11-04 Pavel Safronov135 }
f58f2e35 2023-01-30 Pavel Safronov136 fmt.Println()
8c15a714 2022-11-04 Pavel Safronov137 return nil
138 },
139 },
6962cd0b 2023-09-14 Pavel Safronov140 {
141 Name: "create",
142 Usage: "create a fully-hardcoded template profile from a file or folder",
143 Flags: []cli.Flag{
144 &cli.StringFlag{
145 Name: "dir",
146 Aliases: []string{"d"},
147 Value: "",
148 Usage: "folder from which to create the template",
37160b89 2026-05-01 Pavel Safronov149 Destination: &av.dirTemplate,
6962cd0b 2023-09-14 Pavel Safronov150 },
151 &cli.StringFlag{
152 Name: "file",
153 Aliases: []string{"f"},
154 Value: "",
155 Usage: "file from which to create the template",
37160b89 2026-05-01 Pavel Safronov156 Destination: &av.fileTemplate,
6962cd0b 2023-09-14 Pavel Safronov157 },
158 },
159 Action: func(c *cli.Context) error {
160 c.Args()
161
37160b89 2026-05-01 Pavel Safronov162 filePath := av.fileTemplate
163 dirPath := av.dirTemplate
6962cd0b 2023-09-14 Pavel Safronov164 if filePath == "" && dirPath == "" {
165 return fmt.Errorf("file or dir not specified, use `file` or `dir` flag")
166 }
167
37160b89 2026-05-01 Pavel Safronov168 profileName := av.profile
6962cd0b 2023-09-14 Pavel Safronov169 if profileName == "" {
170 profileName = "temp"
171 }
172
173 sourcePath := filePath
174 profileType := FileType
175 if dirPath != "" {
176 profileType = FolderType
177 sourcePath = dirPath
178 }
179 profile := Profile{
180 Name: profileName,
181 Description: fmt.Sprintf("temp %s profile generated from %s", profileType, sourcePath),
182 Type: profileType,
183 TargetDirectory: ".",
184 }
185
186 dir, file := path.Split(sourcePath)
187 profile.TargetDirectory = dir
188 profile.NamePatterns = []ParameterizedText{
189 ParameterizedText(file),
190 }
191 profile.PostScripts = []ParameterizedText{
192 ParameterizedText(fmt.Sprintf("code ./%s", file)),
193 }
194
195 if filePath != "" {
37160b89 2026-05-01 Pavel Safronov196 fileContent, err := readFile(filePath)
6962cd0b 2023-09-14 Pavel Safronov197 if err != nil {
198 return fmt.Errorf("error reading file %s: %w", filePath, err)
199 }
37160b89 2026-05-01 Pavel Safronov200 profile.FileTemplate = ParameterizedText(fileContent)
6962cd0b 2023-09-14 Pavel Safronov201 } else {
37160b89 2026-05-01 Pavel Safronov202 var files []FileInFolder
203 err := filepath.WalkDir(dirPath, func(p string, d fs.DirEntry, err error) error {
6962cd0b 2023-09-14 Pavel Safronov204 if err != nil {
37160b89 2026-05-01 Pavel Safronov205 fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", p, err)
6962cd0b 2023-09-14 Pavel Safronov206 return err
207 }
208
37160b89 2026-05-01 Pavel Safronov209 isRoot := p == dirPath
6962cd0b 2023-09-14 Pavel Safronov210 isDir := d.IsDir()
37160b89 2026-05-01 Pavel Safronov211 if isRoot || isDir {
6962cd0b 2023-09-14 Pavel Safronov212 return nil
213 }
37160b89 2026-05-01 Pavel Safronov214 relPath, err := filepath.Rel(dirPath, p)
6962cd0b 2023-09-14 Pavel Safronov215 if err != nil {
37160b89 2026-05-01 Pavel Safronov216 return fmt.Errorf("error getting relative path for %s: %w", p, err)
6962cd0b 2023-09-14 Pavel Safronov217 }
37160b89 2026-05-01 Pavel Safronov218 contents, err := readFile(p)
6962cd0b 2023-09-14 Pavel Safronov219 if err != nil {
37160b89 2026-05-01 Pavel Safronov220 return fmt.Errorf("error reading file %s: %w", p, err)
6962cd0b 2023-09-14 Pavel Safronov221 }
37160b89 2026-05-01 Pavel Safronov222 files = append(files, FileInFolder{
6962cd0b 2023-09-14 Pavel Safronov223 Name: ParameterizedText(relPath),
224 FileTemplate: ParameterizedText(contents),
37160b89 2026-05-01 Pavel Safronov225 })
6962cd0b 2023-09-14 Pavel Safronov226 return nil
227 })
228 if err != nil {
229 return fmt.Errorf("error walking dir %s: %w", dirPath, err)
230 }
231 profile.Files = files
232 }
233
37160b89 2026-05-01 Pavel Safronov234 cfg := Config{Profiles: []Profile{profile}}
6962cd0b 2023-09-14 Pavel Safronov235 writer := strings.Builder{}
236 encoder := yaml.NewEncoder(&writer)
37160b89 2026-05-01 Pavel Safronov237 if err := encoder.Encode(cfg); err != nil {
6962cd0b 2023-09-14 Pavel Safronov238 return fmt.Errorf("error encoding config: %v", err)
239 }
37160b89 2026-05-01 Pavel Safronov240 fmt.Println(writer.String())
6962cd0b 2023-09-14 Pavel Safronov241 return nil
242 },
243 },
8c15a714 2022-11-04 Pavel Safronov244 {
245 Name: "get",
246 Usage: "get a profile",
247 Action: func(c *cli.Context) error {
37160b89 2026-05-01 Pavel Safronov248 s, err := createSettings(av, c)
8c15a714 2022-11-04 Pavel Safronov249 if err != nil {
250 return fmt.Errorf("error creating settings: %v", err)
251 }
37160b89 2026-05-01 Pavel Safronov252 cfg := s.config
8c15a714 2022-11-04 Pavel Safronov253
37160b89 2026-05-01 Pavel Safronov254 index, err := findProfileIndex(cfg.Profiles, s.profile)
8c15a714 2022-11-04 Pavel Safronov255 if err != nil {
37160b89 2026-05-01 Pavel Safronov256 return fmt.Errorf("profile '%v' not found", s.profile)
8c15a714 2022-11-04 Pavel Safronov257 }
37160b89 2026-05-01 Pavel Safronov258 profile := cfg.Profiles[index]
8c15a714 2022-11-04 Pavel Safronov259
260 writer := strings.Builder{}
261 encoder := yaml.NewEncoder(&writer)
37160b89 2026-05-01 Pavel Safronov262 if err = encoder.Encode(profile); err != nil {
8c15a714 2022-11-04 Pavel Safronov263 return fmt.Errorf("error encoding profile: %v", err)
264 }
265
37160b89 2026-05-01 Pavel Safronov266 fmt.Printf("Profile '%v':\n", s.profile)
267 fmt.Println(writer.String())
f58f2e35 2023-01-30 Pavel Safronov268 fmt.Println()
8c15a714 2022-11-04 Pavel Safronov269 return nil
270 },
271 },
e5793361 2026-01-16 Pavel Safronov272 {
273 Name: "init",
274 Usage: "if global template config is not present, initializes the global template config with default profiles",
275 Action: func(c *cli.Context) error {
276 usr, err := user.Current()
277 if err != nil {
278 log.Fatal(err)
279 }
37160b89 2026-05-01 Pavel Safronov280 fullConfigPath := getFullPath(av.configFilePath, *usr)
e5793361 2026-01-16 Pavel Safronov281
282 _, err = os.Stat(fullConfigPath)
283 if err == nil {
284 fmt.Printf("Config file already exists at '%v', skipping init\n", fullConfigPath)
285 fmt.Println()
286 return nil
287 }
288
289 fmt.Printf("Setting config at '%v' to default\n", fullConfigPath)
37160b89 2026-05-01 Pavel Safronov290 if err = saveConfig(fullConfigPath, defaultConfig()); err != nil {
e5793361 2026-01-16 Pavel Safronov291 return fmt.Errorf("error saving default config: %v", err)
292 }
293 fmt.Println()
294 return nil
295 },
296 },
8c15a714 2022-11-04 Pavel Safronov297 {
298 Name: "reset",
299 Usage: "reset the global template config to default profiles",
300 Action: func(c *cli.Context) error {
873846b9 2022-12-09 Pavel Safronov301 usr, err := user.Current()
8c15a714 2022-11-04 Pavel Safronov302 if err != nil {
873846b9 2022-12-09 Pavel Safronov303 log.Fatal(err)
8c15a714 2022-11-04 Pavel Safronov304 }
37160b89 2026-05-01 Pavel Safronov305 fullConfigPath := getFullPath(av.configFilePath, *usr)
8c15a714 2022-11-04 Pavel Safronov306
307 fmt.Printf("Resetting config at '%v' to default\n", fullConfigPath)
37160b89 2026-05-01 Pavel Safronov308 if err = saveConfig(fullConfigPath, defaultConfig()); err != nil {
8c15a714 2022-11-04 Pavel Safronov309 return fmt.Errorf("error saving default config: %v", err)
310 }
f58f2e35 2023-01-30 Pavel Safronov311 fmt.Println()
8c15a714 2022-11-04 Pavel Safronov312 return nil
313 },
314 },
2543ce03 2023-01-29 Pavel Safronov315 {
316 Name: "version",
317 Usage: "print version",
318 Action: func(c *cli.Context) error {
37160b89 2026-05-01 Pavel Safronov319 fmt.Println(version)
2543ce03 2023-01-29 Pavel Safronov320 return nil
321 },
322 },
8c15a714 2022-11-04 Pavel Safronov323 },
37160b89 2026-05-01 Pavel Safronov324 Action: func(c *cli.Context) error {
325 s, err := createSettings(av, c)
05f9e8ef 2022-10-06 Pavel Safronov326 if err != nil {
b1ded4b4 2022-10-30 Pavel Safronov327 return fmt.Errorf("error parsing arguments: %w", err)
05f9e8ef 2022-10-06 Pavel Safronov328 }
37160b89 2026-05-01 Pavel Safronov329 if err = runMain(s); err != nil {
b1ded4b4 2022-10-30 Pavel Safronov330 return fmt.Errorf("error in main logic: %w", err)
55d96f9c 2022-09-23 Pavel Safronov331 }
f58f2e35 2023-01-30 Pavel Safronov332 fmt.Println()
b1ded4b4 2022-10-30 Pavel Safronov333 return nil
334 },
335 }
55d96f9c 2022-09-23 Pavel Safronov336
37160b89 2026-05-01 Pavel Safronov337 return app.Run(args)
b1ded4b4 2022-10-30 Pavel Safronov338}
319b8b3f 2022-09-22 Pavel Safronov339
37160b89 2026-05-01 Pavel Safronov340func createSettings(av argumentValues, context *cli.Context) (settings, error) {
341 ev, err := readEnvVars()
9573ca02 2022-11-16 Pavel Safronov342 if err != nil {
37160b89 2026-05-01 Pavel Safronov343 return settings{}, fmt.Errorf("error getting env var values: %w", err)
9573ca02 2022-11-16 Pavel Safronov344 }
873846b9 2022-12-09 Pavel Safronov345
37160b89 2026-05-01 Pavel Safronov346 s := settings{
347 argumentValues: av,
348 envVarValues: ev,
b1ded4b4 2022-10-30 Pavel Safronov349 }
319b8b3f 2022-09-22 Pavel Safronov350
b1ded4b4 2022-10-30 Pavel Safronov351 args := context.Args()
886f7fa2 2022-11-02 Pavel Safronov352
8fc7c452 2023-03-29 Pavel Safronov353 var level log.Level
37160b89 2026-05-01 Pavel Safronov354 switch strings.ToLower(s.logLevel) {
886f7fa2 2022-11-02 Pavel Safronov355 case "debug":
8fc7c452 2023-03-29 Pavel Safronov356 level = log.DebugLevel
886f7fa2 2022-11-02 Pavel Safronov357 case "info":
8fc7c452 2023-03-29 Pavel Safronov358 level = log.InfoLevel
886f7fa2 2022-11-02 Pavel Safronov359 case "warn":
8fc7c452 2023-03-29 Pavel Safronov360 level = log.WarnLevel
886f7fa2 2022-11-02 Pavel Safronov361 case "error":
8fc7c452 2023-03-29 Pavel Safronov362 level = log.ErrorLevel
886f7fa2 2022-11-02 Pavel Safronov363 case "fatal":
8fc7c452 2023-03-29 Pavel Safronov364 level = log.FatalLevel
886f7fa2 2022-11-02 Pavel Safronov365 case "panic":
8fc7c452 2023-03-29 Pavel Safronov366 level = log.PanicLevel
886f7fa2 2022-11-02 Pavel Safronov367 default:
37160b89 2026-05-01 Pavel Safronov368 return settings{}, fmt.Errorf("invalid log level specified %v", s.logLevel)
886f7fa2 2022-11-02 Pavel Safronov369 }
8fc7c452 2023-03-29 Pavel Safronov370 log.SetLevel(level)
886f7fa2 2022-11-02 Pavel Safronov371 log.SetOutput(os.Stdout)
372
373 log.WithFields(log.Fields{"args": args}).Debug("Args parsed")
319b8b3f 2022-09-22 Pavel Safronov374
37160b89 2026-05-01 Pavel Safronov375 skipProfile := len(s.fileTemplate) > 0 || len(s.dirTemplate) > 0 || len(s.scriptTemplate) > 0
8fc7c452 2023-03-29 Pavel Safronov376 currentParamIndex := 0
377
378 if !skipProfile {
37160b89 2026-05-01 Pavel Safronov379 if s.profile == "" && args.Len() > currentParamIndex {
380 s.profile = args.Get(currentParamIndex)
8fc7c452 2023-03-29 Pavel Safronov381 }
37160b89 2026-05-01 Pavel Safronov382 if s.profile != "" {
383 fmt.Println("Profile:", s.profile)
7add4991 2026-01-31 Pavel Safronov384 }
8fc7c452 2023-03-29 Pavel Safronov385 currentParamIndex++
b1ded4b4 2022-10-30 Pavel Safronov386 }
4a7759db 2022-09-24 Pavel Safronov387
37160b89 2026-05-01 Pavel Safronov388 if s.projectName == "" && args.Len() > currentParamIndex {
389 s.projectName = args.Get(currentParamIndex)
b1ded4b4 2022-10-30 Pavel Safronov390 }
37160b89 2026-05-01 Pavel Safronov391 if s.projectName != "" {
392 fmt.Println("Project name:", s.projectName)
7add4991 2026-01-31 Pavel Safronov393 }
8179bb1c 2022-10-26 Pavel Safronov394
37160b89 2026-05-01 Pavel Safronov395 _, err = os.Stat(s.extrasFilePath)
ae89a42a 2022-11-02 Pavel Safronov396 if err == nil {
37160b89 2026-05-01 Pavel Safronov397 extras, err := loadExtras(s.extrasFilePath)
ae89a42a 2022-11-02 Pavel Safronov398 if err != nil {
37160b89 2026-05-01 Pavel Safronov399 return s, fmt.Errorf("error loading extras from %v: %w", s.extrasFilePath, err)
ae89a42a 2022-11-02 Pavel Safronov400 }
37160b89 2026-05-01 Pavel Safronov401 s.extras = extras
b1ded4b4 2022-10-30 Pavel Safronov402 }
4a7759db 2022-09-24 Pavel Safronov403
b1ded4b4 2022-10-30 Pavel Safronov404 usr, err := user.Current()
405 if err != nil {
406 log.Fatal(err)
407 }
37160b89 2026-05-01 Pavel Safronov408 s.usr = usr
4a7361b0 2022-09-21 Pavel Safronov409
37160b89 2026-05-01 Pavel Safronov410 fullConfigPath := getFullPath(s.configFilePath, *usr)
411 s.fullConfigPath = fullConfigPath
8c15a714 2022-11-04 Pavel Safronov412
f58f2e35 2023-01-30 Pavel Safronov413 var fullConfig Config
37160b89 2026-05-01 Pavel Safronov414 if s.useDefaultConfig {
f58f2e35 2023-01-30 Pavel Safronov415 fullConfig = defaultConfig()
b1ded4b4 2022-10-30 Pavel Safronov416 } else {
f58f2e35 2023-01-30 Pavel Safronov417 config, err := loadConfig(fullConfigPath)
b1ded4b4 2022-10-30 Pavel Safronov418 if err != nil {
37160b89 2026-05-01 Pavel Safronov419 return s, fmt.Errorf("error loading config from %v: %w\nConsider running `template reset` to recreate the config file, but keep in mind that `reset` will remove existing config settings", fullConfigPath, err)
5e424f1b 2022-11-12 Pavel Safronov420 }
bbd011d0 2022-12-10 Pavel Safronov421 fmt.Printf("Loaded config from %v\n", fullConfigPath)
37160b89 2026-05-01 Pavel Safronov422 fullConfig = loadAndMergeUserConfig(s.userConfigPath, usr, config)
b1ded4b4 2022-10-30 Pavel Safronov423 }
37160b89 2026-05-01 Pavel Safronov424 s.config = fullConfig
5e424f1b 2022-11-12 Pavel Safronov425
37160b89 2026-05-01 Pavel Safronov426 return s, nil
8c15a714 2022-11-04 Pavel Safronov427}
b1ded4b4 2022-10-30 Pavel Safronov428
f58f2e35 2023-01-30 Pavel Safronov429func loadAndMergeUserConfig(userConfigPath string, usr *user.User, globalConfig Config) Config {
430 fullUserConfigPath := getFullPath(userConfigPath, *usr)
431 _, err := os.Stat(fullUserConfigPath)
432 if err != nil {
433 fmt.Printf("User config file not found at %v, skipping\n", fullUserConfigPath)
434 return globalConfig
435 }
436
437 userConfig, err := loadConfig(fullUserConfigPath)
438 if err != nil {
439 fmt.Printf("Error loading user config from %v: %v\n", fullUserConfigPath, err)
440 return globalConfig
441 }
442
443 fmt.Printf("Loaded user config from %v, combining with global config\n", fullUserConfigPath)
444 fullConfig, err := mergeConfigs(globalConfig, userConfig)
445 if err != nil {
446 fmt.Printf("Error merging configs: %v\n", err)
447 }
448
449 return fullConfig
450}
451
37160b89 2026-05-01 Pavel Safronov452func readEnvVars() (envVarValues, error) {
453 ev := envVarValues{}
9573ca02 2022-11-16 Pavel Safronov454
455 useDefaultConfigText := os.Getenv("TEMPLATE_USE_DEFAULT_CONFIG")
456 if useDefaultConfigText != "" {
457 useDefaultConfig, err := strconv.ParseBool(useDefaultConfigText)
458 if err != nil {
37160b89 2026-05-01 Pavel Safronov459 return ev, fmt.Errorf("error parsing env var TEMPLATE_USE_DEFAULT_CONFIG: \"%s\" %w", useDefaultConfigText, err)
9573ca02 2022-11-16 Pavel Safronov460 }
37160b89 2026-05-01 Pavel Safronov461 ev.useDefaultConfig = useDefaultConfig
9573ca02 2022-11-16 Pavel Safronov462 }
463
37160b89 2026-05-01 Pavel Safronov464 return ev, nil
9573ca02 2022-11-16 Pavel Safronov465}
466
37160b89 2026-05-01 Pavel Safronov467func findProfile(s settings) (Profile, error) {
468 var profile Profile
469 cfg := s.config
470 createTempProfile := len(s.fileTemplate) > 0 || len(s.dirTemplate) > 0 || len(s.scriptTemplate) > 0
8fc7c452 2023-03-29 Pavel Safronov471
472 if createTempProfile {
37160b89 2026-05-01 Pavel Safronov473 if len(s.scriptTemplate) > 0 {
fd0a4446 2023-04-03 Pavel Safronov474 profile = Profile{
475 Name: "temp",
476 Description: "temp-script-profile",
477 Type: ScriptType,
37160b89 2026-05-01 Pavel Safronov478 PostScripts: []ParameterizedText{ParameterizedText(s.scriptTemplate)},
fd0a4446 2023-04-03 Pavel Safronov479 }
480 } else {
481 profileType := FileType
37160b89 2026-05-01 Pavel Safronov482 if len(s.dirTemplate) > 0 {
fd0a4446 2023-04-03 Pavel Safronov483 profileType = FolderType
484 }
37160b89 2026-05-01 Pavel Safronov485 var patterns []ParameterizedText
486 if len(s.fileTemplate) > 0 {
487 patterns = append(patterns, ParameterizedText(s.fileTemplate))
488 } else if len(s.dirTemplate) > 0 {
489 patterns = append(patterns, ParameterizedText(s.dirTemplate))
fd0a4446 2023-04-03 Pavel Safronov490 }
491 profile = Profile{
492 Name: "temp",
493 Description: fmt.Sprintf("temp-%s-profile", profileType),
494 Type: profileType,
495 NamePatterns: patterns,
496 }
ae89a42a 2022-11-02 Pavel Safronov497 }
498 } else {
37160b89 2026-05-01 Pavel Safronov499 if s.profile == "" {
500 var err error
501 profile, err = findDefaultProfile(cfg.Profiles)
8fc7c452 2023-03-29 Pavel Safronov502 if err != nil {
503 return profile, fmt.Errorf("no profile specified and no default profile found")
504 }
505 } else {
37160b89 2026-05-01 Pavel Safronov506 index, err := findProfileIndex(cfg.Profiles, s.profile)
8fc7c452 2023-03-29 Pavel Safronov507 if err != nil {
508 return profile, fmt.Errorf("error finding profile: %v", err)
509 }
37160b89 2026-05-01 Pavel Safronov510 profile = cfg.Profiles[index]
ae89a42a 2022-11-02 Pavel Safronov511 }
b1ded4b4 2022-10-30 Pavel Safronov512 }
8c15a714 2022-11-04 Pavel Safronov513 return profile, nil
514}
515
37160b89 2026-05-01 Pavel Safronov516func runMain(s settings) error {
517 profile, err := findProfile(s)
8c15a714 2022-11-04 Pavel Safronov518 if err != nil {
519 return fmt.Errorf("error finding profile: %v", err)
520 }
b1ded4b4 2022-10-30 Pavel Safronov521
bbd011d0 2022-12-10 Pavel Safronov522 stat, err := os.Stdin.Stat()
523 if err != nil {
524 return fmt.Errorf("error getting stdin stat: %w", err)
525 }
37160b89 2026-05-01 Pavel Safronov526 var pipeInputScanner *bufio.Scanner
dc7227fa 2022-12-10 Pavel Safronov527 incomingPipeData := (stat.Mode() & os.ModeCharDevice) == 0
528 if incomingPipeData {
bbd011d0 2022-12-10 Pavel Safronov529 pipeInputScanner = bufio.NewScanner(os.Stdin)
dc7227fa 2022-12-10 Pavel Safronov530 fmt.Printf("Reading from stdin\n")
bbd011d0 2022-12-10 Pavel Safronov531 }
532
37160b89 2026-05-01 Pavel Safronov533 errors := processProfile(profile, s.projectName, s.extras, *s.usr, pipeInputScanner)
b1ded4b4 2022-10-30 Pavel Safronov534 if len(errors) > 0 {
535 return fmt.Errorf("error processing profile: %v", errors)
536 }
537
538 return nil
f819f801 2022-09-21 Pavel Safronov539}