github.com/mloves0824/enron/cmd/enron@v0.0.0-20230830012320-113bbf6be3c8/internal/project/project.go (about)

     1  package project
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/AlecAivazis/survey/v2"
    13  	"github.com/spf13/cobra"
    14  
    15  	"github.com/mloves0824/enron/cmd/enron/internal/base"
    16  )
    17  
    18  // CmdNew represents the new command.
    19  var CmdNew = &cobra.Command{
    20  	Use:   "new",
    21  	Short: "Create a service template",
    22  	Long:  "Create a service project using the repository template. Example: enron new helloworld",
    23  	Run:   run,
    24  }
    25  
    26  var (
    27  	repoURL string
    28  	branch  string
    29  	timeout string
    30  	nomod   bool
    31  )
    32  
    33  func init() {
    34  	if repoURL = os.Getenv("ENRON_LAYOUT_REPO"); repoURL == "" {
    35  		repoURL = "https://github.com/mloves0824/enron-layout.git"
    36  	}
    37  	timeout = "60s"
    38  	CmdNew.Flags().StringVarP(&repoURL, "repo-url", "r", repoURL, "layout repo")
    39  	CmdNew.Flags().StringVarP(&branch, "branch", "b", branch, "repo branch")
    40  	CmdNew.Flags().StringVarP(&timeout, "timeout", "t", timeout, "time out")
    41  	CmdNew.Flags().BoolVarP(&nomod, "nomod", "", nomod, "retain go mod")
    42  }
    43  
    44  func run(_ *cobra.Command, args []string) {
    45  	wd, err := os.Getwd()
    46  	if err != nil {
    47  		panic(err)
    48  	}
    49  	t, err := time.ParseDuration(timeout)
    50  	if err != nil {
    51  		panic(err)
    52  	}
    53  	ctx, cancel := context.WithTimeout(context.Background(), t)
    54  	defer cancel()
    55  	name := ""
    56  	if len(args) == 0 {
    57  		prompt := &survey.Input{
    58  			Message: "What is project name ?",
    59  			Help:    "Created project name.",
    60  		}
    61  		err = survey.AskOne(prompt, &name)
    62  		if err != nil || name == "" {
    63  			return
    64  		}
    65  	} else {
    66  		name = args[0]
    67  	}
    68  	projectName, workingDir := processProjectParams(name, wd)
    69  	p := &Project{Name: projectName}
    70  	done := make(chan error, 1)
    71  	go func() {
    72  		if !nomod {
    73  			done <- p.New(ctx, workingDir, repoURL, branch)
    74  			return
    75  		}
    76  		projectRoot := getgomodProjectRoot(workingDir)
    77  		if gomodIsNotExistIn(projectRoot) {
    78  			done <- fmt.Errorf("🚫 go.mod don't exists in %s", projectRoot)
    79  			return
    80  		}
    81  
    82  		packagePath, e := filepath.Rel(projectRoot, filepath.Join(workingDir, projectName))
    83  		if e != nil {
    84  			done <- fmt.Errorf("🚫 failed to get relative path: %v", err)
    85  			return
    86  		}
    87  		packagePath = strings.ReplaceAll(packagePath, "\\", "/")
    88  
    89  		mod, e := base.ModulePath(filepath.Join(projectRoot, "go.mod"))
    90  		if e != nil {
    91  			done <- fmt.Errorf("🚫 failed to parse `go.mod`: %v", e)
    92  			return
    93  		}
    94  		// Get the relative path for adding a project based on Go modules
    95  		p.Path = filepath.Join(strings.TrimPrefix(workingDir, projectRoot+"/"), p.Name)
    96  		done <- p.Add(ctx, workingDir, repoURL, branch, mod, packagePath)
    97  	}()
    98  	select {
    99  	case <-ctx.Done():
   100  		if errors.Is(ctx.Err(), context.DeadlineExceeded) {
   101  			fmt.Fprint(os.Stderr, "\033[31mERROR: project creation timed out\033[m\n")
   102  			return
   103  		}
   104  		fmt.Fprintf(os.Stderr, "\033[31mERROR: failed to create project(%s)\033[m\n", ctx.Err().Error())
   105  	case err = <-done:
   106  		if err != nil {
   107  			fmt.Fprintf(os.Stderr, "\033[31mERROR: Failed to create project(%s)\033[m\n", err.Error())
   108  		}
   109  	}
   110  }
   111  
   112  func processProjectParams(projectName string, workingDir string) (projectNameResult, workingDirResult string) {
   113  	_projectDir := projectName
   114  	_workingDir := workingDir
   115  	// Process ProjectName with system variable
   116  	if strings.HasPrefix(projectName, "~") {
   117  		homeDir, err := os.UserHomeDir()
   118  		if err != nil {
   119  			// cannot get user home return fallback place dir
   120  			return _projectDir, _workingDir
   121  		}
   122  		_projectDir = filepath.Join(homeDir, projectName[2:])
   123  	}
   124  
   125  	// check path is relative
   126  	if !filepath.IsAbs(projectName) {
   127  		absPath, err := filepath.Abs(projectName)
   128  		if err != nil {
   129  			return _projectDir, _workingDir
   130  		}
   131  		_projectDir = absPath
   132  	}
   133  
   134  	return filepath.Base(_projectDir), filepath.Dir(_projectDir)
   135  }
   136  
   137  func getgomodProjectRoot(dir string) string {
   138  	if dir == filepath.Dir(dir) {
   139  		return dir
   140  	}
   141  	if gomodIsNotExistIn(dir) {
   142  		return getgomodProjectRoot(filepath.Dir(dir))
   143  	}
   144  	return dir
   145  }
   146  
   147  func gomodIsNotExistIn(dir string) bool {
   148  	_, e := os.Stat(filepath.Join(dir, "go.mod"))
   149  	return os.IsNotExist(e)
   150  }