diff --git a/cmd/get/main.go b/cmd/get.go similarity index 72% rename from cmd/get/main.go rename to cmd/get.go index 9b19fa5..d44926d 100644 --- a/cmd/get/main.go +++ b/cmd/get.go @@ -4,27 +4,28 @@ import ( "git-get/pkg" "git-get/pkg/cfg" "git-get/pkg/git" + "os" "github.com/spf13/cobra" "github.com/spf13/viper" ) -const example = ` git get grdl/git-get +const getExample = ` git get grdl/git-get git get https://github.com/grdl/git-get.git git get git@github.com:grdl/git-get.git git get -d path/to/dump/file` -var cmd = &cobra.Command{ - Use: "git get ", - Short: "Clone git repository into an automatically created directory tree based on the repo's URL.", - Example: example, - RunE: run, - Args: cobra.MaximumNArgs(1), // TODO: add custom validator - Version: cfg.Version(), - SilenceUsage: true, // We don't want to show usage on legit errors (eg, wrong path, repo already existing etc.) -} +func newGetCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "git get ", + Short: "Clone git repository into an automatically created directory tree based on the repo's URL.", + Example: getExample, + RunE: runGetCommand, + Args: cobra.MaximumNArgs(1), // TODO: add custom validator + Version: cfg.Version(), + SilenceUsage: true, // We don't want to show usage on legit errors (eg, wrong path, repo already existing etc.) + } -func init() { cmd.PersistentFlags().StringP(cfg.KeyBranch, "b", "", "Branch (or tag) to checkout after cloning.") cmd.PersistentFlags().StringP(cfg.KeyDefaultHost, "t", cfg.Defaults[cfg.KeyDefaultHost], "Host to use when doesn't have a specified host.") cmd.PersistentFlags().StringP(cfg.KeyDefaultScheme, "c", cfg.Defaults[cfg.KeyDefaultScheme], "Scheme to use when doesn't have a specified scheme.") @@ -41,10 +42,10 @@ func init() { viper.BindPFlag(cfg.KeyReposRoot, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot)) viper.BindPFlag(cfg.KeySkipHost, cmd.PersistentFlags().Lookup(cfg.KeySkipHost)) - cfg.Init(&git.ConfigGlobal{}) + return cmd } -func run(cmd *cobra.Command, args []string) error { +func runGetCommand(cmd *cobra.Command, args []string) error { var url string if len(args) > 0 { url = args[0] @@ -64,6 +65,17 @@ func run(cmd *cobra.Command, args []string) error { return pkg.Get(config) } -func main() { - cmd.Execute() +func runGet(args []string) { + // Initialize configuration + cfg.Init(&git.ConfigGlobal{}) + + // Create and execute the get command + cmd := newGetCommand() + + // Set args for cobra to parse + cmd.SetArgs(args) + + if err := cmd.Execute(); err != nil { + os.Exit(1) + } } diff --git a/cmd/list/main.go b/cmd/list.go similarity index 63% rename from cmd/list/main.go rename to cmd/list.go index 0bffae6..cf3b244 100644 --- a/cmd/list/main.go +++ b/cmd/list.go @@ -5,22 +5,23 @@ import ( "git-get/pkg" "git-get/pkg/cfg" "git-get/pkg/git" + "os" "strings" "github.com/spf13/cobra" "github.com/spf13/viper" ) -var cmd = &cobra.Command{ - Use: "git list", - Short: "List all repositories cloned by 'git get' and their status.", - RunE: run, - Args: cobra.NoArgs, - Version: cfg.Version(), - SilenceUsage: true, // We don't want to show usage on legit errors (eg, wrong path, repo already existing etc.) -} +func newListCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "git list", + Short: "List all repositories cloned by 'git get' and their status.", + RunE: runListCommand, + Args: cobra.NoArgs, + Version: cfg.Version(), + SilenceUsage: true, // We don't want to show usage on legit errors (eg, wrong path, repo already existing etc.) + } -func init() { cmd.PersistentFlags().BoolP(cfg.KeyFetch, "f", false, "First fetch from remotes before listing repositories.") cmd.PersistentFlags().StringP(cfg.KeyOutput, "o", cfg.Defaults[cfg.KeyOutput], fmt.Sprintf("Output format. Allowed values: [%s].", strings.Join(cfg.AllowedOut, ", "))) cmd.PersistentFlags().StringP(cfg.KeyReposRoot, "r", cfg.Defaults[cfg.KeyReposRoot], "Path to repos root where repositories are cloned.") @@ -31,10 +32,10 @@ func init() { viper.BindPFlag(cfg.KeyOutput, cmd.PersistentFlags().Lookup(cfg.KeyOutput)) viper.BindPFlag(cfg.KeyReposRoot, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot)) - cfg.Init(&git.ConfigGlobal{}) + return cmd } -func run(cmd *cobra.Command, args []string) error { +func runListCommand(cmd *cobra.Command, args []string) error { cfg.Expand(cfg.KeyReposRoot) config := &pkg.ListCfg{ @@ -46,6 +47,17 @@ func run(cmd *cobra.Command, args []string) error { return pkg.List(config) } -func main() { - cmd.Execute() +func runList(args []string) { + // Initialize configuration + cfg.Init(&git.ConfigGlobal{}) + + // Create and execute the list command + cmd := newListCommand() + + // Set args for cobra to parse + cmd.SetArgs(args) + + if err := cmd.Execute(); err != nil { + os.Exit(1) + } } diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..01e5512 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "os" + "path/filepath" + "strings" +) + +func main() { + // This program behaves as a git subcommand (see https://git.github.io/htmldocs/howto/new-command.html) + // When added to PATH, git recognizes it as its subcommand and it can be invoked as "git get..." or "git list..." + // It can also be invoked as a regular binary with subcommands: "git-get get..." or "git-get list" + // The following flow detects the invokation method and runs the appropriate command. + + programName := filepath.Base(os.Args[0]) + + // Remove common executable extensions + programName = strings.TrimSuffix(programName, ".exe") + + // Determine which command to run based on program name or first argument + var command string + var args []string + + switch programName { + case "git-get": + // Check if first argument is a subcommand + if len(os.Args) > 1 && (os.Args[1] == "get" || os.Args[1] == "list") { + // Called as: git-get get or git-get list + command = os.Args[1] + args = os.Args[2:] + } else { + // Called as: git-get (default to get command) + command = "get" + args = os.Args[1:] + } + case "git-list": + // Called as: git-list (symlinked binary) + command = "list" + args = os.Args[1:] + default: + // Fallback: use first argument as command + if len(os.Args) > 1 { + command = os.Args[1] + args = os.Args[2:] + } else { + command = "get" + args = []string{} + } + } + + // Execute the appropriate command + switch command { + case "get": + runGet(args) + case "list": + runList(args) + default: + // Default to get command for unknown commands + runGet(os.Args[1:]) + } +}