From 1a23a7777562efc921b13af99477cbcddb1e0427 Mon Sep 17 00:00:00 2001 From: AlexNg Date: Thu, 29 Aug 2024 03:01:20 +0800 Subject: [PATCH] feat: Add cloning repository to tmp Signed-off-by: AlexNg --- cmd/commands/new.go | 63 +++++++++++++++++++++++++++++++++++++++++++++ cmd/options/new.go | 31 +++++++++++++++++++--- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/cmd/commands/new.go b/cmd/commands/new.go index 4b5701f..79db09e 100644 --- a/cmd/commands/new.go +++ b/cmd/commands/new.go @@ -1,9 +1,14 @@ package commands import ( + "fmt" "os" + "os/signal" + "path/filepath" + "syscall" "github.com/caffeine-addictt/template/cmd/options" + "github.com/caffeine-addictt/template/cmd/utils" "github.com/charmbracelet/huh" "github.com/spf13/cobra" ) @@ -14,6 +19,29 @@ var NewCmd = &cobra.Command{ Short: "create a new project", Long: "Create a new project from a template", Run: func(cmd *cobra.Command, args []string) { + tmpDir, err := cloneGitRepo() + if err != nil { + cmd.PrintErrf("Could not clone git repo: %s", err) + } + gracefullyCleanupDir(tmpDir) + defer cleanupDir(tmpDir) + + // Resolve dir + if options.NewOpts.Directory.Value() != "" { + tmpDir = filepath.Join(tmpDir, options.NewOpts.Directory.Value()) + + ok, err := utils.IsDir(tmpDir) + if err != nil { + cmd.PrintErrln(err) + } + if !ok { + cmd.PrintErrf("directory '%s' does not exist\n", options.NewOpts.Directory.Value()) + } + } + + // TODO: handle parsing template.json + // TODO: handle Prompts + // TODO: handle writing files in async }, } @@ -22,3 +50,38 @@ func init() { NewCmd.Flags().VarP(&options.NewOpts.Branch, "branch", "b", "branch to clone from [default: main/master]") NewCmd.Flags().VarP(&options.NewOpts.Directory, "directory", "D", "which directory of the template to use [default: /]") } + +// For cloning git repo with spinner +func cloneGitRepo() (string, error) { + outCh := make(chan string, 1) + errCh := make(chan error, 1) + + err := spinner.New().Action(func() { options.NewOpts.CloneRepo(outCh, errCh) }).Run() + if err != nil { + return "", err + } + + return <-outCh, <-errCh +} + +// To catch interrupts and gracefully cleanup +func gracefullyCleanupDir(dir string) { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + go func() { + sig := <-sigs + fmt.Printf("%v received, cleaning up...\n", sig) + cleanupDir(dir) + }() +} + +func cleanupDir(dir string) { + if err := os.RemoveAll(dir); err != nil { + fmt.Printf("Failed to clean up %s: %s\n", dir, err) + os.Exit(1) + return + } + + os.Exit(0) +} diff --git a/cmd/options/new.go b/cmd/options/new.go index 011d430..c436c46 100644 --- a/cmd/options/new.go +++ b/cmd/options/new.go @@ -1,8 +1,10 @@ package options import ( + "errors" + "os" + "os/exec" - "github.com/caffeine-addictt/template/cmd/utils" "github.com/caffeine-addictt/template/cmd/utils/types" ) @@ -25,11 +27,34 @@ type NewOptions struct { Directory types.ValueGuard[string] } +// To clone the repository +func (o *NewOptions) CloneRepo(out chan string, e chan error) { + tmpDirPath, err := os.MkdirTemp("", "template-*") + if err != nil { + e <- err + return + } - + var c *exec.Cmd + if o.Branch.Value() != "" { + c = exec.Command("git", "clone", "--depth", "1", "--branch", o.Branch.Value(), o.Repo.Value(), tmpDirPath) + } else { + c = exec.Command("git", "clone", "--depth", "1", o.Repo.Value(), tmpDirPath) } - return err + c.Stdin = os.Stdin + c.Stdout = os.Stdout + c.Stderr = os.Stderr + + if err := c.Run(); err != nil { + if errCleanup := os.RemoveAll(tmpDirPath); errCleanup != nil { + e <- errors.Join(errCleanup, err) + } else { + e <- err + } + return } + out <- tmpDirPath + e <- nil }