From c1b9e358515a0618a849e6f4bba396fef73f45aa Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Sat, 19 Apr 2025 20:48:06 +0200 Subject: [PATCH 01/15] chore: change to viper --- go.mod | 14 +++++++++++++- go.sum | 31 +++++++++++++++++++++++++++++- src/cmd/root.go | 5 +++++ src/cmd/utils/author_file_utils.go | 6 +----- src/cmd/utils/test_git_wrapper | 1 + 5 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 src/cmd/utils/test_git_wrapper diff --git a/go.mod b/go.mod index 3893e56..d17e08d 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,8 @@ require ( github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b // indirect github.com/dlclark/regexp2 v1.11.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect @@ -34,15 +36,25 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sahilm/fuzzy v0.1.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/viper v1.20.1 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/yuin/goldmark v1.7.4 // indirect github.com/yuin/goldmark-emoji v1.0.3 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/net v0.38.0 // indirect golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/term v0.30.0 // indirect golang.org/x/text v0.23.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c028ca4..0ef9b6f 100644 --- a/go.sum +++ b/go.sum @@ -31,10 +31,16 @@ github.com/charmbracelet/x/exp/teatest v0.0.0-20241024145942-ad25fd0d5a9e/go.mod github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= github.com/cpuguy83/go-md2man/v2 v2.0.4/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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -64,22 +70,44 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= @@ -95,4 +123,5 @@ golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/src/cmd/root.go b/src/cmd/root.go index 12b09f4..e3758ba 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -138,6 +138,11 @@ func Execute() { // check for update check_update() + err := utils.FetchConfig() + if err != nil { + panic(fmt.Sprintf("Error fetching config: %v", err)) + } + // author file check author_file, err := utils.CheckAuthorFile(os.Stdin, os.Stdout) if err != nil { diff --git a/src/cmd/utils/author_file_utils.go b/src/cmd/utils/author_file_utils.go index 0fdd93b..9154ec9 100644 --- a/src/cmd/utils/author_file_utils.go +++ b/src/cmd/utils/author_file_utils.go @@ -14,11 +14,7 @@ import ( // An example of the author file can be found in the examples folder of the repo func Find_authorfile() string { if os.Getenv("author_file") == "" { - dirs, err := os.UserConfigDir() - if err != nil { - panic(fmt.Sprintf("Error getting user config directory: %v", err)) - } - return (dirs + "/cocommit/authors.json") + return ConfigVar.GetAuthorFile() } else { return os.Getenv("author_file") } diff --git a/src/cmd/utils/test_git_wrapper b/src/cmd/utils/test_git_wrapper new file mode 100644 index 0000000..3f3f005 --- /dev/null +++ b/src/cmd/utils/test_git_wrapper @@ -0,0 +1 @@ +Test content \ No newline at end of file From 1d51c55bc5f9573bffe22933bcdf71ad23065915 Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Sat, 19 Apr 2025 20:48:20 +0200 Subject: [PATCH 02/15] refactor: remove fetch call --- src/cmd/root.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/cmd/root.go b/src/cmd/root.go index e3758ba..12b09f4 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -138,11 +138,6 @@ func Execute() { // check for update check_update() - err := utils.FetchConfig() - if err != nil { - panic(fmt.Sprintf("Error fetching config: %v", err)) - } - // author file check author_file, err := utils.CheckAuthorFile(os.Stdin, os.Stdout) if err != nil { From 3574d442a60ebfd6948f5df949d296ff2aff8d3d Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Sat, 19 Apr 2025 20:48:40 +0200 Subject: [PATCH 03/15] chore: remove test file --- src/cmd/utils/test_git_wrapper | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/cmd/utils/test_git_wrapper diff --git a/src/cmd/utils/test_git_wrapper b/src/cmd/utils/test_git_wrapper deleted file mode 100644 index 3f3f005..0000000 --- a/src/cmd/utils/test_git_wrapper +++ /dev/null @@ -1 +0,0 @@ -Test content \ No newline at end of file From 3e1ba8d965bc8b50cb60eba123a193da646b5009 Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Sun, 20 Apr 2025 22:07:12 +0200 Subject: [PATCH 04/15] feat: add config utils functions to handle config fetching and updating --- src/cmd/utils/config.go | 176 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/cmd/utils/config.go diff --git a/src/cmd/utils/config.go b/src/cmd/utils/config.go new file mode 100644 index 0000000..8e3b4d3 --- /dev/null +++ b/src/cmd/utils/config.go @@ -0,0 +1,176 @@ +package utils + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/spf13/viper" +) + +var ConfigVar *Config + +var ( + defaultConfigLocations = []string{ + "", + os.Getenv("HOME") + "/.config/cocommit", + os.Getenv("HOME") + "/cocommit", + "/etc/cocommit", + "/usr/local/etc/cocommit", + } + configName = "config" + configType = "toml" +) + +type Config struct { + Settings struct { + AuthorFile string `mapstructure:"author_file"` + StartingScope string `mapstructure:"starting_scope"` + Editor string `mapstructure:"editor"` + } `mapstructure:"settings"` +} + +func init() { + configDir, err := os.UserConfigDir() + if err == nil { + defaultConfigLocations[0] = filepath.Join(configDir, "cocommit") + } +} + +func LoadConfig() (*Config, error) { + v := viper.New() + v.SetConfigName(configName) + v.SetConfigType(configType) + + // Set default values + v.SetDefault("settings.author_file", defaultConfigLocations[0]+"/authors.json") + v.SetDefault("settings.starting_scope", "git") + v.SetDefault("settings.editor", "built-in") + + // Add search paths + for _, path := range defaultConfigLocations { + if path != "" { + v.AddConfigPath(path) + } + } + + // Try to read config + if err := v.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + if err := handleMissingConfig(v); err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf("config error: %w", err) + } + } + + var cfg Config + if err := v.Unmarshal(&cfg); err != nil { + return nil, fmt.Errorf("config unmarshal error: %w", err) + } + if cfg.Settings.AuthorFile == "" { + cfg.Settings.AuthorFile = defaultConfigLocations[0] + "/authors.json" + } + + return &cfg, nil +} + +func (c *Config) SetGlobalConfig() { + if ConfigVar == nil { + ConfigVar = c + // This doesnt really do much right now but might be useful later + viper.WatchConfig() + } +} + +func handleMissingConfig(v *viper.Viper) error { + fmt.Println("Config file not found. Would you like to create one? (y/n)") + var response string + if _, err := fmt.Scanln(&response); err != nil { + return fmt.Errorf("error reading response: %w", err) + } + + yesResponses := map[string]bool{"y": true, "Y": true, "yes": true, "Yes": true, "YES": true} + if !yesResponses[strings.TrimSpace(response)] { + return fmt.Errorf("config file not found") + } + + return createConfig(v) +} + +func createConfig(v *viper.Viper) error { + fmt.Println("Where would you like to create the config file?") + for i, path := range defaultConfigLocations { + fmt.Printf("%d. %s\n", i, path) + } + fmt.Println("Please enter the number of the location or a custom path:") + + var response string + if _, err := fmt.Scanln(&response); err != nil { + return fmt.Errorf("error reading response: %w", err) + } + + var configPath string + if num, err := strconv.Atoi(response); err == nil && num >= 0 && num < len(defaultConfigLocations) { + configPath = defaultConfigLocations[num] + } else { + configPath = response + } + + // Ensure directory exists + if err := os.MkdirAll(configPath, 0755); err != nil { + return fmt.Errorf("failed to create config directory: %w", err) + } + + // Set the config file path + fullPath := filepath.Join(configPath, fmt.Sprintf("%s.%s", configName, configType)) + v.SetConfigFile(fullPath) + + // Write default config + if err := v.SafeWriteConfig(); err != nil { + return fmt.Errorf("failed to write config: %w", err) + } + + fmt.Printf("Config file created at: %s\n", fullPath) + return nil +} + +func (c *Config) Save() error { + v := viper.New() + + // Set all configuration values from the struct + v.Set("settings.author_file", c.Settings.AuthorFile) + v.Set("settings.starting_scope", c.Settings.StartingScope) + v.Set("settings.editor", c.Settings.Editor) + + v.SetConfigName(configName) + v.SetConfigType(configType) + + // Try to determine the original config file location + if viper.ConfigFileUsed() != "" { + v.SetConfigFile(viper.ConfigFileUsed()) + } else { + // Fall back to first default location if no existing config + if len(defaultConfigLocations) > 0 && defaultConfigLocations[0] != "" { + v.SetConfigFile(filepath.Join(defaultConfigLocations[0], fmt.Sprintf("%s.%s", configName, configType))) + } else { + return fmt.Errorf("no config file location available") + } + } + + // Ensure the directory exists + configDir := filepath.Dir(v.ConfigFileUsed()) + if err := os.MkdirAll(configDir, 0755); err != nil { + return fmt.Errorf("failed to create config directory: %w", err) + } + + // Write the config file + if err := v.WriteConfig(); err != nil { + return fmt.Errorf("failed to save config: %w", err) + } + + return nil +} \ No newline at end of file From f89d399e179dc959286a632f1e8e742fdf4cdd9a Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Sun, 20 Apr 2025 22:13:45 +0200 Subject: [PATCH 05/15] refactor: add config load to author func --- src/cmd/utils/author_file_utils.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cmd/utils/author_file_utils.go b/src/cmd/utils/author_file_utils.go index 9154ec9..af74039 100644 --- a/src/cmd/utils/author_file_utils.go +++ b/src/cmd/utils/author_file_utils.go @@ -14,7 +14,12 @@ import ( // An example of the author file can be found in the examples folder of the repo func Find_authorfile() string { if os.Getenv("author_file") == "" { - return ConfigVar.GetAuthorFile() + if ConfigVar == nil { + cfg, _ := LoadConfig() + cfg.SetGlobalConfig() + } + + return ConfigVar.Settings.AuthorFile } else { return os.Getenv("author_file") } From 2686f32184ea5c2314bdcde1db9d1f3f10b466b4 Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Sun, 20 Apr 2025 22:16:21 +0200 Subject: [PATCH 06/15] refactor: make sure lists are ordered correctly and add configurable scopes --- src/cmd/tui/tui_list.go | 42 ++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/cmd/tui/tui_list.go b/src/cmd/tui/tui_list.go index 71b0339..e83368a 100644 --- a/src/cmd/tui/tui_list.go +++ b/src/cmd/tui/tui_list.go @@ -142,7 +142,7 @@ const ( type Model struct { list list.Model - swap_lists [][]list.Item + swap_lists [3][]list.Item keys *listKeyMap quitting bool scope int @@ -255,8 +255,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.scope == git_scope { m.scope = local_scope m.list.Title = title_text + local_scope_style.Render("Scope: LOCAL") - if len(m.swap_lists) < 2 { - m.swap_lists = append(m.swap_lists, generate_list(local_scope)) + if len(m.swap_lists[1]) == 0 { + m.swap_lists[1] = generate_list(local_scope) } m.list.SetItems(m.swap_lists[1]) m.list.ResetFilter() @@ -265,8 +265,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.scope == local_scope { m.scope = mixed_scope m.list.Title = title_text + mixed_scope_style.Render("Scope: MIXED") - if len(m.swap_lists) < 3 { - m.swap_lists = append(m.swap_lists, generate_list(mixed_scope)) + if len(m.swap_lists[2]) == 0 { + m.swap_lists[2] = generate_list(mixed_scope) } m.list.SetItems(m.swap_lists[2]) m.list.ResetFilter() @@ -275,6 +275,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.scope == mixed_scope { m.scope = git_scope m.list.Title = title_text + git_scope_style.Render("Scope: GIT") + if len(m.swap_lists[0]) == 0 { + m.swap_lists[0] = generate_list(git_scope) + } m.list.SetItems(m.swap_lists[0]) m.list.ResetFilter() return m, nil @@ -360,6 +363,19 @@ func generate_list(scope int) []list.Item { } +func ConvertStringScopeToIOTA(scope string) int { + switch scope { + case "git": + return git_scope + case "local": + return local_scope + case "mixed": + return mixed_scope + default: + return -1 + } +} + func (m Model) View() string { if sub_model != nil { return sub_model.View() @@ -391,6 +407,7 @@ func listModel(scope ...int) Model { // Add items to the list if len(scope) == 0 { + git_scope := ConvertStringScopeToIOTA(utils.ConfigVar.Settings.StartingScope) scope = append(scope, git_scope) } items := generate_list(scope[0]) @@ -402,7 +419,16 @@ func listModel(scope ...int) Model { const defaultWidth = 20 l := list.New(items, itemDelegate{}, defaultWidth, listHeight) - l.Title = title_text + lipgloss.NewStyle().Foreground(lipgloss.Color("170")).Render("Scope: GIT") + + + switch scope[0] { + case git_scope: + l.Title = title_text + git_scope_style.Render("Scope: GIT") + case local_scope: + l.Title = title_text + local_scope_style.Render("Scope: LOCAL") + case mixed_scope: + l.Title = title_text + mixed_scope_style.Render("Scope: MIXED") + } l.SetShowStatusBar(false) l.SetFilteringEnabled(true) // Enable filtering l.Styles.Title = titleStyle @@ -427,7 +453,9 @@ func listModel(scope ...int) Model { } l.Styles.HelpStyle = helpStyle - model := Model{list: l, swap_lists: [][]list.Item{items}, keys: listKeys, scope: git_scope} + swapLists := [3][]list.Item{} + swapLists[scope[0]] = items + model := Model{list: l, swap_lists: swapLists, keys: listKeys, scope: scope[0]} //TODO: figure out async create // IDEA DO IT WITH CHANNELS From eac0e43023e2ba7e4f0e865916f2f2bd81049dcd Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Tue, 3 Jun 2025 17:28:09 +0200 Subject: [PATCH 07/15] fix: if author file was not set empty string not caught --- src/cmd/utils/author_file_utils.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cmd/utils/author_file_utils.go b/src/cmd/utils/author_file_utils.go index af74039..58bee17 100644 --- a/src/cmd/utils/author_file_utils.go +++ b/src/cmd/utils/author_file_utils.go @@ -18,7 +18,9 @@ func Find_authorfile() string { cfg, _ := LoadConfig() cfg.SetGlobalConfig() } - + if ConfigVar.Settings.AuthorFile == "" { + panic("No author file found, please set the author_file in the config file or set the environment variable 'author_file'") + } return ConfigVar.Settings.AuthorFile } else { return os.Getenv("author_file") From 45d4c6e8b4977c959fcbb966a94d76a2064c6dd7 Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Tue, 3 Jun 2025 17:28:37 +0200 Subject: [PATCH 08/15] refactor: add env var for config location --- src/cmd/utils/config.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cmd/utils/config.go b/src/cmd/utils/config.go index 8e3b4d3..8ae52e8 100644 --- a/src/cmd/utils/config.go +++ b/src/cmd/utils/config.go @@ -15,6 +15,7 @@ var ConfigVar *Config var ( defaultConfigLocations = []string{ "", + os.Getenv("COCOMMIT_CONFIG"), os.Getenv("HOME") + "/.config/cocommit", os.Getenv("HOME") + "/cocommit", "/etc/cocommit", @@ -40,6 +41,8 @@ func init() { } func LoadConfig() (*Config, error) { + // TODO: create if and give param as default config location + v := viper.New() v.SetConfigName(configName) v.SetConfigType(configType) From bbce5d3fab1b8d9cfaba68dcfd0f8bc423aa11ac Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Tue, 3 Jun 2025 17:29:25 +0200 Subject: [PATCH 09/15] test: add config to tests --- src/cmd/tui/tui_test.go | 24 ++++++++++++++++++++---- src/cmd/utils/util_test.go | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/cmd/tui/tui_test.go b/src/cmd/tui/tui_test.go index 69f627d..c4286f8 100644 --- a/src/cmd/tui/tui_test.go +++ b/src/cmd/tui/tui_test.go @@ -38,7 +38,14 @@ const author_data = ` } }` +const config_data = `[settings] +author_file = "author_file_test" +starting_scope = "git" +editor = "built-in" +` + var envVar string +var configVar string func setup() { // setup test data @@ -49,6 +56,14 @@ func setup() { os.Setenv("author_file", "author_file_test") envVar = os.Getenv("author_file") + err = os.WriteFile("test_config.toml", []byte(config_data), 0644) + if err != nil { + panic(err) + } + + os.Setenv("COCOMMIT_CONFIG", "test_config.toml") + configVar = os.Getenv("COCOMMIT_CONFIG") + utils.Define_users("author_file_test") } @@ -56,6 +71,7 @@ func teardown() { // remove test data os.Remove("author_file_test") os.Setenv("author_file", envVar) + os.Remove("test_config.toml") } func keyPress(tm *teatest.TestModel, key string) { @@ -610,8 +626,8 @@ func Test_ScopesLocal(t *testing.T) { t.Errorf("Expected model, got %T", fm) } - if m.scope!= local_scope { - t.Errorf("Expected scope to be %v, got %v", local_scope, m.scope) + if m.scope!= mixed_scope { + t.Errorf("Expected scope to be %v, got %v", mixed_scope, m.scope) } } @@ -634,8 +650,8 @@ func Test_ScopesMixed(t *testing.T) { t.Errorf("Expected model, got %T", fm) } - if m.scope != mixed_scope { - t.Errorf("Expected scope to be %v, got %v", mixed_scope, m.scope) + if m.scope != local_scope { + t.Errorf("Expected scope to be %v, got %v", local_scope, m.scope) } } diff --git a/src/cmd/utils/util_test.go b/src/cmd/utils/util_test.go index 3418f69..8b392c4 100644 --- a/src/cmd/utils/util_test.go +++ b/src/cmd/utils/util_test.go @@ -38,10 +38,25 @@ const author_data = ` } }` + +const config_data = `[settings] +author_file = "author_file_test" +starting_scope = "git" +editor = "built-in" +` + var envVar = os.Getenv("author_file") func setup() { // setup test data + err := os.WriteFile("test_config.toml", []byte(config_data), 0644) + if err != nil { + panic(err) + } + + os.Setenv("COCOMMIT_CONFIG", "test_config.toml") + + utils.Find_authorfile() os.WriteFile("author_file_test", []byte(author_data), 0644) os.Setenv("author_file", "author_file_test") } @@ -50,6 +65,7 @@ func teardown() { // remove test data os.Remove("author_file_test") os.Setenv("author_file", envVar) + os.Remove("test_config.toml") } // Author tests BEGIN @@ -128,10 +144,12 @@ func Test_FindAuthorFilePanic(t *testing.T) { originalAuthorFile := os.Getenv("author_file") originalHome := os.Getenv("HOME") orignalXDG := os.Getenv("XDG_CONFIG_HOME") + originalAuthorFile_Config := utils.ConfigVar.Settings.AuthorFile // Test Find_authorfile panic defer func() { // Reset environment variables + utils.ConfigVar.Settings.AuthorFile = originalAuthorFile_Config os.Setenv("author_file", originalAuthorFile) os.Setenv("HOME", originalHome) os.Setenv("XDG_CONFIG_HOME", orignalXDG) @@ -143,6 +161,7 @@ func Test_FindAuthorFilePanic(t *testing.T) { // Set environment variables to empty strings // to trigger the panic + utils.ConfigVar.Settings.AuthorFile = "" os.Setenv("author_file", "") os.Setenv("HOME", "") os.Setenv("XDG_CONFIG_HOME", "") From cbcd5ae1d1c37ed56b89e69772404cc7ccabc7a5 Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Tue, 3 Jun 2025 17:36:17 +0200 Subject: [PATCH 10/15] fix: add config to cmd tests --- src/cmd/cmd_test.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/cmd/cmd_test.go b/src/cmd/cmd_test.go index 3fb045d..b11dfa5 100644 --- a/src/cmd/cmd_test.go +++ b/src/cmd/cmd_test.go @@ -34,11 +34,25 @@ const author_data = ` } }` +const config_data = `[settings] +author_file = "author_file_test" +starting_scope = "git" +editor = "built-in" +` + var envVar = utils.Find_authorfile() func setup() { + // setup config file + err := os.WriteFile("config.toml", []byte(config_data), 0644) + if err != nil { + panic(err) + } + os.Setenv("COCOMMIT_CONFIG", "config.toml") + utils.Find_authorfile() + // setup test data - err := os.WriteFile("author_file_test", []byte(author_data), 0644) + err = os.WriteFile("author_file_test", []byte(author_data), 0644) if err != nil { panic(err) } @@ -50,6 +64,7 @@ func teardown() { // remove test data os.Remove("author_file_test") os.Setenv("author_file", envVar) + os.Remove("config.toml") } // Skip cobra cmd tests on CI causes problems apparenly idk why From 97dac5169a7a963d9871c575eb7625033acff879 Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Tue, 3 Jun 2025 17:39:17 +0200 Subject: [PATCH 11/15] test: try to fix config file not being loaded --- src/cmd/tui/tui_test.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/cmd/tui/tui_test.go b/src/cmd/tui/tui_test.go index c4286f8..3ec66be 100644 --- a/src/cmd/tui/tui_test.go +++ b/src/cmd/tui/tui_test.go @@ -48,21 +48,23 @@ var envVar string var configVar string func setup() { - // setup test data - err := os.WriteFile("author_file_test", []byte(author_data), 0644) - if err != nil { - panic(err) - } - os.Setenv("author_file", "author_file_test") - envVar = os.Getenv("author_file") - - err = os.WriteFile("test_config.toml", []byte(config_data), 0644) + err := os.WriteFile("test_config.toml", []byte(config_data), 0644) if err != nil { panic(err) } os.Setenv("COCOMMIT_CONFIG", "test_config.toml") configVar = os.Getenv("COCOMMIT_CONFIG") + utils.Find_authorfile() + + + // setup test data + err = os.WriteFile("author_file_test", []byte(author_data), 0644) + if err != nil { + panic(err) + } + os.Setenv("author_file", "author_file_test") + envVar = os.Getenv("author_file") utils.Define_users("author_file_test") } From ff9b8739cb284635272ddad9ed09e2423b9623cf Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Tue, 3 Jun 2025 19:47:04 +0200 Subject: [PATCH 12/15] refactor: change config to be optional and create default state --- src/cmd/cmd_test.go | 16 +----------- src/cmd/tui/tui_test.go | 20 +-------------- src/cmd/utils/author_file_utils.go | 40 ++++++++++++++++++++++++++---- src/cmd/utils/config.go | 13 ++++++---- src/cmd/utils/util_test.go | 15 +++++------ 5 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/cmd/cmd_test.go b/src/cmd/cmd_test.go index b11dfa5..825d413 100644 --- a/src/cmd/cmd_test.go +++ b/src/cmd/cmd_test.go @@ -34,25 +34,11 @@ const author_data = ` } }` -const config_data = `[settings] -author_file = "author_file_test" -starting_scope = "git" -editor = "built-in" -` - var envVar = utils.Find_authorfile() func setup() { - // setup config file - err := os.WriteFile("config.toml", []byte(config_data), 0644) - if err != nil { - panic(err) - } - os.Setenv("COCOMMIT_CONFIG", "config.toml") - utils.Find_authorfile() - // setup test data - err = os.WriteFile("author_file_test", []byte(author_data), 0644) + err := os.WriteFile("author_file_test", []byte(author_data), 0644) if err != nil { panic(err) } diff --git a/src/cmd/tui/tui_test.go b/src/cmd/tui/tui_test.go index 3ec66be..4059476 100644 --- a/src/cmd/tui/tui_test.go +++ b/src/cmd/tui/tui_test.go @@ -38,28 +38,11 @@ const author_data = ` } }` -const config_data = `[settings] -author_file = "author_file_test" -starting_scope = "git" -editor = "built-in" -` - var envVar string -var configVar string func setup() { - err := os.WriteFile("test_config.toml", []byte(config_data), 0644) - if err != nil { - panic(err) - } - - os.Setenv("COCOMMIT_CONFIG", "test_config.toml") - configVar = os.Getenv("COCOMMIT_CONFIG") - utils.Find_authorfile() - - // setup test data - err = os.WriteFile("author_file_test", []byte(author_data), 0644) + err := os.WriteFile("author_file_test", []byte(author_data), 0644) if err != nil { panic(err) } @@ -73,7 +56,6 @@ func teardown() { // remove test data os.Remove("author_file_test") os.Setenv("author_file", envVar) - os.Remove("test_config.toml") } func keyPress(tm *teatest.TestModel, key string) { diff --git a/src/cmd/utils/author_file_utils.go b/src/cmd/utils/author_file_utils.go index 58bee17..e8ebe05 100644 --- a/src/cmd/utils/author_file_utils.go +++ b/src/cmd/utils/author_file_utils.go @@ -13,15 +13,45 @@ import ( // that contains the names and emails of the users that are allowed to commit // An example of the author file can be found in the examples folder of the repo func Find_authorfile() string { + var file string + if os.Getenv("author_file") == "" { if ConfigVar == nil { cfg, _ := LoadConfig() - cfg.SetGlobalConfig() + if cfg == nil { + // mimic the default config structure + cfg = &Config{ + Settings: struct { + AuthorFile string `mapstructure:"author_file"` + StartingScope string `mapstructure:"starting_scope"` + Editor string `mapstructure:"editor"` + }{ + AuthorFile: "", + StartingScope: "git", + Editor: "built-in", + }, + } + cfg.SetGlobalConfig() + } } - if ConfigVar.Settings.AuthorFile == "" { - panic("No author file found, please set the author_file in the config file or set the environment variable 'author_file'") - } - return ConfigVar.Settings.AuthorFile + + if ConfigVar.Settings.AuthorFile != "" { + file = ConfigVar.Settings.AuthorFile + } else if os.Getenv("author_file") != "" { + file = os.Getenv("author_file") + } else { + userconf, err :=os.UserConfigDir() + if err != nil { + panic(fmt.Sprintf("Error getting user config dir: %v", err)) + } + + if _, err := os.Stat(userconf+"/cocommit/authors.json"); os.IsNotExist(err) { + panic(fmt.Sprintf("No author file set, please set the author_file environment variable or create a config file using the command: cocommit config -c")) + } else { + file = userconf + "/cocommit/authors.json" + } + } + return file } else { return os.Getenv("author_file") } diff --git a/src/cmd/utils/config.go b/src/cmd/utils/config.go index 8ae52e8..d151e2d 100644 --- a/src/cmd/utils/config.go +++ b/src/cmd/utils/config.go @@ -62,12 +62,15 @@ func LoadConfig() (*Config, error) { // Try to read config if err := v.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { - if err := handleMissingConfig(v); err != nil { - return nil, err - } - } else { - return nil, fmt.Errorf("config error: %w", err) + return nil, nil } + // if _, ok := err.(viper.ConfigFileNotFoundError); ok { + // if err := handleMissingConfig(v); err != nil { + // return nil, err + // } + // } else { + // return nil, fmt.Errorf("config error: %w", err) + // } } var cfg Config diff --git a/src/cmd/utils/util_test.go b/src/cmd/utils/util_test.go index 8b392c4..4a624d9 100644 --- a/src/cmd/utils/util_test.go +++ b/src/cmd/utils/util_test.go @@ -48,15 +48,17 @@ editor = "built-in" var envVar = os.Getenv("author_file") func setup() { + os.Setenv("author_file", "") + // setup test data - err := os.WriteFile("test_config.toml", []byte(config_data), 0644) + err := os.WriteFile("config.toml", []byte(config_data), 0644) if err != nil { panic(err) } - os.Setenv("COCOMMIT_CONFIG", "test_config.toml") + os.Setenv("COCOMMIT_CONFIG", "config.toml") - utils.Find_authorfile() + os.WriteFile("author_file_test", []byte(author_data), 0644) os.Setenv("author_file", "author_file_test") } @@ -65,7 +67,7 @@ func teardown() { // remove test data os.Remove("author_file_test") os.Setenv("author_file", envVar) - os.Remove("test_config.toml") + os.Remove("config.toml") } // Author tests BEGIN @@ -140,16 +142,16 @@ func Test_CreateAuthor(t *testing.T) { } func Test_FindAuthorFilePanic(t *testing.T) { + setup() + defer teardown() // Save original environment variables originalAuthorFile := os.Getenv("author_file") originalHome := os.Getenv("HOME") orignalXDG := os.Getenv("XDG_CONFIG_HOME") - originalAuthorFile_Config := utils.ConfigVar.Settings.AuthorFile // Test Find_authorfile panic defer func() { // Reset environment variables - utils.ConfigVar.Settings.AuthorFile = originalAuthorFile_Config os.Setenv("author_file", originalAuthorFile) os.Setenv("HOME", originalHome) os.Setenv("XDG_CONFIG_HOME", orignalXDG) @@ -161,7 +163,6 @@ func Test_FindAuthorFilePanic(t *testing.T) { // Set environment variables to empty strings // to trigger the panic - utils.ConfigVar.Settings.AuthorFile = "" os.Setenv("author_file", "") os.Setenv("HOME", "") os.Setenv("XDG_CONFIG_HOME", "") From 0da8fc69981bc6aface583c635f60e9f7d34ec0c Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Tue, 3 Jun 2025 19:59:00 +0200 Subject: [PATCH 13/15] fix: env var test updated for panic --- src/cmd/utils/util_test.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/cmd/utils/util_test.go b/src/cmd/utils/util_test.go index 4a624d9..888c05a 100644 --- a/src/cmd/utils/util_test.go +++ b/src/cmd/utils/util_test.go @@ -173,14 +173,10 @@ func Test_FindAuthorFileEnv(t *testing.T) { // Test Find_authorfile with env var setup() defer teardown() - os.Setenv("author_file", "") + os.Setenv("author_file", "author_file_test") authorfile := utils.Find_authorfile() - configdir, err := os.UserConfigDir() - if err != nil { - t.Fatalf("Failed to get user config directory: %v", err) - } - if authorfile != configdir+"/cocommit/authors.json" { - t.Errorf("Find_authorfile() = %v; want %v", authorfile, configdir+"/cocommit/authors.json") + if authorfile != "author_file_test" { + t.Errorf("Find_authorfile() = %v; want %v", authorfile, "author_file_test") } } From 1f7e571fb91498fd1105f2b60549a607473d6a2e Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Tue, 3 Jun 2025 19:59:00 +0200 Subject: [PATCH 14/15] fix: env var test updated for panic fix: recover in test --- src/cmd/utils/util_test.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/cmd/utils/util_test.go b/src/cmd/utils/util_test.go index 4a624d9..433adf6 100644 --- a/src/cmd/utils/util_test.go +++ b/src/cmd/utils/util_test.go @@ -173,16 +173,23 @@ func Test_FindAuthorFileEnv(t *testing.T) { // Test Find_authorfile with env var setup() defer teardown() + + // Save original environment variable + originalAuthorFile := os.Getenv("author_file") + + defer func() { + // Reset environment variable + os.Setenv("author_file", originalAuthorFile) + + if r := recover(); r == nil { + t.Errorf("Find_authorfile() did not panic") + } + }() + + // Set an invalid environment variable to trigger panic os.Setenv("author_file", "") - authorfile := utils.Find_authorfile() - configdir, err := os.UserConfigDir() - if err != nil { - t.Fatalf("Failed to get user config directory: %v", err) - } - if authorfile != configdir+"/cocommit/authors.json" { - t.Errorf("Find_authorfile() = %v; want %v", authorfile, configdir+"/cocommit/authors.json") - } - + + utils.Find_authorfile() } func Test_CreateAuthorPanicOnFileError(t *testing.T) { From 84de8e1e4e2e17eabe24479d37e0b4b59a533653 Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Tue, 3 Jun 2025 20:12:15 +0200 Subject: [PATCH 15/15] fix: attempt to fix cmd tests --- src/cmd/cmd_test.go | 4 +--- src/cmd/users.go | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cmd/cmd_test.go b/src/cmd/cmd_test.go index 825d413..1496c4f 100644 --- a/src/cmd/cmd_test.go +++ b/src/cmd/cmd_test.go @@ -6,8 +6,6 @@ import ( "os" "strings" "testing" - - "github.com/Slug-Boi/cocommit/src/cmd/utils" ) const author_data = ` @@ -34,7 +32,7 @@ const author_data = ` } }` -var envVar = utils.Find_authorfile() +var envVar string func setup() { // setup test data diff --git a/src/cmd/users.go b/src/cmd/users.go index 012d280..8654e29 100644 --- a/src/cmd/users.go +++ b/src/cmd/users.go @@ -12,7 +12,7 @@ import ( "github.com/spf13/cobra" ) -var authorfile = utils.Find_authorfile() +var authorfile string // usersCmd represents the users command func UsersCmd() *cobra.Command { @@ -21,6 +21,9 @@ func UsersCmd() *cobra.Command { Short: "Displays all users from the author file located at:\n" + authorfile, Long: `Displays all users from the author file located at:` + "\n" + authorfile, Run: func(cmd *cobra.Command, args []string) { + if authorfile == "" { + authorfile = utils.Find_authorfile() + } if update { update_msg() }