From ab39926c41fc5d857ec3f239b619a0721ccfd9f7 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 20 Nov 2016 11:46:42 +0100 Subject: [PATCH 1/4] Enhances the package dir parser The function parsePackagesFromDir() was panicing when it encounter some "invalid" Go files. Instead, this will allow it to display a warning and continue the execution of the command. --- g_docs.go | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/g_docs.go b/g_docs.go index 07f432f..3fa28ee 100644 --- a/g_docs.go +++ b/g_docs.go @@ -32,8 +32,6 @@ import ( "gopkg.in/yaml.v2" - "io/ioutil" - "github.com/astaxie/beego/swagger" "github.com/astaxie/beego/utils" ) @@ -87,39 +85,57 @@ func init() { parsePackagesFromDir(curPath) } -func parsePackagesFromDir(path string) { - parsePackageFromDir(path) - list, err := ioutil.ReadDir(path) - if err != nil { - logger.Fatalf("Cannot read directory '%s': %s", path, err) - } - for _, item := range list { - if item.IsDir() && item.Name() != "vendor" { - parsePackagesFromDir(path + "/" + item.Name()) - } +func parsePackagesFromDir(dirpath string) { + c := make(chan error) + + go func() { + filepath.Walk(dirpath, func(fpath string, fileInfo os.FileInfo, err error) error { + if err != nil { + return nil + } + if !fileInfo.IsDir() { + return nil + } + + if fileInfo.Name() != "vendor" { + err = parsePackageFromDir(fpath) + if err != nil { + // Send the error to through the channel and continue walking + c <- fmt.Errorf("Error while parsing directory: %s", err.Error()) + return nil + } + } + return nil + }) + close(c) + }() + + for err := range c { + logger.Warnf("%s", err) } } -func parsePackageFromDir(path string) { +func parsePackageFromDir(path string) error { fileSet := token.NewFileSet() folderPkgs, err := parser.ParseDir(fileSet, path, func(info os.FileInfo) bool { name := info.Name() return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") }, parser.ParseComments) if err != nil { - logger.Fatalf("Error while parsing dir at '%s': %s", path, err) + return err } for k, v := range folderPkgs { astPkgs[k] = v } + + return nil } func generateDocs(curpath string) { fset := token.NewFileSet() f, err := parser.ParseFile(fset, path.Join(curpath, "routers", "router.go"), nil, parser.ParseComments) - if err != nil { logger.Fatalf("Error while parsing router.go: %s", err) } @@ -147,13 +163,13 @@ func generateDocs(curpath string) { rootapi.Infos.Contact.URL = strings.TrimSpace(s[len("@URL"):]) } else if strings.HasPrefix(s, "@LicenseUrl") { if rootapi.Infos.License == nil { - rootapi.Infos.License = &swagger.License{URL: strings.TrimSpace(s[len("@LicenseUrl"):])} + rootapi.Infos.License = &swagger.License{URL: strings.TrimSpace(s[len("@LicenseUrl"):])} } else { - rootapi.Infos.License.URL = strings.TrimSpace(s[len("@LicenseUrl"):]) + rootapi.Infos.License.URL = strings.TrimSpace(s[len("@LicenseUrl"):]) } } else if strings.HasPrefix(s, "@License") { if rootapi.Infos.License == nil { - rootapi.Infos.License = &swagger.License{Name: strings.TrimSpace(s[len("@License"):])} + rootapi.Infos.License = &swagger.License{Name: strings.TrimSpace(s[len("@License"):])} } else { rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):]) } From 7fcbba0f53fae506defd95e0b06505ffed535c56 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 20 Nov 2016 11:54:12 +0100 Subject: [PATCH 2/4] Initialize the logger templates inside the logger singleton once (and only once) --- logger.go | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/logger.go b/logger.go index 9689a00..e9e081f 100644 --- a/logger.go +++ b/logger.go @@ -64,28 +64,26 @@ var ( debugLogRecordTemplate *template.Template ) -func init() { - var ( - err error - simpleLogFormat = `{{Now "2006/01/02 15:04:05"}} {{.Level}} ▶ {{.ID}} {{.Message}}{{EndLine}}` - debugLogFormat = `{{Now "2006/01/02 15:04:05"}} {{.Level}} ▶ {{.ID}} {{.Filename}}:{{.LineNo}} {{.Message}}{{EndLine}}` - ) - - // Initialize and parse logging templates - funcs := template.FuncMap{ - "Now": Now, - "EndLine": EndLine, - } - logRecordTemplate, err = template.New("logRecordTemplate").Funcs(funcs).Parse(simpleLogFormat) - MustCheck(err) - debugLogRecordTemplate, err = template.New("dbgLogRecordTemplate").Funcs(funcs).Parse(debugLogFormat) - MustCheck(err) -} - // GetBeeLogger initializes the logger instance with a NewColorWriter output // and returns a singleton func GetBeeLogger(w io.Writer) *BeeLogger { once.Do(func() { + var ( + err error + simpleLogFormat = `{{Now "2006/01/02 15:04:05"}} {{.Level}} ▶ {{.ID}} {{.Message}}{{EndLine}}` + debugLogFormat = `{{Now "2006/01/02 15:04:05"}} {{.Level}} ▶ {{.ID}} {{.Filename}}:{{.LineNo}} {{.Message}}{{EndLine}}` + ) + + // Initialize and parse logging templates + funcs := template.FuncMap{ + "Now": Now, + "EndLine": EndLine, + } + logRecordTemplate, err = template.New("simpleLogFormat").Funcs(funcs).Parse(simpleLogFormat) + MustCheck(err) + debugLogRecordTemplate, err = template.New("debugLogFormat").Funcs(funcs).Parse(debugLogFormat) + MustCheck(err) + instance = &BeeLogger{output: NewColorWriter(w)} }) return instance From 35384b463e3bf63030a4fe54bbe1abccfd9c4fbd Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 20 Nov 2016 11:55:44 +0100 Subject: [PATCH 3/4] This allows the isBeegoProject() function to do the walking inside a Goroutine --- util.go | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/util.go b/util.go index c3cc754..ef28fb2 100644 --- a/util.go +++ b/util.go @@ -70,24 +70,34 @@ func GetGOPATHs() []string { func isBeegoProject(thePath string) bool { mainFiles := []string{} hasBeegoRegex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`) + c := make(chan error) // Walk the application path tree to look for main files. // Main files must satisfy the 'hasBeegoRegex' regular expression. - err := filepath.Walk(thePath, func(fpath string, f os.FileInfo, err error) error { - if !f.IsDir() { // Skip sub-directories - data, _err := ioutil.ReadFile(fpath) - if _err != nil { - return _err + go func() { + filepath.Walk(thePath, func(fpath string, f os.FileInfo, err error) error { + if err != nil { + return nil } - if len(hasBeegoRegex.Find(data)) > 0 { - mainFiles = append(mainFiles, fpath) - } - } - return nil - }) + // Skip sub-directories + if !f.IsDir() { + var data []byte + data, err = ioutil.ReadFile(fpath) + if err != nil { + c <- err + return nil + } - if err != nil { - log.Fatalf("Unable to walk '%s' tree: %v", thePath, err) - return false + if len(hasBeegoRegex.Find(data)) > 0 { + mainFiles = append(mainFiles, fpath) + } + } + return nil + }) + close(c) + }() + + if err := <-c; err != nil { + logger.Fatalf("Unable to walk '%s' tree: %s", thePath, err) } if len(mainFiles) > 0 { From 03f2057eb0ef4f1112aae08eebcb65101910cae9 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 20 Nov 2016 22:28:52 +0100 Subject: [PATCH 4/4] Added PreRun phase to Command struct Now each command has a PreRun function that will execute before calling the Run() function. This allows to show the banner and do some pre-check work. Also moved parsePackagesFromDir() to the main function to avoid getting called each time 'bee' is invoked. --- apiapp.go | 5 ++--- bale.go | 5 ++--- bee.go | 17 ++++++++++++++++- fix.go | 4 ++-- g.go | 3 +-- g_docs.go | 2 -- hproseapp.go | 3 +-- migrate.go | 3 +-- new.go | 14 +++++++------- pack.go | 7 ++++--- run.go | 3 +-- rundocs.go | 1 + test.go | 1 + 13 files changed, 39 insertions(+), 29 deletions(-) diff --git a/apiapp.go b/apiapp.go index ca8cf22..4c39c96 100644 --- a/apiapp.go +++ b/apiapp.go @@ -538,14 +538,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") } func createapi(cmd *Command, args []string) int { - ShowShortVersionBanner() - w := NewColorWriter(os.Stdout) if len(args) < 1 { @@ -656,7 +655,7 @@ func checkEnv(appname string) (apppath, packpath string, err error) { // we use the first path gopath := gps[0] - logger.Warn("You current workdir is not inside $GOPATH/src") + logger.Warn("You current workdir is not inside $GOPATH/src.") logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) gosrcpath := path.Join(gopath, "src") diff --git a/bale.go b/bale.go index df599b5..e7a9aa1 100644 --- a/bale.go +++ b/bale.go @@ -32,7 +32,7 @@ var cmdBale = &Command{ Long: ` Bale command compress all the static files in to a single binary file. -This is usefull to not have to carry static files including js, css, images +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. @@ -43,11 +43,10 @@ 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() } } func runBale(cmd *Command, args []string) int { - ShowShortVersionBanner() - err := loadConfig() if err != nil { logger.Fatalf("Failed to load configuration: %s", err) diff --git a/bee.go b/bee.go index 645a979..11e14fd 100644 --- a/bee.go +++ b/bee.go @@ -33,6 +33,9 @@ type Command struct { // The args are the arguments after the command name. Run func(cmd *Command, args []string) int + // PreRun performs an operation before running the command + PreRun func(cmd *Command, args []string) + // UsageLine is the one-line usage message. // The first word in the line is taken to be the command name. UsageLine string @@ -93,6 +96,8 @@ var commands = []*Command{ var logger = GetBeeLogger(os.Stdout) func main() { + currentpath, _ := os.Getwd() + flag.Usage = usage flag.Parse() log.SetFlags(0) @@ -116,6 +121,17 @@ func main() { cmd.Flag.Parse(args[1:]) args = cmd.Flag.Args() } + + if cmd.PreRun != nil { + cmd.PreRun(cmd, args) + } + + // Check if current directory is inside the GOPATH, + // if so parse the packages inside it. + if strings.Contains(currentpath, GetGOPATHs()[0]+"/src") { + parsePackagesFromDir(currentpath) + } + os.Exit(cmd.Run(cmd, args)) return } @@ -142,7 +158,6 @@ Additional help topics: {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} Use "bee help [topic]" for more information about that topic. - ` var helpTemplate = `{{if .Runnable}}usage: bee {{.UsageLine}} diff --git a/fix.go b/fix.go index 60e166b..929297c 100644 --- a/fix.go +++ b/fix.go @@ -23,12 +23,12 @@ bee fix help to upgrade the application to beego 1.6 func init() { cmdFix.Run = runFix + cmdFix.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } } func runFix(cmd *Command, args []string) int { - ShowShortVersionBanner() - logger.Info("Upgrading the application...") + dir, err := os.Getwd() if err != nil { logger.Fatalf("Error while getting the current working directory: %s", err) diff --git a/g.go b/g.go index b8589b4..e25bdb1 100644 --- a/g.go +++ b/g.go @@ -69,6 +69,7 @@ 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") @@ -77,8 +78,6 @@ func init() { } func generateCode(cmd *Command, args []string) int { - ShowShortVersionBanner() - currpath, _ := os.Getwd() if len(args) < 1 { logger.Fatal("Command is missing") diff --git a/g_docs.go b/g_docs.go index 3fa28ee..ca9a877 100644 --- a/g_docs.go +++ b/g_docs.go @@ -80,9 +80,7 @@ func init() { importlist = make(map[string]string) controllerList = make(map[string]map[string]*swagger.Item) modelsList = make(map[string]map[string]swagger.Schema) - curPath, _ := os.Getwd() astPkgs = map[string]*ast.Package{} - parsePackagesFromDir(curPath) } func parsePackagesFromDir(dirpath string) { diff --git a/hproseapp.go b/hproseapp.go index c9a4dfd..6b2cf6f 100644 --- a/hproseapp.go +++ b/hproseapp.go @@ -298,14 +298,13 @@ 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") } func createhprose(cmd *Command, args []string) int { - ShowShortVersionBanner() - w := NewColorWriter(os.Stdout) curpath, _ := os.Getwd() diff --git a/migrate.go b/migrate.go index b4fde98..3d89991 100644 --- a/migrate.go +++ b/migrate.go @@ -56,14 +56,13 @@ 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") } // runMigration is the entry point for starting a migration func runMigration(cmd *Command, args []string) int { - ShowShortVersionBanner() - currpath, _ := os.Getwd() gps := GetGOPATHs() diff --git a/new.go b/new.go index 6ff411c..b8be305 100644 --- a/new.go +++ b/new.go @@ -52,24 +52,24 @@ the following files/directories structure: func init() { cmdNew.Run = createApp + cmdNew.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } } func createApp(cmd *Command, args []string) int { - ShowShortVersionBanner() w := NewColorWriter(os.Stdout) + if len(args) != 1 { - logger.Error("Argument [appname] is missing") - os.Exit(2) + logger.Fatal("Argument [appname] is missing") } + apppath, packpath, err := checkEnv(args[0]) if err != nil { - fmt.Println(err) - os.Exit(2) + logger.Fatalf("%s", err) } if isExist(apppath) { - logger.Errorf("Path (%s) already exists", apppath) - logger.Warn("Do you want to overwrite it? [Yes|No] ") + logger.Errorf(bold("Application '%s' already exists"), apppath) + logger.Warn(bold("Do you want to overwrite it? [Yes|No] ")) if !askForConfirmation() { os.Exit(2) } diff --git a/pack.go b/pack.go index 2695840..547a3a1 100644 --- a/pack.go +++ b/pack.go @@ -101,6 +101,7 @@ func init() { fs.BoolVar(&verbose, "v", false, "verbose") cmdPack.Flag = *fs cmdPack.Run = packApp + cmdPack.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } w = NewColorWriter(os.Stdout) } @@ -449,8 +450,6 @@ func packDirectory(excludePrefix []string, excludeSuffix []string, } func packApp(cmd *Command, args []string) int { - ShowShortVersionBanner() - curPath, _ := os.Getwd() thePath := "" @@ -597,11 +596,13 @@ func packApp(cmd *Command, args []string) int { } } + logger.Infof("Writing to output: %s", outputP) + err = packDirectory(exp, exs, exr, tmpdir, thePath) if err != nil { logger.Fatal(err.Error()) } - logger.Infof("Writing to output: %s", outputP) + logger.Success("Application packed!") return 0 } diff --git a/run.go b/run.go index bdee001..0f0637f 100644 --- a/run.go +++ b/run.go @@ -56,6 +56,7 @@ 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") @@ -67,8 +68,6 @@ func init() { } func runApp(cmd *Command, args []string) int { - ShowShortVersionBanner() - if len(args) == 0 || args[0] == "watchall" { currpath, _ = os.Getwd() diff --git a/rundocs.go b/rundocs.go index e374329..645d391 100644 --- a/rundocs.go +++ b/rundocs.go @@ -53,6 +53,7 @@ var docport docValue func init() { cmdRundocs.Run = runDocs + cmdRundocs.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } cmdRundocs.Flag.Var(&isDownload, "isDownload", "weather download the Swagger Docs") cmdRundocs.Flag.Var(&docport, "docport", "doc server port") } diff --git a/test.go b/test.go index 2dcf070..f8be62b 100644 --- a/test.go +++ b/test.go @@ -31,6 +31,7 @@ var cmdTest = &Command{ func init() { cmdTest.Run = testApp + cmdTest.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } } func safePathAppend(arr []string, paths ...string) []string {