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 }