template / commit
Merge branch 'extras' into 'main'
.gitignore | 1 + .vscode/launch.json | 38 +++++++++++++++++-------------- config.go | 43 +++++++++++++++++++++++++++++++++++ extras-test.yaml | 2 ++ extras.go | 24 ++++++++++++++++++++ extras_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++ file.go | 14 +++++++----- go.mod | 3 +++ go.sum | 11 +++++++++ main.go | 64 ++++++++++++++++++++++++++++++++-------------------- name_pattern.go | 4 +++- name_pattern_test.go | 34 ++++++++++++++++------------ postscript.go | 12 ++++++---- profile.go | 10 ++++---- 14 files changed, 240 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index c47543f..984b600 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .git template.exe +template diff --git a/.vscode/launch.json b/.vscode/launch.json index 99d1872..addb872 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,14 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Launch tests", + "type": "go", + "request": "launch", + "mode": "test", + "program": ".", + "args": [] + }, { "name": "Clean install", "type": "PowerShell", @@ -17,6 +25,7 @@ "request": "launch", "mode": "auto", "program": ".", + "args": ["--default-config"] }, { "name": "Run: template blog", @@ -24,9 +33,7 @@ "request": "launch", "mode": "auto", "program": ".", - "args": [ - "blog" - ] + "args": ["blog", "--default-config"] }, { "name": "Run: template daily", @@ -34,9 +41,7 @@ "request": "launch", "mode": "auto", "program": ".", - "args": [ - "daily" - ] + "args": ["daily", "--default-config"] }, { "name": "Run: template work", @@ -44,9 +49,7 @@ "request": "launch", "mode": "auto", "program": ".", - "args": [ - "work" - ] + "args": ["work", "--default-config"] }, { "name": "Run: template --profile work --project testProj", @@ -58,7 +61,8 @@ "--profile", "work", "--project", - "testProj" + "testProj", + "--default-config" ] }, { @@ -67,10 +71,7 @@ "request": "launch", "mode": "auto", "program": ".", - "args": [ - "note", - "whoa" - ] + "args": ["note", "whoa", "--default-config"] }, { "name": "Run: template test blah", @@ -79,11 +80,14 @@ "mode": "auto", "program": ".", "args": [ + "--profile", "test", + "--name", "blah", - "--value1=1", - "--value2=two" + "--extras-yaml", + "extras-test.yaml", + "--default-config" ] } ] -} \ No newline at end of file +} diff --git a/config.go b/config.go index 2df6583..7bc8769 100644 --- a/config.go +++ b/config.go @@ -196,6 +196,8 @@ Today's notes PostScripts: []string{ "echo \"Name={{.Name}}\"", "echo \"Path={{.Path}}\"", + "echo \"Value1={{.Extras.value1}}\"", + "echo \"Value2={{.Extras.value2}}\"", }, }, }, @@ -233,6 +235,9 @@ func validateConfig(config Config) error { if profile.Type != FileType && profile.FileTemplatePath != "" { return fmt.Errorf("file template path is not supported for profile %s, which has Type=%s", profile.Name, profile.Type) } + if profile.Type != ScriptType && profile.NamePatterns != nil && len(profile.NamePatterns) > 1 { + return fmt.Errorf("multiple name patterns are not supported for profile %s, which has Type=%s", profile.Name, profile.Type) + } if profile.ScriptShell == "" && profile.PostScripts != nil && len(profile.PostScripts) > 0 { return fmt.Errorf("script shell is required for profile %s", profile.Name) } @@ -269,3 +274,41 @@ func saveConfig(path string, config Config) error { return nil } + +func getOrCreateConfig(configFilePath string) (Config, error) { + emptyConfig := Config{} + + fullConfigPath, err := getFullPath(configFilePath) + if err != nil { + return emptyConfig, fmt.Errorf("failed to get full path for config file %v: %v", configFilePath, err) + } + + // try and load config + config, err := loadConfig(fullConfigPath) + if err != nil { + fmt.Printf("No config present at '%v', creating and using sample config\n", fullConfigPath) + + // create config and save + err := saveConfig(fullConfigPath, defaultConfig()) + if err != nil { + return emptyConfig, fmt.Errorf("error creating config: %v", err) + } + + // load config + config, err = loadConfig(fullConfigPath) + if err != nil { + return emptyConfig, fmt.Errorf("error loading config: %v", err) + } + } else { + // log successful load + fmt.Printf("Config loaded from '%v'\n", fullConfigPath) + } + + // validate config + err = validateConfig(config) + if err != nil { + return emptyConfig, fmt.Errorf("error validating config: %v", err) + } + + return config, nil +} diff --git a/extras-test.yaml b/extras-test.yaml new file mode 100644 index 0000000..b63838a --- /dev/null +++ b/extras-test.yaml @@ -0,0 +1,2 @@ +value1: "1" +value2: "two" diff --git a/extras.go b/extras.go new file mode 100644 index 0000000..d7a0293 --- /dev/null +++ b/extras.go @@ -0,0 +1,24 @@ +package main + +import ( + "os" + + "gopkg.in/yaml.v3" +) + +func loadExtras(path string) (map[string]string, error) { + emptyMap := make(map[string]string) + + file, error := os.ReadFile(path) + if error != nil { + return emptyMap, error + } + + result := make(map[string]string) + error = yaml.Unmarshal(file, &result) + if error != nil { + return emptyMap, error + } + + return result, nil +} diff --git a/extras_test.go b/extras_test.go new file mode 100644 index 0000000..9a72542 --- /dev/null +++ b/extras_test.go @@ -0,0 +1,52 @@ +package main + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" +) + +func TestExtras(t *testing.T) { + // setup code + extras := map[string]string{ + "version": "1", + "id": "123", + "customer": "abc", + } + + file, error := os.CreateTemp("", "extras.yaml") + if error != nil { + t.Error("Error creating temp file: ", error) + } + + // cleanup code + deleteFile := func() { + os.Remove(file.Name()) + } + defer deleteFile() + + // write extras to file + encoder := yaml.NewEncoder(file) + error = encoder.Encode(extras) + if error != nil { + t.Error("Error encoding extras: ", error) + } + + // close file + error = file.Close() + if error != nil { + t.Error("Error closing file: ", error) + } + + // file name + fileName := file.Name() + + // happy path + loadedExtras, error := loadExtras(fileName) + if error != nil { + t.Error("Error loading extras: ", error) + } + assert.Equal(t, extras, loadedExtras) +} diff --git a/file.go b/file.go index a82aa49..f604e55 100644 --- a/file.go +++ b/file.go @@ -14,9 +14,10 @@ type FilePatternData struct { ProjectName string Time string Year, Month, Day string + Extras map[string]string } -func createFilePatternData(now time.Time, projectName string, name string) FilePatternData { +func createFilePatternData(now time.Time, projectName string, name string, extras map[string]string) FilePatternData { return FilePatternData{ Name: name, ProjectName: projectName, @@ -24,15 +25,16 @@ func createFilePatternData(now time.Time, projectName string, name string) FileP Year: now.Format("2006"), Month: now.Format("01"), Day: now.Format("02"), + Extras: extras, } } -func createTemplateFile(srcPath string, destPath string, projectName string, name string) error { +func createTemplateFile(srcPath string, destPath string, projectName string, name string, extras map[string]string) error { now := time.Now() - filePatternData := createFilePatternData(now, projectName, name) + filePatternData := createFilePatternData(now, projectName, name, extras) // standardize paths - srcPath,err := getFullPath(srcPath) + srcPath, err := getFullPath(srcPath) if err != nil { return err } @@ -63,9 +65,9 @@ func createTemplateFile(srcPath string, destPath string, projectName string, nam return nil } -func createTemplateString(templateText string, projectName string, name string) (string, error) { +func createTemplateString(templateText string, projectName string, name string, extras map[string]string) (string, error) { now := time.Now() - filePatternData := createFilePatternData(now, projectName, name) + filePatternData := createFilePatternData(now, projectName, name, extras) // parse template template, err := template.New("text-template").Parse(templateText) diff --git a/go.mod b/go.mod index fb5860f..d26de5c 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module gitlab.com/fuzzy-rp/template go 1.19 require ( + github.com/stretchr/testify v1.8.0 github.com/urfave/cli/v2 v2.16.3 golang.org/x/exp v0.0.0-20220921164117-439092de6870 gopkg.in/yaml.v3 v3.0.1 @@ -10,6 +11,8 @@ require ( require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect ) diff --git a/go.sum b/go.sum index 059a9a7..536755f 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,17 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk= github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -10,5 +20,6 @@ golang.org/x/exp v0.0.0-20220921164117-439092de6870 h1:j8b6j9gzSigH28O5SjSpQSSh9 golang.org/x/exp v0.0.0-20220921164117-439092de6870/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 4276b22..028a555 100644 --- a/main.go +++ b/main.go @@ -11,14 +11,18 @@ import ( ) func main() { - configFilePath := getFullPath("~/.template.yaml") var profileName string var projectName string + var configFilePath string + var extrasFilePath string + var useDefaultConfig bool + var err error app := &cli.App{ Name: "template", Usage: "create file or directory from a template", EnableBashCompletion: true, + Suggest: true, Flags: []cli.Flag{ &cli.StringFlag{ Name: "profile", @@ -37,37 +41,45 @@ func main() { Value: "", Destination: &projectName, }, + &cli.PathFlag{ + Name: "config-yaml", + Aliases: []string{"c"}, + Usage: "path to the config yaml file", + Value: "~/.template.yaml", + Destination: &configFilePath, + }, + &cli.PathFlag{ + Name: "extras-yaml", + Aliases: []string{"e"}, + Usage: "path to the extras yaml file", + Value: "~/.template-extras.yaml", + Destination: &extrasFilePath, + }, + &cli.BoolFlag{ + Name: "default-config", + Aliases: []string{"d"}, + Usage: "use the default hard-coded config (testing only)", + Value: false, + Destination: &useDefaultConfig, + }, }, Action: func(context *cli.Context) error { - // try and load config - config, err := loadConfig(configFilePath) - if err != nil { - fmt.Printf("No config present at '%v', creating and using sample config\n", configFilePath) - // create config and save - err := saveConfig(configFilePath, defaultConfig()) - if err != nil { - return fmt.Errorf("error creating config: %v", err) - } - - // load config - config, err = loadConfig(configFilePath) + // get config + var config Config + if useDefaultConfig { + config = defaultConfig() + } else { + config, err = getOrCreateConfig(configFilePath) if err != nil { - return fmt.Errorf("error loading config: %v", err) + return err } - } else { - // log successful load - fmt.Printf("Config loaded from '%v'\n", configFilePath) - } - - // validate config - err = validateConfig(config) - if err != nil { - return fmt.Errorf("error validating config: %v", err) } // read args args := context.Args() + fmt.Println("args:", args) + // args.Get(0) // get profile name from args // if not set explicitly, use first arg, if present @@ -86,6 +98,10 @@ func main() { } // extra features + extras, err := loadExtras(extrasFilePath) + if err != nil { + return nil + } // list profiles if profileName == "list" { @@ -120,7 +136,7 @@ func main() { } // process profile - err = processProfile(profile, projectName) + err = processProfile(profile, projectName, extras) if err != nil { return fmt.Errorf("error processing profile: %v", err) } diff --git a/name_pattern.go b/name_pattern.go index d16494a..c383b63 100644 --- a/name_pattern.go +++ b/name_pattern.go @@ -12,9 +12,10 @@ import ( type NamePatternData struct { Year, Month, Day, Hour, Minute, Second string ProjectName string + Extras map[string]string } -func createNewNameForProfile(now time.Time, projectName string, profile Profile, existingEntities []string) (string, error) { +func createNewNameForProfile(now time.Time, projectName string, profile Profile, extras map[string]string, existingEntities []string) (string, error) { for index, namePattern := range profile.NamePatterns { startingVersion := 1 @@ -34,6 +35,7 @@ func createNewNameForProfile(now time.Time, projectName string, profile Profile, Minute: now.Format("04"), Second: now.Format("05"), ProjectName: projectName, + Extras: extras, } for { diff --git a/name_pattern_test.go b/name_pattern_test.go index 61aa852..f0e4dc9 100644 --- a/name_pattern_test.go +++ b/name_pattern_test.go @@ -8,25 +8,28 @@ import ( func TestCreateNewName(t *testing.T) { // setup code + extras := make(map[string]string) + extras["test1"] = "test1" + // Mon Jan 2 15:04:05 MST 2006 now := time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC) profile := Profile{ IsDefault: true, Type: FileType, NamePatterns: []string{ - "{{.Year}}-{{.Month}}-{{.Day}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.txt", - "{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.txt", - "{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}-{{.Minute}}-{{.Second}}-{{.ProjectName}}.txt", + "{{.Extras.test1}}-{{.Year}}-{{.Month}}-{{.Day}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.txt", + "{{.Extras.test1}}-{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.txt", + "{{.Extras.test1}}-{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}-{{.Minute}}-{{.Second}}-{{.ProjectName}}.txt", }, } - newNameNoProject := "2006-01-02.txt" - newName1 := "2006-01-02-test-project.txt" - newName2 := "2006-01-02-15-test-project.txt" - newName3 := "2006-01-02-15-04-05-test-project.txt" + newNameNoProject := "test1-2006-01-02.txt" + newName1 := "test1-2006-01-02-test-project.txt" + newName2 := "test1-2006-01-02-15-test-project.txt" + newName3 := "test1-2006-01-02-15-04-05-test-project.txt" testHappyPath := func(projectName string, expectedName string, existingEntities []string) func(t *testing.T) { return func(t *testing.T) { - newName, err := createNewNameForProfile(now, projectName, profile, existingEntities) + newName, err := createNewNameForProfile(now, projectName, profile, extras, existingEntities) if err != nil { t.Error("Error creating new name: ", err) } @@ -37,7 +40,7 @@ func TestCreateNewName(t *testing.T) { } testErrorPath := func(projectName string, existingEntities []string) func(t *testing.T) { return func(t *testing.T) { - _, err := createNewNameForProfile(now, projectName, profile, existingEntities) + _, err := createNewNameForProfile(now, projectName, profile, extras, existingEntities) if err == nil { t.Error("Expected error but got none") } @@ -57,22 +60,25 @@ func TestCreateNewName(t *testing.T) { } func TestCreateNewName_Index(t *testing.T) { + extras := make(map[string]string) + extras["test1"] = "test1" + // Mon Jan 2 15:04:05 MST 2006 now := time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC) profile := Profile{ IsDefault: true, Type: FileType, NamePatterns: []string{ - "{{$version := getVersion}}{{.Year}}-{{.Month}}-{{.Day}}v{{$version}}.txt", + "{{.Extras.test1}}-{{$version := getVersion}}{{.Year}}-{{.Month}}-{{.Day}}v{{$version}}.txt", }, } - newName1 := "2006-01-02v1.txt" - newName2 := "2006-01-02v2.txt" - newName3 := "2006-01-02v3.txt" + newName1 := "test1-2006-01-02v1.txt" + newName2 := "test1-2006-01-02v2.txt" + newName3 := "test1-2006-01-02v3.txt" testHappyPath := func(expectedName string, existingEntities []string) func(t *testing.T) { return func(t *testing.T) { - newName, err := createNewNameForProfile(now, "test-project", profile, existingEntities) + newName, err := createNewNameForProfile(now, "test-project", profile, extras, existingEntities) if err != nil { t.Error("Error creating new name: ", err) } diff --git a/postscript.go b/postscript.go index def2ccb..dfc2024 100644 --- a/postscript.go +++ b/postscript.go @@ -6,11 +6,12 @@ import ( ) type PostScriptPatternData struct { - Name string - Path string + Name string + Path string + Extras map[string]string } -func createPostScriptArrays(profile Profile, newName string, fullPath string) ([]string, error) { +func createPostScriptArrays(profile Profile, newName string, fullPath string, extras map[string]string) ([]string, error) { array := make([]string, 0) if profile.PostScripts == nil || len(profile.PostScripts) == 0 { @@ -18,8 +19,9 @@ func createPostScriptArrays(profile Profile, newName string, fullPath string) ([ } patternData := PostScriptPatternData{ - Name: newName, - Path: fullPath, + Name: newName, + Path: fullPath, + Extras: extras, } for _, postScriptPattern := range profile.PostScripts { diff --git a/profile.go b/profile.go index 8208fc2..6389435 100644 --- a/profile.go +++ b/profile.go @@ -32,7 +32,7 @@ func findProfile(config Config, profileName string) (Profile, error) { return Profile{}, errors.New("couldn't find profile") } -func processProfile(profile Profile, projectName string) error { +func processProfile(profile Profile, projectName string, extras map[string]string) error { // validate profile if profile.NamePatterns == nil || len(profile.NamePatterns) == 0 { return errors.New("no name patterns specified") @@ -87,7 +87,7 @@ func processProfile(profile Profile, projectName string) error { // generate a new name based on time, profile, and existing entities now := time.Now() - newName, err := createNewNameForProfile(now, projectName, profile, entities) + newName, err := createNewNameForProfile(now, projectName, profile, extras, entities) if err != nil { return err } @@ -114,13 +114,13 @@ func processProfile(profile Profile, projectName string) error { useTemplate := profile.FileTemplate != "" if useTemplateFile { fmt.Printf("Creating file '%v' from template: %v\n", fullPath, profile.FileTemplatePath) - err := createTemplateFile(profile.FileTemplatePath, fullPath, projectName, newName) + err := createTemplateFile(profile.FileTemplatePath, fullPath, projectName, newName, extras) if err != nil { return fmt.Errorf("error creating template file: %v", err) } } else if useTemplate { fmt.Printf("Creating file '%v' from template string\n", fullPath) - templateString, err := createTemplateString(profile.FileTemplate, projectName, newName) + templateString, err := createTemplateString(profile.FileTemplate, projectName, newName, extras) if err != nil { return fmt.Errorf("error creating template: %v", err) } @@ -134,7 +134,7 @@ func processProfile(profile Profile, projectName string) error { } // execute PostScript, if present - postScripts, err := createPostScriptArrays(profile, newName, fullPath) + postScripts, err := createPostScriptArrays(profile, newName, fullPath, extras) if err != nil { return err }