github.com/segakazzz/buffalo@v0.16.22-0.20210119082501-1f52048d3feb/buffalo/cmd/new.go (about)

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"os/user"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	pop "github.com/gobuffalo/buffalo-pop/v2/genny/newapp"
    13  	"github.com/gobuffalo/buffalo/genny/assets/standard"
    14  	"github.com/gobuffalo/buffalo/genny/assets/webpack"
    15  	"github.com/gobuffalo/buffalo/genny/ci"
    16  	"github.com/gobuffalo/buffalo/genny/docker"
    17  	"github.com/gobuffalo/buffalo/genny/newapp/api"
    18  	"github.com/gobuffalo/buffalo/genny/newapp/core"
    19  	"github.com/gobuffalo/buffalo/genny/newapp/web"
    20  	"github.com/gobuffalo/buffalo/genny/refresh"
    21  	"github.com/gobuffalo/buffalo/genny/vcs"
    22  	"github.com/gobuffalo/envy"
    23  	fname "github.com/gobuffalo/flect/name"
    24  	"github.com/gobuffalo/genny/v2"
    25  	"github.com/gobuffalo/genny/v2/gogen"
    26  	"github.com/gobuffalo/logger"
    27  	"github.com/gobuffalo/meta"
    28  	"github.com/gobuffalo/packr/v2/plog"
    29  	"github.com/gobuffalo/plush/v4"
    30  	"github.com/segakazzz/buffalo/internal/takeon/github.com/markbates/errx"
    31  	"github.com/sirupsen/logrus"
    32  	"github.com/spf13/cobra"
    33  	"github.com/spf13/pflag"
    34  	"github.com/spf13/viper"
    35  )
    36  
    37  type newAppOptions struct {
    38  	Options *core.Options
    39  	Module  string
    40  	Force   bool
    41  	Verbose bool
    42  	DryRun  bool
    43  }
    44  
    45  func parseNewOptions(args []string) (newAppOptions, error) {
    46  	nopts := newAppOptions{
    47  		Force:   viper.GetBool("force"),
    48  		Verbose: viper.GetBool("verbose"),
    49  		DryRun:  viper.GetBool("dry-run"),
    50  		Module:  viper.GetString("module"),
    51  	}
    52  
    53  	if len(args) == 0 {
    54  		return nopts, fmt.Errorf("you must enter a name for your new application")
    55  	}
    56  	if configError != nil {
    57  		return nopts, configError
    58  	}
    59  
    60  	pwd, err := os.Getwd()
    61  	if err != nil {
    62  		return nopts, err
    63  	}
    64  	app := meta.New(pwd)
    65  	app.WithGrifts = true
    66  	app.Name = fname.New(args[0])
    67  	app.Bin = filepath.Join("bin", app.Name.String())
    68  
    69  	if app.Name.String() == "." {
    70  		app.Name = fname.New(filepath.Base(app.Root))
    71  	} else {
    72  		app.Root = filepath.Join(app.Root, app.Name.File().String())
    73  	}
    74  
    75  	if len(nopts.Module) == 0 {
    76  		aa := meta.New(app.Root)
    77  		app.PackageRoot(aa.PackagePkg)
    78  	} else {
    79  		app.PackageRoot(nopts.Module)
    80  	}
    81  
    82  	app.AsAPI = viper.GetBool("api")
    83  	app.VCS = viper.GetString("vcs")
    84  
    85  	app.WithPop = !viper.GetBool("skip-pop")
    86  	app.WithWebpack = !viper.GetBool("skip-webpack")
    87  	app.WithYarn = !viper.GetBool("skip-yarn")
    88  	app.WithNodeJs = app.WithWebpack
    89  	app.AsWeb = !app.AsAPI
    90  
    91  	if app.AsAPI {
    92  		app.WithWebpack = false
    93  		app.WithYarn = false
    94  		app.WithNodeJs = false
    95  	}
    96  
    97  	opts := &core.Options{}
    98  
    99  	x := viper.GetString("docker")
   100  	if len(x) > 0 && x != "none" {
   101  		opts.Docker = &docker.Options{
   102  			Style: x,
   103  		}
   104  		app.WithDocker = true
   105  	}
   106  
   107  	x = viper.GetString("ci-provider")
   108  	if len(x) > 0 && x != "none" {
   109  		opts.CI = &ci.Options{
   110  			Provider: x,
   111  			DBType:   viper.GetString("db-type"),
   112  		}
   113  	}
   114  
   115  	if len(app.VCS) > 0 && app.VCS != "none" {
   116  		opts.VCS = &vcs.Options{
   117  			Provider: app.VCS,
   118  		}
   119  	}
   120  
   121  	if app.WithPop {
   122  		d := viper.GetString("db-type")
   123  		if d == "sqlite3" {
   124  			app.WithSQLite = true
   125  		}
   126  
   127  		opts.Pop = &pop.Options{
   128  			Prefix:  app.Name.File().String(),
   129  			Dialect: d,
   130  		}
   131  	}
   132  
   133  	opts.Refresh = &refresh.Options{}
   134  
   135  	opts.App = app
   136  	nopts.Options = opts
   137  	return nopts, nil
   138  }
   139  
   140  var configError error
   141  
   142  var newCmd = &cobra.Command{
   143  	Use:   "new [name]",
   144  	Short: "Creates a new Buffalo application",
   145  	RunE: func(cmd *cobra.Command, args []string) error {
   146  		// Restore default values after usage (useful for testing)
   147  		defer func() {
   148  			cmd.Flags().Visit(func(f *pflag.Flag) {
   149  				f.Value.Set(f.DefValue)
   150  			})
   151  			viper.BindPFlags(cmd.Flags())
   152  		}()
   153  
   154  		nopts, err := parseNewOptions(args)
   155  		if err != nil {
   156  			return err
   157  		}
   158  
   159  		opts := nopts.Options
   160  		app := opts.App
   161  
   162  		ctx := context.Background()
   163  
   164  		run := genny.WetRunner(ctx)
   165  		lg := logger.New(logger.DebugLevel)
   166  		run.Logger = lg
   167  		if nopts.Verbose {
   168  			plog.Logger = lg
   169  		}
   170  
   171  		if nopts.DryRun {
   172  			run = genny.DryRunner(ctx)
   173  		}
   174  		run.Root = app.Root
   175  		if nopts.Force {
   176  			os.RemoveAll(app.Root)
   177  		}
   178  
   179  		var gg *genny.Group
   180  
   181  		if app.AsAPI {
   182  			gg, err = api.New(&api.Options{
   183  				Options: opts,
   184  			})
   185  		} else {
   186  			wo := &web.Options{
   187  				Options: opts,
   188  			}
   189  			if app.WithWebpack {
   190  				wo.Webpack = &webpack.Options{}
   191  			} else if !app.AsAPI {
   192  				wo.Standard = &standard.Options{}
   193  			}
   194  			gg, err = web.New(wo)
   195  		}
   196  		if err != nil {
   197  			if errx.Unwrap(err) == core.ErrNotInGoPath {
   198  				return notInGoPath(app)
   199  			}
   200  			return err
   201  		}
   202  		run.WithGroup(gg)
   203  
   204  		if err := run.WithNew(gogen.Fmt(app.Root)); err != nil {
   205  			return err
   206  		}
   207  
   208  		// setup VCS last
   209  		if opts.VCS != nil {
   210  			// add the VCS generator
   211  			if err := run.WithNew(vcs.New(opts.VCS)); err != nil {
   212  				return err
   213  			}
   214  		}
   215  
   216  		if err := run.Run(); err != nil {
   217  			return err
   218  		}
   219  
   220  		run.Logger.Infof("Congratulations! Your application, %s, has been successfully built!", app.Name)
   221  		run.Logger.Infof("You can find your new application at: %v", app.Root)
   222  		run.Logger.Info("Please read the README.md file in your new application for next steps on running your application.")
   223  		return nil
   224  	},
   225  }
   226  
   227  func currentUser() (string, error) {
   228  	if _, err := exec.LookPath("git"); err == nil {
   229  		if b, err := exec.Command("git", "config", "github.user").Output(); err == nil {
   230  			return string(b), nil
   231  		}
   232  	}
   233  	u, err := user.Current()
   234  	if err != nil {
   235  		return "", err
   236  	}
   237  	username := u.Username
   238  	if t := strings.Split(username, `\`); len(t) > 0 {
   239  		username = t[len(t)-1]
   240  	}
   241  	return username, nil
   242  }
   243  
   244  func notInGoPath(app meta.App) error {
   245  	username, err := currentUser()
   246  	if err != nil {
   247  		return err
   248  	}
   249  	pwd, _ := os.Getwd()
   250  	t, err := plush.Render(notInGoWorkspace, plush.NewContextWith(map[string]interface{}{
   251  		"name":     app.Name,
   252  		"gopath":   envy.GoPath(),
   253  		"current":  pwd,
   254  		"username": username,
   255  	}))
   256  	if err != nil {
   257  		return err
   258  	}
   259  	logrus.Error(t)
   260  	os.Exit(-1)
   261  	return nil
   262  }
   263  
   264  func init() {
   265  	decorate("new", newCmd)
   266  	RootCmd.AddCommand(newCmd)
   267  	newCmd.Flags().Bool("api", false, "skip all front-end code and configure for an API server")
   268  	newCmd.Flags().BoolP("force", "f", false, "delete and remake if the app already exists")
   269  	newCmd.Flags().BoolP("dry-run", "d", false, "dry run")
   270  	newCmd.Flags().BoolP("verbose", "v", false, "verbosely print out the go get commands")
   271  	newCmd.Flags().Bool("skip-pop", false, "skips adding pop/soda to your app")
   272  	newCmd.Flags().Bool("skip-webpack", false, "skips adding Webpack to your app")
   273  	newCmd.Flags().Bool("skip-yarn", false, "use npm instead of yarn for frontend dependencies management")
   274  	newCmd.Flags().String("db-type", "postgres", fmt.Sprintf("specify the type of database you want to use [%s]", strings.Join(pop.AvailableDialects, ", ")))
   275  	newCmd.Flags().String("docker", "multi", "specify the type of Docker file to generate [none, multi, standard]")
   276  	newCmd.Flags().String("ci-provider", "none", "specify the type of ci file you would like buffalo to generate [none, travis, gitlab-ci, circleci]")
   277  	newCmd.Flags().String("vcs", "git", "specify the Version control system you would like to use [none, git, bzr]")
   278  	newCmd.Flags().String("module", "", "specify the root module (package) name. [defaults to 'automatic']")
   279  	viper.BindPFlags(newCmd.Flags())
   280  	cfgFile := newCmd.PersistentFlags().String("config", "", "config file (default is $HOME/.buffalo.yaml)")
   281  	skipConfig := newCmd.Flags().Bool("skip-config", false, "skips using the config file")
   282  	cobra.OnInitialize(initConfig(skipConfig, cfgFile))
   283  }
   284  
   285  func initConfig(skipConfig *bool, cfgFile *string) func() {
   286  	return func() {
   287  		if *skipConfig {
   288  			return
   289  		}
   290  
   291  		var err error
   292  		if *cfgFile != "" { // enable ability to specify config file via flag
   293  			viper.SetConfigFile(*cfgFile)
   294  			// Will error only if the --config flag is used
   295  			if err = viper.ReadInConfig(); err != nil {
   296  				configError = err
   297  			}
   298  		} else {
   299  			viper.SetConfigName(".buffalo") // name of config file (without extension)
   300  			viper.AddConfigPath("$HOME")    // adding home directory as first search path
   301  			viper.AutomaticEnv()            // read in environment variables that match
   302  			viper.ReadInConfig()
   303  		}
   304  
   305  	}
   306  }
   307  
   308  const notInGoWorkspace = `Oops! It would appear that you are not in your Go Workspace.
   309  
   310  Your $GOPATH is set to "<%= gopath %>".
   311  
   312  You are currently in "<%= current %>".
   313  
   314  The standard location for putting Go projects is something along the lines of "$GOPATH/src/github.com/<%= username %>/<%= name %>" (adjust accordingly).
   315  
   316  We recommend you go to "$GOPATH/src/github.com/<%= username %>/" and try "buffalo new <%= name %>" again.`