From ad34b7be2a3dbbaa1ab6d113cea86ad022ba89bf Mon Sep 17 00:00:00 2001 From: ZetaChow Date: Fri, 9 Sep 2016 15:20:59 +0800 Subject: [PATCH 01/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E4=BB=A5=E7=AC=A6=E5=90=88Lint?= =?UTF-8?q?=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g_appcode.go | 8 +++++++- g_controllers.go | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/g_appcode.go b/g_appcode.go index 76d78ad..5d44663 100644 --- a/g_appcode.go +++ b/g_appcode.go @@ -1168,11 +1168,12 @@ import ( "github.com/astaxie/beego" ) -// oprations for {{ctrlName}} +// {{ctrlName}}Controller oprations for {{ctrlName}} type {{ctrlName}}Controller struct { beego.Controller } +// URLMapping ... func (c *{{ctrlName}}Controller) URLMapping() { c.Mapping("Post", c.Post) c.Mapping("GetOne", c.GetOne) @@ -1181,6 +1182,7 @@ func (c *{{ctrlName}}Controller) URLMapping() { c.Mapping("Delete", c.Delete) } +// Post ... // @Title Post // @Description create {{ctrlName}} // @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content" @@ -1202,6 +1204,7 @@ func (c *{{ctrlName}}Controller) Post() { c.ServeJSON() } +// Get ... // @Title Get // @Description get {{ctrlName}} by id // @Param id path string true "The key for staticblock" @@ -1220,6 +1223,7 @@ func (c *{{ctrlName}}Controller) GetOne() { c.ServeJSON() } +// GetAll ... // @Title Get All // @Description get {{ctrlName}} // @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..." @@ -1282,6 +1286,7 @@ func (c *{{ctrlName}}Controller) GetAll() { c.ServeJSON() } +// Update ... // @Title Update // @Description update the {{ctrlName}} // @Param id path string true "The id you want to update" @@ -1305,6 +1310,7 @@ func (c *{{ctrlName}}Controller) Put() { c.ServeJSON() } +// Delete ... // @Title Delete // @Description delete the {{ctrlName}} // @Param id path string true "The id you want to delete" diff --git a/g_controllers.go b/g_controllers.go index 79ed554..15ab0bd 100644 --- a/g_controllers.go +++ b/g_controllers.go @@ -82,11 +82,12 @@ import ( "github.com/astaxie/beego" ) -// operations for {{controllerName}} +// {{controllerName}}Controller operations for {{controllerName}} type {{controllerName}}Controller struct { beego.Controller } +// URLMapping ... func (c *{{controllerName}}Controller) URLMapping() { c.Mapping("Post", c.Post) c.Mapping("GetOne", c.GetOne) @@ -95,6 +96,7 @@ func (c *{{controllerName}}Controller) URLMapping() { c.Mapping("Delete", c.Delete) } +// Post ... // @Title Create // @Description create {{controllerName}} // @Param body body models.{{controllerName}} true "body for {{controllerName}} content" @@ -105,6 +107,7 @@ func (c *{{controllerName}}Controller) Post() { } +// GetOne ... // @Title GetOne // @Description get {{controllerName}} by id // @Param id path string true "The key for staticblock" @@ -115,6 +118,7 @@ func (c *{{controllerName}}Controller) GetOne() { } +// GetAll ... // @Title GetAll // @Description get {{controllerName}} // @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..." @@ -130,6 +134,7 @@ func (c *{{controllerName}}Controller) GetAll() { } +// Update ... // @Title Update // @Description update the {{controllerName}} // @Param id path string true "The id you want to update" @@ -141,6 +146,7 @@ func (c *{{controllerName}}Controller) Put() { } +// Delete ... // @Title Delete // @Description delete the {{controllerName}} // @Param id path string true "The id you want to delete" @@ -164,11 +170,12 @@ import ( "github.com/astaxie/beego" ) -// oprations for {{controllerName}} +// {{controllerName}}Controller oprations for {{controllerName}} type {{controllerName}}Controller struct { beego.Controller } +// URLMapping ... func (c *{{controllerName}}Controller) URLMapping() { c.Mapping("Post", c.Post) c.Mapping("GetOne", c.GetOne) @@ -177,6 +184,7 @@ func (c *{{controllerName}}Controller) URLMapping() { c.Mapping("Delete", c.Delete) } +// Post ... // @Title Post // @Description create {{controllerName}} // @Param body body models.{{controllerName}} true "body for {{controllerName}} content" @@ -195,6 +203,7 @@ func (c *{{controllerName}}Controller) Post() { c.ServeJSON() } +// Get ... // @Title Get // @Description get {{controllerName}} by id // @Param id path string true "The key for staticblock" @@ -213,6 +222,7 @@ func (c *{{controllerName}}Controller) GetOne() { c.ServeJSON() } +// GetAll ... // @Title Get All // @Description get {{controllerName}} // @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..." @@ -275,6 +285,7 @@ func (c *{{controllerName}}Controller) GetAll() { c.ServeJSON() } +// Update ... // @Title Update // @Description update the {{controllerName}} // @Param id path string true "The id you want to update" @@ -295,6 +306,7 @@ func (c *{{controllerName}}Controller) Put() { c.ServeJSON() } +// Delete ... // @Title Delete // @Description delete the {{controllerName}} // @Param id path string true "The id you want to delete" From 411aa430805140f9b56b5adbf71a295e189399d6 Mon Sep 17 00:00:00 2001 From: ZetaChow Date: Fri, 9 Sep 2016 15:27:36 +0800 Subject: [PATCH 02/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9PUT=E5=92=8CGet=20One?= =?UTF-8?q?=20=E9=94=99=E8=AF=AF=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g_appcode.go | 8 ++++---- g_controllers.go | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/g_appcode.go b/g_appcode.go index 5d44663..9276360 100644 --- a/g_appcode.go +++ b/g_appcode.go @@ -1204,8 +1204,8 @@ func (c *{{ctrlName}}Controller) Post() { c.ServeJSON() } -// Get ... -// @Title Get +// GetOne ... +// @Title Get One // @Description get {{ctrlName}} by id // @Param id path string true "The key for staticblock" // @Success 200 {object} models.{{ctrlName}} @@ -1286,8 +1286,8 @@ func (c *{{ctrlName}}Controller) GetAll() { c.ServeJSON() } -// Update ... -// @Title Update +// Put ... +// @Title Put // @Description update the {{ctrlName}} // @Param id path string true "The id you want to update" // @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content" diff --git a/g_controllers.go b/g_controllers.go index 15ab0bd..7c782b6 100644 --- a/g_controllers.go +++ b/g_controllers.go @@ -134,8 +134,8 @@ func (c *{{controllerName}}Controller) GetAll() { } -// Update ... -// @Title Update +// Put ... +// @Title Put // @Description update the {{controllerName}} // @Param id path string true "The id you want to update" // @Param body body models.{{controllerName}} true "body for {{controllerName}} content" @@ -203,8 +203,8 @@ func (c *{{controllerName}}Controller) Post() { c.ServeJSON() } -// Get ... -// @Title Get +// GetOne ... +// @Title Get One // @Description get {{controllerName}} by id // @Param id path string true "The key for staticblock" // @Success 200 {object} models.{{controllerName}} @@ -285,8 +285,8 @@ func (c *{{controllerName}}Controller) GetAll() { c.ServeJSON() } -// Update ... -// @Title Update +// Put ... +// @Title Put // @Description update the {{controllerName}} // @Param id path string true "The id you want to update" // @Param body body models.{{controllerName}} true "body for {{controllerName}} content" From 0d0bea81bf67f667ec45441e07a1f94a74a881a3 Mon Sep 17 00:00:00 2001 From: ZetaChow Date: Fri, 9 Sep 2016 15:38:44 +0800 Subject: [PATCH 03/37] =?UTF-8?q?=E5=8E=BB=E6=8E=89offset(int64)=E8=B5=8B0?= =?UTF-8?q?=E5=80=BC,=E5=8E=BB=E6=8E=89query=20(map[...]...)=E5=AE=9A?= =?UTF-8?q?=E4=B9=89,=E4=BB=A5=E7=AC=A6=E5=90=88Lint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g_appcode.go | 4 ++-- g_controllers.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/g_appcode.go b/g_appcode.go index 9276360..4e90107 100644 --- a/g_appcode.go +++ b/g_appcode.go @@ -1239,9 +1239,9 @@ func (c *{{ctrlName}}Controller) GetAll() { var fields []string var sortby []string var order []string - var query map[string]string = make(map[string]string) + var query = make(map[string]string) var limit int64 = 10 - var offset int64 = 0 + var offset int64 // fields: col1,col2,entity.col3 if v := c.GetString("fields"); v != "" { diff --git a/g_controllers.go b/g_controllers.go index 7c782b6..9d0b0ae 100644 --- a/g_controllers.go +++ b/g_controllers.go @@ -238,9 +238,9 @@ func (c *{{controllerName}}Controller) GetAll() { var fields []string var sortby []string var order []string - var query map[string]string = make(map[string]string) + var query = make(map[string]string) var limit int64 = 10 - var offset int64 = 0 + var offset int64 // fields: col1,col2,entity.col3 if v := c.GetString("fields"); v != "" { From 1b75ccb9fe595062cc147586ad55364718fa7729 Mon Sep 17 00:00:00 2001 From: Sergey Lanzman Date: Thu, 15 Sep 2016 00:09:54 +0300 Subject: [PATCH 04/37] Improve swagger (Main params,yaml --- g_docs.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/g_docs.go b/g_docs.go index 83bca73..cd1b958 100644 --- a/g_docs.go +++ b/g_docs.go @@ -31,6 +31,8 @@ import ( "strings" "unicode" + "gopkg.in/yaml.v2" + "github.com/astaxie/beego/swagger" "github.com/astaxie/beego/utils" ) @@ -83,10 +85,18 @@ func generateDocs(curpath string) { rootapi.Infos.TermsOfService = strings.TrimSpace(s[len("@TermsOfServiceUrl"):]) } else if strings.HasPrefix(s, "@Contact") { rootapi.Infos.Contact.EMail = strings.TrimSpace(s[len("@Contact"):]) + } else if strings.HasPrefix(s, "@Name") { + rootapi.Infos.Contact.Name = strings.TrimSpace(s[len("@Name"):]) + } else if strings.HasPrefix(s, "@URL") { + rootapi.Infos.Contact.URL = strings.TrimSpace(s[len("@URL"):]) } else if strings.HasPrefix(s, "@License") { rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):]) } else if strings.HasPrefix(s, "@LicenseUrl") { rootapi.Infos.License.URL = strings.TrimSpace(s[len("@LicenseUrl"):]) + } else if strings.HasPrefix(s, "@Schemes") { + rootapi.Schemes = strings.Split(strings.TrimSpace(s[len("@Schemes"):]), ",") + } else if strings.HasPrefix(s, "@Host") { + rootapi.Host = strings.TrimSpace(s[len("@Host"):]) } } } @@ -155,16 +165,20 @@ func generateDocs(curpath string) { } os.Mkdir(path.Join(curpath, "swagger"), 0755) fd, err := os.Create(path.Join(curpath, "swagger", "swagger.json")) + fdyml, err := os.Create(path.Join(curpath, "swagger", "swagger.yml")) if err != nil { panic(err) } + defer fdyml.Close() defer fd.Close() dt, err := json.MarshalIndent(rootapi, "", " ") - if err != nil { + dtyml, erryml := yaml.Marshal(rootapi) + if err != nil || erryml != nil { panic(err) } _, err = fd.Write(dt) - if err != nil { + _, erryml = fdyml.Write(dtyml) + if err != nil || erryml != nil { panic(err) } } From b282761292e7c51e4ac8e8daac16ec54f7ea5619 Mon Sep 17 00:00:00 2001 From: OTER Date: Thu, 15 Sep 2016 22:40:03 +0300 Subject: [PATCH 05/37] Fix .exe postfix issue on Windows platform. --- migrate.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/migrate.go b/migrate.go index a32cc8e..22bfac3 100644 --- a/migrate.go +++ b/migrate.go @@ -23,6 +23,7 @@ import ( "strconv" "strings" "time" + "runtime" ) var cmdMigrate = &Command{ @@ -145,8 +146,12 @@ func migrateRefresh(currpath, driver, connStr string) { // migrate generates source code, build it, and invoke the binary who does the actual migration func migrate(goal, currpath, driver, connStr string) { - dir := path.Join(currpath, "database", "migrations") - binary := "m" + dir := path.Join(currpath, "database", "migrations") + postfix := "" + if runtime.GOOS == "windows" { + postfix = ".exe" + } + binary := "m" + postfix source := binary + ".go" // connect to database db, err := sql.Open(driver, connStr) From 465115c28e20d837922b7b64b8f8671cc609bdeb Mon Sep 17 00:00:00 2001 From: Filip Date: Sat, 17 Sep 2016 21:54:00 +0200 Subject: [PATCH 06/37] Fix incorrect generation of postgres table id type --- g_migration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g_migration.go b/g_migration.go index c0485ef..ed3dfc4 100644 --- a/g_migration.go +++ b/g_migration.go @@ -129,7 +129,7 @@ func (m postgresqlDriver) generateSQLFromFields(fields string) string { return "" } if i == 0 && strings.ToLower(kv[0]) != "id" { - sql += "id interger serial primary key," + sql += "id serial primary key," } sql += snakeString(kv[0]) + " " + typ + "," if tag != "" { From 205428abcead96aac3aaf3fa8f8fe8ca5be1eff2 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 22 Sep 2016 22:26:39 +0800 Subject: [PATCH 07/37] fix the licence issue --- g_docs.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/g_docs.go b/g_docs.go index cd1b958..8a7f99d 100644 --- a/g_docs.go +++ b/g_docs.go @@ -90,9 +90,17 @@ func generateDocs(curpath string) { } else if strings.HasPrefix(s, "@URL") { rootapi.Infos.Contact.URL = strings.TrimSpace(s[len("@URL"):]) } else if strings.HasPrefix(s, "@License") { - rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):]) + if rootapi.Infos.License == nil { + rootapi.Infos.License = &swagger.License{Name: strings.TrimSpace(s[len("@License"):])} + } else { + rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):]) + } } else if strings.HasPrefix(s, "@LicenseUrl") { - rootapi.Infos.License.URL = strings.TrimSpace(s[len("@LicenseUrl"):]) + if rootapi.Infos.License == nil { + rootapi.Infos.License = &swagger.License{URL: strings.TrimSpace(s[len("@LicenseUrl"):])} + } else { + rootapi.Infos.License.URL = strings.TrimSpace(s[len("@LicenseUrl"):]) + } } else if strings.HasPrefix(s, "@Schemes") { rootapi.Schemes = strings.Split(strings.TrimSpace(s[len("@Schemes"):]), ",") } else if strings.HasPrefix(s, "@Host") { From 29c9e1bab765bd1b360edfbc1e3a6d8a50571206 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 22 Sep 2016 23:04:58 +0800 Subject: [PATCH 08/37] fix the map type generate --- g_docs.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/g_docs.go b/g_docs.go index 8a7f99d..a92c917 100644 --- a/g_docs.go +++ b/g_docs.go @@ -683,12 +683,18 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string } } } else { - if isBasicType(realType) { + if sType == "object" { + mp.Ref = "#/definitions/" + realType + } else if isBasicType(realType) { typeFormat := strings.Split(sType, ":") mp.Type = typeFormat[0] mp.Format = typeFormat[1] - } else if sType == "object" { - mp.Ref = "#/definitions/" + realType + } else if realType == "map" { + typeFormat := strings.Split(sType, ":") + mp.AdditionalProperties = &swagger.Propertie{ + Type: typeFormat[0], + Format: typeFormat[1], + } } } if field.Names != nil { @@ -768,7 +774,11 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) { case *ast.StarExpr: return false, fmt.Sprint(t.X), "object" case *ast.MapType: - return false, fmt.Sprint(t.Value), "object" + val := fmt.Sprintf("%v", t.Value) + if isBasicType(val) { + return false, "map", basicTypes[val] + } + return false, val, "object" } if k, ok := basicTypes[fmt.Sprint(f.Type)]; ok { return false, fmt.Sprint(f.Type), k From f7d2e04e070140f9ebee4f02c89f00f312c5e0a2 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 22 Sep 2016 23:12:55 +0800 Subject: [PATCH 09/37] change to 1.5.2 --- bee.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bee.go b/bee.go index 76c5359..f89e791 100644 --- a/bee.go +++ b/bee.go @@ -25,7 +25,7 @@ import ( "strings" ) -const version = "1.5.1" +const version = "1.5.2" type Command struct { // Run runs the command. From 4ebf7eab65bf47b12beae5958175364b1adf3c6a Mon Sep 17 00:00:00 2001 From: Sergey Lanzman Date: Sun, 25 Sep 2016 01:34:26 +0300 Subject: [PATCH 10/37] add support multi package with gaoling way myPackage.struct --- g_docs.go | 127 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 45 deletions(-) diff --git a/g_docs.go b/g_docs.go index a92c917..c74abdc 100644 --- a/g_docs.go +++ b/g_docs.go @@ -33,6 +33,8 @@ import ( "gopkg.in/yaml.v2" + "io/ioutil" + "github.com/astaxie/beego/swagger" "github.com/astaxie/beego/utils" ) @@ -50,6 +52,30 @@ var importlist map[string]string var controllerList map[string]map[string]*swagger.Item //controllername Paths items var modelsList map[string]map[string]swagger.Schema var rootapi swagger.Swagger +var astPkgs map[string]*ast.Package + +// refer to builtin.go +var basicTypes = map[string]string{ + "bool": "boolean:", + "uint": "integer:int32", + "uint8": "integer:int32", + "uint16": "integer:int32", + "uint32": "integer:int32", + "uint64": "integer:int64", + "int": "integer:int64", + "int8": "integer:int32", + "int16:int32": "integer:int32", + "int32": "integer:int32", + "int64": "integer:int64", + "uintptr": "integer:int64", + "float32": "number:float", + "float64": "number:double", + "string": "string:", + "complex64": "number:float", + "complex128": "number:double", + "byte": "string:byte", + "rune": "string:byte", +} func init() { pkgCache = make(map[string]struct{}) @@ -57,6 +83,35 @@ 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(path string) { + parsePackageFromDir(path) + list, _ := ioutil.ReadDir(path) + for _, item := range list { + if item.IsDir() && item.Name() != "vendor" { + parsePackagesFromDir(path + "/" + item.Name()) + } + } +} + +func parsePackageFromDir(path string) { + 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 { + ColorLog("[ERRO] the model %s parser.ParseDir error\n", path) + os.Exit(1) + } + for k, v := range folderPkgs { + astPkgs[k] = v + } } func generateDocs(curpath string) { @@ -414,13 +469,13 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat schema.Type = typeFormat[0] schema.Format = typeFormat[1] } else { - cmpath, m, mod, realTypes := getModel(schemaName) + m, mod, realTypes := getModel(schemaName) schema.Ref = "#/definitions/" + m if _, ok := modelsList[pkgpath+controllerName]; !ok { modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0) } modelsList[pkgpath+controllerName][schemaName] = mod - appendModels(cmpath, pkgpath, controllerName, realTypes) + appendModels(pkgpath, controllerName, realTypes) } if isArray { rs.Schema = &swagger.Schema{ @@ -460,7 +515,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat pp := strings.Split(p[2], ".") typ := pp[len(pp)-1] if len(pp) >= 2 { - cmpath, m, mod, realTypes := getModel(p[2]) + m, mod, realTypes := getModel(p[2]) para.Schema = &swagger.Schema{ Ref: "#/definitions/" + m, } @@ -468,7 +523,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0) } modelsList[pkgpath+controllerName][typ] = mod - appendModels(cmpath, pkgpath, controllerName, realTypes) + appendModels(pkgpath, controllerName, realTypes) } else { isArray := false paraType := "" @@ -610,22 +665,10 @@ func getparams(str string) []string { return r } -func getModel(str string) (pkgpath, objectname string, m swagger.Schema, realTypes []string) { +func getModel(str string) (objectname string, m swagger.Schema, realTypes []string) { strs := strings.Split(str, ".") objectname = strs[len(strs)-1] - pkgpath = strings.Join(strs[:len(strs)-1], "/") - curpath, _ := os.Getwd() - pkgRealpath := path.Join(curpath, pkgpath) - fileSet := token.NewFileSet() - astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool { - name := info.Name() - return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") - }, parser.ParseComments) - - if err != nil { - ColorLog("[ERRO] the model %s parser.ParseDir error\n", str) - os.Exit(1) - } + packageName := "" m.Type = "object" for _, pkg := range astPkgs { for _, fl := range pkg.Files { @@ -634,7 +677,8 @@ func getModel(str string) (pkgpath, objectname string, m swagger.Schema, realTyp if k != objectname { continue } - parseObject(d, k, &m, &realTypes, astPkgs) + packageName = pkg.Name + parseObject(d, k, &m, &realTypes, astPkgs, pkg.Name) } } } @@ -647,11 +691,12 @@ func getModel(str string) (pkgpath, objectname string, m swagger.Schema, realTyp if len(rootapi.Definitions) == 0 { rootapi.Definitions = make(map[string]swagger.Schema) } + objectname = packageName + "." + objectname rootapi.Definitions[objectname] = m return } -func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package) { +func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package, packageName string) { ts, ok := d.Decl.(*ast.TypeSpec) if !ok { ColorLog("Unknown type without TypeSec: %v\n", d) @@ -666,7 +711,18 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string if st.Fields.List != nil { m.Properties = make(map[string]swagger.Propertie) for _, field := range st.Fields.List { + realType := "" isSlice, realType, sType := typeAnalyser(field) + if (isSlice && isBasicType(realType)) || sType == "object" { + if len(strings.Split(realType, " ")) > 1 { + realType = strings.Replace(realType, " ", ".", -1) + realType = strings.Replace(realType, "&", "", -1) + realType = strings.Replace(realType, "{", "", -1) + realType = strings.Replace(realType, "}", "", -1) + } else { + realType = packageName + "." + realType + } + } *realTypes = append(*realTypes, realType) mp := swagger.Propertie{} if isSlice { @@ -747,7 +803,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string for _, fl := range pkg.Files { for nameOfObj, obj := range fl.Scope.Objects { if obj.Name == fmt.Sprint(field.Type) { - parseObject(obj, nameOfObj, m, realTypes, astPkgs) + parseObject(obj, nameOfObj, m, realTypes, astPkgs, pkg.Name) } } } @@ -793,18 +849,6 @@ func isBasicType(Type string) bool { return false } -// refer to builtin.go -var basicTypes = map[string]string{ - "bool": "boolean:", - "uint": "integer:int32", "uint8": "integer:int32", "uint16": "integer:int32", "uint32": "integer:int32", "uint64": "integer:int64", - "int": "integer:int64", "int8": "integer:int32", "int16:int32": "integer:int32", "int32": "integer:int32", "int64": "integer:int64", - "uintptr": "integer:int64", - "float32": "number:float", "float64": "number:double", - "string": "string:", - "complex64": "number:float", "complex128": "number:double", - "byte": "string:byte", "rune": "string:byte", -} - // regexp get json tag func grepJSONTag(tag string) string { r, _ := regexp.Compile(`json:"([^"]*)"`) @@ -816,23 +860,16 @@ func grepJSONTag(tag string) string { } // append models -func appendModels(cmpath, pkgpath, controllerName string, realTypes []string) { - var p string - if cmpath != "" { - p = strings.Join(strings.Split(cmpath, "/"), ".") + "." - } else { - p = "" - } +func appendModels(pkgpath, controllerName string, realTypes []string) { for _, realType := range realTypes { if realType != "" && !isBasicType(strings.TrimLeft(realType, "[]")) && !strings.HasPrefix(realType, "map") && !strings.HasPrefix(realType, "&") { - if _, ok := modelsList[pkgpath+controllerName][p+realType]; ok { + if _, ok := modelsList[pkgpath+controllerName][realType]; ok { continue } - //fmt.Printf(pkgpath + ":" + controllerName + ":" + cmpath + ":" + realType + "\n") - _, _, mod, newRealTypes := getModel(p + realType) - modelsList[pkgpath+controllerName][p+realType] = mod - appendModels(cmpath, pkgpath, controllerName, newRealTypes) + _, mod, newRealTypes := getModel(realType) + modelsList[pkgpath+controllerName][realType] = mod + appendModels(pkgpath, controllerName, newRealTypes) } } } From 287af20d1a3e4c0a263f7848009f7b8aa20fc1b6 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Mon, 26 Sep 2016 20:25:15 +0200 Subject: [PATCH 11/37] fix: This fixes #247 Moved isBeegoProject(app) method from pack.go to util.go. Rewrite it to walk the application folder tree looking for main files. --- pack.go | 19 ------------------- util.go | 39 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/pack.go b/pack.go index bdf3581..d6d84e4 100644 --- a/pack.go +++ b/pack.go @@ -21,7 +21,6 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" "os/exec" path "path/filepath" @@ -454,24 +453,6 @@ func packDirectory(excludePrefix []string, excludeSuffix []string, return } -func isBeegoProject(thePath string) bool { - fh, _ := os.Open(thePath) - fis, _ := fh.Readdir(-1) - regex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`) - for _, fi := range fis { - if fi.IsDir() == false && strings.HasSuffix(fi.Name(), ".go") { - data, err := ioutil.ReadFile(path.Join(thePath, fi.Name())) - if err != nil { - continue - } - if len(regex.Find(data)) > 0 { - return true - } - } - } - return false -} - func packApp(cmd *Command, args []string) int { ShowShortVersionBanner() diff --git a/util.go b/util.go index fd2e1ce..32db2f5 100644 --- a/util.go +++ b/util.go @@ -15,14 +15,16 @@ package main import ( + "fmt" + "io/ioutil" "log" "os" + "path" "path/filepath" + "regexp" "runtime" "strings" "time" - "path" - "fmt" ) // Go is a basic promise implementation: it wraps calls a function in a goroutine @@ -35,7 +37,7 @@ func Go(f func() error) chan error { return ch } -// if os.env DEBUG set, debug is on +// Debugf outputs a formtted debug message, when os.env DEBUG is set. func Debugf(format string, a ...interface{}) { if os.Getenv("DEBUG") != "" { _, file, line, ok := runtime.Caller(1) @@ -174,6 +176,37 @@ func GetGOPATHs() []string { return paths } +func isBeegoProject(thePath string) bool { + mainFiles := []string{} + hasBeegoRegex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`) + // 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 + } + if len(hasBeegoRegex.Find(data)) > 0 { + mainFiles = append(mainFiles, fpath) + } + } + return nil + }) + + if err != nil { + log.Fatalf("Unable to walk '%s' tree: %v", thePath, err) + return false + } + + if len(mainFiles) > 0 { + return true + } + return false +} + +// SearchGOPATHs searchs the user GOPATH(s) for the specified application name. +// It returns a boolean, the application's GOPATH and its full path. func SearchGOPATHs(app string) (bool, string, string) { gps := GetGOPATHs() if len(gps) == 0 { From 56568f4d2c60703d10b3bfb4e7326a9a6244a639 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Mon, 26 Sep 2016 20:32:14 +0200 Subject: [PATCH 12/37] tweak: Exit with message instead of a panic In case no application is found in GOPATH(s), exit with a friendly message instead of a panic. Also removed useless else block in watch.go. --- run.go | 8 ++++---- watch.go | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/run.go b/run.go index 8aab6b5..b84e199 100644 --- a/run.go +++ b/run.go @@ -87,7 +87,7 @@ func runApp(cmd *Command, args []string) int { currentGoPath = _gopath appname = path.Base(currpath) } else { - panic(fmt.Sprintf("No Beego application '%s' found in your GOPATH", args[0])) + exitPrint(fmt.Sprintf("No Beego application '%s' found in your GOPATH", args[0])) } ColorLog("[INFO] Using '%s' as 'appname'\n", appname) @@ -103,13 +103,13 @@ func runApp(cmd *Command, args []string) int { Debugf("current path:%s\n", currpath) - if runmode == "prod" || runmode == "dev"{ + if runmode == "prod" || runmode == "dev" { os.Setenv("BEEGO_RUNMODE", runmode) ColorLog("[INFO] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE")) - }else if runmode != ""{ + } else if runmode != "" { os.Setenv("BEEGO_RUNMODE", runmode) ColorLog("[WARN] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE")) - }else if os.Getenv("BEEGO_RUNMODE") != ""{ + } else if os.Getenv("BEEGO_RUNMODE") != "" { ColorLog("[WARN] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE")) } diff --git a/watch.go b/watch.go index efde48a..1c8f593 100644 --- a/watch.go +++ b/watch.go @@ -240,9 +240,8 @@ func shouldIgnoreFile(filename string) bool { } if r.MatchString(filename) { return true - } else { - continue } + continue } return false } From d93e9a4271a69a3f0e7957e72e37b7cb1dd1d82d Mon Sep 17 00:00:00 2001 From: Sergey Lanzman Date: Tue, 27 Sep 2016 18:18:03 +0300 Subject: [PATCH 13/37] Update g_docs.go --- g_docs.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/g_docs.go b/g_docs.go index c74abdc..9d6ffe9 100644 --- a/g_docs.go +++ b/g_docs.go @@ -90,7 +90,11 @@ func init() { func parsePackagesFromDir(path string) { parsePackageFromDir(path) - list, _ := ioutil.ReadDir(path) + list, err := ioutil.ReadDir(path) + if err != nil { + ColorLog("[ERRO] Can't read directory %s : %s\n", path, err) + os.Exit(1) + } for _, item := range list { if item.IsDir() && item.Name() != "vendor" { parsePackagesFromDir(path + "/" + item.Name()) From 31406ebe5a48da20cc37209da700e4c4f1bd13dc Mon Sep 17 00:00:00 2001 From: Konstantin Date: Sat, 8 Oct 2016 11:35:26 +0300 Subject: [PATCH 14/37] Default values in structure Swagger default value for golang stucture. format: doc:"default(my_defaul_value)" type MyStruct struct { Template string `json:"template" doc:"default(

Hello, {{ name }}!

)"` Name string `json:"name"` MyNumber int64 `json:"temp" doc:"default(10)"` MyBool bool `json:"bl" doc:"default(true)"` MyFloat float64 `json:"fl" doc:"default(12.1234)"` } --- g_docs.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/g_docs.go b/g_docs.go index 9d6ffe9..4d9f279 100644 --- a/g_docs.go +++ b/g_docs.go @@ -770,6 +770,30 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string var tagValues []string stag := reflect.StructTag(strings.Trim(field.Tag.Value, "`")) + + defaultValue := stag.Get("doc") + if defaultValue != ""{ + r, _ := regexp.Compile(`default\((.*)\)`) + if r.MatchString(defaultValue) { + res := r.FindStringSubmatch(defaultValue) + mp.Default = res[1] + switch realType{ + case "int","int64", "int32", "int16", "int8": + mp.Default, _ = strconv.Atoi(res[1]) + case "bool": + mp.Default, _ = strconv.ParseBool(res[1]) + case "float64": + mp.Default, _ = strconv.ParseFloat(res[1], 64) + case "float32": + mp.Default, _ = strconv.ParseFloat(res[1], 32) + default: + mp.Default = res[1] + } + }else{ + ColorLog("[WARN] Invalid default value: %s\n", defaultValue) + } + } + tag := stag.Get("json") if tag != "" { From e8f85a57c27ecbdaecfd147099cbc42945a0c41c Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 10 Oct 2016 17:36:51 +0300 Subject: [PATCH 15/37] Add types error handler (log) the usual warning not panic --- g_docs.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/g_docs.go b/g_docs.go index 4d9f279..a0ae1f6 100644 --- a/g_docs.go +++ b/g_docs.go @@ -779,15 +779,24 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string mp.Default = res[1] switch realType{ case "int","int64", "int32", "int16", "int8": - mp.Default, _ = strconv.Atoi(res[1]) + if mp.Default, err = strconv.Atoi(res[1]); err != nil{ + ColorLog("[WARN] Invalid default value type(%s): %s\n",realType, res[1]) + } + case "bool": - mp.Default, _ = strconv.ParseBool(res[1]) + if mp.Default, err = strconv.ParseBool(res[1]); err != nil{ + ColorLog("[WARN] Invalid default value type(%s): %s\n",realType, res[1]) + } case "float64": - mp.Default, _ = strconv.ParseFloat(res[1], 64) + if mp.Default, err = strconv.ParseFloat(res[1], 64); err != nil{ + ColorLog("[WARN] Invalid default value type(%s): %s\n",realType, res[1]) + } case "float32": - mp.Default, _ = strconv.ParseFloat(res[1], 32) - default: - mp.Default = res[1] + if mp.Default, err = strconv.ParseFloat(res[1], 32); err != nil{ + ColorLog("[WARN] Invalid default value type(%s): %s\n",realType, res[1]) + } + default: + mp.Default = res[1] } }else{ ColorLog("[WARN] Invalid default value: %s\n", defaultValue) From 7c7e75435c3968c10b836d18cd23f5ca877fdd69 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 10 Oct 2016 19:49:33 +0300 Subject: [PATCH 16/37] Update g_docs.go --- g_docs.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/g_docs.go b/g_docs.go index a0ae1f6..6f78329 100644 --- a/g_docs.go +++ b/g_docs.go @@ -769,6 +769,8 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string } var tagValues []string + var err error + stag := reflect.StructTag(strings.Trim(field.Tag.Value, "`")) defaultValue := stag.Get("doc") From 5cc09e5c6c161195a8dd050a65bdaab1fed7cd03 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sat, 22 Oct 2016 15:44:22 +0200 Subject: [PATCH 17/37] This fixes #295 --- banner.go | 2 +- version.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/banner.go b/banner.go index af33ef3..fefa37b 100644 --- a/banner.go +++ b/banner.go @@ -57,7 +57,7 @@ func show(out io.Writer, content string) { } err = t.Execute(out, vars{ - runtime.Version(), + getGoVersion(), runtime.GOOS, runtime.GOARCH, runtime.NumCPU(), diff --git a/version.go b/version.go index d9442aa..b95c3ea 100644 --- a/version.go +++ b/version.go @@ -6,8 +6,10 @@ import ( "fmt" "io" "os" + "os/exec" path "path/filepath" "regexp" + "strings" ) var cmdVersion = &Command{ @@ -114,3 +116,16 @@ func getBeegoVersion() string { } return "Beego not installed. Please install it first: https://github.com/astaxie/beego" } + +func getGoVersion() string { + var ( + cmdOut []byte + err error + ) + + if cmdOut, err = exec.Command("go", "version").Output(); err != nil { + fmt.Fprintln(os.Stderr, "There was an error running go version command:", err) + os.Exit(2) + } + return strings.Split(string(cmdOut), " ")[2] +} From 0ccc6306b7def1bed1eb70d7080e8c7f975e76ef Mon Sep 17 00:00:00 2001 From: Laoliu Date: Tue, 1 Nov 2016 18:59:32 +0800 Subject: [PATCH 18/37] Update hprose 2.0 --- hproseapp.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/hproseapp.go b/hproseapp.go index e6f7e1c..82d3dad 100644 --- a/hproseapp.go +++ b/hproseapp.go @@ -10,7 +10,7 @@ * * * Build rpc application use Hprose base on beego * * * - * LastModified: Oct 13, 2014 * + * LastModified: Oct 31, 2016 * * Author: Liu jian * * * \**********************************************************/ @@ -63,16 +63,41 @@ EnableDocs = true var hproseMaingo = `package main import ( + "fmt" + "reflect" + "{{.Appname}}/models" - "github.com/hprose/hprose-go/hprose" + "github.com/hprose/hprose-golang/rpc" "github.com/astaxie/beego" ) +func logInvokeHandler( + name string, + args []reflect.Value, + context rpc.Context, + next rpc.NextInvokeHandler) (results []reflect.Value, err error) { + fmt.Printf("%s(%v) = ", name, args) + results, err = next(name, args, context) + fmt.Printf("%v %v\r\n", results, err) + return +} + func main() { - service := hprose.NewHttpService() + // Create WebSocketServer + // service := rpc.NewWebSocketService() + + // Create Http Server + service := rpc.NewHTTPService() + + // Use Logger Middleware + service.AddInvokeHandler(logInvokeHandler) + + // Publish Functions service.AddFunction("AddOne", models.AddOne) service.AddFunction("GetOne", models.GetOne) + + // Start Service beego.Handler("/", service) beego.Run() } @@ -81,8 +106,11 @@ func main() { var hproseMainconngo = `package main import ( + "fmt" + "reflect" + "{{.Appname}}/models" - "github.com/hprose/hprose-go/hprose" + "github.com/hprose/hprose-golang/rpc" "github.com/astaxie/beego" "github.com/astaxie/beego/orm" @@ -93,9 +121,30 @@ func init() { orm.RegisterDataBase("default", "{{.DriverName}}", "{{.conn}}") } +func logInvokeHandler( + name string, + args []reflect.Value, + context rpc.Context, + next rpc.NextInvokeHandler) (results []reflect.Value, err error) { + fmt.Printf("%s(%v) = ", name, args) + results, err = next(name, args, context) + fmt.Printf("%v %v\r\n", results, err) + return +} + func main() { - service := hprose.NewHttpService() + // Create WebSocketServer + // service := rpc.NewWebSocketService() + + // Create Http Server + service := rpc.NewHTTPService() + + // Use Logger Middleware + service.AddInvokeHandler(logInvokeHandler) + {{HproseFunctionList}} + + // Start Service beego.Handler("/", service) beego.Run() } From 3199a019d76a5ee27b4a08d931f5a42385979d3c Mon Sep 17 00:00:00 2001 From: Zhang Qiang Date: Sat, 29 Oct 2016 22:36:41 +0800 Subject: [PATCH 19/37] Swagger: support default value columns for @Param This patch support default value after 'type' column if total columns is six. This enhancement also is compatiable with old five columns. --- g_docs.go | 85 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/g_docs.go b/g_docs.go index 6f78329..6088781 100644 --- a/g_docs.go +++ b/g_docs.go @@ -557,10 +557,15 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat para.Format = paraFormat } } - if len(p) > 4 { + switch len(p) { + case 5: para.Required, _ = strconv.ParseBool(p[3]) para.Description = strings.Trim(p[4], `" `) - } else { + case 6: + para.Default = str2RealType(p[3], para.Type) + para.Required, _ = strconv.ParseBool(p[4]) + para.Description = strings.Trim(p[5], `" `) + default: para.Description = strings.Trim(p[3], `" `) } opts.Parameters = append(opts.Parameters, para) @@ -646,16 +651,12 @@ func getparams(str string) []string { var j int var start bool var r []string - for i, c := range []rune(str) { - if unicode.IsSpace(c) { + var quoted int8 + for _, c := range []rune(str) { + if unicode.IsSpace(c) && quoted == 0 { if !start { continue } else { - if j == 3 { - r = append(r, string(s)) - r = append(r, strings.TrimSpace((str[i+1:]))) - break - } start = false j++ r = append(r, string(s)) @@ -663,9 +664,17 @@ func getparams(str string) []string { continue } } + start = true + if c == '"' { + quoted ^= 1 + continue + } s = append(s, c) } + if len(s) > 0 { + r = append(r, string(s)) + } return r } @@ -769,42 +778,21 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string } var tagValues []string - var err error - + stag := reflect.StructTag(strings.Trim(field.Tag.Value, "`")) - + defaultValue := stag.Get("doc") - if defaultValue != ""{ + if defaultValue != "" { r, _ := regexp.Compile(`default\((.*)\)`) if r.MatchString(defaultValue) { res := r.FindStringSubmatch(defaultValue) - mp.Default = res[1] - switch realType{ - case "int","int64", "int32", "int16", "int8": - if mp.Default, err = strconv.Atoi(res[1]); err != nil{ - ColorLog("[WARN] Invalid default value type(%s): %s\n",realType, res[1]) - } + mp.Default = str2RealType(res[1], realType) - case "bool": - if mp.Default, err = strconv.ParseBool(res[1]); err != nil{ - ColorLog("[WARN] Invalid default value type(%s): %s\n",realType, res[1]) - } - case "float64": - if mp.Default, err = strconv.ParseFloat(res[1], 64); err != nil{ - ColorLog("[WARN] Invalid default value type(%s): %s\n",realType, res[1]) - } - case "float32": - if mp.Default, err = strconv.ParseFloat(res[1], 32); err != nil{ - ColorLog("[WARN] Invalid default value type(%s): %s\n",realType, res[1]) - } - default: - mp.Default = res[1] - } - }else{ + } else { ColorLog("[WARN] Invalid default value: %s\n", defaultValue) } } - + tag := stag.Get("json") if tag != "" { @@ -926,3 +914,28 @@ func urlReplace(src string) string { } return strings.Join(pt, "/") } + +func str2RealType(s string, typ string) interface{} { + var err error + var ret interface{} + + switch typ { + case "int", "int64", "int32", "int16", "int8": + ret, err = strconv.Atoi(s) + case "bool": + ret, err = strconv.ParseBool(s) + case "float64": + ret, err = strconv.ParseFloat(s, 64) + case "float32": + ret, err = strconv.ParseFloat(s, 32) + default: + return s + } + + if err != nil { + ColorLog("[WARN] Invalid default value type(%s): %s\n", typ, s) + return s + } + + return ret +} From 440c5e967c0a8d286fd338f741fc7d8cf0c595b0 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Thu, 3 Nov 2016 16:41:51 +0100 Subject: [PATCH 20/37] Rewrite configuration loading Rewrited loadConfig() function to walk the project directory looking for bee.json and Beefile. If no configuration file found, it fallbacks to the default configuration. In case of an error, a log message is printed to the console. --- conf.go | 103 ++++++++++++++++++++++++++++++++++++++++++-------------- run.go | 2 +- 2 files changed, 78 insertions(+), 27 deletions(-) diff --git a/conf.go b/conf.go index b26f26b..68422bb 100644 --- a/conf.go +++ b/conf.go @@ -19,6 +19,9 @@ import ( "io/ioutil" "os" + "io" + "path/filepath" + "gopkg.in/yaml.v2" ) @@ -75,42 +78,58 @@ var conf struct { } // loadConfig loads customized configuration. -func loadConfig() error { - foundConf := false - f, err := os.Open("bee.json") - if err == nil { - defer f.Close() - ColorLog("[INFO] Detected bee.json\n") - d := json.NewDecoder(f) - err = d.Decode(&conf) +func loadConfig() (err error) { + err = filepath.Walk(".", func(path string, fileInfo os.FileInfo, err error) error { if err != nil { - return err + return nil } - foundConf = true - } - byml, erryml := ioutil.ReadFile("Beefile") - if erryml == nil { - ColorLog("[INFO] Detected Beefile\n") - err = yaml.Unmarshal(byml, &conf) - if err != nil { - return err + + if fileInfo.IsDir() { + return nil } - foundConf = true - } - if !foundConf { - // Use default. + + if fileInfo.Name() == "bee.json" { + ColorLog("[INFO] Loading configuration from 'bee.json'...\n") + err = parseJSON(path, conf) + if err != nil { + ColorLog("[ERRO] Failed to parse JSON file: %v\n", err) + return err + } + return io.EOF + } + + if fileInfo.Name() == "Beefile" { + ColorLog("[INFO] Loading configuration from 'Beefile'...\n") + err = parseYAML(path, conf) + if err != nil { + ColorLog("[ERRO] Failed to parse YAML file: %v\n", err) + return err + } + return io.EOF + } + return nil + }) + + // In case no configuration file found or an error different than io.EOF, + // fallback to default configuration + if err != io.EOF { + ColorLog("[INFO] Loading default configuration...\n") err = json.Unmarshal([]byte(defaultConf), &conf) if err != nil { - return err + return } } - // Check format version. + + // No need to return io.EOF error + err = nil + + // Check format version if conf.Version != ConfVer { - ColorLog("[WARN] Your bee.json is out-of-date, please update!\n") + ColorLog("[WARN] Your bee.json is outdated. Please do consider updating it.\n") ColorLog("[HINT] Compare bee.json under bee source code path and yours\n") } - // Set variables. + // Set variables if len(conf.DirStruct.Controllers) == 0 { conf.DirStruct.Controllers = "controllers" } @@ -118,7 +137,39 @@ func loadConfig() error { conf.DirStruct.Models = "models" } - // Append watch exts. + // Append watch exts watchExts = append(watchExts, conf.WatchExt...) + return +} + +func parseJSON(path string, v interface{}) error { + var ( + data []byte + err error + ) + data, err = ioutil.ReadFile(path) + if err != nil { + return err + } + err = json.Unmarshal(data, &v) + if err != nil { + return err + } + return nil +} + +func parseYAML(path string, v interface{}) error { + var ( + data []byte + err error + ) + data, err = ioutil.ReadFile(path) + if err != nil { + return err + } + err = yaml.Unmarshal(data, &v) + if err != nil { + return err + } return nil } diff --git a/run.go b/run.go index b84e199..8f4d3a3 100644 --- a/run.go +++ b/run.go @@ -115,7 +115,7 @@ func runApp(cmd *Command, args []string) int { err := loadConfig() if err != nil { - ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err) + ColorLog("[ERRO] Failed to load configuration [ %s ]\n", err) } var paths []string From f7001294a9f6daeb240a73e16d93723bd2ac8a2b Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 9 Nov 2016 20:17:55 +0800 Subject: [PATCH 21/37] fix #312 --- new.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/new.go b/new.go index e0ba92f..2f9e406 100644 --- a/new.go +++ b/new.go @@ -171,13 +171,13 @@ func init() { } -// TestMain is a sample to run an endpoint test -func TestMain(t *testing.T) { +// TestBeego is a sample to run an endpoint test +func TestBeego(t *testing.T) { r, _ := http.NewRequest("GET", "/", nil) w := httptest.NewRecorder() beego.BeeApp.Handlers.ServeHTTP(w, r) - beego.Trace("testing", "TestMain", "Code[%d]\n%s", w.Code, w.Body.String()) + beego.Trace("testing", "TestBeego", "Code[%d]\n%s", w.Code, w.Body.String()) Convey("Subject: Test Station Endpoint\n", t, func() { Convey("Status Code Should Be 200", func() { From 04c0f9d7b410afff701118f1f09fe850c148a9da Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 10 Nov 2016 16:51:50 +0800 Subject: [PATCH 22/37] output error --- g_docs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/g_docs.go b/g_docs.go index 6088781..d7216cd 100644 --- a/g_docs.go +++ b/g_docs.go @@ -110,7 +110,7 @@ func parsePackageFromDir(path string) { }, parser.ParseComments) if err != nil { - ColorLog("[ERRO] the model %s parser.ParseDir error\n", path) + ColorLog("[ERRO] the model %s parser.ParseDir error: %s\n", path, err) os.Exit(1) } for k, v := range folderPkgs { @@ -357,7 +357,7 @@ func analisyscontrollerPkg(localName, pkgpath string) { }, parser.ParseComments) if err != nil { - ColorLog("[ERRO] the %s pkg parser.ParseDir error\n", pkgpath) + ColorLog("[ERRO] the %s pkg parser.ParseDir error: %s\n", pkgpath, err) os.Exit(1) } for _, pkg := range astPkgs { From b4e13b55298ac2ca65e93c12ef718a93b9f6854a Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 13 Nov 2016 13:46:32 +0100 Subject: [PATCH 23/37] Logging infrastructure for Bee logger.go implements the logging infrastructure for Bee. The end user can set the preferred backend (that implements io.Writer). It supports multiple logging levels, and logs are sent to the output colored based on each level. Debug logs are treated differently, i.e. they will be sent to the output unless DEBUG_ENABLED is set to "1". --- logger.go | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 logger.go diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..567b073 --- /dev/null +++ b/logger.go @@ -0,0 +1,260 @@ +// 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 + +import ( + "errors" + "fmt" + "io" + "os" + "path/filepath" + "runtime" + "sync" + "sync/atomic" + "text/template" +) + +var errInvalidLogLevel = errors.New("logger: invalid log level") + +const ( + levelCritical = iota + levelFatal + levelSuccess + levelHint + levelDebug + levelInfo + levelWarn + levelError +) + +var ( + sequenceNo uint64 + logger *BeeLogger +) + +// BeeLogger logs logging records to the specified io.Writer +type BeeLogger struct { + mu sync.Mutex + output io.Writer +} + +// LogRecord represents a log record and contains the timestamp when the record +// was created, an increasing id, level and the actual formatted log line. +type LogRecord struct { + ID uint64 + Level string + Message string + Filename string + LineNo int +} + +var ( + logRecordTemplate *template.Template + debugLogRecordTemplate *template.Template + debugLogFormat string +) + +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) + + // Initialize the logger instance with a NewColorWriter output + logger = &BeeLogger{output: NewColorWriter(os.Stdout)} +} + +// SetOutput sets the logger output destination +func (l *BeeLogger) SetOutput(w io.Writer) { + l.mu.Lock() + defer l.mu.Unlock() + l.output = NewColorWriter(w) +} + +func (l *BeeLogger) getLevelTag(level int) string { + switch level { + case levelFatal: + return "FATAL " + case levelSuccess: + return "SUCCESS " + case levelHint: + return "HINT " + case levelDebug: + return "DEBUG " + case levelInfo: + return "INFO " + case levelWarn: + return "WARN " + case levelError: + return "ERROR " + case levelCritical: + return "CRITICAL" + default: + panic(errInvalidLogLevel) + } +} + +func (l *BeeLogger) getColorLevel(level int) string { + switch level { + case levelCritical: + return RedBold(l.getLevelTag(level)) + case levelFatal: + return RedBold(l.getLevelTag(level)) + case levelInfo: + return BlueBold(l.getLevelTag(level)) + case levelHint: + return CyanBold(l.getLevelTag(level)) + case levelDebug: + return YellowBold(l.getLevelTag(level)) + case levelError: + return RedBold(l.getLevelTag(level)) + case levelWarn: + return YellowBold(l.getLevelTag(level)) + case levelSuccess: + return GreenBold(l.getLevelTag(level)) + default: + panic(errInvalidLogLevel) + } +} + +// mustLog logs the message according to the specified level and arguments. +// It panics in case of an error. +func (l *BeeLogger) mustLog(level int, message string, args ...interface{}) { + // Create the logging record and pass into the output + record := LogRecord{ + ID: atomic.AddUint64(&sequenceNo, 1), + Level: l.getColorLevel(level), + Message: fmt.Sprintf(message, args...), + } + + err := logRecordTemplate.Execute(l.output, record) + MustCheck(err) +} + +// mustLogDebug logs a debug message only if debug mode +// is enabled. i.e. DEBUG_ENABLED="1" +func (l *BeeLogger) mustLogDebug(message string, args ...interface{}) { + if !IsDebugEnabled() { + return + } + + // Change the output to Stderr + l.SetOutput(os.Stderr) + + // Create the log record and Get the filename + // and the line number of the caller + _, file, line, _ := runtime.Caller(1) + record := LogRecord{ + ID: atomic.AddUint64(&sequenceNo, 1), + Level: l.getColorLevel(levelDebug), + Message: fmt.Sprintf(message, args...), + LineNo: line, + Filename: filepath.Base(file), + } + err := debugLogRecordTemplate.Execute(l.output, record) + MustCheck(err) +} + +// Debug outputs a debug log message +func (l *BeeLogger) Debug(message string) { + l.mustLogDebug(message) +} + +// Debugf outputs a formatted debug log message +func (l *BeeLogger) Debugf(message string, vars ...interface{}) { + l.mustLogDebug(message, vars...) +} + +// Info outputs an information log message +func (l *BeeLogger) Info(message string) { + l.mustLog(levelInfo, message) +} + +// Infof outputs a formatted information log message +func (l *BeeLogger) Infof(message string, vars ...interface{}) { + l.mustLog(levelInfo, message, vars...) +} + +// Warn outputs a warning log message +func (l *BeeLogger) Warn(message string) { + l.mustLog(levelWarn, message) +} + +// Warnf outputs a formatted warning log message +func (l *BeeLogger) Warnf(message string, vars ...interface{}) { + l.mustLog(levelWarn, message, vars...) +} + +// Error outputs an error log message +func (l *BeeLogger) Error(message string) { + l.mustLog(levelError, message) +} + +// Errorf outputs a formatted error log message +func (l *BeeLogger) Errorf(message string, vars ...interface{}) { + l.mustLog(levelError, message, vars...) +} + +// Fatal outputs a fatal log message and exists +func (l *BeeLogger) Fatal(message string) { + l.mustLog(levelFatal, message) + os.Exit(255) +} + +// Fatalf outputs a formatted log message and exists +func (l *BeeLogger) Fatalf(message string, vars ...interface{}) { + l.mustLog(levelFatal, message, vars...) + os.Exit(255) +} + +// Success outputs a success log message +func (l *BeeLogger) Success(message string) { + l.mustLog(levelSuccess, message) +} + +// Successf outputs a formatted success log message +func (l *BeeLogger) Successf(message string, vars ...interface{}) { + l.mustLog(levelSuccess, message, vars...) +} + +// Hint outputs a hint log message +func (l *BeeLogger) Hint(message string) { + l.mustLog(levelHint, message) +} + +// Hintf outputs a formatted hint log message +func (l *BeeLogger) Hintf(message string, vars ...interface{}) { + l.mustLog(levelHint, message, vars...) +} + +// Critical outputs a critical log message +func (l *BeeLogger) Critical(message string) { + l.mustLog(levelCritical, message) +} + +// Criticalf outputs a formatted critical log message +func (l *BeeLogger) Criticalf(message string, vars ...interface{}) { + l.mustLog(levelCritical, message, vars...) +} From af8e06cd536ff143b38208d905ed59ec8a83121a Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 13 Nov 2016 14:10:11 +0100 Subject: [PATCH 24/37] Added more helper functions to output colored text --- color.go | 79 +++++++++++++++++++++++++++++++++++++++++- colorwriter_windows.go | 2 +- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/color.go b/color.go index 583d6c6..3896fe9 100644 --- a/color.go +++ b/color.go @@ -14,7 +14,10 @@ package main -import "io" +import ( + "fmt" + "io" +) type outputMode int @@ -49,3 +52,77 @@ func NewModeColorWriter(w io.Writer, mode outputMode) io.Writer { } return w } + +func bold(message string) string { + return fmt.Sprintf("\x1b[1m%s\x1b[21m", message) +} + +// Cyan returns a cyan string +func Cyan(message string) string { + return fmt.Sprintf("\x1b[36m%s\x1b[0m", message) +} + +// Blue returns a blue string +func Blue(message string) string { + return fmt.Sprintf("\x1b[34m%s\x1b[0m", message) +} + +// Red returns a red string +func Red(message string) string { + return fmt.Sprintf("\x1b[31m%s\x1b[0m", message) +} + +// Green returns a green string +func Green(message string) string { + return fmt.Sprintf("\x1b[32m%s\x1b[0m", message) +} + +// Yellow returns a yellow string +func Yellow(message string) string { + return fmt.Sprintf("\x1b[33m%s\x1b[0m", message) +} + +// Gray returns a gray string +func Gray(message string) string { + return fmt.Sprintf("\x1b[37m%s\x1b[0m", message) +} + +// Magenta returns a magenta string +func Magenta(message string) string { + return fmt.Sprintf("\x1b[35m%s\x1b[0m", message) +} + +// CyanBold returns a cyan bold string +func CyanBold(message string) string { + return fmt.Sprintf("\x1b[36m%s\x1b[0m", bold(message)) +} + +// BlueBold returns a blue bold string +func BlueBold(message string) string { + return fmt.Sprintf("\x1b[34m%s\x1b[0m", bold(message)) +} + +// RedBold returns a red bold string +func RedBold(message string) string { + return fmt.Sprintf("\x1b[31m%s\x1b[0m", bold(message)) +} + +// GreenBold returns a green bold string +func GreenBold(message string) string { + return fmt.Sprintf("\x1b[32m%s\x1b[0m", bold(message)) +} + +// YellowBold returns a yellow bold string +func YellowBold(message string) string { + return fmt.Sprintf("\x1b[33m%s\x1b[0m", bold(message)) +} + +// GrayBold returns a gray bold string +func GrayBold(message string) string { + return fmt.Sprintf("\x1b[37m%s\x1b[0m", bold(message)) +} + +// MagentaBold returns a magenta bold string +func MagentaBold(message string) string { + return fmt.Sprintf("\x1b[35m%s\x1b[0m", bold(message)) +} diff --git a/colorwriter_windows.go b/colorwriter_windows.go index 0a8e88f..7c9e709 100644 --- a/colorwriter_windows.go +++ b/colorwriter_windows.go @@ -419,7 +419,7 @@ func (cw *colorWriter) Write(p []byte) (int, error) { } if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode { - nw, err = cw.w.Write(p[first:len(p)]) + nw, err = cw.w.Write(p[first:]) r += nw } From cb47cd011cc6bbc520fecf3eedf399e15ba4fea4 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 13 Nov 2016 14:12:38 +0100 Subject: [PATCH 25/37] Moved all utility functions to one place --- util.go | 176 ++++++++++++++++++-------------------------------------- 1 file changed, 55 insertions(+), 121 deletions(-) diff --git a/util.go b/util.go index 32db2f5..7229161 100644 --- a/util.go +++ b/util.go @@ -19,6 +19,7 @@ import ( "io/ioutil" "log" "os" + "os/exec" "path" "path/filepath" "regexp" @@ -37,124 +38,14 @@ func Go(f func() error) chan error { return ch } -// Debugf outputs a formtted debug message, when os.env DEBUG is set. -func Debugf(format string, a ...interface{}) { - if os.Getenv("DEBUG") != "" { - _, file, line, ok := runtime.Caller(1) - if !ok { - file = "" - line = -1 - } else { - file = filepath.Base(file) - } - fmt.Fprintf(os.Stderr, fmt.Sprintf("[debug] %s:%d %s\n", file, line, format), a...) - } +// Now returns the current local time in the specified layout +func Now(layout string) string { + return time.Now().Format(layout) } -const ( - Gray = uint8(iota + 90) - Red - Green - Yellow - Blue - Magenta - //NRed = uint8(31) // Normal - EndColor = "\033[0m" - - INFO = "INFO" - TRAC = "TRAC" - ERRO = "ERRO" - WARN = "WARN" - SUCC = "SUCC" -) - -// ColorLog colors log and print to stdout. -// See color rules in function 'ColorLogS'. -func ColorLog(format string, a ...interface{}) { - fmt.Print(ColorLogS(format, a...)) -} - -// ColorLogS colors log and return colored content. -// Log format: [ error ]. -// Level: TRAC -> blue; ERRO -> red; WARN -> Magenta; SUCC -> green; others -> default. -// Content: default; path: yellow; error -> red. -// Level has to be surrounded by "[" and "]". -// Highlights have to be surrounded by "# " and " #"(space), "#" will be deleted. -// Paths have to be surrounded by "( " and " )"(space). -// Errors have to be surrounded by "[ " and " ]"(space). -// Note: it hasn't support windows yet, contribute is welcome. -func ColorLogS(format string, a ...interface{}) string { - log := fmt.Sprintf(format, a...) - - var clog string - - if runtime.GOOS != "windows" { - // Level. - i := strings.Index(log, "]") - if log[0] == '[' && i > -1 { - clog += "[" + getColorLevel(log[1:i]) + "]" - } - - log = log[i+1:] - - // Error. - log = strings.Replace(log, "[ ", fmt.Sprintf("[\033[%dm", Red), -1) - log = strings.Replace(log, " ]", EndColor+"]", -1) - - // Path. - log = strings.Replace(log, "( ", fmt.Sprintf("(\033[%dm", Yellow), -1) - log = strings.Replace(log, " )", EndColor+")", -1) - - // Highlights. - log = strings.Replace(log, "# ", fmt.Sprintf("\033[%dm", Gray), -1) - log = strings.Replace(log, " #", EndColor, -1) - - log = clog + log - - } else { - // Level. - i := strings.Index(log, "]") - if log[0] == '[' && i > -1 { - clog += "[" + log[1:i] + "]" - } - - log = log[i+1:] - - // Error. - log = strings.Replace(log, "[ ", "[", -1) - log = strings.Replace(log, " ]", "]", -1) - - // Path. - log = strings.Replace(log, "( ", "(", -1) - log = strings.Replace(log, " )", ")", -1) - - // Highlights. - log = strings.Replace(log, "# ", "", -1) - log = strings.Replace(log, " #", "", -1) - - log = clog + log - } - - return time.Now().Format("2006/01/02 15:04:05 ") + log -} - -// getColorLevel returns colored level string by given level. -func getColorLevel(level string) string { - level = strings.ToUpper(level) - switch level { - case INFO: - return fmt.Sprintf("\033[%dm%s\033[0m", Blue, level) - case TRAC: - return fmt.Sprintf("\033[%dm%s\033[0m", Blue, level) - case ERRO: - return fmt.Sprintf("\033[%dm%s\033[0m", Red, level) - case WARN: - return fmt.Sprintf("\033[%dm%s\033[0m", Magenta, level) - case SUCC: - return fmt.Sprintf("\033[%dm%s\033[0m", Green, level) - default: - return level - } +// EndLine returns the a newline escape character +func EndLine() string { + return "\n" } // IsExist returns whether a file or directory exists. @@ -210,8 +101,7 @@ func isBeegoProject(thePath string) bool { func SearchGOPATHs(app string) (bool, string, string) { gps := GetGOPATHs() if len(gps) == 0 { - ColorLog("[ERRO] Fail to start [ %s ]\n", "GOPATH environment variable is not set or empty") - os.Exit(2) + logger.Fatal("GOPATH environment variable is not set or empty") } // Lookup the application inside the user workspace(s) @@ -282,7 +172,7 @@ func snakeString(s string) string { } data = append(data, d) } - return strings.ToLower(string(data[:len(data)])) + return strings.ToLower(string(data[:])) } func camelString(s string) string { @@ -306,7 +196,25 @@ func camelString(s string) string { } data = append(data, d) } - return string(data[:len(data)]) + return string(data[:]) +} + +// camelCase converts a _ delimited string to camel case +// e.g. very_important_person => VeryImportantPerson +func camelCase(in string) string { + tokens := strings.Split(in, "_") + for i := range tokens { + tokens[i] = strings.Title(strings.Trim(tokens[i], " ")) + } + return strings.Join(tokens, "") +} + +// formatSourceCode formats source files +func formatSourceCode(filename string) { + cmd := exec.Command("gofmt", "-w", filename) + if err := cmd.Run(); err != nil { + logger.Warnf("Error while running gofmt: %s", err) + } } // The string flag list, implemented flag.Value interface @@ -324,7 +232,33 @@ func (s *strFlags) Set(value string) error { // CloseFile attempts to close the passed file // or panics with the actual error func CloseFile(f *os.File) { - if err := f.Close(); err != nil { + err := f.Close() + MustCheck(err) +} + +// MustCheck panics when the error is not nil +func MustCheck(err error) { + if err != nil { panic(err) } } + +func exitPrint(con string) { + fmt.Fprintln(os.Stderr, con) + os.Exit(2) +} + +// WriteToFile creates a file and writes content to it +func WriteToFile(filename, content string) { + f, err := os.Create(filename) + MustCheck(err) + defer CloseFile(f) + _, err = f.WriteString(content) + MustCheck(err) +} + +// IsDebugEnabled checks if DEBUG_ENABLED is set or not +func IsDebugEnabled() bool { + debugMode := os.Getenv("DEBUG_ENABLED") + return map[string]bool{"1": true, "0": false}[debugMode] +} From 0e54238559098c8c960193d4416b16ad7590c156 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 13 Nov 2016 15:06:34 +0100 Subject: [PATCH 26/37] Included leading zeros for the log record ID --- logger.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/logger.go b/logger.go index 567b073..290264b 100644 --- a/logger.go +++ b/logger.go @@ -52,7 +52,7 @@ type BeeLogger struct { // LogRecord represents a log record and contains the timestamp when the record // was created, an increasing id, level and the actual formatted log line. type LogRecord struct { - ID uint64 + ID string Level string Message string Filename string @@ -144,7 +144,7 @@ func (l *BeeLogger) getColorLevel(level int) string { func (l *BeeLogger) mustLog(level int, message string, args ...interface{}) { // Create the logging record and pass into the output record := LogRecord{ - ID: atomic.AddUint64(&sequenceNo, 1), + ID: fmt.Sprintf("%04d", atomic.AddUint64(&sequenceNo, 1)), Level: l.getColorLevel(level), Message: fmt.Sprintf(message, args...), } @@ -167,7 +167,7 @@ func (l *BeeLogger) mustLogDebug(message string, args ...interface{}) { // and the line number of the caller _, file, line, _ := runtime.Caller(1) record := LogRecord{ - ID: atomic.AddUint64(&sequenceNo, 1), + ID: fmt.Sprintf("%04d", atomic.AddUint64(&sequenceNo, 1)), Level: l.getColorLevel(levelDebug), Message: fmt.Sprintf(message, args...), LineNo: line, From cf7aef47f054e528658035f5236c40cb661cc890 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 13 Nov 2016 15:14:48 +0100 Subject: [PATCH 27/37] Implementing the new logging infrastructure Moved logging to the new logging infrastructure by removing the use of ColorLog() function. Added more documentation. Also fixed some typos in comments and function names. --- apiapp.go | 23 +++--- autorouter.go | 29 +++---- bale.go | 39 +++++---- banner.go | 21 +---- bee.go | 2 + code.go | 4 +- conf.go | 18 ++--- fix.go | 21 ++--- g.go | 63 +++++++-------- g_appcode.go | 196 ++++++++++++++++++++------------------------- g_controllers.go | 15 ++-- g_docs.go | 75 ++++++++--------- g_hproseappcode.go | 32 +++----- g_migration.go | 17 ++-- g_model.go | 13 ++- g_scaffold.go | 12 +-- g_views.go | 17 ++-- hproseapp.go | 19 +++-- migrate.go | 119 ++++++++++++--------------- new.go | 20 ++--- pack.go | 35 ++++---- run.go | 34 ++++---- rundocs.go | 31 +++---- test.go | 26 +++--- version.go | 11 ++- watch.go | 54 +++++++------ 26 files changed, 421 insertions(+), 525 deletions(-) diff --git a/apiapp.go b/apiapp.go index 892719b..6ecd3ae 100644 --- a/apiapp.go +++ b/apiapp.go @@ -549,8 +549,7 @@ func createapi(cmd *Command, args []string) int { w := NewColorWriter(os.Stdout) if len(args) < 1 { - ColorLog("[ERRO] Argument [appname] is missing\n") - os.Exit(2) + logger.Fatal("Argument [appname] is missing") } if len(args) > 1 { @@ -568,7 +567,7 @@ func createapi(cmd *Command, args []string) int { if conn == "" { } - ColorLog("[INFO] Creating API...\n") + 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") @@ -599,9 +598,9 @@ func createapi(cmd *Command, args []string) int { -1, ), ) - ColorLog("[INFO] Using '%s' as 'driver'\n", driver) - ColorLog("[INFO] Using '%s' as 'conn'\n", conn) - ColorLog("[INFO] Using '%s' as 'tables'\n", tables) + logger.Infof("Using '%s' as 'driver'", driver) + logger.Infof("Using '%s' as 'conn'", conn) + logger.Infof("Using '%s' as 'tables'", tables) generateAppcode(string(driver), string(conn), "3", string(tables), apppath) } else { os.Mkdir(path.Join(apppath, "models"), 0755) @@ -635,15 +634,14 @@ func createapi(cmd *Command, args []string) int { WriteToFile(path.Join(apppath, "main.go"), strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1)) } - ColorLog("[SUCC] New API successfully created!\n") + logger.Success("New API successfully created!") return 0 } func checkEnv(appname string) (apppath, packpath string, err error) { gps := GetGOPATHs() if len(gps) == 0 { - ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty") - os.Exit(2) + logger.Fatal("GOPATH environment variable is not set or empty") } currpath, _ := os.Getwd() currpath = path.Join(currpath, appname) @@ -658,15 +656,16 @@ func checkEnv(appname string) (apppath, packpath string, err error) { // In case of multiple paths in the GOPATH, by default // we use the first path gopath := gps[0] - ColorLog("[%s]You current workdir is not a $GOPATH/src, bee will create the application in GOPATH: %s\n", WARN, gopath) - Debugf("GOPATH: %s", gopath) + + logger.Warn("You current workdir is not inside $GOPATH/src") + logger.Debugf("GOPATH: %s", gopath) gosrcpath := path.Join(gopath, "src") apppath = path.Join(gosrcpath, appname) if _, e := os.Stat(apppath); os.IsNotExist(e) == false { err = fmt.Errorf("Cannot create application without removing '%s' first.", apppath) - ColorLog("[ERRO] Path '%s' already exists\n", apppath) + logger.Errorf("Path '%s' already exists", apppath) return } packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(path.Separator)), "/") diff --git a/autorouter.go b/autorouter.go index 1273551..259f5da 100644 --- a/autorouter.go +++ b/autorouter.go @@ -66,7 +66,7 @@ func getControllerInfo(path string) (map[string][]string, error) { files := make([]*source, 0, len(fis)) for _, fi := range fis { - // Only load go files. + // Only load Go files if strings.HasSuffix(fi.Name(), ".go") { f, err := os.Open(path + "/" + fi.Name()) if err != nil { @@ -107,7 +107,7 @@ func getControllerInfo(path string) (map[string][]string, error) { return cm, nil } -// A source describles a source code file. +// source represents a source code file. type source struct { name string data []byte @@ -120,27 +120,25 @@ func (s *source) ModTime() time.Time { return time.Time{} } func (s *source) IsDir() bool { return false } func (s *source) Sys() interface{} { return nil } -// A routerWalker holds the state used when building the documentation. +// routerWalker holds the state used when building the documentation. type routerWalker struct { pdoc *Package - srcs map[string]*source // Source files. + srcs map[string]*source // Source files fset *token.FileSet - buf []byte // scratch space for printNode method. + buf []byte // scratch space for printNode method } // Package represents full information and documentation for a package. type Package struct { ImportPath string - - // Top-level declarations. - Types []*Type + Types []*Type // Top-level declarations } // Type represents structs and interfaces. type Type struct { - Name string // Type name. + Name string // Type name Decl string - Methods []*Func // Exported methods. + Methods []*Func // Exported methods } // Func represents functions @@ -150,7 +148,7 @@ type Func struct { // build generates data from source files. func (w *routerWalker) build(srcs []*source) (*Package, error) { - // Add source files to walker, I skipped references here. + // Add source files to walker, I skipped references here w.srcs = make(map[string]*source) for _, src := range srcs { w.srcs[src.name] = src @@ -158,7 +156,7 @@ func (w *routerWalker) build(srcs []*source) (*Package, error) { w.fset = token.NewFileSet() - // Find the package and associated files. + // Find the package and associated files ctxt := gobuild.Context{ GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, @@ -174,7 +172,7 @@ func (w *routerWalker) build(srcs []*source) (*Package, error) { } bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0) - // Continue if there are no Go source files; we still want the directory info. + // Continue if there are no Go source files; we still want the directory info _, nogo := err.(*gobuild.NoGoError) if err != nil { if nogo { @@ -272,9 +270,8 @@ func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, e name = strings.TrimPrefix(name, "biogo.") // It's also common for the last element of the path to contain an - // extra "go" prefix, but not always. TODO: examine unresolved ids to - // detect when trimming the "go" prefix is appropriate. - + // extra "go" prefix, but not always. + // TODO: examine unresolved ids to detect when trimming the "go" prefix is appropriate. pkg = ast.NewObj(ast.Pkg, name) pkg.Data = ast.NewScope(nil) imports[path] = pkg diff --git a/bale.go b/bale.go index 2bf14f9..df599b5 100644 --- a/bale.go +++ b/bale.go @@ -50,19 +50,19 @@ func runBale(cmd *Command, args []string) int { err := loadConfig() if err != nil { - ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err) + logger.Fatalf("Failed to load configuration: %s", err) } os.RemoveAll("bale") os.Mkdir("bale", os.ModePerm) - // Pack and compress data. + // Pack and compress data for _, p := range conf.Bale.Dirs { if !isExist(p) { - ColorLog("[WARN] Skipped directory( %s )\n", p) + logger.Warnf("Skipped directory: %s", p) continue } - ColorLog("[INFO] Packaging directory( %s )\n", p) + logger.Infof("Packaging directory: %s", p) filepath.Walk(p, walkFn) } @@ -74,22 +74,21 @@ func runBale(cmd *Command, args []string) int { fw, err := os.Create("bale.go") if err != nil { - ColorLog("[ERRO] Fail to create file[ %s ]\n", err) - os.Exit(2) + logger.Fatalf("Failed to create file: %s", err) } defer fw.Close() _, err = fw.Write(buf.Bytes()) if err != nil { - ColorLog("[ERRO] Fail to write data[ %s ]\n", err) - os.Exit(2) + logger.Fatalf("Failed to write data: %s", err) } - ColorLog("[SUCC] Baled resources successfully!\n") + logger.Success("Baled resources successfully!") return 0 } const ( + // BaleHeader ... BaleHeader = `package main import( @@ -150,14 +149,13 @@ func walkFn(resPath string, info os.FileInfo, err error) error { return nil } - // Open resource files. + // Open resource files fr, err := os.Open(resPath) if err != nil { - ColorLog("[ERRO] Fail to read file[ %s ]\n", err) - os.Exit(2) + logger.Fatalf("Failed to read file: %s", err) } - // Convert path. + // Convert path resPath = strings.Replace(resPath, "_", "_0_", -1) resPath = strings.Replace(resPath, ".", "_1_", -1) resPath = strings.Replace(resPath, "-", "_2_", -1) @@ -168,19 +166,18 @@ func walkFn(resPath string, info os.FileInfo, err error) error { } resPath = strings.Replace(resPath, sep, "_4_", -1) - // Create corresponding Go source files. + // Create corresponding Go source files os.MkdirAll(path.Dir(resPath), os.ModePerm) fw, err := os.Create("bale/" + resPath + ".go") if err != nil { - ColorLog("[ERRO] Fail to create file[ %s ]\n", err) - os.Exit(2) + logger.Fatalf("Failed to create file: %s", err) } defer fw.Close() - // Write header. + // Write header fmt.Fprintf(fw, Header, resPath) - // Copy and compress data. + // Copy and compress data gz := gzip.NewWriter(&ByteWriter{Writer: fw}) io.Copy(gz, fr) gz.Close() @@ -202,6 +199,7 @@ func filterSuffix(name string) bool { } const ( + // Header ... Header = `package bale import( @@ -212,6 +210,7 @@ import( func R%s() []byte { gz, err := gzip.NewReader(bytes.NewBuffer([]byte{` + // Footer ... Footer = ` })) @@ -229,6 +228,7 @@ func R%s() []byte { var newline = []byte{'\n'} +// ByteWriter ... type ByteWriter struct { io.Writer c int @@ -244,12 +244,9 @@ func (w *ByteWriter) Write(p []byte) (n int, err error) { w.Writer.Write(newline) w.c = 0 } - fmt.Fprintf(w.Writer, "0x%02x,", p[n]) w.c++ } - n++ - return } diff --git a/banner.go b/banner.go index fefa37b..b9a4789 100644 --- a/banner.go +++ b/banner.go @@ -6,7 +6,6 @@ import ( "os" "runtime" "text/template" - "time" ) type vars struct { @@ -21,25 +20,17 @@ type vars struct { BeegoVersion string } -// Now returns the current local time in the specified layout -func Now(layout string) string { - return time.Now().Format(layout) -} - // InitBanner loads the banner and prints it to output // All errors are ignored, the application will not // print the banner in case of error. func InitBanner(out io.Writer, in io.Reader) { if in == nil { - ColorLog("[ERRO] The input is nil\n") - os.Exit(2) + logger.Fatal("The input is nil") } banner, err := ioutil.ReadAll(in) if err != nil { - ColorLog("[ERRO] Error trying to read the banner\n") - ColorLog("[HINT] %v\n", err) - os.Exit(2) + logger.Fatalf("Error while trying to read the banner: %s", err) } show(out, string(banner)) @@ -51,9 +42,7 @@ func show(out io.Writer, content string) { Parse(content) if err != nil { - ColorLog("[ERRO] Cannot parse the banner template\n") - ColorLog("[HINT] %v\n", err) - os.Exit(2) + logger.Fatalf("Cannot parse the banner template: %s", err) } err = t.Execute(out, vars{ @@ -67,7 +56,5 @@ func show(out io.Writer, content string) { version, getBeegoVersion(), }) - if err != nil { - panic(err) - } + MustCheck(err) } diff --git a/bee.go b/bee.go index f89e791..4ae5111 100644 --- a/bee.go +++ b/bee.go @@ -27,6 +27,7 @@ import ( const version = "1.5.2" +// Command is the unit of execution type Command struct { // Run runs the command. // The args are the arguments after the command name. @@ -60,6 +61,7 @@ func (c *Command) Name() string { return name } +// 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))) diff --git a/code.go b/code.go index c341a74..335b021 100644 --- a/code.go +++ b/code.go @@ -109,12 +109,12 @@ func (v *annotationVisitor) Visit(n ast.Node) ast.Visitor { v.ignoreName() ast.Walk(v, n.Type) case *ast.Field: - for _ = range n.Names { + for range n.Names { v.ignoreName() } ast.Walk(v, n.Type) case *ast.ValueSpec: - for _ = range n.Names { + for range n.Names { v.add(AnchorAnnotation, "") } if n.Type != nil { diff --git a/conf.go b/conf.go index 68422bb..e02d701 100644 --- a/conf.go +++ b/conf.go @@ -25,7 +25,7 @@ import ( "gopkg.in/yaml.v2" ) -const ConfVer = 0 +const confVer = 0 var defaultConf = `{ "version": 0, @@ -89,20 +89,20 @@ func loadConfig() (err error) { } if fileInfo.Name() == "bee.json" { - ColorLog("[INFO] Loading configuration from 'bee.json'...\n") + logger.Info("Loading configuration from 'bee.json'...") err = parseJSON(path, conf) if err != nil { - ColorLog("[ERRO] Failed to parse JSON file: %v\n", err) + logger.Errorf("Failed to parse JSON file: %s", err) return err } return io.EOF } if fileInfo.Name() == "Beefile" { - ColorLog("[INFO] Loading configuration from 'Beefile'...\n") + logger.Info("Loading configuration from 'Beefile'...") err = parseYAML(path, conf) if err != nil { - ColorLog("[ERRO] Failed to parse YAML file: %v\n", err) + logger.Errorf("Failed to parse YAML file: %s", err) return err } return io.EOF @@ -113,7 +113,7 @@ func loadConfig() (err error) { // In case no configuration file found or an error different than io.EOF, // fallback to default configuration if err != io.EOF { - ColorLog("[INFO] Loading default configuration...\n") + logger.Info("Loading default configuration...") err = json.Unmarshal([]byte(defaultConf), &conf) if err != nil { return @@ -124,9 +124,9 @@ func loadConfig() (err error) { err = nil // Check format version - if conf.Version != ConfVer { - ColorLog("[WARN] Your bee.json is outdated. Please do consider updating it.\n") - ColorLog("[HINT] Compare bee.json under bee source code path and yours\n") + if conf.Version != confVer { + logger.Warn("Your configuration file is outdated. Please do consider updating it.") + logger.Hint("Check the latest version of bee's configuration file.") } // Set variables diff --git a/fix.go b/fix.go index e4adde6..60e166b 100644 --- a/fix.go +++ b/fix.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "go/parser" "go/token" "io/ioutil" @@ -8,7 +9,6 @@ import ( "path/filepath" "regexp" "strings" - "fmt" ) var cmdFix = &Command{ @@ -28,11 +28,12 @@ func init() { func runFix(cmd *Command, args []string) int { ShowShortVersionBanner() - ColorLog("[INFO] Upgrading the application...\n") + logger.Info("Upgrading the application...") dir, err := os.Getwd() if err != nil { - ColorLog("[ERRO] GetCurrent Path:%s\n", err) + logger.Fatalf("Error while getting the current working directory: %s", err) } + filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if info.IsDir() { if strings.HasPrefix(info.Name(), ".") { @@ -49,11 +50,11 @@ func runFix(cmd *Command, args []string) int { err = fixFile(path) fmt.Println("\tfix\t", path) if err != nil { - ColorLog("[ERRO] Could not fix file: %s\n", err) + logger.Errorf("Could not fix file: %s", err) } return err }) - ColorLog("[INFO] Upgrade done!\n") + logger.Success("Upgrade done!") return 0 } @@ -167,18 +168,18 @@ func fixFile(file string) error { } fixed := rp.Replace(string(content)) - // forword the RequestBody from the replace + // Forword the RequestBody from the replace // "Input.Request", "Input.Context.Request", fixed = strings.Replace(fixed, "Input.Context.RequestBody", "Input.RequestBody", -1) - // regexp replace + // Regexp replace pareg := regexp.MustCompile(`(Input.Params\[")(.*)("])`) fixed = pareg.ReplaceAllString(fixed, "Input.Param(\"$2\")") pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])(\s)(=)(\s)(.*)`) fixed = pareg.ReplaceAllString(fixed, "Input.SetData(\"$2\", $7)") pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])`) fixed = pareg.ReplaceAllString(fixed, "Input.Data(\"$2\")") - // fix the cache object Put method + // Fix the cache object Put method pareg = regexp.MustCompile(`(\.Put\(\")(.*)(\",)(\s)(.*)(,\s*)([^\*.]*)(\))`) if pareg.MatchString(fixed) && strings.HasSuffix(file, ".go") { fixed = pareg.ReplaceAllString(fixed, ".Put(\"$2\", $5, $7*time.Second)") @@ -199,11 +200,11 @@ func fixFile(file string) error { fixed = strings.Replace(fixed, "import (", "import (\n\t\"time\"", 1) } } - // replace the v.Apis in docs.go + // Replace the v.Apis in docs.go if strings.Contains(file, "docs.go") { fixed = strings.Replace(fixed, "v.Apis", "v.APIs", -1) } - // replace the config file + // Replace the config file if strings.HasSuffix(file, ".conf") { fixed = strings.Replace(fixed, "HttpCertFile", "HTTPSCertFile", -1) fixed = strings.Replace(fixed, "HttpKeyFile", "HTTPSKeyFile", -1) diff --git a/g.go b/g.go index bc4b123..e3a18d2 100644 --- a/g.go +++ b/g.go @@ -81,29 +81,28 @@ func generateCode(cmd *Command, args []string) int { currpath, _ := os.Getwd() if len(args) < 1 { - ColorLog("[ERRO] command is missing\n") - os.Exit(2) + logger.Fatal("Command is missing") } gps := GetGOPATHs() if len(gps) == 0 { - ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty") - os.Exit(2) + logger.Fatal("GOPATH environment variable is not set or empty") } + gopath := gps[0] - Debugf("GOPATH: %s", gopath) + + logger.Debugf("GOPATH: %s", gopath) gcmd := args[0] switch gcmd { case "scaffold": if len(args) < 2 { - ColorLog("[ERRO] Wrong number of arguments\n") - ColorLog("[HINT] Usage: bee generate scaffold [scaffoldname] [-fields=\"\"]\n") - os.Exit(2) + logger.Fatal("Wrong number of arguments. Run: bee help generate") } + // Load the configuration err := loadConfig() if err != nil { - ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err) + logger.Fatalf("Failed to load configuration: %s", err) } cmd.Flag.Parse(args[2:]) if driver == "" { @@ -119,19 +118,18 @@ func generateCode(cmd *Command, args []string) int { } } if fields == "" { - ColorLog("[ERRO] Wrong number of arguments\n") - ColorLog("[HINT] Usage: bee generate scaffold [scaffoldname] [-fields=\"title:string,body:text\"]\n") - os.Exit(2) + logger.Hint("fields option should not be empty, i.e. -fields=\"title:string,body:text\"") + logger.Fatal("Wrong number of arguments. Run: bee help generate") } sname := args[1] generateScaffold(sname, fields.String(), currpath, driver.String(), conn.String()) case "docs": generateDocs(currpath) case "appcode": - // load config + // Load the configuration err := loadConfig() if err != nil { - ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err) + logger.Fatalf("Failed to load configuration: %s", err) } cmd.Flag.Parse(args[1:]) if driver == "" { @@ -153,20 +151,20 @@ func generateCode(cmd *Command, args []string) int { if level == "" { level = "3" } - ColorLog("[INFO] Using '%s' as 'driver'\n", driver) - ColorLog("[INFO] Using '%s' as 'conn'\n", conn) - ColorLog("[INFO] Using '%s' as 'tables'\n", tables) - ColorLog("[INFO] Using '%s' as 'level'\n", level) + logger.Infof("Using '%s' as 'driver'", driver) + logger.Infof("Using '%s' as 'conn'", conn) + logger.Infof("Using '%s' as 'tables'", tables) + logger.Infof("Using '%s' as 'level'", level) generateAppcode(driver.String(), conn.String(), level.String(), tables.String(), currpath) case "migration": if len(args) < 2 { - ColorLog("[ERRO] Wrong number of arguments\n") - ColorLog("[HINT] Usage: bee generate migration [migrationname] [-fields=\"\"]\n") - os.Exit(2) + logger.Fatal("Wrong number of arguments. Run: bee help generate") } cmd.Flag.Parse(args[2:]) mname := args[1] - ColorLog("[INFO] Using '%s' as migration name\n", mname) + + logger.Infof("Using '%s' as migration name", mname) + upsql := "" downsql := "" if fields != "" { @@ -180,21 +178,16 @@ func generateCode(cmd *Command, args []string) int { cname := args[1] generateController(cname, currpath) } else { - ColorLog("[ERRO] Wrong number of arguments\n") - ColorLog("[HINT] Usage: bee generate controller [controllername]\n") - os.Exit(2) + logger.Fatal("Wrong number of arguments. Run: bee help generate") } case "model": if len(args) < 2 { - ColorLog("[ERRO] Wrong number of arguments\n") - ColorLog("[HINT] Usage: bee generate model [modelname] [-fields=\"\"]\n") - os.Exit(2) + logger.Fatal("Wrong number of arguments. Run: bee help generate") } cmd.Flag.Parse(args[2:]) if fields == "" { - ColorLog("[ERRO] Wrong number of arguments\n") - ColorLog("[HINT] Usage: bee generate model [modelname] [-fields=\"title:string,body:text\"]\n") - os.Exit(2) + logger.Hint("fields option should not be empty, i.e. -fields=\"title:string,body:text\"") + logger.Fatal("Wrong number of arguments. Run: bee help generate") } sname := args[1] generateModel(sname, fields.String(), currpath) @@ -203,13 +196,11 @@ func generateCode(cmd *Command, args []string) int { cname := args[1] generateView(cname, currpath) } else { - ColorLog("[ERRO] Wrong number of arguments\n") - ColorLog("[HINT] Usage: bee generate view [viewpath]\n") - os.Exit(2) + logger.Fatal("Wrong number of arguments. Run: bee help generate") } default: - ColorLog("[ERRO] Command is missing\n") + logger.Fatal("Command is missing") } - ColorLog("[SUCC] %s successfully generated!\n", strings.Title(gcmd)) + logger.Successf("%s successfully generated!", strings.Title(gcmd)) return 0 } diff --git a/g_appcode.go b/g_appcode.go index 4e90107..7778c82 100644 --- a/g_appcode.go +++ b/g_appcode.go @@ -18,7 +18,6 @@ import ( "database/sql" "fmt" "os" - "os/exec" "path" "path/filepath" "regexp" @@ -39,7 +38,7 @@ type DbTransformer interface { GetTableNames(conn *sql.DB) []string GetConstraints(conn *sql.DB, table *Table, blackList map[string]bool) GetColumns(conn *sql.DB, table *Table, blackList map[string]bool) - GetGoDataType(sqlType string) string + GetGoDataType(sqlType string) (string, error) } // MysqlDB is the MySQL version of DbTransformer @@ -265,9 +264,7 @@ func generateAppcode(driver, connStr, level, tables, currpath string) { case "3": mode = OModel | OController | ORouter default: - ColorLog("[ERRO] Invalid 'level' option: %s\n", level) - ColorLog("[HINT] Level must be either 1, 2 or 3\n") - os.Exit(2) + logger.Fatal("Invalid level value. Must be either \"1\", \"2\", or \"3\"") } var selectedTables map[string]bool if tables != "" { @@ -280,12 +277,9 @@ func generateAppcode(driver, connStr, level, tables, currpath string) { case "mysql": case "postgres": case "sqlite": - ColorLog("[ERRO] Generating app code from SQLite database is not supported yet.\n") - os.Exit(2) + logger.Fatal("Generating app code from SQLite database is not supported yet.") default: - ColorLog("[ERRO] Unknown database driver: %s\n", driver) - ColorLog("[HINT] Driver must be one of mysql, postgres or sqlite\n") - os.Exit(2) + logger.Fatal("Unknown database driver. Must be either \"mysql\", \"postgres\" or \"sqlite\"") } gen(driver, connStr, mode, selectedTables, currpath) } @@ -295,12 +289,11 @@ func generateAppcode(driver, connStr, level, tables, currpath string) { func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, apppath string) { db, err := sql.Open(dbms, connStr) if err != nil { - ColorLog("[ERRO] Could not connect to %s database: %s, %s\n", dbms, connStr, err) - os.Exit(2) + logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err) } defer db.Close() if trans, ok := dbDriver[dbms]; ok { - ColorLog("[INFO] Analyzing database tables...\n") + logger.Info("Analyzing database tables...") tableNames := trans.GetTableNames(db) tables := getTableObjects(tableNames, db, trans) mvcPath := new(MvcPath) @@ -311,25 +304,21 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, ap pkgPath := getPackagePath(apppath) writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) } else { - ColorLog("[ERRO] Generating app code from %s database is not supported yet.\n", dbms) - os.Exit(2) + logger.Fatalf("Generating app code from '%s' database is not supported yet.", dbms) } } -// getTables gets a list table names in current database +// GetTableNames returns a slice of table names in the current database func (*MysqlDB) GetTableNames(db *sql.DB) (tables []string) { rows, err := db.Query("SHOW TABLES") if err != nil { - ColorLog("[ERRO] Could not show tables\n") - ColorLog("[HINT] Check your connection string\n") - os.Exit(2) + logger.Fatalf("Could not show tables: %s", err) } defer rows.Close() for rows.Next() { var name string if err := rows.Scan(&name); err != nil { - ColorLog("[ERRO] Could not show tables\n") - os.Exit(2) + logger.Fatalf("Could not show tables: %s", err) } tables = append(tables, name) } @@ -358,8 +347,8 @@ func getTableObjects(tableNames []string, db *sql.DB, dbTransformer DbTransforme return } -// getConstraints gets primary key, unique key and foreign keys of a table from information_schema -// and fill in Table struct +// GetConstraints gets primary key, unique key and foreign keys of a table from +// information_schema and fill in the Table struct func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bool) { rows, err := db.Query( `SELECT @@ -372,14 +361,12 @@ func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bo c.table_schema = database() AND c.table_name = ? AND u.table_schema = database() AND u.table_name = ?`, table.Name, table.Name) // u.position_in_unique_constraint, if err != nil { - ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for PK/UK/FK information\n") - os.Exit(2) + logger.Fatal("Could not query INFORMATION_SCHEMA for PK/UK/FK information") } for rows.Next() { var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil { - ColorLog("[ERRO] Could not read INFORMATION_SCHEMA for PK/UK/FK information\n") - os.Exit(2) + logger.Fatal("Could not read INFORMATION_SCHEMA for PK/UK/FK information") } constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos := string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes), @@ -389,7 +376,7 @@ func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bo table.Pk = columnName } else { table.Pk = "" - // add table to blacklist so that other struct will not reference it, because we are not + // Add table to blacklist so that other struct will not reference it, because we are not // registering blacklisted tables blackList[table.Name] = true } @@ -406,11 +393,11 @@ func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bo } } -// getColumns retrieve columns details from information_schema -// and fill in the Column struct +// GetColumns retrieves columns details from +// information_schema and fill in the Column struct func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[string]bool) { // retrieve columns - colDefRows, _ := db.Query( + colDefRows, err := db.Query( `SELECT column_name, data_type, column_type, is_nullable, column_default, extra FROM @@ -418,20 +405,28 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin WHERE table_schema = database() AND table_name = ?`, table.Name) + if err != nil { + logger.Fatalf("Could not query the database: %s", err) + } defer colDefRows.Close() + for colDefRows.Next() { // datatype as bytes so that SQL values can be retrieved var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil { - ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for column information\n") - os.Exit(2) + logger.Fatal("Could not query INFORMATION_SCHEMA for column information") } colName, dataType, columnType, isNullable, columnDefault, extra := string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes) + // create a column col := new(Column) col.Name = camelCase(colName) - col.Type = mysqlDB.GetGoDataType(dataType) + col.Type, err = mysqlDB.GetGoDataType(dataType) + if err != nil { + logger.Fatalf("%s", err) + } + // Tag info tag := new(OrmTag) tag.Column = colName @@ -466,7 +461,10 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin if isSQLSignedIntType(dataType) { sign := extractIntSignness(columnType) if sign == "unsigned" && extra != "auto_increment" { - col.Type = mysqlDB.GetGoDataType(dataType + " " + sign) + col.Type, err = mysqlDB.GetGoDataType(dataType + " " + sign) + if err != nil { + logger.Fatalf("%s", err) + } } } if isSQLStringType(dataType) { @@ -500,15 +498,13 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin } // GetGoDataType maps an SQL data type to Golang data type -func (*MysqlDB) GetGoDataType(sqlType string) (goType string) { +func (*MysqlDB) GetGoDataType(sqlType string) (string, error) { var typeMapping = map[string]string{} typeMapping = typeMappingMysql if v, ok := typeMapping[sqlType]; ok { - return v + return v, nil } - ColorLog("[ERRO] data type (%s) not found!\n", sqlType) - os.Exit(2) - return goType + return "", fmt.Errorf("data type '%s' not found", sqlType) } // GetTableNames for PostgreSQL @@ -519,16 +515,14 @@ func (*PostgresDB) GetTableNames(db *sql.DB) (tables []string) { table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema')`) if err != nil { - ColorLog("[ERRO] Could not show tables: %s\n", err) - ColorLog("[HINT] Check your connection string\n") - os.Exit(2) + logger.Fatalf("Could not show tables: %s", err) } defer rows.Close() + for rows.Next() { var name string if err := rows.Scan(&name); err != nil { - ColorLog("[ERRO] Could not show tables\n") - os.Exit(2) + logger.Fatalf("Could not show tables: %s", err) } tables = append(tables, name) } @@ -558,14 +552,13 @@ func (*PostgresDB) GetConstraints(db *sql.DB, table *Table, blackList map[string AND u.table_name = $2`, table.Name, table.Name) // u.position_in_unique_constraint, if err != nil { - ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s", err) } + for rows.Next() { var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil { - ColorLog("[ERRO] Could not read INFORMATION_SCHEMA for PK/UK/FK information\n") - os.Exit(2) + logger.Fatalf("Could not read INFORMATION_SCHEMA for PK/UK/FK information: %s", err) } constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos := string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes), @@ -595,7 +588,7 @@ func (*PostgresDB) GetConstraints(db *sql.DB, table *Table, blackList map[string // GetColumns for PostgreSQL func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map[string]bool) { // retrieve columns - colDefRows, _ := db.Query( + colDefRows, err := db.Query( `SELECT column_name, data_type, @@ -614,20 +607,27 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map table_catalog = current_database() AND table_schema NOT IN ('pg_catalog', 'information_schema') AND table_name = $1`, table.Name) + if err != nil { + logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err) + } defer colDefRows.Close() + for colDefRows.Next() { // datatype as bytes so that SQL values can be retrieved var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil { - ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for column information\n") - os.Exit(2) + logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err) } colName, dataType, columnType, isNullable, columnDefault, extra := string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes) - // create a column + // Create a column col := new(Column) col.Name = camelCase(colName) - col.Type = postgresDB.GetGoDataType(dataType) + col.Type, err = postgresDB.GetGoDataType(dataType) + if err != nil { + logger.Fatalf("%s", err) + } + // Tag info tag := new(OrmTag) tag.Column = colName @@ -690,13 +690,11 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map } // GetGoDataType returns the Go type from the mapped Postgres type -func (*PostgresDB) GetGoDataType(sqlType string) (goType string) { +func (*PostgresDB) GetGoDataType(sqlType string) (string, error) { if v, ok := typeMappingPostgres[sqlType]; ok { - return v + return v, nil } - ColorLog("[ERRO] data type (%s) not found!\n", sqlType) - os.Exit(2) - return goType + return "", fmt.Errorf("data type '%s' not found", sqlType) } // deleteAndRecreatePaths removes several directories completely @@ -717,15 +715,15 @@ func createPaths(mode byte, paths *MvcPath) { // Newly geneated files will be inside these folders. func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { if (OModel & mode) == OModel { - ColorLog("[INFO] Creating model files...\n") + logger.Info("Creating model files...") writeModelFiles(tables, paths.ModelPath, selectedTables) } if (OController & mode) == OController { - ColorLog("[INFO] Creating controller files...\n") + logger.Info("Creating controller files...") writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath) } if (ORouter & mode) == ORouter { - ColorLog("[INFO] Creating router files...\n") + logger.Info("Creating router files...") writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath) } } @@ -746,21 +744,21 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo var f *os.File var err error if isExist(fpath) { - ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath) + logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) if askForConfirmation() { f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) if err != nil { - ColorLog("[WARN] %v\n", err) + logger.Warnf("%s", err) continue } } else { - ColorLog("[WARN] Skipped create file '%s'\n", fpath) + logger.Warnf("Skipped create file '%s'", fpath) continue } } else { f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) if err != nil { - ColorLog("[WARN] %v\n", err) + logger.Warnf("%s", err) continue } } @@ -773,7 +771,8 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1) fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1) fileStr = strings.Replace(fileStr, "{{tableName}}", tb.Name, -1) - // if table contains time field, import time.Time package + + // If table contains time field, import time.Time package timePkg := "" importTimePkg := "" if tb.ImportTimePkg { @@ -783,8 +782,7 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1) fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) if _, err := f.WriteString(fileStr); err != nil { - ColorLog("[ERRO] Could not write model file to %s\n", fpath) - os.Exit(2) + logger.Fatalf("Could not write model file to '%s': %s", fpath, err) } CloseFile(f) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") @@ -797,7 +795,7 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri w := NewColorWriter(os.Stdout) for _, tb := range tables { - // if selectedTables map is not nil and this table is not selected, ignore it + // If selectedTables map is not nil and this table is not selected, ignore it if selectedTables != nil { if _, selected := selectedTables[tb.Name]; !selected { continue @@ -811,29 +809,28 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri var f *os.File var err error if isExist(fpath) { - ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath) + logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) if askForConfirmation() { f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) if err != nil { - ColorLog("[WARN] %v\n", err) + logger.Warnf("%s", err) continue } } else { - ColorLog("[WARN] Skipped create file '%s'\n", fpath) + logger.Warnf("Skipped create file '%s'", fpath) continue } } else { f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) if err != nil { - ColorLog("[WARN] %v\n", err) + logger.Warnf("%s", err) continue } } fileStr := strings.Replace(CtrlTPL, "{{ctrlName}}", camelCase(tb.Name), -1) fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1) if _, err := f.WriteString(fileStr); err != nil { - ColorLog("[ERRO] Could not write controller file to %s\n", fpath) - os.Exit(2) + logger.Fatalf("Could not write controller file to '%s': %s", fpath, err) } CloseFile(f) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") @@ -847,7 +844,7 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo var nameSpaces []string for _, tb := range tables { - // if selectedTables map is not nil and this table is not selected, ignore it + // If selectedTables map is not nil and this table is not selected, ignore it if selectedTables != nil { if _, selected := selectedTables[tb.Name]; !selected { continue @@ -856,63 +853,44 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo if tb.Pk == "" { continue } - // add namespaces + // Add namespaces nameSpace := strings.Replace(NamespaceTPL, "{{nameSpace}}", tb.Name, -1) nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", camelCase(tb.Name), -1) nameSpaces = append(nameSpaces, nameSpace) } - // add export controller + // Add export controller fpath := path.Join(rPath, "router.go") routerStr := strings.Replace(RouterTPL, "{{nameSpaces}}", strings.Join(nameSpaces, ""), 1) routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1) var f *os.File var err error if isExist(fpath) { - ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath) + logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) if askForConfirmation() { f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) if err != nil { - ColorLog("[WARN] %v\n", err) + logger.Warnf("%s", err) return } } else { - ColorLog("[WARN] Skipped create file '%s'\n", fpath) + logger.Warnf("Skipped create file '%s'", fpath) return } } else { f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) if err != nil { - ColorLog("[WARN] %v\n", err) + logger.Warnf("%s", err) return } } if _, err := f.WriteString(routerStr); err != nil { - ColorLog("[ERRO] Could not write router file to '%s'\n", fpath) - os.Exit(2) + logger.Fatalf("Could not write router file to '%s': %s", fpath, err) } CloseFile(f) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") formatSourceCode(fpath) } -// formatSourceCode formats source files -func formatSourceCode(filename string) { - cmd := exec.Command("gofmt", "-w", filename) - if err := cmd.Run(); err != nil { - ColorLog("[WARN] gofmt err: %s\n", err) - } -} - -// camelCase converts a _ delimited string to camel case -// e.g. very_important_person => VeryImportantPerson -func camelCase(in string) string { - tokens := strings.Split(in, "_") - for i := range tokens { - tokens[i] = strings.Title(strings.Trim(tokens[i], " ")) - } - return strings.Join(tokens, "") -} - func isSQLTemporalType(t string) bool { return t == "date" || t == "datetime" || t == "timestamp" || t == "time" } @@ -972,12 +950,12 @@ func getFileName(tbName string) (filename string) { func getPackagePath(curpath string) (packpath string) { gopath := os.Getenv("GOPATH") - Debugf("gopath:%s", gopath) if gopath == "" { - ColorLog("[ERRO] You should set GOPATH in the env") - os.Exit(2) + logger.Fatal("GOPATH environment variable is not set or empty") } + logger.Debugf("GOPATH: %s", gopath) + appsrcpath := "" haspath := false wgopath := filepath.SplitList(gopath) @@ -993,13 +971,11 @@ func getPackagePath(curpath string) (packpath string) { } if !haspath { - ColorLog("[ERRO] Can't generate application code outside of GOPATH '%s'\n", gopath) - os.Exit(2) + logger.Fatalf("Cannot generate application code outside of GOPATH '%s'", gopath) } if curpath == appsrcpath { - ColorLog("[ERRO] Can't generate application code outside of application PATH \n") - os.Exit(2) + logger.Fatal("Cannot generate application code outside of application path") } packpath = strings.Join(strings.Split(curpath[len(appsrcpath)+1:], string(filepath.Separator)), "/") diff --git a/g_controllers.go b/g_controllers.go index 9d0b0ae..5220210 100644 --- a/g_controllers.go +++ b/g_controllers.go @@ -21,9 +21,6 @@ import ( "strings" ) -// article -// cms/article -// func generateController(cname, currpath string) { w := NewColorWriter(os.Stdout) @@ -36,15 +33,14 @@ func generateController(cname, currpath string) { packageName = p[i+1 : len(p)-1] } - ColorLog("[INFO] Using '%s' as controller name\n", controllerName) - ColorLog("[INFO] Using '%s' as package name\n", packageName) + logger.Infof("Using '%s' as controller name", controllerName) + logger.Infof("Using '%s' as package name", packageName) fp := path.Join(currpath, "controllers", p) if _, err := os.Stat(fp); os.IsNotExist(err) { // Create the controller's directory if err := os.MkdirAll(fp, 0777); err != nil { - ColorLog("[ERRO] Could not create controllers directory: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create controllers directory: %s", err) } } @@ -56,7 +52,7 @@ func generateController(cname, currpath string) { var content string if _, err := os.Stat(modelPath); err == nil { - ColorLog("[INFO] Using matching model '%s'\n", controllerName) + logger.Infof("Using matching model '%s'", controllerName) content = strings.Replace(controllerModelTpl, "{{packageName}}", packageName, -1) pkgPath := getPackagePath(currpath) content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1) @@ -71,8 +67,7 @@ func generateController(cname, currpath string) { formatSourceCode(fpath) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") } else { - ColorLog("[ERRO] Could not create controller file: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create controller file: %s", err) } } diff --git a/g_docs.go b/g_docs.go index d7216cd..f3ddc16 100644 --- a/g_docs.go +++ b/g_docs.go @@ -26,7 +26,6 @@ import ( "path/filepath" "reflect" "regexp" - "runtime" "strconv" "strings" "unicode" @@ -92,8 +91,7 @@ func parsePackagesFromDir(path string) { parsePackageFromDir(path) list, err := ioutil.ReadDir(path) if err != nil { - ColorLog("[ERRO] Can't read directory %s : %s\n", path, err) - os.Exit(1) + logger.Fatalf("Cannot read directory '%s': %s", path, err) } for _, item := range list { if item.IsDir() && item.Name() != "vendor" { @@ -108,11 +106,10 @@ func parsePackageFromDir(path string) { name := info.Name() return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") }, parser.ParseComments) - if err != nil { - ColorLog("[ERRO] the model %s parser.ParseDir error: %s\n", path, err) - os.Exit(1) + logger.Fatalf("Error while parsing dir at '%s': %s", path, err) } + for k, v := range folderPkgs { astPkgs[k] = v } @@ -124,13 +121,13 @@ func generateDocs(curpath string) { f, err := parser.ParseFile(fset, path.Join(curpath, "routers", "router.go"), nil, parser.ParseComments) if err != nil { - ColorLog("[ERRO] parse router.go error\n") - os.Exit(2) + logger.Fatalf("Error while parsing router.go: %s", err) } rootapi.Infos = swagger.Information{} rootapi.SwaggerVersion = "2.0" - //analysis API comments + + // Analyse API comments if f.Comments != nil { for _, c := range f.Comments { for _, s := range strings.Split(c.Text(), "\n") { @@ -168,13 +165,14 @@ func generateDocs(curpath string) { } } } - // analisys controller package + + // Analyse controller package for _, im := range f.Imports { localName := "" if im.Name != nil { localName = im.Name.Name } - analisyscontrollerPkg(localName, im.Path.Value) + analyseControllerPkg(localName, im.Path.Value) } for _, d := range f.Decls { switch specDecl := d.(type) { @@ -184,11 +182,11 @@ func generateDocs(curpath string) { case *ast.AssignStmt: for _, l := range stmt.Rhs { if v, ok := l.(*ast.CallExpr); ok { - // analisys NewNamespace, it will return version and the subfunction + // Analyse NewNamespace, it will return version and the subfunction if selName := v.Fun.(*ast.SelectorExpr).Sel.String(); selName != "NewNamespace" { continue } - version, params := analisysNewNamespace(v) + version, params := analyseNewNamespace(v) if rootapi.BasePath == "" && version != "" { rootapi.BasePath = version } @@ -197,12 +195,12 @@ func generateDocs(curpath string) { case *ast.CallExpr: controllerName := "" if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" { - s, params := analisysNewNamespace(pp) + s, params := analyseNewNamespace(pp) for _, sp := range params { switch pp := sp.(type) { case *ast.CallExpr: if pp.Fun.(*ast.SelectorExpr).Sel.String() == "NSInclude" { - controllerName = analisysNSInclude(s, pp) + controllerName = analyseNSInclude(s, pp) if v, ok := controllerComments[controllerName]; ok { rootapi.Tags = append(rootapi.Tags, swagger.Tag{ Name: strings.Trim(s, "/"), @@ -213,7 +211,7 @@ func generateDocs(curpath string) { } } } else if selname == "NSInclude" { - controllerName = analisysNSInclude("", pp) + controllerName = analyseNSInclude("", pp) if v, ok := controllerComments[controllerName]; ok { rootapi.Tags = append(rootapi.Tags, swagger.Tag{ Name: controllerName, // if the NSInclude has no prefix, we use the controllername as the tag @@ -250,8 +248,8 @@ func generateDocs(curpath string) { } } -// return version and the others params -func analisysNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) { +// analyseNewNamespace returns version and the others params +func analyseNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) { for i, p := range ce.Args { if i == 0 { switch pp := p.(type) { @@ -265,7 +263,7 @@ func analisysNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) { return } -func analisysNSInclude(baseurl string, ce *ast.CallExpr) string { +func analyseNSInclude(baseurl string, ce *ast.CallExpr) string { cname := "" for _, p := range ce.Args { x := p.(*ast.UnaryExpr).X.(*ast.CompositeLit).Type.(*ast.SelectorExpr) @@ -313,7 +311,7 @@ func analisysNSInclude(baseurl string, ce *ast.CallExpr) string { return cname } -func analisyscontrollerPkg(localName, pkgpath string) { +func analyseControllerPkg(localName, pkgpath string) { pkgpath = strings.Trim(pkgpath, "\"") if isSystemPackage(pkgpath) { return @@ -329,7 +327,7 @@ func analisyscontrollerPkg(localName, pkgpath string) { } gopath := os.Getenv("GOPATH") if gopath == "" { - panic("please set gopath") + logger.Fatal("GOPATH environment variable is not set or empty") } pkgRealpath := "" @@ -347,18 +345,16 @@ func analisyscontrollerPkg(localName, pkgpath string) { } pkgCache[pkgpath] = struct{}{} } else { - ColorLog("[ERRO] the %s pkg not exist in gopath\n", pkgpath) - os.Exit(1) + logger.Fatalf("Package '%s' does not exist in the GOPATH", pkgpath) } + fileSet := token.NewFileSet() astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool { name := info.Name() return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") }, parser.ParseComments) - if err != nil { - ColorLog("[ERRO] the %s pkg parser.ParseDir error: %s\n", pkgpath, err) - os.Exit(1) + logger.Fatalf("Error while parsing dir at '%s': %s", pkgpath, err) } for _, pkg := range astPkgs { for _, fl := range pkg.Files { @@ -367,7 +363,7 @@ func analisyscontrollerPkg(localName, pkgpath string) { case *ast.FuncDecl: if specDecl.Recv != nil && len(specDecl.Recv.List) > 0 { if t, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr); ok { - // parse controller method + // Parse controller method parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(t.X), pkgpath) } } @@ -377,7 +373,7 @@ func analisyscontrollerPkg(localName, pkgpath string) { switch tp := s.(*ast.TypeSpec).Type.(type) { case *ast.StructType: _ = tp.Struct - //parse controller definition comments + // Parse controller definition comments if strings.TrimSpace(specDecl.Doc.Text()) != "" { controllerComments[pkgpath+s.(*ast.TypeSpec).Name.String()] = specDecl.Doc.Text() } @@ -391,10 +387,11 @@ func analisyscontrollerPkg(localName, pkgpath string) { } func isSystemPackage(pkgpath string) bool { - goroot := runtime.GOROOT() + goroot := os.Getenv("GOROOT") if goroot == "" { - panic("goroot is empty, do you install Go right?") + logger.Fatalf("GOROOT environment variable is not set or empty") } + wg, _ := filepath.EvalSymlinks(filepath.Join(goroot, "src", "pkg", pkgpath)) if utils.FileExists(wg) { return true @@ -460,8 +457,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat ss = strings.TrimSpace(ss[pos:]) schemaName, pos := peekNextSplitString(ss) if schemaName == "" { - ColorLog("[ERRO][%s.%s] Schema must follow {object} or {array}\n", controllerName, funcName) - os.Exit(-1) + logger.Fatalf("[%s.%s] Schema must follow {object} or {array}", controllerName, funcName) } if strings.HasPrefix(schemaName, "[]") { schemaName = schemaName[2:] @@ -498,7 +494,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat para := swagger.Parameter{} p := getparams(strings.TrimSpace(t[len("@Param "):])) if len(p) < 4 { - panic(controllerName + "_" + funcName + "'s comments @Param at least should has 4 params") + logger.Fatal(controllerName + "_" + funcName + "'s comments @Param should have at least 4 params") } para.Name = p[0] switch p[1] { @@ -513,7 +509,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat case "body": break default: - ColorLog("[WARN][%s.%s] Unknow param location: %s, Possible values are `query`, `header`, `path`, `formData` or `body`.\n", controllerName, funcName, p[1]) + logger.Warnf("[%s.%s] Unknown param location: %s. Possible values are `query`, `header`, `path`, `formData` or `body`.\n", controllerName, funcName, p[1]) } para.In = p[1] pp := strings.Split(p[2], ".") @@ -544,7 +540,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat paraType = typeFormat[0] paraFormat = typeFormat[1] } else { - ColorLog("[WARN][%s.%s] Unknow param type: %s\n", controllerName, funcName, typ) + logger.Warnf("[%s.%s] Unknown param type: %s\n", controllerName, funcName, typ) } if isArray { para.Type = "array" @@ -697,7 +693,7 @@ func getModel(str string) (objectname string, m swagger.Schema, realTypes []stri } } if m.Title == "" { - ColorLog("[WARN]can't find the object: %s\n", str) + logger.Warnf("Cannot find the object: %s", str) // TODO remove when all type have been supported //os.Exit(1) } @@ -712,8 +708,7 @@ func getModel(str string) (objectname string, m swagger.Schema, realTypes []stri func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package, packageName string) { ts, ok := d.Decl.(*ast.TypeSpec) if !ok { - ColorLog("Unknown type without TypeSec: %v\n", d) - os.Exit(1) + logger.Fatalf("Unknown type without TypeSec: %v\n", d) } // TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc... st, ok := ts.Type.(*ast.StructType) @@ -789,7 +784,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string mp.Default = str2RealType(res[1], realType) } else { - ColorLog("[WARN] Invalid default value: %s\n", defaultValue) + logger.Warnf("Invalid default value: %s", defaultValue) } } @@ -933,7 +928,7 @@ func str2RealType(s string, typ string) interface{} { } if err != nil { - ColorLog("[WARN] Invalid default value type(%s): %s\n", typ, s) + logger.Warnf("Invalid default value type '%s': %s", typ, s) return s } diff --git a/g_hproseappcode.go b/g_hproseappcode.go index 2d95aec..3ce52ae 100644 --- a/g_hproseappcode.go +++ b/g_hproseappcode.go @@ -38,9 +38,7 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) { case "3": mode = OModel | OController | ORouter default: - ColorLog("[ERRO] Invalid 'level' option: %s\n", level) - ColorLog("[HINT] Level must be either 1, 2 or 3\n") - os.Exit(2) + logger.Fatal("Invalid 'level' option. Level must be either \"1\", \"2\" or \"3\"") } var selectedTables map[string]bool if tables != "" { @@ -53,12 +51,9 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) { case "mysql": case "postgres": case "sqlite": - ColorLog("[ERRO] Generating app code from SQLite database is not supported yet.\n") - os.Exit(2) + logger.Fatal("Generating app code from SQLite database is not supported yet") default: - ColorLog("[ERRO] Unknown database driver: %s\n", driver) - ColorLog("[HINT] Driver must be one of mysql, postgres or sqlite\n") - os.Exit(2) + logger.Fatalf("Unknown database driver '%s'. Driver must be one of mysql, postgres or sqlite", driver) } genHprose(driver, connStr, mode, selectedTables, currpath) } @@ -68,12 +63,11 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) { func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bool, currpath string) { db, err := sql.Open(dbms, connStr) if err != nil { - ColorLog("[ERRO] Could not connect to %s database: %s, %s\n", dbms, connStr, err) - os.Exit(2) + logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err) } defer db.Close() if trans, ok := dbDriver[dbms]; ok { - ColorLog("[INFO] Analyzing database tables...\n") + logger.Info("Analyzing database tables...") tableNames := trans.GetTableNames(db) tables := getTableObjects(tableNames, db, trans) mvcPath := new(MvcPath) @@ -82,8 +76,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo pkgPath := getPackagePath(currpath) writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) } else { - ColorLog("[ERRO] Generating app code from %s database is not supported yet.\n", dbms) - os.Exit(2) + logger.Fatalf("Generating app code from '%s' database is not supported yet", dbms) } } @@ -92,7 +85,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo // Newly geneated files will be inside these folders. func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { if (OModel & mode) == OModel { - ColorLog("[INFO] Creating model files...\n") + logger.Info("Creating model files...") writeHproseModelFiles(tables, paths.ModelPath, selectedTables) } } @@ -113,21 +106,21 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str var f *os.File var err error if isExist(fpath) { - ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath) + logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) if askForConfirmation() { f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) if err != nil { - ColorLog("[WARN] %v\n", err) + logger.Warnf("%s", err) continue } } else { - ColorLog("[WARN] Skipped create file '%s'\n", fpath) + logger.Warnf("Skipped create file '%s'", fpath) continue } } else { f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) if err != nil { - ColorLog("[WARN] %v\n", err) + logger.Warnf("%s", err) continue } } @@ -150,8 +143,7 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1) fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) if _, err := f.WriteString(fileStr); err != nil { - ColorLog("[ERRO] Could not write model file to '%s'\n", fpath) - os.Exit(2) + logger.Fatalf("Could not write model file to '%s'", fpath) } CloseFile(f) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") diff --git a/g_migration.go b/g_migration.go index ed3dfc4..8ab3f1f 100644 --- a/g_migration.go +++ b/g_migration.go @@ -51,12 +51,12 @@ func (m mysqlDriver) generateSQLFromFields(fields string) string { for i, v := range fds { kv := strings.SplitN(v, ":", 2) if len(kv) != 2 { - ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n") + logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) return "" } typ, tag := m.getSQLType(kv[1]) if typ == "" { - ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n") + logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) return "" } if i == 0 && strings.ToLower(kv[0]) != "id" { @@ -120,12 +120,12 @@ func (m postgresqlDriver) generateSQLFromFields(fields string) string { for i, v := range fds { kv := strings.SplitN(v, ":", 2) if len(kv) != 2 { - ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n") + logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) return "" } typ, tag := m.getSQLType(kv[1]) if typ == "" { - ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n") + logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) return "" } if i == 0 && strings.ToLower(kv[0]) != "id" { @@ -177,7 +177,8 @@ func newDBDriver() DBDriver { case "postgres": return postgresqlDriver{} default: - panic("driver not supported") + logger.Fatal("Driver not supported") + return nil } } @@ -190,8 +191,7 @@ func generateMigration(mname, upsql, downsql, curpath string) { if _, err := os.Stat(migrationFilePath); os.IsNotExist(err) { // create migrations directory if err := os.MkdirAll(migrationFilePath, 0777); err != nil { - ColorLog("[ERRO] Could not create migration directory: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create migration directory: %s", err) } } // create file @@ -208,8 +208,7 @@ func generateMigration(mname, upsql, downsql, curpath string) { formatSourceCode(fpath) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") } else { - ColorLog("[ERRO] Could not create migration file: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create migration file: %s", err) } } diff --git a/g_model.go b/g_model.go index ae23731..fc94309 100644 --- a/g_model.go +++ b/g_model.go @@ -35,19 +35,17 @@ func generateModel(mname, fields, currpath string) { modelStruct, hastime, err := getStruct(modelName, fields) if err != nil { - ColorLog("[ERRO] Could not generate the model struct: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not generate the model struct: %s", err) } - ColorLog("[INFO] Using '%s' as model name\n", modelName) - ColorLog("[INFO] Using '%s' as package name\n", packageName) + logger.Infof("Using '%s' as model name", modelName) + logger.Infof("Using '%s' as package name", packageName) fp := path.Join(currpath, "models", p) if _, err := os.Stat(fp); os.IsNotExist(err) { // Create the model's directory if err := os.MkdirAll(fp, 0777); err != nil { - ColorLog("[ERRO] Could not create the model directory: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create the model directory: %s", err) } } @@ -67,8 +65,7 @@ func generateModel(mname, fields, currpath string) { formatSourceCode(fpath) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") } else { - ColorLog("[ERRO] Could not create model file: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create model file: %s", err) } } diff --git a/g_scaffold.go b/g_scaffold.go index cccda16..dd43425 100644 --- a/g_scaffold.go +++ b/g_scaffold.go @@ -3,7 +3,7 @@ package main import "strings" func generateScaffold(sname, fields, currpath, driver, conn string) { - ColorLog("[INFO] Do you want to create a '%v' model? [Yes|No] ", sname) + logger.Infof("Do you want to create a '%s' model? [Yes|No] ", sname) // Generate the model if askForConfirmation() { @@ -11,19 +11,19 @@ func generateScaffold(sname, fields, currpath, driver, conn string) { } // Generate the controller - ColorLog("[INFO] Do you want to create a '%v' controller? [Yes|No] ", sname) + logger.Infof("Do you want to create a '%s' controller? [Yes|No] ", sname) if askForConfirmation() { generateController(sname, currpath) } // Generate the views - ColorLog("[INFO] Do you want to create views for this '%v' resource? [Yes|No] ", sname) + logger.Infof("Do you want to create views for this '%s' resource? [Yes|No] ", sname) if askForConfirmation() { generateView(sname, currpath) } // Generate a migration - ColorLog("[INFO] Do you want to create a '%v' migration and schema for this resource? [Yes|No] ", sname) + logger.Infof("Do you want to create a '%s' migration and schema for this resource? [Yes|No] ", sname) if askForConfirmation() { upsql := "" downsql := "" @@ -40,9 +40,9 @@ func generateScaffold(sname, fields, currpath, driver, conn string) { } // Run the migration - ColorLog("[INFO] Do you want to migrate the database? [Yes|No] ") + logger.Infof("Do you want to migrate the database? [Yes|No] ") if askForConfirmation() { migrateUpdate(currpath, driver, conn) } - ColorLog("[INFO] All done! Don't forget to add beego.Router(\"/%v\" ,&controllers.%vController{}) to routers/route.go\n", sname, strings.Title(sname)) + logger.Successf("All done! Don't forget to add beego.Router(\"/%s\" ,&controllers.%sController{}) to routers/route.go\n", sname, strings.Title(sname)) } diff --git a/g_views.go b/g_views.go index c078223..aa7815e 100644 --- a/g_views.go +++ b/g_views.go @@ -25,13 +25,12 @@ import ( func generateView(viewpath, currpath string) { w := NewColorWriter(os.Stdout) - ColorLog("[INFO] Generating view...\n") + logger.Info("Generating view...") absViewPath := path.Join(currpath, "views", viewpath) err := os.MkdirAll(absViewPath, os.ModePerm) if err != nil { - ColorLog("[ERRO] Could not create '%s' view: %s\n", viewpath, err) - os.Exit(2) + logger.Fatalf("Could not create '%s' view: %s", viewpath, err) } cfile := path.Join(absViewPath, "index.tpl") @@ -40,8 +39,7 @@ func generateView(viewpath, currpath string) { f.WriteString(cfile) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") } else { - ColorLog("[ERRO] Could not create view file: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create view file: %s", err) } cfile = path.Join(absViewPath, "show.tpl") @@ -50,8 +48,7 @@ func generateView(viewpath, currpath string) { f.WriteString(cfile) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") } else { - ColorLog("[ERRO] Could not create view file: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create view file: %s", err) } cfile = path.Join(absViewPath, "create.tpl") @@ -60,8 +57,7 @@ func generateView(viewpath, currpath string) { f.WriteString(cfile) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") } else { - ColorLog("[ERRO] Could not create view file: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create view file: %s", err) } cfile = path.Join(absViewPath, "edit.tpl") @@ -70,7 +66,6 @@ func generateView(viewpath, currpath string) { f.WriteString(cfile) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") } else { - ColorLog("[ERRO] Could not create view file: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create view file: %s", err) } } diff --git a/hproseapp.go b/hproseapp.go index 82d3dad..c9a4dfd 100644 --- a/hproseapp.go +++ b/hproseapp.go @@ -65,7 +65,7 @@ var hproseMaingo = `package main import ( "fmt" "reflect" - + "{{.Appname}}/models" "github.com/hprose/hprose-golang/rpc" @@ -90,7 +90,7 @@ func main() { // Create Http Server service := rpc.NewHTTPService() - // Use Logger Middleware + // Use Logger Middleware service.AddInvokeHandler(logInvokeHandler) // Publish Functions @@ -139,7 +139,7 @@ func main() { // Create Http Server service := rpc.NewHTTPService() - // Use Logger Middleware + // Use Logger Middleware service.AddInvokeHandler(logInvokeHandler) {{HproseFunctionList}} @@ -314,8 +314,7 @@ func createhprose(cmd *Command, args []string) int { } apppath, packpath, err := checkEnv(args[0]) if err != nil { - fmt.Println(err) - os.Exit(2) + logger.Fatalf("%s", err) } if driver == "" { driver = "mysql" @@ -323,7 +322,7 @@ func createhprose(cmd *Command, args []string) int { if conn == "" { } - ColorLog("[INFO] Creating Hprose application...\n") + 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") @@ -334,9 +333,9 @@ func createhprose(cmd *Command, args []string) int { strings.Replace(hproseconf, "{{.Appname}}", args[0], -1)) if conn != "" { - ColorLog("[INFO] Using '%s' as 'driver'\n", driver) - ColorLog("[INFO] Using '%s' as 'conn'\n", conn) - ColorLog("[INFO] Using '%s' as 'tables'\n", tables) + logger.Infof("Using '%s' as 'driver'", driver) + 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") maingoContent := strings.Replace(hproseMainconngo, "{{.Appname}}", packpath, -1) @@ -369,6 +368,6 @@ func createhprose(cmd *Command, args []string) int { WriteToFile(path.Join(apppath, "main.go"), strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1)) } - ColorLog("[SUCC] New Hprose application successfully created!\n") + logger.Success("New Hprose application successfully created!") return 0 } diff --git a/migrate.go b/migrate.go index 22bfac3..873fbb5 100644 --- a/migrate.go +++ b/migrate.go @@ -16,14 +16,13 @@ package main import ( "database/sql" - "fmt" "os" "os/exec" "path" + "runtime" "strconv" "strings" "time" - "runtime" ) var cmdMigrate = &Command{ @@ -69,18 +68,20 @@ func runMigration(cmd *Command, args []string) int { gps := GetGOPATHs() if len(gps) == 0 { - ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty") - os.Exit(2) + logger.Fatal("GOPATH environment variable is not set or empty") } - gopath := gps[0] - Debugf("GOPATH: %s", gopath) - // load config + gopath := gps[0] + + logger.Debugf("GOPATH: %s", gopath) + + // Load the configuration err := loadConfig() if err != nil { - ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err) + logger.Errorf("Failed to load configuration: %s", err) } - // getting command line arguments + + // Getting command line arguments if len(args) != 0 { cmd.Flag.Parse(args[1:]) } @@ -96,31 +97,30 @@ func runMigration(cmd *Command, args []string) int { mConn = "root:@tcp(127.0.0.1:3306)/test" } } - ColorLog("[INFO] Using '%s' as 'driver'\n", mDriver) - ColorLog("[INFO] Using '%s' as 'conn'\n", mConn) + logger.Infof("Using '%s' as 'driver'", mDriver) + logger.Infof("Using '%s' as 'conn'", mConn) driverStr, connStr := string(mDriver), string(mConn) if len(args) == 0 { // run all outstanding migrations - ColorLog("[INFO] Running all outstanding migrations\n") + logger.Info("Running all outstanding migrations") migrateUpdate(currpath, driverStr, connStr) } else { mcmd := args[0] switch mcmd { case "rollback": - ColorLog("[INFO] Rolling back the last migration operation\n") + logger.Info("Rolling back the last migration operation") migrateRollback(currpath, driverStr, connStr) case "reset": - ColorLog("[INFO] Reseting all migrations\n") + logger.Info("Reseting all migrations") migrateReset(currpath, driverStr, connStr) case "refresh": - ColorLog("[INFO] Refreshing all migrations\n") + logger.Info("Refreshing all migrations") migrateRefresh(currpath, driverStr, connStr) default: - ColorLog("[ERRO] Command is missing\n") - os.Exit(2) + logger.Fatal("Command is missing") } } - ColorLog("[SUCC] Migration successful!\n") + logger.Success("Migration successful!") return 0 } @@ -146,21 +146,21 @@ func migrateRefresh(currpath, driver, connStr string) { // migrate generates source code, build it, and invoke the binary who does the actual migration func migrate(goal, currpath, driver, connStr string) { - dir := path.Join(currpath, "database", "migrations") + dir := path.Join(currpath, "database", "migrations") postfix := "" if runtime.GOOS == "windows" { postfix = ".exe" } binary := "m" + postfix source := binary + ".go" - // connect to database + + // Connect to database db, err := sql.Open(driver, connStr) if err != nil { - ColorLog("[ERRO] Could not connect to %s: %s\n", driver, connStr) - ColorLog("[ERRO] Error: %v", err.Error()) - os.Exit(2) + logger.Fatalf("Could not connect to database using '%s': %s", connStr, err) } defer db.Close() + checkForSchemaUpdateTable(db, driver) latestName, latestTime := getLatestMigration(db, goal) writeMigrationSourceFile(dir, source, driver, connStr, latestTime, latestName, goal) @@ -175,50 +175,44 @@ func migrate(goal, currpath, driver, connStr string) { func checkForSchemaUpdateTable(db *sql.DB, driver string) { showTableSQL := showMigrationsTableSQL(driver) if rows, err := db.Query(showTableSQL); err != nil { - ColorLog("[ERRO] Could not show migrations table: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not show migrations table: %s", err) } else if !rows.Next() { - // no migrations table, create anew + // No migrations table, create new ones createTableSQL := createMigrationsTableSQL(driver) - ColorLog("[INFO] Creating 'migrations' table...\n") + + logger.Infof("Creating 'migrations' table...") + if _, err := db.Query(createTableSQL); err != nil { - ColorLog("[ERRO] Could not create migrations table: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create migrations table: %s", err) } } - // checking that migrations table schema are expected + // Checking that migrations table schema are expected selectTableSQL := selectMigrationsTableSQL(driver) if rows, err := db.Query(selectTableSQL); err != nil { - ColorLog("[ERRO] Could not show columns of migrations table: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not show columns of migrations table: %s", err) } else { for rows.Next() { var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte if err := rows.Scan(&fieldBytes, &typeBytes, &nullBytes, &keyBytes, &defaultBytes, &extraBytes); err != nil { - ColorLog("[ERRO] Could not read column information: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not read column information: %s", err) } fieldStr, typeStr, nullStr, keyStr, defaultStr, extraStr := string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes) if fieldStr == "id_migration" { if keyStr != "PRI" || extraStr != "auto_increment" { - ColorLog("[ERRO] Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s\n", keyStr, extraStr) - ColorLog("[HINT] Expecting KEY: PRI, EXTRA: auto_increment\n") - os.Exit(2) + logger.Hint("Expecting KEY: PRI, EXTRA: auto_increment") + logger.Fatalf("Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s", keyStr, extraStr) } } else if fieldStr == "name" { if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" { - ColorLog("[ERRO] Column migration.name type mismatch: TYPE: %s, NULL: %s\n", typeStr, nullStr) - ColorLog("[HINT] Expecting TYPE: varchar, NULL: YES\n") - os.Exit(2) + logger.Hint("Expecting TYPE: varchar, NULL: YES") + logger.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr) } - } else if fieldStr == "created_at" { if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" { - ColorLog("[ERRO] Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s\n", typeStr, defaultStr) - ColorLog("[HINT] Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP\n") - os.Exit(2) + logger.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP") + logger.Fatalf("Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s", typeStr, defaultStr) } } } @@ -262,26 +256,22 @@ func selectMigrationsTableSQL(driver string) string { func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64) { sql := "SELECT name FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1" if rows, err := db.Query(sql); err != nil { - ColorLog("[ERRO] Could not retrieve migrations: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not retrieve migrations: %s", err) } else { if rows.Next() { if err := rows.Scan(&file); err != nil { - ColorLog("[ERRO] Could not read migrations in database: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not read migrations in database: %s", err) } createdAtStr := file[len(file)-15:] if t, err := time.Parse("20060102_150405", createdAtStr); err != nil { - ColorLog("[ERRO] Could not parse time: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not parse time: %s", err) } else { createdAt = t.Unix() } } else { // migration table has no 'update' record, no point rolling back if goal == "rollback" { - ColorLog("[ERRO] There is nothing to rollback\n") - os.Exit(2) + logger.Fatal("There is nothing to rollback") } file, createdAt = "", 0 } @@ -293,8 +283,7 @@ func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64) func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime int64, latestName string, task string) { changeDir(dir) if f, err := os.OpenFile(source, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err != nil { - ColorLog("[ERRO] Could not create file: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not create file: %s", err) } else { content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1) content = strings.Replace(content, "{{ConnStr}}", connStr, -1) @@ -302,8 +291,7 @@ func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime in content = strings.Replace(content, "{{LatestName}}", latestName, -1) content = strings.Replace(content, "{{Task}}", task, -1) if _, err := f.WriteString(content); err != nil { - ColorLog("[ERRO] Could not write to file: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not write to file: %s", err) } CloseFile(f) } @@ -314,7 +302,7 @@ func buildMigrationBinary(dir, binary string) { changeDir(dir) cmd := exec.Command("go", "build", "-o", binary) if out, err := cmd.CombinedOutput(); err != nil { - ColorLog("[ERRO] Could not build migration binary: %s\n", err) + logger.Errorf("Could not build migration binary: %s", err) formatShellErrOutput(string(out)) removeTempFile(dir, binary) removeTempFile(dir, binary+".go") @@ -328,7 +316,7 @@ func runMigrationBinary(dir, binary string) { cmd := exec.Command("./" + binary) if out, err := cmd.CombinedOutput(); err != nil { formatShellOutput(string(out)) - ColorLog("[ERRO] Could not run migration binary: %s\n", err) + logger.Errorf("Could not run migration binary: %s", err) removeTempFile(dir, binary) removeTempFile(dir, binary+".go") os.Exit(2) @@ -341,8 +329,7 @@ func runMigrationBinary(dir, binary string) { // It exits the system when encouter an error func changeDir(dir string) { if err := os.Chdir(dir); err != nil { - ColorLog("[ERRO] Could not find migration directory: %s\n", err) - os.Exit(2) + logger.Fatalf("Could not find migration directory: %s", err) } } @@ -350,7 +337,7 @@ func changeDir(dir string) { func removeTempFile(dir, file string) { changeDir(dir) if err := os.Remove(file); err != nil { - ColorLog("[WARN] Could not remove temporary file: %s\n", err) + logger.Warnf("Could not remove temporary file: %s", err) } } @@ -358,8 +345,7 @@ func removeTempFile(dir, file string) { func formatShellErrOutput(o string) { for _, line := range strings.Split(o, "\n") { if line != "" { - ColorLog("[ERRO] -| ") - fmt.Println(line) + logger.Errorf("|> %s", line) } } } @@ -368,13 +354,13 @@ func formatShellErrOutput(o string) { func formatShellOutput(o string) { for _, line := range strings.Split(o, "\n") { if line != "" { - ColorLog("[INFO] -| ") - fmt.Println(line) + logger.Infof("|> %s", line) } } } const ( + // MigrationMainTPL migration main template MigrationMainTPL = `package main import( @@ -414,6 +400,7 @@ func main(){ } ` + // MYSQLMigrationDDL MySQL migration SQL MYSQLMigrationDDL = ` CREATE TABLE migrations ( id_migration int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key', @@ -425,7 +412,7 @@ CREATE TABLE migrations ( PRIMARY KEY (id_migration) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ` - + // POSTGRESMigrationDDL Postgres migration SQL POSTGRESMigrationDDL = ` CREATE TYPE migrations_status AS ENUM('update', 'rollback'); diff --git a/new.go b/new.go index 2f9e406..6ff411c 100644 --- a/new.go +++ b/new.go @@ -58,7 +58,7 @@ func createApp(cmd *Command, args []string) int { ShowShortVersionBanner() w := NewColorWriter(os.Stdout) if len(args) != 1 { - ColorLog("[ERRO] Argument [appname] is missing\n") + logger.Error("Argument [appname] is missing") os.Exit(2) } apppath, packpath, err := checkEnv(args[0]) @@ -68,14 +68,14 @@ func createApp(cmd *Command, args []string) int { } if isExist(apppath) { - ColorLog("[ERRO] Path (%s) already exists\n", apppath) - ColorLog("[WARN] Do you want to overwrite it? [Yes|No] ") + logger.Errorf("Path (%s) already exists", apppath) + logger.Warn("Do you want to overwrite it? [Yes|No] ") if !askForConfirmation() { os.Exit(2) } } - ColorLog("[INFO] Creating application...\n") + 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") @@ -117,7 +117,7 @@ func createApp(cmd *Command, args []string) int { fmt.Fprintf(w, "\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)) - ColorLog("[SUCC] New application successfully created!\n") + logger.Success("New application successfully created!") return 0 } @@ -302,13 +302,3 @@ var indextpl = ` ` - -// WriteToFile creates a file and writes content to it -func WriteToFile(filename, content string) { - f, err := os.Create(filename) - defer CloseFile(f) - if err != nil { - panic(err) - } - f.WriteString(content) -} diff --git a/pack.go b/pack.go index d6d84e4..2695840 100644 --- a/pack.go +++ b/pack.go @@ -104,11 +104,6 @@ func init() { w = NewColorWriter(os.Stdout) } -func exitPrint(con string) { - fmt.Fprintln(os.Stderr, con) - os.Exit(2) -} - type walker interface { isExclude(string) bool isEmpty(string) bool @@ -397,10 +392,10 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) { func packDirectory(excludePrefix []string, excludeSuffix []string, excludeRegexp []*regexp.Regexp, includePath ...string) (err error) { - ColorLog("Excluding relpath prefix: %s\n", strings.Join(excludePrefix, ":")) - ColorLog("Excluding relpath suffix: %s\n", strings.Join(excludeSuffix, ":")) + logger.Infof("Excluding relpath prefix: %s", strings.Join(excludePrefix, ":")) + logger.Infof("Excluding relpath suffix: %s", strings.Join(excludeSuffix, ":")) if len(excludeRegexp) > 0 { - ColorLog("Excluding filename regex: `%s`\n", strings.Join(excludeR, "`, `")) + logger.Infof("Excluding filename regex: `%s`", strings.Join(excludeR, "`, `")) } w, err := os.OpenFile(outputP, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) @@ -477,17 +472,17 @@ func packApp(cmd *Command, args []string) int { thePath, err := path.Abs(appPath) if err != nil { - exitPrint(fmt.Sprintf("Wrong app path: %s", thePath)) + logger.Fatalf("Wrong application path: %s", thePath) } if stat, err := os.Stat(thePath); os.IsNotExist(err) || stat.IsDir() == false { - exitPrint(fmt.Sprintf("App path does not exist: %s", thePath)) + logger.Fatalf("Application path does not exist: %s", thePath) } if isBeegoProject(thePath) == false { - exitPrint(fmt.Sprintf("Bee does not support non Beego project")) + logger.Fatal("Bee does not support non Beego project") } - ColorLog("Packaging application: %s\n", thePath) + logger.Infof("Packaging application on '%s'...", thePath) appName := path.Base(thePath) @@ -507,7 +502,7 @@ func packApp(cmd *Command, args []string) int { os.Mkdir(tmpdir, 0700) if build { - ColorLog("Building application...\n") + logger.Info("Building application...") var envs []string for _, env := range buildEnvs { parts := strings.SplitN(env, "=", 2) @@ -529,7 +524,7 @@ func packApp(cmd *Command, args []string) int { os.Setenv("GOOS", goos) os.Setenv("GOARCH", goarch) - ColorLog("Env: GOOS=%s GOARCH=%s\n", goos, goarch) + logger.Infof("Using: GOOS=%s GOARCH=%s", goos, goarch) binPath := path.Join(tmpdir, appName) if goos == "windows" { @@ -552,10 +547,10 @@ func packApp(cmd *Command, args []string) int { execmd.Dir = thePath err = execmd.Run() if err != nil { - exitPrint(err.Error()) + logger.Fatal(err.Error()) } - ColorLog("Build successful\n") + logger.Success("Build successful!") } switch format { @@ -573,7 +568,7 @@ func packApp(cmd *Command, args []string) int { if _, err := os.Stat(outputP); err != nil { err = os.MkdirAll(outputP, 0755) if err != nil { - exitPrint(err.Error()) + logger.Fatal(err.Error()) } } @@ -595,7 +590,7 @@ func packApp(cmd *Command, args []string) int { for _, r := range excludeR { if len(r) > 0 { if re, err := regexp.Compile(r); err != nil { - exitPrint(err.Error()) + logger.Fatal(err.Error()) } else { exr = append(exr, re) } @@ -604,9 +599,9 @@ func packApp(cmd *Command, args []string) int { err = packDirectory(exp, exs, exr, tmpdir, thePath) if err != nil { - exitPrint(err.Error()) + logger.Fatal(err.Error()) } - ColorLog("Writing to output: `%s`\n", outputP) + logger.Infof("Writing to output: %s", outputP) return 0 } diff --git a/run.go b/run.go index 8f4d3a3..88797ab 100644 --- a/run.go +++ b/run.go @@ -15,7 +15,6 @@ package main import ( - "fmt" "io/ioutil" "os" path "path/filepath" @@ -77,9 +76,8 @@ func runApp(cmd *Command, args []string) int { appname = path.Base(currpath) currentGoPath = _gopath } else { - exitPrint(fmt.Sprintf("Bee does not support non Beego project: %s", currpath)) + logger.Fatalf("No Beego application '%s' found in your GOPATH", currpath) } - ColorLog("[INFO] Using '%s' as 'appname'\n", appname) } else { // Check if passed Bee application path/name exists in the GOPATH(s) if found, _gopath, _path := SearchGOPATHs(args[0]); found { @@ -87,35 +85,35 @@ func runApp(cmd *Command, args []string) int { currentGoPath = _gopath appname = path.Base(currpath) } else { - exitPrint(fmt.Sprintf("No Beego application '%s' found in your GOPATH", args[0])) + logger.Fatalf("No Beego application '%s' found in your GOPATH", args[0]) } - ColorLog("[INFO] Using '%s' as 'appname'\n", appname) - if strings.HasSuffix(appname, ".go") && isExist(currpath) { - ColorLog("[WARN] The appname is in conflict with currpath's file, do you want to build appname as %s\n", appname) - ColorLog("[INFO] Do you want to overwrite it? [yes|no]] ") + logger.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname) + logger.Info("Do you want to overwrite it? [yes|no] ") if !askForConfirmation() { return 0 } } } - Debugf("current path:%s\n", currpath) + logger.Infof("Using '%s' as 'appname'", appname) + + logger.Debugf("Current path: %s", currpath) if runmode == "prod" || runmode == "dev" { os.Setenv("BEEGO_RUNMODE", runmode) - ColorLog("[INFO] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE")) + logger.Infof("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) } else if runmode != "" { os.Setenv("BEEGO_RUNMODE", runmode) - ColorLog("[WARN] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE")) + logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) } else if os.Getenv("BEEGO_RUNMODE") != "" { - ColorLog("[WARN] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE")) + logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) } err := loadConfig() if err != nil { - ColorLog("[ERRO] Failed to load configuration [ %s ]\n", err) + logger.Fatalf("Failed to load configuration: %s", err) } var paths []string @@ -143,10 +141,10 @@ func runApp(cmd *Command, args []string) int { } if gendoc == "true" { NewWatcher(paths, files, true) - Autobuild(files, true) + AutoBuild(files, true) } else { NewWatcher(paths, files, false) - Autobuild(files, false) + AutoBuild(files, false) } for { @@ -202,16 +200,16 @@ func isExcluded(filePath string) bool { for _, p := range excludedPaths { absP, err := path.Abs(p) if err != nil { - ColorLog("[ERROR] Can not get absolute path of [ %s ]\n", p) + logger.Errorf("Cannot get absolute path of '%s'", p) continue } absFilePath, err := path.Abs(filePath) if err != nil { - ColorLog("[ERROR] Can not get absolute path of [ %s ]\n", filePath) + logger.Errorf("Cannot get absolute path of '%s'", filePath) break } if strings.HasPrefix(absFilePath, absP) { - ColorLog("[INFO] Excluding from watching [ %s ]\n", filePath) + logger.Infof("'%s' is not being watched", filePath) return true } } diff --git a/rundocs.go b/rundocs.go index 35eef7e..e374329 100644 --- a/rundocs.go +++ b/rundocs.go @@ -17,7 +17,6 @@ import ( "archive/zip" "fmt" "io" - "log" "net/http" "os" "strings" @@ -63,18 +62,22 @@ func runDocs(cmd *Command, args []string) int { downloadFromURL(swaggerlink, "swagger.zip") err := unzipAndDelete("swagger.zip") if err != nil { - fmt.Println("has err exet unzipAndDelete", err) + logger.Errorf("Error while unzipping 'swagger.zip' file: %s", err) } } if docport == "" { docport = "8089" } if _, err := os.Stat("swagger"); err != nil && os.IsNotExist(err) { - fmt.Println("there's no swagger, please use bee rundocs -isDownload=true downlaod first") - os.Exit(2) + logger.Fatal("No Swagger dist found. Run: bee rundocs -isDownload=true") + } + + logger.Infof("Starting the docs server on: http://127.0.0.1:%s", docport) + + err := http.ListenAndServe(":"+string(docport), http.FileServer(http.Dir("swagger"))) + if err != nil { + logger.Fatalf("%s", err) } - fmt.Println("start the docs server on: http://127.0.0.1:" + docport) - log.Fatal(http.ListenAndServe(":"+string(docport), http.FileServer(http.Dir("swagger")))) return 0 } @@ -85,36 +88,36 @@ func downloadFromURL(url, fileName string) { } else if fd.Size() == int64(0) { down = true } else { - ColorLog("[%s] Filename %s already exist\n", INFO, fileName) + logger.Infof("'%s' already exists", fileName) return } if down { - ColorLog("[%s]Downloading %s to %s\n", SUCC, url, fileName) + logger.Infof("Downloading '%s' to '%s'...", url, fileName) output, err := os.Create(fileName) if err != nil { - ColorLog("[%s]Error while creating %s: %s\n", ERRO, fileName, err) + logger.Errorf("Error while creating '%s': %s", fileName, err) return } defer output.Close() response, err := http.Get(url) if err != nil { - ColorLog("[%s]Error while downloading %s:%s\n", ERRO, url, err) + logger.Errorf("Error while downloading '%s': %s", url, err) return } defer response.Body.Close() n, err := io.Copy(output, response.Body) if err != nil { - ColorLog("[%s]Error while downloading %s:%s\n", ERRO, url, err) + logger.Errorf("Error while downloading '%s': %s", url, err) return } - ColorLog("[%s] %d bytes downloaded.\n", SUCC, n) + logger.Successf("%d bytes downloaded!", n) } } func unzipAndDelete(src string) error { - ColorLog("[%s]start to unzip file from %s\n", INFO, src) + logger.Infof("Unzipping '%s'...", src) r, err := zip.OpenReader(src) if err != nil { return err @@ -146,6 +149,6 @@ func unzipAndDelete(src string) error { } } } - ColorLog("[%s]Start delete src file %s\n", INFO, src) + logger.Successf("Done! Deleting '%s'...", src) return os.RemoveAll(src) } diff --git a/test.go b/test.go index de5116d..b05c10b 100644 --- a/test.go +++ b/test.go @@ -51,19 +51,19 @@ var started = make(chan bool) func testApp(cmd *Command, args []string) int { if len(args) != 1 { - ColorLog("[ERRO] Cannot start running[ %s ]\n", - "argument 'appname' is missing") - os.Exit(2) + logger.Fatalf("Failed to start: %s", "argument 'appname' is missing") } - crupath, _ := os.Getwd() - Debugf("current path:%s\n", crupath) + + currpath, _ := os.Getwd() + + logger.Debugf("Current path: %s", currpath) err := loadConfig() if err != nil { - ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err) + logger.Fatalf("Failed to load configuration: %s", err) } var paths []string - readAppDirectories(crupath, &paths) + readAppDirectories(currpath, &paths) NewWatcher(paths, nil, false) appname = args[0] @@ -76,7 +76,7 @@ func testApp(cmd *Command, args []string) int { } func runTest() { - ColorLog("[INFO] Start testing...\n") + logger.Info("Start testing...") time.Sleep(time.Second * 1) crupwd, _ := os.Getwd() testDir := path.Join(crupwd, "tests") @@ -88,14 +88,14 @@ func runTest() { icmd := exec.Command("go", "test") icmd.Stdout = os.Stdout icmd.Stderr = os.Stderr - ColorLog("[TRAC] ============== Test Begin ===================\n") + logger.Info("============== Test Begin ===================") err = icmd.Run() - ColorLog("[TRAC] ============== Test End ===================\n") + logger.Info("============== Test End =====================") if err != nil { - ColorLog("[ERRO] ============== Test failed ===================\n") - ColorLog("[ERRO] %s", err) + logger.Error("============== Test failed ===================") + logger.Errorf("Cause: %s", err) return } - ColorLog("[SUCC] Test finish\n") + logger.Success("Test Completed") } diff --git a/version.go b/version.go index b95c3ea..02e1414 100644 --- a/version.go +++ b/version.go @@ -78,7 +78,7 @@ func getBeegoVersion() string { return "" } if gopath == "" { - err = fmt.Errorf("You should set GOPATH env variable") + err = fmt.Errorf("You need to set GOPATH environment variable") return "" } wgopath := path.SplitList(gopath) @@ -90,11 +90,11 @@ func getBeegoVersion() string { if os.IsNotExist(err) { continue } - ColorLog("[ERRO] Get `beego.go` has error\n") + logger.Error("Error while getting stats of 'beego.go'") } fd, err := os.Open(filename) if err != nil { - ColorLog("[ERRO] Open `beego.go` has error\n") + logger.Error("Error while reading 'beego.go'") continue } reader := bufio.NewReader(fd) @@ -114,7 +114,7 @@ func getBeegoVersion() string { } } - return "Beego not installed. Please install it first: https://github.com/astaxie/beego" + return "Beego is not installed. Please do consider installing it first: https://github.com/astaxie/beego" } func getGoVersion() string { @@ -124,8 +124,7 @@ func getGoVersion() string { ) if cmdOut, err = exec.Command("go", "version").Output(); err != nil { - fmt.Fprintln(os.Stderr, "There was an error running go version command:", err) - os.Exit(2) + logger.Fatalf("There was an error running go version command: %s", err) } return strings.Split(string(cmdOut), " ")[2] } diff --git a/watch.go b/watch.go index 1c8f593..34c03b3 100644 --- a/watch.go +++ b/watch.go @@ -16,7 +16,6 @@ package main import ( "bytes" - "fmt" "github.com/howeyc/fsnotify" "os" "os/exec" @@ -34,11 +33,11 @@ var ( scheduleTime time.Time ) +// NewWatcher starts an fsnotify Watcher on the specified paths func NewWatcher(paths []string, files []string, isgenerate bool) { watcher, err := fsnotify.NewWatcher() if err != nil { - ColorLog("[ERRO] Fail to create new Watcher[ %s ]\n", err) - os.Exit(2) + logger.Fatalf("Failed to create watcher: %s", err) } go func() { @@ -57,14 +56,14 @@ func NewWatcher(paths []string, files []string, isgenerate bool) { mt := getFileModTime(e.Name) if t := eventTime[e.Name]; mt == t { - ColorLog("[SKIP] # %s #\n", e.String()) + logger.Infof(bold("Skipping: ")+"%s", e.String()) isbuild = false } eventTime[e.Name] = mt if isbuild { - ColorLog("[EVEN] %s\n", e) + logger.Infof("Event fired: %s", e) go func() { // Wait 1s before autobuild util there is no file change. scheduleTime = time.Now().Add(1 * time.Second) @@ -76,22 +75,21 @@ func NewWatcher(paths []string, files []string, isgenerate bool) { return } - Autobuild(files, isgenerate) + AutoBuild(files, isgenerate) }() } case err := <-watcher.Error: - ColorLog("[WARN] %s\n", err.Error()) // No need to exit here + logger.Warnf("Watcher error: %s", err.Error()) // No need to exit here } } }() - ColorLog("[INFO] Initializing watcher...\n") + logger.Info("Initializing watcher...") for _, path := range paths { - ColorLog("[TRAC] Directory( %s )\n", path) + logger.Infof(bold("Watching: ")+"%s", path) err = watcher.Watch(path) if err != nil { - ColorLog("[ERRO] Fail to watch directory[ %s ]\n", err) - os.Exit(2) + logger.Fatalf("Failed to watch directory: %s", err) } } @@ -102,25 +100,26 @@ func getFileModTime(path string) int64 { path = strings.Replace(path, "\\", "/", -1) f, err := os.Open(path) if err != nil { - ColorLog("[ERRO] Fail to open file[ %s ]\n", err) + logger.Errorf("Failed to open file on '%s': %s", path, err) return time.Now().Unix() } defer f.Close() fi, err := f.Stat() if err != nil { - ColorLog("[ERRO] Fail to get file information[ %s ]\n", err) + logger.Errorf("Failed to get file stats: %s", err) return time.Now().Unix() } return fi.ModTime().Unix() } -func Autobuild(files []string, isgenerate bool) { +// AutoBuild builds the specified set of files +func AutoBuild(files []string, isgenerate bool) { state.Lock() defer state.Unlock() - ColorLog("[INFO] Start building...\n") + logger.Info("Start building...") os.Chdir(currpath) @@ -162,7 +161,7 @@ func Autobuild(files []string, isgenerate bool) { icmd.Stdout = os.Stdout icmd.Stderr = os.Stderr icmd.Run() - ColorLog("============== generate docs ===================\n") + logger.Info("============== Generate Docs ===================") } if err == nil { @@ -186,35 +185,38 @@ func Autobuild(files []string, isgenerate bool) { } if err != nil { - ColorLog("[ERRO] ============== Build failed ===================\n") + logger.Error("============== Build Failed ===================") return } - ColorLog("[SUCC] Build was successful\n") + logger.Success("Built Successfully!") Restart(appname) } +// Kill kills the running command process func Kill() { defer func() { if e := recover(); e != nil { - fmt.Println("Kill.recover -> ", e) + logger.Infof("Kill recover: %s", e) } }() if cmd != nil && cmd.Process != nil { err := cmd.Process.Kill() if err != nil { - fmt.Println("Kill -> ", err) + logger.Errorf("Error while killing cmd process: %s", err) } } } +// Restart kills the running command process and starts it again func Restart(appname string) { - Debugf("kill running process") + logger.Debugf("Kill running process") Kill() go Start(appname) } +// Start starts the command process func Start(appname string) { - ColorLog("[INFO] Restarting %s ...\n", appname) + logger.Infof("Restarting '%s'...", appname) if strings.Index(appname, "./") == -1 { appname = "./" + appname } @@ -226,17 +228,17 @@ func Start(appname string) { cmd.Env = append(os.Environ(), conf.Envs...) go cmd.Run() - ColorLog("[INFO] %s is running...\n", appname) + logger.Successf("'%s' is running...", appname) started <- true } -// Should ignore filenames generated by -// Emacs, Vim or SublimeText +// shouldIgnoreFile ignores filenames generated by Emacs, Vim or SublimeText. +// It returns true if the file should be ignored, false otherwise. func shouldIgnoreFile(filename string) bool { for _, regex := range ignoredFilesRegExps { r, err := regexp.Compile(regex) if err != nil { - panic("Could not compile the regex: " + regex) + logger.Fatalf("Could not compile regular expression: %s", err) } if r.MatchString(filename) { return true From 63b5a81104944d7ad0a4840907f9e8757a20bcfd Mon Sep 17 00:00:00 2001 From: hcrgm Date: Mon, 14 Nov 2016 19:07:22 +0800 Subject: [PATCH 28/37] Update g_docs.go --- g_docs.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/g_docs.go b/g_docs.go index f3ddc16..07f432f 100644 --- a/g_docs.go +++ b/g_docs.go @@ -145,17 +145,17 @@ func generateDocs(curpath string) { rootapi.Infos.Contact.Name = strings.TrimSpace(s[len("@Name"):]) } else if strings.HasPrefix(s, "@URL") { rootapi.Infos.Contact.URL = strings.TrimSpace(s[len("@URL"):]) - } else if strings.HasPrefix(s, "@License") { - if rootapi.Infos.License == nil { - rootapi.Infos.License = &swagger.License{Name: strings.TrimSpace(s[len("@License"):])} - } else { - rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):]) - } } 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"):])} + } else { + rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):]) } } else if strings.HasPrefix(s, "@Schemes") { rootapi.Schemes = strings.Split(strings.TrimSpace(s[len("@Schemes"):]), ",") From deacdaa6675f03b14160136d7d3bcace5780009e Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Mon, 14 Nov 2016 18:02:29 +0100 Subject: [PATCH 29/37] Fixes the filename and lineNo in debug log message --- apiapp.go | 2 +- g.go | 2 +- g_appcode.go | 2 +- logger.go | 15 ++++++--------- migrate.go | 2 +- run.go | 2 +- test.go | 2 +- util.go | 12 ++++++++++++ watch.go | 5 +++-- 9 files changed, 27 insertions(+), 17 deletions(-) diff --git a/apiapp.go b/apiapp.go index 6ecd3ae..a1442d7 100644 --- a/apiapp.go +++ b/apiapp.go @@ -658,7 +658,7 @@ func checkEnv(appname string) (apppath, packpath string, err error) { gopath := gps[0] logger.Warn("You current workdir is not inside $GOPATH/src") - logger.Debugf("GOPATH: %s", gopath) + logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) gosrcpath := path.Join(gopath, "src") apppath = path.Join(gosrcpath, appname) diff --git a/g.go b/g.go index e3a18d2..b8589b4 100644 --- a/g.go +++ b/g.go @@ -91,7 +91,7 @@ func generateCode(cmd *Command, args []string) int { gopath := gps[0] - logger.Debugf("GOPATH: %s", gopath) + logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) gcmd := args[0] switch gcmd { diff --git a/g_appcode.go b/g_appcode.go index 7778c82..6d9d5a7 100644 --- a/g_appcode.go +++ b/g_appcode.go @@ -954,7 +954,7 @@ func getPackagePath(curpath string) (packpath string) { logger.Fatal("GOPATH environment variable is not set or empty") } - logger.Debugf("GOPATH: %s", gopath) + logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) appsrcpath := "" haspath := false diff --git a/logger.go b/logger.go index 290264b..ded3e9e 100644 --- a/logger.go +++ b/logger.go @@ -19,7 +19,6 @@ import ( "io" "os" "path/filepath" - "runtime" "sync" "sync/atomic" "text/template" @@ -155,7 +154,7 @@ func (l *BeeLogger) mustLog(level int, message string, args ...interface{}) { // mustLogDebug logs a debug message only if debug mode // is enabled. i.e. DEBUG_ENABLED="1" -func (l *BeeLogger) mustLogDebug(message string, args ...interface{}) { +func (l *BeeLogger) mustLogDebug(message string, file string, line int, args ...interface{}) { if !IsDebugEnabled() { return } @@ -163,9 +162,7 @@ func (l *BeeLogger) mustLogDebug(message string, args ...interface{}) { // Change the output to Stderr l.SetOutput(os.Stderr) - // Create the log record and Get the filename - // and the line number of the caller - _, file, line, _ := runtime.Caller(1) + // Create the log record record := LogRecord{ ID: fmt.Sprintf("%04d", atomic.AddUint64(&sequenceNo, 1)), Level: l.getColorLevel(levelDebug), @@ -178,13 +175,13 @@ func (l *BeeLogger) mustLogDebug(message string, args ...interface{}) { } // Debug outputs a debug log message -func (l *BeeLogger) Debug(message string) { - l.mustLogDebug(message) +func (l *BeeLogger) Debug(message string, file string, line int) { + l.mustLogDebug(message, file, line) } // Debugf outputs a formatted debug log message -func (l *BeeLogger) Debugf(message string, vars ...interface{}) { - l.mustLogDebug(message, vars...) +func (l *BeeLogger) Debugf(message string, file string, line int, vars ...interface{}) { + l.mustLogDebug(message, file, line, vars...) } // Info outputs an information log message diff --git a/migrate.go b/migrate.go index 873fbb5..b4fde98 100644 --- a/migrate.go +++ b/migrate.go @@ -73,7 +73,7 @@ func runMigration(cmd *Command, args []string) int { gopath := gps[0] - logger.Debugf("GOPATH: %s", gopath) + logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) // Load the configuration err := loadConfig() diff --git a/run.go b/run.go index 88797ab..bdee001 100644 --- a/run.go +++ b/run.go @@ -99,7 +99,7 @@ func runApp(cmd *Command, args []string) int { logger.Infof("Using '%s' as 'appname'", appname) - logger.Debugf("Current path: %s", currpath) + logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath) if runmode == "prod" || runmode == "dev" { os.Setenv("BEEGO_RUNMODE", runmode) diff --git a/test.go b/test.go index b05c10b..2dcf070 100644 --- a/test.go +++ b/test.go @@ -56,7 +56,7 @@ func testApp(cmd *Command, args []string) int { currpath, _ := os.Getwd() - logger.Debugf("Current path: %s", currpath) + logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath) err := loadConfig() if err != nil { diff --git a/util.go b/util.go index 7229161..c3cc754 100644 --- a/util.go +++ b/util.go @@ -262,3 +262,15 @@ func IsDebugEnabled() bool { debugMode := os.Getenv("DEBUG_ENABLED") return map[string]bool{"1": true, "0": false}[debugMode] } + +// __FILE__ returns the file name in which the function was invoked +func __FILE__() string { + _, file, _, _ := runtime.Caller(1) + return file +} + +// __LINE__ returns the line number at which the function was invoked +func __LINE__() int { + _, _, line, _ := runtime.Caller(1) + return line +} diff --git a/watch.go b/watch.go index 34c03b3..a030c3b 100644 --- a/watch.go +++ b/watch.go @@ -16,7 +16,6 @@ package main import ( "bytes" - "github.com/howeyc/fsnotify" "os" "os/exec" "regexp" @@ -24,6 +23,8 @@ import ( "strings" "sync" "time" + + "github.com/howeyc/fsnotify" ) var ( @@ -209,7 +210,7 @@ func Kill() { // Restart kills the running command process and starts it again func Restart(appname string) { - logger.Debugf("Kill running process") + logger.Debugf("Kill running process", __FILE__(), __LINE__()) Kill() go Start(appname) } From 21fc1775514586a796e1a4f4ded8c567c442a605 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Tue, 15 Nov 2016 12:25:37 +0100 Subject: [PATCH 30/37] Acquire lock when logging --- apiapp.go | 3 +-- logger.go | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apiapp.go b/apiapp.go index a1442d7..ca8cf22 100644 --- a/apiapp.go +++ b/apiapp.go @@ -558,8 +558,7 @@ func createapi(cmd *Command, args []string) int { apppath, packpath, err := checkEnv(args[0]) if err != nil { - fmt.Println(err) - os.Exit(2) + logger.Fatalf("%s", err) } if driver == "" { driver = "mysql" diff --git a/logger.go b/logger.go index ded3e9e..8189a52 100644 --- a/logger.go +++ b/logger.go @@ -61,7 +61,6 @@ type LogRecord struct { var ( logRecordTemplate *template.Template debugLogRecordTemplate *template.Template - debugLogFormat string ) func init() { @@ -141,6 +140,10 @@ func (l *BeeLogger) getColorLevel(level int) string { // mustLog logs the message according to the specified level and arguments. // It panics in case of an error. func (l *BeeLogger) mustLog(level int, message string, args ...interface{}) { + // Acquire the lock + l.mu.Lock() + defer l.mu.Unlock() + // Create the logging record and pass into the output record := LogRecord{ ID: fmt.Sprintf("%04d", atomic.AddUint64(&sequenceNo, 1)), From 6b27ef186af6958793336947098035fb8052c89a Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Wed, 16 Nov 2016 12:33:01 +0100 Subject: [PATCH 31/37] This makes the logger instance a singleton --- bee.go | 2 ++ logger.go | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/bee.go b/bee.go index 4ae5111..645a979 100644 --- a/bee.go +++ b/bee.go @@ -90,6 +90,8 @@ var commands = []*Command{ cmdFix, } +var logger = GetBeeLogger(os.Stdout) + func main() { flag.Usage = usage flag.Parse() diff --git a/logger.go b/logger.go index 8189a52..9689a00 100644 --- a/logger.go +++ b/logger.go @@ -39,7 +39,8 @@ const ( var ( sequenceNo uint64 - logger *BeeLogger + instance *BeeLogger + once sync.Once ) // BeeLogger logs logging records to the specified io.Writer @@ -79,9 +80,15 @@ func init() { MustCheck(err) debugLogRecordTemplate, err = template.New("dbgLogRecordTemplate").Funcs(funcs).Parse(debugLogFormat) MustCheck(err) +} - // Initialize the logger instance with a NewColorWriter output - logger = &BeeLogger{output: NewColorWriter(os.Stdout)} +// GetBeeLogger initializes the logger instance with a NewColorWriter output +// and returns a singleton +func GetBeeLogger(w io.Writer) *BeeLogger { + once.Do(func() { + instance = &BeeLogger{output: NewColorWriter(w)} + }) + return instance } // SetOutput sets the logger output destination From fb9367760415f0e290c2a2793642ab66e31307d7 Mon Sep 17 00:00:00 2001 From: "Ronnie A. Baez Sesto" Date: Wed, 16 Nov 2016 16:39:54 -0400 Subject: [PATCH 32/37] isnull filter needs boolean value for the Filter function With changes suggested by @amrfaissal --- g_appcode.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/g_appcode.go b/g_appcode.go index 7778c82..a109671 100644 --- a/g_appcode.go +++ b/g_appcode.go @@ -1038,7 +1038,11 @@ func GetAll{{modelName}}(query map[string]string, fields []string, sortby []stri for k, v := range query { // rewrite dot-notation to Object__Attribute k = strings.Replace(k, ".", "__", -1) - qs = qs.Filter(k, v) + if strings.Contains(k, "isnull") { + qs = qs.Filter(k, (v == "true" || v == "1")) + } else { + qs = qs.Filter(k, v) + } } // order by: var sortFields []string From ab39926c41fc5d857ec3f239b619a0721ccfd9f7 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 20 Nov 2016 11:46:42 +0100 Subject: [PATCH 33/37] 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 34/37] 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 35/37] 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 36/37] 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 { From e39dc9fb7f36dde1a707fdb53e69aecb21ce028a Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Mon, 21 Nov 2016 15:23:08 +0100 Subject: [PATCH 37/37] #323: Removes the check if the current path is a Beego project --- pack.go | 4 ---- run.go | 4 ++-- util.go | 11 ++++------- version.go | 2 +- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/pack.go b/pack.go index 2695840..30f7b50 100644 --- a/pack.go +++ b/pack.go @@ -478,10 +478,6 @@ func packApp(cmd *Command, args []string) int { logger.Fatalf("Application path does not exist: %s", thePath) } - if isBeegoProject(thePath) == false { - logger.Fatal("Bee does not support non Beego project") - } - logger.Infof("Packaging application on '%s'...", thePath) appName := path.Base(thePath) diff --git a/run.go b/run.go index bdee001..ecf09ae 100644 --- a/run.go +++ b/run.go @@ -76,7 +76,7 @@ func runApp(cmd *Command, args []string) int { appname = path.Base(currpath) currentGoPath = _gopath } else { - logger.Fatalf("No Beego application '%s' found in your GOPATH", currpath) + logger.Fatalf("No application '%s' found in your GOPATH", currpath) } } else { // Check if passed Bee application path/name exists in the GOPATH(s) @@ -85,7 +85,7 @@ func runApp(cmd *Command, args []string) int { currentGoPath = _gopath appname = path.Base(currpath) } else { - logger.Fatalf("No Beego application '%s' found in your GOPATH", args[0]) + logger.Fatalf("No application '%s' found in your GOPATH", args[0]) } if strings.HasSuffix(appname, ".go") && isExist(currpath) { diff --git a/util.go b/util.go index c3cc754..04a3c17 100644 --- a/util.go +++ b/util.go @@ -17,7 +17,6 @@ package main import ( "fmt" "io/ioutil" - "log" "os" "os/exec" "path" @@ -67,7 +66,8 @@ func GetGOPATHs() []string { return paths } -func isBeegoProject(thePath string) bool { +// IsBeegoProject checks whether the current path is a Beego application or not +func IsBeegoProject(thePath string) bool { mainFiles := []string{} hasBeegoRegex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`) // Walk the application path tree to look for main files. @@ -86,7 +86,7 @@ func isBeegoProject(thePath string) bool { }) if err != nil { - log.Fatalf("Unable to walk '%s' tree: %v", thePath, err) + logger.Fatalf("Unable to walk '%s' tree: %v", thePath, err) return false } @@ -116,9 +116,6 @@ func SearchGOPATHs(app string) (bool, string, string) { } if isExist(currentPath) { - if !isBeegoProject(currentPath) { - continue - } return true, gopath, currentPath } } @@ -134,7 +131,7 @@ func askForConfirmation() bool { var response string _, err := fmt.Scanln(&response) if err != nil { - log.Fatal(err) + logger.Fatalf("%s", err) } okayResponses := []string{"y", "Y", "yes", "Yes", "YES"} nokayResponses := []string{"n", "N", "no", "No", "NO"} diff --git a/version.go b/version.go index 02e1414..60dbfd7 100644 --- a/version.go +++ b/version.go @@ -124,7 +124,7 @@ func getGoVersion() string { ) if cmdOut, err = exec.Command("go", "version").Output(); err != nil { - logger.Fatalf("There was an error running go version command: %s", err) + logger.Fatalf("There was an error running 'go version' command: %s", err) } return strings.Split(string(cmdOut), " ")[2] }