From 6cf7b5d5189d13b1a0c2c2a89d8104f6f5234924 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Thu, 1 Dec 2016 21:18:20 +0100 Subject: [PATCH 1/7] Enhanced the usage and help output Now each command has its own output destination, which can be changed using a SetOutput(writer) method. Added ability to display the options of each command. --- bee.go | 102 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/bee.go b/bee.go index 11e14fd..cda8ac4 100644 --- a/bee.go +++ b/bee.go @@ -52,6 +52,9 @@ type Command struct { // CustomFlags indicates that the command will do its own // flag parsing. CustomFlags bool + + // output out writer if set in SetOutput(w) + output *io.Writer } // Name returns the command's name: the first word in the usage line. @@ -64,10 +67,24 @@ func (c *Command) Name() string { return name } +// SetOutput sets the destination for usage and error messages. +// If output is nil, os.Stderr is used. +func (c *Command) SetOutput(output io.Writer) { + c.output = &output +} + +// Out returns the out writer of the current command. +// If cmd.output is nil, os.Stderr is used. +func (c *Command) Out() io.Writer { + if c.output != nil { + return *c.output + } + return NewColorWriter(os.Stderr) +} + // Usage puts out the usage for the command. func (c *Command) Usage() { - fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine) - fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(string(c.Long))) + tmpl(helpTemplate, c) os.Exit(2) } @@ -77,7 +94,15 @@ func (c *Command) Runnable() bool { return c.Run != nil } -var commands = []*Command{ +func (c *Command) Options() map[string]string { + options := make(map[string]string) + c.Flag.VisitAll(func(f *flag.Flag) { + options[f.Name] = f.Usage + }) + return options +} + +var availableCommands = []*Command{ cmdNew, cmdRun, cmdPack, @@ -103,6 +128,7 @@ func main() { log.SetFlags(0) args := flag.Args() + if len(args) < 1 { usage() } @@ -112,7 +138,7 @@ func main() { return } - for _, cmd := range commands { + for _, cmd := range availableCommands { if cmd.Name() == args[0] && cmd.Run != nil { cmd.Flag.Usage = func() { cmd.Usage() } if cmd.CustomFlags { @@ -137,46 +163,57 @@ func main() { } } - fmt.Fprintf(os.Stderr, "bee: unknown subcommand %q\nRun 'bee help' for usage.\n", args[0]) - os.Exit(2) + printErrorAndExit("Unknown subcommand") } -var usageTemplate = `Bee is a tool for managing beego framework. +var usageTemplate = `Bee is a Fast and Flexible tool for managing your Beego Web Application. -Usage: +{{"USAGE" | headline}} + {{"bee command [arguments]" | bold}} - bee command [arguments] - -The commands are: +{{"AVAILABLE COMMANDS" | headline}} {{range .}}{{if .Runnable}} - {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + {{.Name | printf "%-11s" | bold}} {{.Short}}{{end}}{{end}} -Use "bee help [command]" for more information about a command. +Use {{"bee help [command]" | bold}} for more information about a command. -Additional help topics: +{{"ADDITIONAL HELP TOPICS" | headline}} {{range .}}{{if not .Runnable}} {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} -Use "bee help [topic]" for more information about that topic. +Use {{"bee help [topic]" | bold}} for more information about that topic. ` -var helpTemplate = `{{if .Runnable}}usage: bee {{.UsageLine}} +var helpTemplate = `{{"USAGE" | headline}} + {{.UsageLine | printf "bee %s" | bold}} +{{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}} + {{$k | printf "-%-11s" | bold}} {{$v}}{{end}}{{endline}}{{end}} +{{"DESCRIPTION" | headline}} + {{.Long | trim}} +` -{{end}}{{.Long | trim}} +var errorTemplate = `bee: %s. +Use {{"bee help" | bold}} for more information. ` func usage() { - tmpl(os.Stdout, usageTemplate, commands) + tmpl(usageTemplate, availableCommands) os.Exit(2) } -func tmpl(w io.Writer, text string, data interface{}) { +func tmpl(text string, data interface{}) { + output := NewColorWriter(os.Stderr) + t := template.New("top") - t.Funcs(template.FuncMap{"trim": func(s template.HTML) template.HTML { - return template.HTML(strings.TrimSpace(string(s))) - }}) + t.Funcs(template.FuncMap{ + "trim": func(s template.HTML) template.HTML { return template.HTML(strings.TrimSpace(string(s))) }, + "bold": bold, + "headline": MagentaBold, + "endline": EndLine, + }) + template.Must(t.Parse(text)) - if err := t.Execute(w, data); err != nil { + if err := t.Execute(output, data); err != nil { panic(err) } } @@ -184,24 +221,23 @@ func tmpl(w io.Writer, text string, data interface{}) { func help(args []string) { if len(args) == 0 { usage() - // not exit 2: succeeded at 'go help'. - return } if len(args) != 1 { - fmt.Fprintf(os.Stdout, "usage: bee help command\n\nToo many arguments given.\n") - os.Exit(2) // failed at 'bee help' + printErrorAndExit("Too many arguments") } arg := args[0] - for _, cmd := range commands { + for _, cmd := range availableCommands { if cmd.Name() == arg { - tmpl(os.Stdout, helpTemplate, cmd) - // not exit 2: succeeded at 'go help cmd'. + tmpl(helpTemplate, cmd) return } } - - fmt.Fprintf(os.Stdout, "Unknown help topic %#q. Run 'bee help'.\n", arg) - os.Exit(2) // failed at 'bee help cmd' + printErrorAndExit("Unknown help topic") +} + +func printErrorAndExit(message string) { + tmpl(fmt.Sprintf(errorTemplate, message), nil) + os.Exit(2) } From 196e732e198cc5f04522f790a692009920a9932e Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sat, 3 Dec 2016 11:46:10 +0100 Subject: [PATCH 2/7] Removes the use of template/html This uses the template/text instead because template/html does not escape quotes and replaces them with their HTML codes. Added two more util functions to make it easy to use bold and colored text in the command long description. --- bee.go | 23 ++++++++--------------- util.go | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/bee.go b/bee.go index cda8ac4..840bf50 100644 --- a/bee.go +++ b/bee.go @@ -18,11 +18,11 @@ package main import ( "flag" "fmt" - "html/template" "io" "log" "os" "strings" + "text/template" ) const version = "1.5.2" @@ -41,10 +41,10 @@ type Command struct { UsageLine string // Short is the short description shown in the 'go help' output. - Short template.HTML + Short string // Long is the long message shown in the 'go help ' output. - Long template.HTML + Long string // Flag is a set of flags specific to this command. Flag flag.FlagSet @@ -189,7 +189,7 @@ var helpTemplate = `{{"USAGE" | headline}} {{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}} {{$k | printf "-%-11s" | bold}} {{$v}}{{end}}{{endline}}{{end}} {{"DESCRIPTION" | headline}} - {{.Long | trim}} + {{tmpltostr .Long . | trim}} ` var errorTemplate = `bee: %s. @@ -204,18 +204,11 @@ func usage() { func tmpl(text string, data interface{}) { output := NewColorWriter(os.Stderr) - t := template.New("top") - t.Funcs(template.FuncMap{ - "trim": func(s template.HTML) template.HTML { return template.HTML(strings.TrimSpace(string(s))) }, - "bold": bold, - "headline": MagentaBold, - "endline": EndLine, - }) - + t := template.New("usage").Funcs(BeeFuncMap()) template.Must(t.Parse(text)) - if err := t.Execute(output, data); err != nil { - panic(err) - } + + err := t.Execute(output, data) + MustCheck(err) } func help(args []string) { diff --git a/util.go b/util.go index a13ade2..fcfaeca 100644 --- a/util.go +++ b/util.go @@ -15,6 +15,7 @@ package main import ( + "bytes" "fmt" "io/ioutil" "os" @@ -24,6 +25,7 @@ import ( "regexp" "runtime" "strings" + "text/template" "time" ) @@ -281,3 +283,26 @@ func __LINE__() int { _, _, line, _ := runtime.Caller(1) return line } + +// BeeFuncMap returns a FuncMap of functions used in different templates. +func BeeFuncMap() template.FuncMap { + return template.FuncMap{ + "trim": strings.TrimSpace, + "bold": bold, + "headline": MagentaBold, + "endline": EndLine, + "tmpltostr": TmplToString, + } +} + +// TmplToString parses a text template and return the result as a string. +func TmplToString(tmpl string, data interface{}) string { + t := template.New("tmpl").Funcs(BeeFuncMap()) + template.Must(t.Parse(tmpl)) + + var doc bytes.Buffer + err := t.Execute(&doc, data) + MustCheck(err) + + return doc.String() +} From e927a9193db06712ecbba19a3948bc55499d4327 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sat, 3 Dec 2016 11:54:41 +0100 Subject: [PATCH 3/7] Enhances the commands short and long description This enhances the output of bee help/usage by using colored and bold text to highlight examples. --- apiapp.go | 89 ++++++++++++++++++++++++---------------------------- bale.go | 21 +++++-------- fix.go | 14 +++++---- g.go | 71 +++++++++++++++++++---------------------- hproseapp.go | 87 +++++++++++++++++++++++--------------------------- migrate.go | 41 +++++++++++------------- new.go | 53 ++++++++++++++----------------- pack.go | 69 +++++++++++++++++----------------------- run.go | 23 +++++++------- version.go | 4 +-- 10 files changed, 215 insertions(+), 257 deletions(-) diff --git a/apiapp.go b/apiapp.go index 4c39c96..9a1950d 100644 --- a/apiapp.go +++ b/apiapp.go @@ -24,39 +24,34 @@ import ( var cmdApiapp = &Command{ // CustomFlags: true, UsageLine: "api [appname]", - Short: "create an API beego application", + Short: "Creates a Beego API application", Long: ` -Create an API beego application. + The command 'api' creates a Beego API application. -bee api [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test] - -tables: a list of table names separated by ',' (default is empty, indicating all tables) - -driver: [mysql | postgres | sqlite] (default: mysql) - -conn: the connection string used by the driver, the default is '' - e.g. for mysql: root:@tcp(127.0.0.1:3306)/test - e.g. for postgres: postgres://postgres:postgres@127.0.0.1:5432/postgres + {{"Example:"|bold}} + $ bee api [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test] -If 'conn' argument is empty, bee api creates an example API application, -when 'conn' argument is provided, bee api generates an API application based -on the existing database. + If 'conn' argument is empty, the command will generate an example API application. Otherwise the command + will connect to your database and generate models based on the existing tables. -The command 'api' creates a folder named [appname] and inside the folder deploy -the following files/directories structure: - - ├── conf - │ └── app.conf - ├── controllers - │ └── object.go - │ └── user.go - ├── routers - │ └── router.go - ├── tests - │ └── default_test.go - ├── main.go - └── models - └── object.go - └── user.go + The command 'api' creates a folder named [appname] with the following structure: + ├── conf + │ └── app.conf + ├── controllers + │ └── object.go + │ └── user.go + ├── routers + │ └── router.go + ├── tests + │ └── default_test.go + ├── main.go + └── models + └── object.go + └── user.go `, + PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + Run: createapi, } var apiconf = `appname = {{.Appname}} @@ -537,15 +532,13 @@ func TestGet(t *testing.T) { ` func init() { - cmdApiapp.Run = createapi - cmdApiapp.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } - cmdApiapp.Flag.Var(&tables, "tables", "specify tables to generate model") - cmdApiapp.Flag.Var(&driver, "driver", "database driver: mysql, postgresql, etc.") - cmdApiapp.Flag.Var(&conn, "conn", "connection string used by the driver to connect to a database instance") + cmdApiapp.Flag.Var(&tables, "tables", "List of table names separated by a comma.") + cmdApiapp.Flag.Var(&driver, "driver", "Database driver. Either mysql, postgres or sqlite.") + cmdApiapp.Flag.Var(&conn, "conn", "Connection string used by the driver to connect to a database instance.") } func createapi(cmd *Command, args []string) int { - w := NewColorWriter(os.Stdout) + output := cmd.Out() if len(args) < 1 { logger.Fatal("Argument [appname] is missing") @@ -568,19 +561,19 @@ func createapi(cmd *Command, args []string) int { logger.Info("Creating API...") os.MkdirAll(apppath, 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") os.Mkdir(path.Join(apppath, "conf"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m") os.Mkdir(path.Join(apppath, "controllers"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers"), "\x1b[0m") os.Mkdir(path.Join(apppath, "tests"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests"), "\x1b[0m") - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(apiconf, "{{.Appname}}", path.Base(args[0]), -1)) if conn != "" { - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") maingoContent := strings.Replace(apiMainconngo, "{{.Appname}}", packpath, -1) maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1) if driver == "mysql" { @@ -602,33 +595,33 @@ func createapi(cmd *Command, args []string) int { generateAppcode(string(driver), string(conn), "3", string(tables), apppath) } else { os.Mkdir(path.Join(apppath, "models"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m") os.Mkdir(path.Join(apppath, "routers"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m") - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "object.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "object.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "controllers", "object.go"), strings.Replace(apiControllers, "{{.Appname}}", packpath, -1)) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "user.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "user.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "controllers", "user.go"), strings.Replace(apiControllers2, "{{.Appname}}", packpath, -1)) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "tests", "default_test.go"), strings.Replace(apiTests, "{{.Appname}}", packpath, -1)) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "routers", "router.go"), strings.Replace(apirouter, "{{.Appname}}", packpath, -1)) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "models", "object.go"), apiModels) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "models", "user.go"), apiModels2) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "main.go"), strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1)) } diff --git a/bale.go b/bale.go index e7a9aa1..2cd6667 100644 --- a/bale.go +++ b/bale.go @@ -28,22 +28,17 @@ import ( var cmdBale = &Command{ UsageLine: "bale", - Short: "packs non-Go files to Go source files", - Long: ` -Bale command compress all the static files in to a single binary file. + Short: "Transforms non-Go files to Go source files", + Long: `Bale command compress all the static files in to a single binary file. -This is useful to not have to carry static files including js, css, images -and views when publishing a project. - -auto-generate unpack function to main package then run it during the runtime. -This is mainly used for zealots who are requiring 100% Go code. + This is useful to not have to carry static files including js, css, images and + views when deploying a Web application. + It will auto-generate an unpack function to the main package then run it during the runtime. + This is mainly used for zealots who are requiring 100% Go code. `, -} - -func init() { - cmdBale.Run = runBale - cmdBale.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } + PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + Run: runBale, } func runBale(cmd *Command, args []string) int { diff --git a/fix.go b/fix.go index 929297c..13453e6 100644 --- a/fix.go +++ b/fix.go @@ -13,11 +13,11 @@ import ( var cmdFix = &Command{ UsageLine: "fix", - Short: "fix the beego application to make it compatible with beego 1.6", - Long: ` -As from beego1.6, there's some incompatible code with the old version. + Short: "Fixes your application by making it compatible with newer versions of Beego", + Long: `As of {{"Beego 1.6"|bold}}, there are some backward compatibility issues. -bee fix help to upgrade the application to beego 1.6 + The command 'fix' will try to solve those issues by upgrading your code base + to be compatible with Beego version 1.6+. `, } @@ -27,6 +27,8 @@ func init() { } func runFix(cmd *Command, args []string) int { + output := cmd.Out() + logger.Info("Upgrading the application...") dir, err := os.Getwd() @@ -48,13 +50,13 @@ func runFix(cmd *Command, args []string) int { return nil } err = fixFile(path) - fmt.Println("\tfix\t", path) + fmt.Fprintf(output, GreenBold("\tfix\t")+"%s\n", path) if err != nil { logger.Errorf("Could not fix file: %s", err) } return err }) - logger.Success("Upgrade done!") + logger.Success("Upgrade Done!") return 0 } diff --git a/g.go b/g.go index e25bdb1..8c133d9 100644 --- a/g.go +++ b/g.go @@ -20,45 +20,42 @@ import ( ) var cmdGenerate = &Command{ - UsageLine: "generate [Command]", - Short: "source code generator", - Long: ` -bee generate scaffold [scaffoldname] [-fields=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] - The generate scaffold command will do a number of things for you. - -fields: a list of table fields. Format: field:type, ... - -driver: [mysql | postgres | sqlite], the default is mysql - -conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test - example: bee generate scaffold post -fields="title:string,body:text" + UsageLine: "generate [command]", + Short: "Source code generator", + Long: `▶ {{"To scaffold out your entire application:"|bold}} -bee generate model [modelname] [-fields=""] - generate RESTFul model based on fields - -fields: a list of table fields. Format: field:type, ... + $ bee generate scaffold [scaffoldname] [-fields="title:string,body:text"] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] -bee generate controller [controllerfile] - generate RESTful controllers + ▶ {{"To generate a Model based on fields:"|bold}} -bee generate view [viewpath] - generate CRUD view in viewpath + $ bee generate model [modelname] [-fields="name:type"] -bee generate migration [migrationfile] [-fields=""] - generate migration file for making database schema update - -fields: a list of table fields. Format: field:type, ... + ▶ {{"To generate a controller:"|bold}} -bee generate docs - generate swagger doc file + $ bee generate controller [controllerfile] -bee generate test [routerfile] - generate testcase + ▶ {{"To generate a CRUD view:"|bold}} -bee generate appcode [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-level=3] - generate appcode based on an existing database - -tables: a list of table names separated by ',', default is empty, indicating all tables - -driver: [mysql | postgres | sqlite], the default is mysql - -conn: the connection string used by the driver. - default for mysql: root:@tcp(127.0.0.1:3306)/test - default for postgres: postgres://postgres:postgres@127.0.0.1:5432/postgres - -level: [1 | 2 | 3], 1 = models; 2 = models,controllers; 3 = models,controllers,router + $ bee generate view [viewpath] + + ▶ {{"To generate a migration file for making database schema updates:"|bold}} + + $ bee generate migration [migrationfile] [-fields="name:type"] + + ▶ {{"To generate swagger doc file:"|bold}} + + $ bee generate docs + + ▶ {{"To generate a test case:"|bold}} + + $ bee generate test [routerfile] + + ▶ {{"To generate appcode based on an existing database:"|bold}} + + $ bee generate appcode [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-level=3] `, + PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + Run: generateCode, } var driver docValue @@ -68,13 +65,11 @@ var tables docValue var fields docValue func init() { - cmdGenerate.Run = generateCode - cmdGenerate.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } - cmdGenerate.Flag.Var(&tables, "tables", "specify tables to generate model") - cmdGenerate.Flag.Var(&driver, "driver", "database driver: mysql, postgresql, etc.") - cmdGenerate.Flag.Var(&conn, "conn", "connection string used by the driver to connect to a database instance") - cmdGenerate.Flag.Var(&level, "level", "1 = models only; 2 = models and controllers; 3 = models, controllers and routers") - cmdGenerate.Flag.Var(&fields, "fields", "specify the fields want to generate.") + cmdGenerate.Flag.Var(&tables, "tables", "List of table names separated by a comma.") + cmdGenerate.Flag.Var(&driver, "driver", "Database driver. Either mysql, postgres or sqlite.") + cmdGenerate.Flag.Var(&conn, "conn", "Connection string used by the driver to connect to a database instance.") + cmdGenerate.Flag.Var(&level, "level", "Either 1, 2 or 3. i.e. 1=models; 2=models and controllers; 3=models, controllers and routers.") + cmdGenerate.Flag.Var(&fields, "fields", "List of table fields.") } func generateCode(cmd *Command, args []string) int { diff --git a/hproseapp.go b/hproseapp.go index 6b2cf6f..0df6e1c 100644 --- a/hproseapp.go +++ b/hproseapp.go @@ -1,19 +1,16 @@ -/**********************************************************\ -| | -| hprose | -| | -| Official WebSite: http://www.hprose.com/ | -| http://www.hprose.org/ | -| | -\**********************************************************/ -/**********************************************************\ - * * - * Build rpc application use Hprose base on beego * - * * - * LastModified: Oct 31, 2016 * - * Author: Liu jian * - * * -\**********************************************************/ +// Copyright 2013 bee authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. package main @@ -27,30 +24,28 @@ import ( var cmdHproseapp = &Command{ // CustomFlags: true, UsageLine: "hprose [appname]", - Short: "create an rpc application use hprose base on beego framework", + Short: "Creates an RPC application based on Hprose and Beego frameworks", Long: ` -create an rpc application use hprose base on beego framework + The command 'hprose' creates an RPC application based on both Beego and Hprose (http://hprose.com/). -bee hprose [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test] - -tables: a list of table names separated by ',', default is empty, indicating all tables - -driver: [mysql | postgres | sqlite], the default is mysql - -conn: the connection string used by the driver, the default is '' - e.g. for mysql: root:@tcp(127.0.0.1:3306)/test - e.g. for postgres: postgres://postgres:postgres@127.0.0.1:5432/postgres + {{"To scaffold out your application, use:"|bold}} -if conn is empty will create a example rpc application. otherwise generate rpc application use hprose based on an existing database. + $ bee hprose [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test] -In the current path, will create a folder named [appname] + If 'conn' is empty, the command will generate a sample application. Otherwise the command + will connect to your database and generate models based on the existing tables. -In the appname folder has the follow struct: + The command 'hprose' creates a folder named [appname] with the following structure: - ├── conf - │ └── app.conf - ├── main.go - └── models - └── object.go - └── user.go + ├── conf + │ └── app.conf + ├── main.go + └── models + └── object.go + └── user.go `, + PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + Run: createhprose, } var hproseconf = `appname = {{.Appname}} @@ -297,15 +292,13 @@ func DeleteUser(uid string) { var hproseAddFunctions = []string{} func init() { - cmdHproseapp.Run = createhprose - cmdHproseapp.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } - cmdHproseapp.Flag.Var(&tables, "tables", "specify tables to generate model") - cmdHproseapp.Flag.Var(&driver, "driver", "database driver: mysql, postgresql, etc.") - cmdHproseapp.Flag.Var(&conn, "conn", "connection string used by the driver to connect to a database instance") + cmdHproseapp.Flag.Var(&tables, "tables", "List of table names separated by a comma.") + cmdHproseapp.Flag.Var(&driver, "driver", "Database driver. Either mysql, postgres or sqlite.") + cmdHproseapp.Flag.Var(&conn, "conn", "Connection string used by the driver to connect to a database instance.") } func createhprose(cmd *Command, args []string) int { - w := NewColorWriter(os.Stdout) + output := cmd.Out() curpath, _ := os.Getwd() if len(args) > 1 { @@ -324,10 +317,10 @@ func createhprose(cmd *Command, args []string) int { logger.Info("Creating Hprose application...") os.MkdirAll(apppath, 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") os.Mkdir(path.Join(apppath, "conf"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m") - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(hproseconf, "{{.Appname}}", args[0], -1)) @@ -336,7 +329,7 @@ func createhprose(cmd *Command, args []string) int { logger.Infof("Using '%s' as 'conn'", conn) logger.Infof("Using '%s' as 'tables'", tables) generateHproseAppcode(string(driver), string(conn), "1", string(tables), path.Join(curpath, args[0])) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") maingoContent := strings.Replace(hproseMainconngo, "{{.Appname}}", packpath, -1) maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1) maingoContent = strings.Replace(maingoContent, "{{HproseFunctionList}}", strings.Join(hproseAddFunctions, ""), -1) @@ -355,15 +348,15 @@ func createhprose(cmd *Command, args []string) int { ) } else { os.Mkdir(path.Join(apppath, "models"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m") - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "models", "object.go"), apiModels) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "models", "user.go"), apiModels2) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "main.go"), strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1)) } diff --git a/migrate.go b/migrate.go index 3d89991..5ef4681 100644 --- a/migrate.go +++ b/migrate.go @@ -27,38 +27,35 @@ import ( var cmdMigrate = &Command{ UsageLine: "migrate [Command]", - Short: "run database migrations", - Long: ` -bee migrate [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] - run all outstanding migrations - -driver: [mysql | postgres | sqlite] (default: mysql) - -conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test + Short: "Runs database migrations", + Long: `The command 'migrate' allows you to run database migrations to keep it up-to-date. -bee migrate rollback [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] - rollback the last migration operation - -driver: [mysql | postgres | sqlite] (default: mysql) - -conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test + ▶ {{"To run all the migrations:"|bold}} -bee migrate reset [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] - rollback all migrations - -driver: [mysql | postgres | sqlite] (default: mysql) - -conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test + $ bee migrate [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] -bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] - rollback all migrations and run them all again - -driver: [mysql | postgres | sqlite] (default: mysql) - -conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test + ▶ {{"To rollback the last migration:"|bold}} + + $ bee migrate rollback [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] + + ▶ {{"To do a reset, which will rollback all the migrations:"|bold}} + + $ bee migrate reset [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] + + ▶ {{"To update your schema:"|bold}} + + $ bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] `, + PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + Run: runMigration, } var mDriver docValue var mConn docValue func init() { - cmdMigrate.Run = runMigration - cmdMigrate.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } - cmdMigrate.Flag.Var(&mDriver, "driver", "database driver: mysql, postgres, sqlite, etc.") - cmdMigrate.Flag.Var(&mConn, "conn", "connection string used by the driver to connect to a database instance") + cmdMigrate.Flag.Var(&mDriver, "driver", "Database driver. Either mysql, postgres or sqlite.") + cmdMigrate.Flag.Var(&mConn, "conn", "Connection string used by the driver to connect to a database instance.") } // runMigration is the entry point for starting a migration diff --git a/new.go b/new.go index b8be305..497ad66 100644 --- a/new.go +++ b/new.go @@ -23,16 +23,15 @@ import ( var cmdNew = &Command{ UsageLine: "new [appname]", - Short: "Create a Beego application", + Short: "Creates a Beego application", Long: ` Creates a Beego application for the given app name in the current directory. -The command 'new' creates a folder named [appname] and inside the folder deploy -the following files/directories structure: + The command 'new' creates a folder named [appname] and generates the following structure: |- main.go |- conf - |- app.conf + |- app.conf |- controllers |- default.go |- models @@ -40,7 +39,7 @@ the following files/directories structure: |- router.go |- tests |- default_test.go - |- static + |- static |- js |- css |- img @@ -48,16 +47,12 @@ the following files/directories structure: index.tpl `, -} - -func init() { - cmdNew.Run = createApp - cmdNew.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } + PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + Run: createApp, } func createApp(cmd *Command, args []string) int { - w := NewColorWriter(os.Stdout) - + output := cmd.Out() if len(args) != 1 { logger.Fatal("Argument [appname] is missing") } @@ -78,43 +73,43 @@ func createApp(cmd *Command, args []string) int { logger.Info("Creating application...") os.MkdirAll(apppath, 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "conf"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf")+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "controllers"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers")+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "models"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models")+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "routers"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "tests"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests")+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "static"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static")+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "static", "js"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "js")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "js")+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "static", "css"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "css")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "css")+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "static", "img"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "img")+string(path.Separator), "\x1b[0m") - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "img")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views")+string(path.Separator), "\x1b[0m") os.Mkdir(path.Join(apppath, "views"), 0755) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", path.Base(args[0]), -1)) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "default.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "default.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "controllers", "default.go"), controllers) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views", "index.tpl"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views", "index.tpl"), "\x1b[0m") WriteToFile(path.Join(apppath, "views", "index.tpl"), indextpl) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "routers", "router.go"), strings.Replace(router, "{{.Appname}}", packpath, -1)) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "tests", "default_test.go"), strings.Replace(test, "{{.Appname}}", packpath, -1)) - fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", packpath, -1)) logger.Success("New application successfully created!") diff --git a/pack.go b/pack.go index 4e16086..d9f8852 100644 --- a/pack.go +++ b/pack.go @@ -36,26 +36,15 @@ import ( var cmdPack = &Command{ CustomFlags: true, UsageLine: "pack", - Short: "Compress a beego project into a single file", - Long: ` -Pack is used to compress a beego project into a single file. -This eases the deployment by extracting the zip file to a server. + Short: "Compresses a Beego application into a single file", + Long: `Pack is used to compress Beego applications into a tarball/zip file. + This eases the deployment by extracting directly the file to a server. --p app path (default is the current path). --b build specify platform app (default: true). --ba additional args of go build --be=[] additional ENV Variables of go build. eg: GOARCH=arm --o compressed file output dir. default use current path --f="" format: tar.gz, zip (default: tar.gz) --exp="" relpath exclude prefix (default: .). use : as separator --exs="" relpath exclude suffix (default: .go:.DS_Store:.tmp). use : as separator - all path use : as separator --exr=[] file/directory name exclude by Regexp (default: ^). --fs=false follow symlink (default: false). --ss=false skip symlink (default: false) - default embed symlink into compressed file --v=false verbose + {{"Example:"|bold}} + $ bee pack -v -ba="-ldflags '-s -w'" `, + PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + Run: packApp, } var ( @@ -71,7 +60,6 @@ var ( buildEnvs ListOpts verbose bool format string - w io.Writer ) type ListOpts []string @@ -87,22 +75,19 @@ func (opts *ListOpts) Set(value string) error { func init() { fs := flag.NewFlagSet("pack", flag.ContinueOnError) - fs.StringVar(&appPath, "p", "", "app path. default is current path") - fs.BoolVar(&build, "b", true, "build specify platform app") - fs.StringVar(&buildArgs, "ba", "", "additional args of go build") - fs.Var(&buildEnvs, "be", "additional ENV Variables of go build. eg: GOARCH=arm") - fs.StringVar(&outputP, "o", "", "compressed file output dir. default use current path") - fs.StringVar(&format, "f", "tar.gz", "format. [ tar.gz / zip ]") - fs.StringVar(&excludeP, "exp", ".", "path exclude prefix. use : as separator") - fs.StringVar(&excludeS, "exs", ".go:.DS_Store:.tmp", "path exclude suffix. use : as separator") - fs.Var(&excludeR, "exr", "filename exclude by Regexp") - fs.BoolVar(&fsym, "fs", false, "follow symlink") - fs.BoolVar(&ssym, "ss", false, "skip symlink") - fs.BoolVar(&verbose, "v", false, "verbose") + fs.StringVar(&appPath, "p", "", "Set the application path. Defaults to the current path.") + fs.BoolVar(&build, "b", true, "Tell the command to do a build for the current platform. Defaults to true.") + fs.StringVar(&buildArgs, "ba", "", "Specify additional args for Go build.") + fs.Var(&buildEnvs, "be", "Specify additional env variables for Go build. e.g. GOARCH=arm.") + fs.StringVar(&outputP, "o", "", "Set the compressed file output path. Defaults to the current path.") + fs.StringVar(&format, "f", "tar.gz", "Set file format. Either tar.gz or zip. Defaults to tar.gz.") + fs.StringVar(&excludeP, "exp", ".", "Set prefixes of paths to be excluded. Uses a column (:) as separator.") + fs.StringVar(&excludeS, "exs", ".go:.DS_Store:.tmp", "Set suffixes of paths to be excluded. Uses a column (:) as separator.") + fs.Var(&excludeR, "exr", "Set a regular expression of files to be excluded.") + fs.BoolVar(&fsym, "fs", false, "Tell the command to follow symlinks. Defaults to false.") + fs.BoolVar(&ssym, "ss", false, "Tell the command to skip symlinks. Defaults to false.") + fs.BoolVar(&verbose, "v", false, "Be more verbose during the operation. Defaults to false.") cmdPack.Flag = *fs - cmdPack.Run = packApp - cmdPack.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } - w = NewColorWriter(os.Stdout) } type walker interface { @@ -127,6 +112,7 @@ type walkFileTree struct { excludeRegexp []*regexp.Regexp excludeSuffix []string allfiles map[string]bool + output *io.Writer } func (wft *walkFileTree) setPrefix(prefix string) { @@ -239,7 +225,7 @@ func (wft *walkFileTree) walkLeaf(fpath string, fi os.FileInfo, err error) error if added, err := wft.wak.compress(name, fpath, fi); added { if verbose { - fmt.Fprintf(w, "\t%s%scompressed%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", name, "\x1b[0m") + fmt.Fprintf(*wft.output, "\t%s%scompressed%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", name, "\x1b[0m") } wft.allfiles[name] = true return err @@ -390,7 +376,7 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) { return true, nil } -func packDirectory(excludePrefix []string, excludeSuffix []string, +func packDirectory(output io.Writer, excludePrefix []string, excludeSuffix []string, excludeRegexp []*regexp.Regexp, includePath ...string) (err error) { logger.Infof("Excluding relpath prefix: %s", strings.Join(excludePrefix, ":")) @@ -408,6 +394,7 @@ func packDirectory(excludePrefix []string, excludeSuffix []string, if format == "zip" { walk := new(zipWalk) + walk.output = &output zw := zip.NewWriter(w) defer func() { zw.Close() @@ -421,6 +408,7 @@ func packDirectory(excludePrefix []string, excludeSuffix []string, wft = walk } else { walk := new(tarWalk) + walk.output = &output cw := gzip.NewWriter(w) tw := tar.NewWriter(cw) @@ -450,6 +438,7 @@ func packDirectory(excludePrefix []string, excludeSuffix []string, } func packApp(cmd *Command, args []string) int { + output := cmd.Out() curPath, _ := os.Getwd() thePath := "" @@ -463,7 +452,7 @@ func packApp(cmd *Command, args []string) int { nArgs = append(nArgs, a) } } - cmdPack.Flag.Parse(nArgs) + cmd.Flag.Parse(nArgs) if path.IsAbs(appPath) == false { appPath = path.Join(curPath, appPath) @@ -532,7 +521,7 @@ func packApp(cmd *Command, args []string) int { } if verbose { - fmt.Fprintf(w, "\t%s%s+ go %s%s%s\n", "\x1b[32m", "\x1b[1m", strings.Join(args, " "), "\x1b[21m", "\x1b[0m") + fmt.Fprintf(output, "\t%s%s+ go %s%s%s\n", "\x1b[32m", "\x1b[1m", strings.Join(args, " "), "\x1b[21m", "\x1b[0m") } execmd := exec.Command("go", args...) @@ -545,7 +534,7 @@ func packApp(cmd *Command, args []string) int { logger.Fatal(err.Error()) } - logger.Success("Build successful!") + logger.Success("Build Successful!") } switch format { @@ -594,7 +583,7 @@ func packApp(cmd *Command, args []string) int { logger.Infof("Writing to output: %s", outputP) - err = packDirectory(exp, exs, exr, tmpdir, thePath) + err = packDirectory(output, exp, exs, exr, tmpdir, thePath) if err != nil { logger.Fatal(err.Error()) } diff --git a/run.go b/run.go index b7526e2..a9b54a4 100644 --- a/run.go +++ b/run.go @@ -24,12 +24,13 @@ import ( var cmdRun = &Command{ UsageLine: "run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true] [-vendor=true] [-e=folderToExclude] [-tags=goBuildTags] [-runmode=BEEGO_RUNMODE]", - Short: "run the app and start a Web server for development", + Short: "Run the application by starting a local development server", Long: ` -Run command will supervise the file system of the beego project using inotify, -it will recompile and restart the app after any modifications. +Run command will supervise the filesystem of the application for any changes, and recompile/restart it. `, + PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + Run: runApp, } var ( @@ -55,15 +56,13 @@ var ( ) func init() { - cmdRun.Run = runApp - cmdRun.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } - cmdRun.Flag.Var(&mainFiles, "main", "specify main go files") - cmdRun.Flag.Var(&gendoc, "gendoc", "auto generate the docs") - cmdRun.Flag.Var(&downdoc, "downdoc", "auto download swagger file when not exist") - cmdRun.Flag.Var(&excludedPaths, "e", "Excluded paths[].") - cmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Watch vendor folder") - cmdRun.Flag.StringVar(&buildTags, "tags", "", "Build tags (https://golang.org/pkg/go/build/)") - cmdRun.Flag.StringVar(&runmode, "runmode", "", "Set BEEGO_RUNMODE env variable.") + cmdRun.Flag.Var(&mainFiles, "main", "Specify main go files.") + cmdRun.Flag.Var(&gendoc, "gendoc", "Enable auto-generate the docs.") + cmdRun.Flag.Var(&downdoc, "downdoc", "Enable auto-download of the swagger file if it does not exist.") + cmdRun.Flag.Var(&excludedPaths, "e", "List of paths to exclude.") + cmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder") + cmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/") + cmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.") exit = make(chan bool) } diff --git a/version.go b/version.go index 60dbfd7..721a885 100644 --- a/version.go +++ b/version.go @@ -14,9 +14,9 @@ import ( var cmdVersion = &Command{ UsageLine: "version", - Short: "prints the current Bee version", + Short: "Prints the current Bee version", Long: ` -Prints the current Bee, Beego and Go version alongside the platform information +Prints the current Bee, Beego and Go version alongside the platform information. `, } From 73aa44e1a79eed868f78ddfc69f99e39d2c4c93f Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sat, 3 Dec 2016 16:06:46 +0100 Subject: [PATCH 4/7] Added a few tweaks to the folder structure in the long description --- apiapp.go | 24 ++++++++++++------------ hproseapp.go | 6 +++--- new.go | 32 ++++++++++++++++---------------- util.go | 11 ++++++----- 4 files changed, 37 insertions(+), 36 deletions(-) diff --git a/apiapp.go b/apiapp.go index 9a1950d..3e2a9be 100644 --- a/apiapp.go +++ b/apiapp.go @@ -36,19 +36,19 @@ var cmdApiapp = &Command{ The command 'api' creates a folder named [appname] with the following structure: - ├── conf - │ └── app.conf - ├── controllers - │ └── object.go - │ └── user.go - ├── routers - │ └── router.go - ├── tests - │ └── default_test.go ├── main.go - └── models - └── object.go - └── user.go + ├── {{"conf"|foldername}} + │ └── app.conf + ├── {{"controllers"|foldername}} + │ └── object.go + │ └── user.go + ├── {{"routers"|foldername}} + │ └── router.go + ├── {{"tests"|foldername}} + │ └── default_test.go + └── {{"models"|foldername}} + └── object.go + └── user.go `, PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, Run: createapi, diff --git a/hproseapp.go b/hproseapp.go index 0df6e1c..edd01b1 100644 --- a/hproseapp.go +++ b/hproseapp.go @@ -37,10 +37,10 @@ var cmdHproseapp = &Command{ The command 'hprose' creates a folder named [appname] with the following structure: - ├── conf - │ └── app.conf ├── main.go - └── models + ├── {{"conf"|foldername}} + │ └── app.conf + └── {{"models"|foldername}} └── object.go └── user.go `, diff --git a/new.go b/new.go index 497ad66..eb3ab60 100644 --- a/new.go +++ b/new.go @@ -29,22 +29,22 @@ Creates a Beego application for the given app name in the current directory. The command 'new' creates a folder named [appname] and generates the following structure: - |- main.go - |- conf - |- app.conf - |- controllers - |- default.go - |- models - |- routers - |- router.go - |- tests - |- default_test.go - |- static - |- js - |- css - |- img - |- views - index.tpl + ├── main.go + ├── {{"conf"|foldername}} + │ └── app.conf + ├── {{"controllers"|foldername}} + │ └── default.go + ├── {{"models"|foldername}} + ├── {{"routers"|foldername}} + │ └── router.go + ├── {{"tests"|foldername}} + │ └── default_test.go + ├── {{"static"|foldername}} + │ └── {{"js"|foldername}} + │ └── {{"css"|foldername}} + │ └── {{"img"|foldername}} + └── {{"views"|foldername}} + └── index.tpl `, PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, diff --git a/util.go b/util.go index fcfaeca..0893144 100644 --- a/util.go +++ b/util.go @@ -287,11 +287,12 @@ func __LINE__() int { // BeeFuncMap returns a FuncMap of functions used in different templates. func BeeFuncMap() template.FuncMap { return template.FuncMap{ - "trim": strings.TrimSpace, - "bold": bold, - "headline": MagentaBold, - "endline": EndLine, - "tmpltostr": TmplToString, + "trim": strings.TrimSpace, + "bold": bold, + "headline": MagentaBold, + "foldername": RedBold, + "endline": EndLine, + "tmpltostr": TmplToString, } } From ce71c916d5bad8fa79e74908621d7762b582b622 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 4 Dec 2016 16:30:38 +0100 Subject: [PATCH 5/7] Displays the default value of a command flag Added ability to display the default value of a command's flag, truncated in case it contains a column (:). Also instead of displaying the entire command usage/help in case of an undefined flag, output a small message with "bee help cmd". --- bee.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/bee.go b/bee.go index 840bf50..d90386f 100644 --- a/bee.go +++ b/bee.go @@ -84,7 +84,7 @@ func (c *Command) Out() io.Writer { // Usage puts out the usage for the command. func (c *Command) Usage() { - tmpl(helpTemplate, c) + tmpl(cmdUsage, c) os.Exit(2) } @@ -97,7 +97,17 @@ func (c *Command) Runnable() bool { func (c *Command) Options() map[string]string { options := make(map[string]string) c.Flag.VisitAll(func(f *flag.Flag) { - options[f.Name] = f.Usage + defaultVal := f.DefValue + if len(defaultVal) > 0 { + if strings.Contains(defaultVal, ":") { + // Truncate the flag's default value by appending '...' at the end + options[f.Name+"="+strings.Split(defaultVal, ":")[0]+":..."] = f.Usage + } else { + options[f.Name+"="+defaultVal] = f.Usage + } + } else { + options[f.Name] = f.Usage + } }) return options } @@ -196,6 +206,8 @@ var errorTemplate = `bee: %s. Use {{"bee help" | bold}} for more information. ` +var cmdUsage = `Use {{printf "bee help %s" .Name | bold}} for more information.{{endline}}` + func usage() { tmpl(usageTemplate, availableCommands) os.Exit(2) From 592eac9974460792b536aabe56e6d82c30fef696 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Mon, 5 Dec 2016 14:40:15 +0100 Subject: [PATCH 6/7] Fixed a typo --- pack.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pack.go b/pack.go index d9f8852..75aaaa7 100644 --- a/pack.go +++ b/pack.go @@ -38,7 +38,7 @@ var cmdPack = &Command{ UsageLine: "pack", Short: "Compresses a Beego application into a single file", Long: `Pack is used to compress Beego applications into a tarball/zip file. - This eases the deployment by extracting directly the file to a server. + This eases the deployment by directly extracting the file to a server. {{"Example:"|bold}} $ bee pack -v -ba="-ldflags '-s -w'" From 8dca816aacd7dd2cdf067e40b17470b442ae5121 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Mon, 5 Dec 2016 14:52:37 +0100 Subject: [PATCH 7/7] Fixed padding for printing an option --- bee.go | 2 +- run.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bee.go b/bee.go index d90386f..397ce9d 100644 --- a/bee.go +++ b/bee.go @@ -197,7 +197,7 @@ Use {{"bee help [topic]" | bold}} for more information about that topic. var helpTemplate = `{{"USAGE" | headline}} {{.UsageLine | printf "bee %s" | bold}} {{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}} - {{$k | printf "-%-11s" | bold}} {{$v}}{{end}}{{endline}}{{end}} + {{$k | printf "-%-12s" | bold}} {{$v}}{{end}}{{endline}}{{end}} {{"DESCRIPTION" | headline}} {{tmpltostr .Long . | trim}} ` diff --git a/run.go b/run.go index a9b54a4..951cfa8 100644 --- a/run.go +++ b/run.go @@ -60,7 +60,7 @@ func init() { cmdRun.Flag.Var(&gendoc, "gendoc", "Enable auto-generate the docs.") cmdRun.Flag.Var(&downdoc, "downdoc", "Enable auto-download of the swagger file if it does not exist.") cmdRun.Flag.Var(&excludedPaths, "e", "List of paths to exclude.") - cmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder") + cmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.") cmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/") cmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.") exit = make(chan bool)