github.com/n3integration/conseil@v0.1.1/actions/app.go (about)

     1  package actions
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  	"text/template"
    12  
    13  	"github.com/n3integration/conseil"
    14  	"github.com/pkg/errors"
    15  
    16  	"gopkg.in/urfave/cli.v1"
    17  )
    18  
    19  const defaultRepo = "github.com"
    20  
    21  var (
    22  	wd        string
    23  	driver    string
    24  	framework string
    25  	host      string
    26  	repo      string
    27  	port      int
    28  
    29  	dep        bool
    30  	git        bool
    31  	mod        bool
    32  	migrations bool
    33  )
    34  
    35  func init() {
    36  	register(cli.Command{
    37  		Name:    "new",
    38  		Aliases: []string{"n"},
    39  		Usage:   "bootstrap a new application",
    40  		Action:  appAction,
    41  		Flags: []cli.Flag{
    42  			cli.StringFlag{
    43  				Name:        "framework",
    44  				Value:       "gin",
    45  				Usage:       fmt.Sprintf("app framework [i.e. %v]", strings.Join(listApps(), ", ")),
    46  				Destination: &framework,
    47  			},
    48  			cli.StringFlag{
    49  				Name:        "host",
    50  				Value:       "127.0.0.1",
    51  				Usage:       "ip address to bind",
    52  				Destination: &host,
    53  			},
    54  			cli.IntFlag{
    55  				Name:        "port",
    56  				Value:       8080,
    57  				Usage:       "local port to bind",
    58  				Destination: &port,
    59  			},
    60  			cli.BoolFlag{
    61  				Name:        "migrations",
    62  				Destination: &migrations,
    63  				Usage:       "whether or not to include support for database migrations",
    64  			},
    65  			cli.StringFlag{
    66  				Name:        "driver",
    67  				Value:       "postgres",
    68  				Usage:       "database driver",
    69  				Destination: &driver,
    70  			},
    71  			cli.StringFlag{
    72  				Name:        "repo",
    73  				Value:       defaultRepo,
    74  				Destination: &repo,
    75  				Usage:       "the git module repository",
    76  			},
    77  			cli.BoolFlag{
    78  				Destination: &dep,
    79  				Name:        "dep",
    80  				Usage:       "whether or not to initialize dependency management through dep",
    81  			},
    82  			cli.BoolFlag{
    83  				Destination: &mod,
    84  				Name:        "mod",
    85  				Usage:       "whether or not to initialize dependency management using go modules",
    86  			},
    87  			cli.BoolFlag{
    88  				Destination: &git,
    89  				Name:        "git",
    90  				Usage:       "whether or not to initialize git repo",
    91  			},
    92  		},
    93  	})
    94  }
    95  
    96  // Context provides the application context options
    97  type Context struct {
    98  	App        string
    99  	Host       string
   100  	Port       int
   101  	Driver     string
   102  	Conn       string
   103  	Import     string
   104  	Migrations bool
   105  }
   106  
   107  func appAction(_ *cli.Context) error {
   108  	if wd == "" {
   109  		wd = "."
   110  	}
   111  
   112  	templates := parseTemplates()
   113  	if err := createWebApp(templates); err != nil {
   114  		return err
   115  	}
   116  
   117  	if migrations {
   118  		if err := stageMigrations(templates); err != nil {
   119  			return err
   120  		}
   121  
   122  		if err := setupDb(templates); err != nil {
   123  			return err
   124  		}
   125  	}
   126  
   127  	if dep {
   128  		if out, err := depInit(); err != nil {
   129  			return err
   130  		} else {
   131  			log.Println(out)
   132  		}
   133  	} else if mod {
   134  		if out, err := modInit(); err != nil {
   135  			return err
   136  		} else {
   137  			log.Println(out)
   138  		}
   139  	}
   140  
   141  	if git {
   142  		if out, err := gitInit(templates); err != nil {
   143  			return err
   144  		} else {
   145  			log.Println(out)
   146  		}
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  func createWebApp(templates *template.Template) error {
   153  	t := templates.Lookup(fmt.Sprintf("templates/app/%s.tpl", framework))
   154  	if t == nil {
   155  		return errors.Errorf("unable to find a '%s' app framework template", framework)
   156  	}
   157  
   158  	app, err := os.Create(filepath.Join(wd, "app.go"))
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	log.Println("creating app...")
   164  	context := &Context{
   165  		Host:       host,
   166  		Port:       port,
   167  		Migrations: migrations,
   168  	}
   169  
   170  	if err := t.Execute(app, context); err != nil {
   171  		return err
   172  	}
   173  
   174  	if framework == "grpc" {
   175  		path := filepath.Join(wd, "proto")
   176  		if err := os.MkdirAll(path, 0755); err != nil {
   177  			return err
   178  		}
   179  
   180  		_, err := os.Create(filepath.Join(path, "rpc.proto"))
   181  		return err
   182  	}
   183  	return nil
   184  }
   185  
   186  func stageMigrations(templates *template.Template) error {
   187  	path := filepath.Join(wd, "sql/migrations")
   188  	log.Println("staging migrations...")
   189  	if err := os.MkdirAll(path, 0755); err != nil {
   190  		return err
   191  	}
   192  
   193  	up, _ := os.Create(filepath.Join(path, "1.up.sql"))
   194  	if err := templates.Lookup("templates/sql/1.up.tpl").Execute(up, nil); err != nil {
   195  		return err
   196  	}
   197  
   198  	down, _ := os.Create(filepath.Join(path, "1.down.sql"))
   199  	return templates.Lookup("templates/sql/1.down.tpl").Execute(down, nil)
   200  }
   201  
   202  func setupDb(templates *template.Template) error {
   203  	dbConn, err := conn(driver)
   204  	if err != nil {
   205  		return err
   206  	}
   207  
   208  	path := filepath.Join(wd, "sql")
   209  	if err := os.MkdirAll(path, 0755); err != nil {
   210  		return err
   211  	}
   212  
   213  	migrations, _ := os.Create(filepath.Join(path, "migrations.go"))
   214  	context := &Context{
   215  		Driver: driver,
   216  		Conn:   dbConn,
   217  		Import: imp(driver),
   218  	}
   219  
   220  	if err := templates.Lookup("templates/sql/migrations.tpl").Execute(migrations, context); err != nil {
   221  		return err
   222  	}
   223  
   224  	sql, _ := os.Create(filepath.Join(path, "sql.go"))
   225  	return templates.Lookup("templates/sql/sql.tpl").Execute(sql, context)
   226  }
   227  
   228  func depInit() (string, error) {
   229  	cmd := exec.Command("dep", "init")
   230  	log.Println("initializing dependencies...")
   231  
   232  	output, err := cmd.CombinedOutput()
   233  	if err != nil {
   234  		return "", errors.Errorf("unable to initialize dep: %s", err)
   235  	}
   236  
   237  	return fmt.Sprintf("%s\n", bytes.TrimSpace(output)), nil
   238  }
   239  
   240  func modInit() (string, error) {
   241  	username, err := gitUsername()
   242  	if err != nil {
   243  		return "", err
   244  	}
   245  
   246  	cmd := exec.Command("go", "mod", "init", fmt.Sprintf("%s/%s/%s", repo, username, getPath()))
   247  	log.Println("initializing go module...")
   248  
   249  	output, err := cmd.CombinedOutput()
   250  	if err != nil {
   251  		return "", errors.Errorf("unable to initialize go modules: %s", output)
   252  	}
   253  
   254  	getCmd := exec.Command("go", "get")
   255  	log.Println("resolving dependencies...")
   256  
   257  	getOutput, err := getCmd.CombinedOutput()
   258  	if err != nil {
   259  		return "", errors.Errorf("unable to resolve dependencies: %s", getOutput)
   260  	}
   261  
   262  	return fmt.Sprintf("%s\n", bytes.TrimSpace(getOutput)), nil
   263  }
   264  
   265  func gitInit(templates *template.Template) (string, error) {
   266  	t := templates.Lookup("templates/gitignore.tpl")
   267  	ign, _ := os.Create(filepath.Join(wd, ".gitignore"))
   268  	context := &Context{
   269  		App: getPath(),
   270  	}
   271  
   272  	if err := t.Execute(ign, context); err != nil {
   273  		return "", err
   274  	}
   275  
   276  	cmd := exec.Command("git", "init")
   277  	log.Println("initializing repo...")
   278  
   279  	output, err := cmd.CombinedOutput()
   280  	if err != nil {
   281  		return "", errors.Errorf("unable to initialize git: %s", err)
   282  	}
   283  
   284  	return fmt.Sprintf("%s\n", bytes.TrimSpace(output)), nil
   285  }
   286  
   287  func gitUsername() (string, error) {
   288  	cmd := exec.Command("git", "config", "--get", "user.name")
   289  	log.Println("checking git configuration...")
   290  
   291  	output, err := cmd.CombinedOutput()
   292  	if err != nil {
   293  		return "", errors.Errorf("unable to retrieve git user.name: %s", err)
   294  	}
   295  
   296  	username := string(bytes.TrimSpace(output))
   297  	if strings.Compare(username, "") == 0 {
   298  		return getPath(), nil
   299  	}
   300  
   301  	return username, nil
   302  }
   303  
   304  func getPath() string {
   305  	wd, _ := os.Getwd()
   306  	return filepath.Base(wd)
   307  }
   308  
   309  func conn(driver string) (string, error) {
   310  	wd, _ := os.Getwd()
   311  	switch driver {
   312  	case "postgres":
   313  		return fmt.Sprintf("postgres://localhost:5432/%s", filepath.Base(wd)), nil
   314  	case "sqlite3":
   315  		return fmt.Sprintf("file:%s.sqlite", filepath.Base(wd)), nil
   316  	}
   317  	return "", fmt.Errorf("%s is not a supported database driver", driver)
   318  }
   319  
   320  func imp(driver string) string {
   321  	switch driver {
   322  	case "postgres":
   323  		return "github.com/lib/pq"
   324  	case "sqlite3":
   325  		return "github.com/mattn/go-sqlite3"
   326  	}
   327  	return ""
   328  }
   329  
   330  func listApps() []string {
   331  	appList := make([]string, 0)
   332  	for _, app := range conseil.AssetNames() {
   333  		if strings.HasPrefix(app, "templates/app/") {
   334  			base := filepath.Base(app)
   335  			appList = append(appList, strings.Replace(base, ".tpl", "", 1))
   336  		}
   337  	}
   338  	return appList
   339  }