Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New/init command #85

Merged
merged 12 commits into from
Aug 28, 2024
87 changes: 87 additions & 0 deletions cmd/commands/new.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
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"

Check failure on line 12 in cmd/commands/new.go

View workflow job for this annotation

GitHub Actions / Test on ubuntu

"github.com/charmbracelet/huh" imported and not used

Check failure on line 12 in cmd/commands/new.go

View workflow job for this annotation

GitHub Actions / Test on windows

"github.com/charmbracelet/huh" imported and not used

Check failure on line 12 in cmd/commands/new.go

View workflow job for this annotation

GitHub Actions / Test on macos

"github.com/charmbracelet/huh" imported and not used
"github.com/spf13/cobra"
)

var NewCmd = &cobra.Command{
Use: "new",
Aliases: []string{"init"},
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
},
}

func init() {
NewCmd.Flags().VarP(&options.NewOpts.Repo, "repo", "r", "community source repository for templates")
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()

Check failure on line 59 in cmd/commands/new.go

View workflow job for this annotation

GitHub Actions / Test on ubuntu

undefined: spinner

Check failure on line 59 in cmd/commands/new.go

View workflow job for this annotation

GitHub Actions / Test on windows

undefined: spinner

Check failure on line 59 in cmd/commands/new.go

View workflow job for this annotation

GitHub Actions / Test on macos

undefined: spinner

Check failure on line 59 in cmd/commands/new.go

View workflow job for this annotation

GitHub Actions / Linting Go

undefined: spinner (typecheck)
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)
}
1 change: 1 addition & 0 deletions cmd/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ import "github.com/spf13/cobra"
// To initialize all the commands as subcommands of root
func InitCommands(root *cobra.Command) {
root.AddCommand(VersionCmd)
root.AddCommand(NewCmd)
}
15 changes: 15 additions & 0 deletions cmd/options/global.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package options

// The global options for the CLI
var GlobalOpts = GlobalOptions{
Debug: false,
Verbose: false,
}

type GlobalOptions struct {
// Wheter or not debug mode should be enabled
Debug bool

// Wheter or not verbose mode should be enabled
Verbose bool
}
60 changes: 60 additions & 0 deletions cmd/options/new.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package options

import (
"errors"
"os"
"os/exec"

"github.com/caffeine-addictt/template/cmd/utils/types"
)

// The options for the new command
var NewOpts = NewOptions{
Repo: *types.NewValueGuardNoParsing("https://github.com/caffeine-addictt/template", types.REPO),
Branch: *types.NewValueGuardNoParsing("", types.BRANCH),
Directory: *types.NewValueGuardNoParsing("template", types.PATH),
}

type NewOptions struct {
// The repository Url to use
// Should be this repository by default
Repo types.ValueGuard[string]

// The branch to use
Branch types.ValueGuard[string]

// The directory of the template to use
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)
}

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
}
40 changes: 0 additions & 40 deletions cmd/options/options.go

This file was deleted.

26 changes: 0 additions & 26 deletions cmd/options/root.go

This file was deleted.

12 changes: 2 additions & 10 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"log"
"os"

"github.com/caffeine-addictt/template/cmd/commands"
"github.com/caffeine-addictt/template/cmd/options"
Expand All @@ -14,19 +13,12 @@ var RootCmd = &cobra.Command{
Use: "template",
Short: "let's make starting new projects feel like a breeze again",
Long: "This tool helps you to create a new project from templates.\n\nLet's make starting new projects feel like a breeze again.",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if err := options.Opts.ResolveOptions(); err != nil {
cmd.PrintErrln(err)
os.Exit(1)
}
},
}

// Setting up configuration
func init() {
RootCmd.PersistentFlags().BoolVarP(&options.Opts.Debug, "debug", "d", false, "debug mode [default: false]")
RootCmd.PersistentFlags().VarP(&options.Opts.Repo, "repo", "r", "community source repository for templates")
RootCmd.PersistentFlags().VarP(&options.Opts.CacheDir, "cache", "C", "where source repository will be cloned to [default: $XDG_CONFIG_HOME/template]")
RootCmd.PersistentFlags().BoolVarP(&options.GlobalOpts.Debug, "debug", "d", false, "debug mode [default: false]")
RootCmd.PersistentFlags().BoolVarP(&options.GlobalOpts.Verbose, "verbose", "v", false, "verbose mode [default: false]")

commands.InitCommands(RootCmd)
}
Expand Down
33 changes: 0 additions & 33 deletions cmd/utils/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,8 @@ package utils
import (
"fmt"
"os"
"path/filepath"
)

func GetDefaultCacheDir() (string, error) {
dirPath := os.Getenv("XDG_CACHE_HOME")

if dirPath == "" {
os.Getenv("LOCALAPPDATA")
}

if dirPath == "" {
dirPath = filepath.Join(os.Getenv("HOME"), ".cache")
}

if dirPath == "" {
dirPath = "/tmp"
}

dirPath = filepath.Join(dirPath, "template")
ok, err := IsDir(dirPath)
if err != nil {
return "", err
}

// Create the directory if it doesn't exist
if !ok {
err := os.Mkdir(dirPath, 0o600) // rw-rw---
if err != nil {
return "", fmt.Errorf("failed to create directory %s", err)
}
}

return dirPath, nil
}

func IsDir(path string) (bool, error) {
fileinfo, err := os.Stat(path)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions cmd/utils/types/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package types

// The <typeStirng> for CLI options
const (
PATH string = "<path>"
REPO string = "<repo>"
BRANCH string = "<branch>"
)
Loading