From b35b72754bab8911a28867c5793bf8f792cd2dfe Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Nov 2025 01:49:59 +0000 Subject: [PATCH 1/6] feat(create): implement create command with name, interval, and script flags Add three required flags to the create command: - --name/-n: Name of the launchd task - --interval/-i: Interval for task execution - --script/-s: Path to the script to execute All flags are marked as required and include appropriate help text. --- cmd/create.go | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 318c494..8bac25a 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -27,26 +27,40 @@ import ( "github.com/spf13/cobra" ) +var ( + name string + interval string + script string +) + // createCmd represents the create command var createCmd = &cobra.Command{ Use: "create", Short: "Create a new launchd cron task", Long: `Create a new launchd cron task with NAME to run SCRIPT over an INTERVAL.`, Run: func(cmd *cobra.Command, args []string) { - fmt.Println("create called") + // Get flag values + name, _ := cmd.Flags().GetString("name") + interval, _ := cmd.Flags().GetString("interval") + script, _ := cmd.Flags().GetString("script") + + fmt.Printf("Creating launchd cron task:\n") + fmt.Printf(" Name: %s\n", name) + fmt.Printf(" Interval: %s\n", interval) + fmt.Printf(" Script: %s\n", script) }, } func init() { rootCmd.AddCommand(createCmd) - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // createCmd.PersistentFlags().String("foo", "", "A help for foo") + // Define flags for the create command + createCmd.Flags().StringVarP(&name, "name", "n", "", "Name of the launchd task (required)") + createCmd.Flags().StringVarP(&interval, "interval", "i", "", "Interval for the task execution (required)") + createCmd.Flags().StringVarP(&script, "script", "s", "", "Path to the script to execute (required)") - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // createCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + // Mark flags as required + createCmd.MarkFlagRequired("name") + createCmd.MarkFlagRequired("interval") + createCmd.MarkFlagRequired("script") } From 52e406ca77d3dfcac6791ad936e77883ab3a9a91 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Nov 2025 01:50:43 +0000 Subject: [PATCH 2/6] chore: add .gitignore to exclude build artifacts --- .gitignore | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50d0fdc --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Go workspace file +go.work + +# Build artifacts +macron +dist/ From 731aded3d36e3070ed448d23efe1727e00c1061e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Nov 2025 01:54:34 +0000 Subject: [PATCH 3/6] feat(create): add validation for interval and script flags - Validate interval can be parsed as time.Duration - Verify script file exists before creating task - Return descriptive errors when validation fails - Changed Run to RunE to support error handling --- cmd/create.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 8bac25a..e80690e 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -23,6 +23,8 @@ package cmd import ( "fmt" + "os" + "time" "github.com/spf13/cobra" ) @@ -38,16 +40,31 @@ var createCmd = &cobra.Command{ Use: "create", Short: "Create a new launchd cron task", Long: `Create a new launchd cron task with NAME to run SCRIPT over an INTERVAL.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { // Get flag values name, _ := cmd.Flags().GetString("name") interval, _ := cmd.Flags().GetString("interval") script, _ := cmd.Flags().GetString("script") + // Validate interval can be parsed as time.Duration + duration, err := time.ParseDuration(interval) + if err != nil { + return fmt.Errorf("invalid interval '%s': must be a valid duration (e.g., 1h, 30m, 1h30m): %w", interval, err) + } + + // Validate script file exists + if _, err := os.Stat(script); os.IsNotExist(err) { + return fmt.Errorf("script file does not exist: %s", script) + } else if err != nil { + return fmt.Errorf("error checking script file: %w", err) + } + fmt.Printf("Creating launchd cron task:\n") fmt.Printf(" Name: %s\n", name) - fmt.Printf(" Interval: %s\n", interval) + fmt.Printf(" Interval: %s (%v)\n", interval, duration) fmt.Printf(" Script: %s\n", script) + + return nil }, } From 948a147a4d84af73162183dd70db7749cca5a7ba Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Nov 2025 14:29:13 +0000 Subject: [PATCH 4/6] feat(create): implement plist generation and file writing - Generate launchd plist XML with proper format - Write plist to ~/Library/LaunchAgents directory - Use naming format: com.macron..plist - Convert time.Duration to seconds for StartInterval - Resolve script to absolute path - Create LaunchAgents directory if it doesn't exist - Add helpful output with launchctl load command --- cmd/create.go | 77 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index e80690e..ebdc5f4 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -24,8 +24,10 @@ package cmd import ( "fmt" "os" + "path/filepath" "time" + "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" ) @@ -35,6 +37,30 @@ var ( script string ) +// generatePlist creates the plist XML content for a launchd task +func generatePlist(label, scriptPath string, intervalSeconds int) string { + return fmt.Sprintf(` + + + + Label + %s + ProgramArguments + + %s + + StartInterval + %d + RunAtLoad + + StandardOutPath + /tmp/%s.stdout + StandardErrorPath + /tmp/%s.stderr + +`, label, scriptPath, intervalSeconds, label, label) +} + // createCmd represents the create command var createCmd = &cobra.Command{ Use: "create", @@ -52,17 +78,56 @@ var createCmd = &cobra.Command{ return fmt.Errorf("invalid interval '%s': must be a valid duration (e.g., 1h, 30m, 1h30m): %w", interval, err) } + // Convert script to absolute path + absScript, err := filepath.Abs(script) + if err != nil { + return fmt.Errorf("error resolving script path: %w", err) + } + // Validate script file exists - if _, err := os.Stat(script); os.IsNotExist(err) { - return fmt.Errorf("script file does not exist: %s", script) + if _, err := os.Stat(absScript); os.IsNotExist(err) { + return fmt.Errorf("script file does not exist: %s", absScript) } else if err != nil { return fmt.Errorf("error checking script file: %w", err) } - fmt.Printf("Creating launchd cron task:\n") - fmt.Printf(" Name: %s\n", name) - fmt.Printf(" Interval: %s (%v)\n", interval, duration) - fmt.Printf(" Script: %s\n", script) + // Get home directory + home, err := homedir.Dir() + if err != nil { + return fmt.Errorf("error getting home directory: %w", err) + } + + // Create LaunchAgents directory path + launchAgentsDir := filepath.Join(home, "Library", "LaunchAgents") + + // Ensure LaunchAgents directory exists + if err := os.MkdirAll(launchAgentsDir, 0755); err != nil { + return fmt.Errorf("error creating LaunchAgents directory: %w", err) + } + + // Generate label and filename + label := fmt.Sprintf("com.macron.%s", name) + plistFilename := fmt.Sprintf("%s.plist", label) + plistPath := filepath.Join(launchAgentsDir, plistFilename) + + // Convert duration to seconds + intervalSeconds := int(duration.Seconds()) + + // Generate plist content + plistContent := generatePlist(label, absScript, intervalSeconds) + + // Write plist file + if err := os.WriteFile(plistPath, []byte(plistContent), 0644); err != nil { + return fmt.Errorf("error writing plist file: %w", err) + } + + fmt.Printf("Successfully created launchd task:\n") + fmt.Printf(" Label: %s\n", label) + fmt.Printf(" Interval: %s (%d seconds)\n", interval, intervalSeconds) + fmt.Printf(" Script: %s\n", absScript) + fmt.Printf(" Plist: %s\n", plistPath) + fmt.Printf("\nTo load the task, run:\n") + fmt.Printf(" launchctl load %s\n", plistPath) return nil }, From 4b25f1d2c7e8cc00ed24f955301b08fe62c4cb19 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Nov 2025 15:28:40 +0000 Subject: [PATCH 5/6] refactor(create): improve code structure and idiomaticity - Fix variable shadowing by renaming package-level flag vars - Extract validation logic into validateAndResolveScript function - Extract plist writing into writePlistFile function - Add constants for labelPrefix and file permissions - Simplify RunE function for better readability - Improve testability by separating concerns - Remove redundant cmd.Flags().GetString() calls --- cmd/create.go | 112 ++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index ebdc5f4..dc2397f 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -31,12 +31,35 @@ import ( "github.com/spf13/cobra" ) +const ( + labelPrefix = "com.macron" + launchAgentsDirPerms = 0755 + plistFilePerms = 0644 +) + var ( - name string - interval string - script string + createName string + createInterval string + createScript string ) +// validateAndResolveScript validates the script exists and returns its absolute path +func validateAndResolveScript(scriptPath string) (string, error) { + absScript, err := filepath.Abs(scriptPath) + if err != nil { + return "", fmt.Errorf("error resolving script path: %w", err) + } + + if _, err := os.Stat(absScript); err != nil { + if os.IsNotExist(err) { + return "", fmt.Errorf("script file does not exist: %s", absScript) + } + return "", fmt.Errorf("error checking script file: %w", err) + } + + return absScript, nil +} + // generatePlist creates the plist XML content for a launchd task func generatePlist(label, scriptPath string, intervalSeconds int) string { return fmt.Sprintf(` @@ -61,69 +84,60 @@ func generatePlist(label, scriptPath string, intervalSeconds int) string { `, label, scriptPath, intervalSeconds, label, label) } +// writePlistFile writes the plist content to the LaunchAgents directory +func writePlistFile(name, absScript string, intervalSeconds int) (string, string, error) { + home, err := homedir.Dir() + if err != nil { + return "", "", fmt.Errorf("error getting home directory: %w", err) + } + + launchAgentsDir := filepath.Join(home, "Library", "LaunchAgents") + if err := os.MkdirAll(launchAgentsDir, launchAgentsDirPerms); err != nil { + return "", "", fmt.Errorf("error creating LaunchAgents directory: %w", err) + } + + label := fmt.Sprintf("%s.%s", labelPrefix, name) + plistPath := filepath.Join(launchAgentsDir, label+".plist") + + plistContent := generatePlist(label, absScript, intervalSeconds) + if err := os.WriteFile(plistPath, []byte(plistContent), plistFilePerms); err != nil { + return "", "", fmt.Errorf("error writing plist file: %w", err) + } + + return label, plistPath, nil +} + // createCmd represents the create command var createCmd = &cobra.Command{ Use: "create", Short: "Create a new launchd cron task", Long: `Create a new launchd cron task with NAME to run SCRIPT over an INTERVAL.`, RunE: func(cmd *cobra.Command, args []string) error { - // Get flag values - name, _ := cmd.Flags().GetString("name") - interval, _ := cmd.Flags().GetString("interval") - script, _ := cmd.Flags().GetString("script") - // Validate interval can be parsed as time.Duration - duration, err := time.ParseDuration(interval) - if err != nil { - return fmt.Errorf("invalid interval '%s': must be a valid duration (e.g., 1h, 30m, 1h30m): %w", interval, err) - } - - // Convert script to absolute path - absScript, err := filepath.Abs(script) + duration, err := time.ParseDuration(createInterval) if err != nil { - return fmt.Errorf("error resolving script path: %w", err) - } - - // Validate script file exists - if _, err := os.Stat(absScript); os.IsNotExist(err) { - return fmt.Errorf("script file does not exist: %s", absScript) - } else if err != nil { - return fmt.Errorf("error checking script file: %w", err) + return fmt.Errorf("invalid interval '%s': must be a valid duration (e.g., 1h, 30m, 1h30m): %w", createInterval, err) } - // Get home directory - home, err := homedir.Dir() + // Validate script file exists and resolve to absolute path + absScript, err := validateAndResolveScript(createScript) if err != nil { - return fmt.Errorf("error getting home directory: %w", err) + return err } - // Create LaunchAgents directory path - launchAgentsDir := filepath.Join(home, "Library", "LaunchAgents") - - // Ensure LaunchAgents directory exists - if err := os.MkdirAll(launchAgentsDir, 0755); err != nil { - return fmt.Errorf("error creating LaunchAgents directory: %w", err) - } - - // Generate label and filename - label := fmt.Sprintf("com.macron.%s", name) - plistFilename := fmt.Sprintf("%s.plist", label) - plistPath := filepath.Join(launchAgentsDir, plistFilename) - // Convert duration to seconds intervalSeconds := int(duration.Seconds()) - // Generate plist content - plistContent := generatePlist(label, absScript, intervalSeconds) - - // Write plist file - if err := os.WriteFile(plistPath, []byte(plistContent), 0644); err != nil { - return fmt.Errorf("error writing plist file: %w", err) + // Write plist file to LaunchAgents directory + label, plistPath, err := writePlistFile(createName, absScript, intervalSeconds) + if err != nil { + return err } + // Output success message fmt.Printf("Successfully created launchd task:\n") fmt.Printf(" Label: %s\n", label) - fmt.Printf(" Interval: %s (%d seconds)\n", interval, intervalSeconds) + fmt.Printf(" Interval: %s (%d seconds)\n", createInterval, intervalSeconds) fmt.Printf(" Script: %s\n", absScript) fmt.Printf(" Plist: %s\n", plistPath) fmt.Printf("\nTo load the task, run:\n") @@ -137,9 +151,9 @@ func init() { rootCmd.AddCommand(createCmd) // Define flags for the create command - createCmd.Flags().StringVarP(&name, "name", "n", "", "Name of the launchd task (required)") - createCmd.Flags().StringVarP(&interval, "interval", "i", "", "Interval for the task execution (required)") - createCmd.Flags().StringVarP(&script, "script", "s", "", "Path to the script to execute (required)") + createCmd.Flags().StringVarP(&createName, "name", "n", "", "Name of the launchd task (required)") + createCmd.Flags().StringVarP(&createInterval, "interval", "i", "", "Interval for the task execution (required)") + createCmd.Flags().StringVarP(&createScript, "script", "s", "", "Path to the script to execute (required)") // Mark flags as required createCmd.MarkFlagRequired("name") From afb4939d9def918bd6cf62e475a4df02bcce61bc Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 10 Nov 2025 02:18:44 +0000 Subject: [PATCH 6/6] fix(create): address critical issues from code review This commit addresses the must-fix items identified in the PR review: 1. Replace deprecated mitchellh/go-homedir with os.UserHomeDir() - Removed archived dependency from both create.go and root.go - Updated go.mod to remove unused dependency 2. Add platform check for macOS only - Added runtime.GOOS check at start of RunE function - Provides clear error message on non-macOS platforms 3. Add check to prevent overwriting existing plists - Check if plist file exists before writing - Return descriptive error with remediation steps 4. Add comprehensive test coverage - Unit tests for generatePlist() function - Unit tests for validateAndResolveScript() function - Label generation validation tests - Command structure validation tests - All tests passing These changes improve code quality, prevent potential bugs, and ensure the command only runs on supported platforms. --- cmd/create.go | 14 ++- cmd/create_test.go | 224 +++++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 4 +- go.mod | 1 - go.sum | 2 - 5 files changed, 237 insertions(+), 8 deletions(-) create mode 100644 cmd/create_test.go diff --git a/cmd/create.go b/cmd/create.go index dc2397f..660fd37 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -25,9 +25,9 @@ import ( "fmt" "os" "path/filepath" + "runtime" "time" - "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" ) @@ -86,7 +86,7 @@ func generatePlist(label, scriptPath string, intervalSeconds int) string { // writePlistFile writes the plist content to the LaunchAgents directory func writePlistFile(name, absScript string, intervalSeconds int) (string, string, error) { - home, err := homedir.Dir() + home, err := os.UserHomeDir() if err != nil { return "", "", fmt.Errorf("error getting home directory: %w", err) } @@ -99,6 +99,11 @@ func writePlistFile(name, absScript string, intervalSeconds int) (string, string label := fmt.Sprintf("%s.%s", labelPrefix, name) plistPath := filepath.Join(launchAgentsDir, label+".plist") + // Check if plist file already exists + if _, err := os.Stat(plistPath); err == nil { + return "", "", fmt.Errorf("plist file already exists: %s (remove it first or use a different name)", plistPath) + } + plistContent := generatePlist(label, absScript, intervalSeconds) if err := os.WriteFile(plistPath, []byte(plistContent), plistFilePerms); err != nil { return "", "", fmt.Errorf("error writing plist file: %w", err) @@ -113,6 +118,11 @@ var createCmd = &cobra.Command{ Short: "Create a new launchd cron task", Long: `Create a new launchd cron task with NAME to run SCRIPT over an INTERVAL.`, RunE: func(cmd *cobra.Command, args []string) error { + // Check platform - only works on macOS + if runtime.GOOS != "darwin" { + return fmt.Errorf("create command is only supported on macOS (current platform: %s)", runtime.GOOS) + } + // Validate interval can be parsed as time.Duration duration, err := time.ParseDuration(createInterval) if err != nil { diff --git a/cmd/create_test.go b/cmd/create_test.go new file mode 100644 index 0000000..fcd959f --- /dev/null +++ b/cmd/create_test.go @@ -0,0 +1,224 @@ +/* +Copyright © 2025 David Hagerty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package cmd + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestGeneratePlist(t *testing.T) { + tests := []struct { + name string + label string + scriptPath string + intervalSeconds int + wantContains []string + }{ + { + name: "basic plist generation", + label: "com.macron.test", + scriptPath: "/path/to/script.sh", + intervalSeconds: 3600, + wantContains: []string{ + "", + "Label", + "com.macron.test", + "ProgramArguments", + "/path/to/script.sh", + "StartInterval", + "3600", + "RunAtLoad", + "", + "/tmp/com.macron.test.stdout", + "/tmp/com.macron.test.stderr", + }, + }, + { + name: "plist with special characters in path", + label: "com.macron.backup", + scriptPath: "/Users/test/My Scripts/backup.sh", + intervalSeconds: 1800, + wantContains: []string{ + "com.macron.backup", + "/Users/test/My Scripts/backup.sh", + "1800", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := generatePlist(tt.label, tt.scriptPath, tt.intervalSeconds) + + for _, want := range tt.wantContains { + if !strings.Contains(got, want) { + t.Errorf("generatePlist() missing expected content:\nwant: %q\ngot: %s", want, got) + } + } + + // Verify it's valid XML structure + if !strings.HasPrefix(got, "") { + t.Errorf("generatePlist() should end with ") + } + }) + } +} + +func TestValidateAndResolveScript(t *testing.T) { + // Create a temporary directory for testing + tempDir := t.TempDir() + + // Create a test script file + testScript := filepath.Join(tempDir, "test-script.sh") + if err := os.WriteFile(testScript, []byte("#!/bin/bash\necho test"), 0755); err != nil { + t.Fatalf("Failed to create test script: %v", err) + } + + tests := []struct { + name string + scriptPath string + wantErr bool + errContains string + }{ + { + name: "valid script with absolute path", + scriptPath: testScript, + wantErr: false, + }, + { + name: "non-existent script", + scriptPath: filepath.Join(tempDir, "nonexistent.sh"), + wantErr: true, + errContains: "does not exist", + }, + { + name: "relative path resolution", + scriptPath: "test-script.sh", + wantErr: false, // Will resolve to current directory + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // For relative path test, change to temp dir + if tt.name == "relative path resolution" { + oldWd, _ := os.Getwd() + defer os.Chdir(oldWd) + os.Chdir(tempDir) + } + + got, err := validateAndResolveScript(tt.scriptPath) + + if (err != nil) != tt.wantErr { + t.Errorf("validateAndResolveScript() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if tt.wantErr && err != nil { + if !strings.Contains(err.Error(), tt.errContains) { + t.Errorf("validateAndResolveScript() error = %v, should contain %q", err, tt.errContains) + } + } + + if !tt.wantErr { + if !filepath.IsAbs(got) { + t.Errorf("validateAndResolveScript() = %v, should be absolute path", got) + } + } + }) + } +} + +func TestWritePlistFile(t *testing.T) { + // This test would require mocking os.UserHomeDir() or running in a controlled environment + // For now, we'll test the error cases that don't depend on the actual home directory + + tests := []struct { + name string + taskName string + script string + intervalSeconds int + wantLabel string + }{ + { + name: "generates correct label", + taskName: "backup", + script: "/usr/local/bin/backup.sh", + intervalSeconds: 3600, + wantLabel: "com.macron.backup", + }, + { + name: "handles special characters in name", + taskName: "my-task_123", + script: "/path/to/script.sh", + intervalSeconds: 1800, + wantLabel: "com.macron.my-task_123", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // We can test label generation logic separately + label := labelPrefix + "." + tt.taskName + if label != tt.wantLabel { + t.Errorf("Label = %v, want %v", label, tt.wantLabel) + } + }) + } +} + +func TestCreateCmdValidation(t *testing.T) { + // Test that the command has the expected structure + if createCmd == nil { + t.Fatal("createCmd should not be nil") + } + + if createCmd.Use != "create" { + t.Errorf("createCmd.Use = %v, want 'create'", createCmd.Use) + } + + if createCmd.RunE == nil { + t.Error("createCmd.RunE should not be nil") + } + + // Verify flags are defined + nameFlag := createCmd.Flags().Lookup("name") + if nameFlag == nil { + t.Error("name flag should be defined") + } + + intervalFlag := createCmd.Flags().Lookup("interval") + if intervalFlag == nil { + t.Error("interval flag should be defined") + } + + scriptFlag := createCmd.Flags().Lookup("script") + if scriptFlag == nil { + t.Error("script flag should be defined") + } +} diff --git a/cmd/root.go b/cmd/root.go index 598d2ce..94134ab 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -27,8 +27,6 @@ import ( "dathagerty.com/go/macron/internal/version" "github.com/spf13/cobra" - - homedir "github.com/mitchellh/go-homedir" "github.com/spf13/viper" ) @@ -80,7 +78,7 @@ func initConfig() { viper.SetConfigFile(cfgFile) } else { // Find home directory. - home, err := homedir.Dir() + home, err := os.UserHomeDir() if err != nil { fmt.Println(err) os.Exit(1) diff --git a/go.mod b/go.mod index 7b1ae7c..095937f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module dathagerty.com/go/macron go 1.24.0 require ( - github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cobra v1.9.1 github.com/spf13/viper v1.20.1 ) diff --git a/go.sum b/go.sum index a15e557..6ee0a3f 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=