Merge pull request #317 from amrfaissal/centralized-logging

New logging infrastructure for Bee
This commit is contained in:
astaxie 2016-11-14 09:24:44 +08:00 committed by GitHub
commit c3c264ddb5
30 changed files with 815 additions and 648 deletions

View File

@ -549,8 +549,7 @@ func createapi(cmd *Command, args []string) int {
w := NewColorWriter(os.Stdout) w := NewColorWriter(os.Stdout)
if len(args) < 1 { if len(args) < 1 {
ColorLog("[ERRO] Argument [appname] is missing\n") logger.Fatal("Argument [appname] is missing")
os.Exit(2)
} }
if len(args) > 1 { if len(args) > 1 {
@ -568,7 +567,7 @@ func createapi(cmd *Command, args []string) int {
if conn == "" { if conn == "" {
} }
ColorLog("[INFO] Creating API...\n") logger.Info("Creating API...")
os.MkdirAll(apppath, 0755) os.MkdirAll(apppath, 0755)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") fmt.Fprintf(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, -1,
), ),
) )
ColorLog("[INFO] Using '%s' as 'driver'\n", driver) logger.Infof("Using '%s' as 'driver'", driver)
ColorLog("[INFO] Using '%s' as 'conn'\n", conn) logger.Infof("Using '%s' as 'conn'", conn)
ColorLog("[INFO] Using '%s' as 'tables'\n", tables) logger.Infof("Using '%s' as 'tables'", tables)
generateAppcode(string(driver), string(conn), "3", string(tables), apppath) generateAppcode(string(driver), string(conn), "3", string(tables), apppath)
} else { } else {
os.Mkdir(path.Join(apppath, "models"), 0755) os.Mkdir(path.Join(apppath, "models"), 0755)
@ -635,15 +634,14 @@ func createapi(cmd *Command, args []string) int {
WriteToFile(path.Join(apppath, "main.go"), WriteToFile(path.Join(apppath, "main.go"),
strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1)) strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1))
} }
ColorLog("[SUCC] New API successfully created!\n") logger.Success("New API successfully created!")
return 0 return 0
} }
func checkEnv(appname string) (apppath, packpath string, err error) { func checkEnv(appname string) (apppath, packpath string, err error) {
gps := GetGOPATHs() gps := GetGOPATHs()
if len(gps) == 0 { if len(gps) == 0 {
ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty") logger.Fatal("GOPATH environment variable is not set or empty")
os.Exit(2)
} }
currpath, _ := os.Getwd() currpath, _ := os.Getwd()
currpath = path.Join(currpath, appname) 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 // In case of multiple paths in the GOPATH, by default
// we use the first path // we use the first path
gopath := gps[0] 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") gosrcpath := path.Join(gopath, "src")
apppath = path.Join(gosrcpath, appname) apppath = path.Join(gosrcpath, appname)
if _, e := os.Stat(apppath); os.IsNotExist(e) == false { if _, e := os.Stat(apppath); os.IsNotExist(e) == false {
err = fmt.Errorf("Cannot create application without removing '%s' first.", apppath) 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 return
} }
packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(path.Separator)), "/") packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(path.Separator)), "/")

View File

@ -66,7 +66,7 @@ func getControllerInfo(path string) (map[string][]string, error) {
files := make([]*source, 0, len(fis)) files := make([]*source, 0, len(fis))
for _, fi := range fis { for _, fi := range fis {
// Only load go files. // Only load Go files
if strings.HasSuffix(fi.Name(), ".go") { if strings.HasSuffix(fi.Name(), ".go") {
f, err := os.Open(path + "/" + fi.Name()) f, err := os.Open(path + "/" + fi.Name())
if err != nil { if err != nil {
@ -107,7 +107,7 @@ func getControllerInfo(path string) (map[string][]string, error) {
return cm, nil return cm, nil
} }
// A source describles a source code file. // source represents a source code file.
type source struct { type source struct {
name string name string
data []byte 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) IsDir() bool { return false }
func (s *source) Sys() interface{} { return nil } 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 { type routerWalker struct {
pdoc *Package pdoc *Package
srcs map[string]*source // Source files. srcs map[string]*source // Source files
fset *token.FileSet 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. // Package represents full information and documentation for a package.
type Package struct { type Package struct {
ImportPath string ImportPath string
Types []*Type // Top-level declarations
// Top-level declarations.
Types []*Type
} }
// Type represents structs and interfaces. // Type represents structs and interfaces.
type Type struct { type Type struct {
Name string // Type name. Name string // Type name
Decl string Decl string
Methods []*Func // Exported methods. Methods []*Func // Exported methods
} }
// Func represents functions // Func represents functions
@ -150,7 +148,7 @@ type Func struct {
// build generates data from source files. // build generates data from source files.
func (w *routerWalker) build(srcs []*source) (*Package, error) { 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) w.srcs = make(map[string]*source)
for _, src := range srcs { for _, src := range srcs {
w.srcs[src.name] = src w.srcs[src.name] = src
@ -158,7 +156,7 @@ func (w *routerWalker) build(srcs []*source) (*Package, error) {
w.fset = token.NewFileSet() w.fset = token.NewFileSet()
// Find the package and associated files. // Find the package and associated files
ctxt := gobuild.Context{ ctxt := gobuild.Context{
GOOS: runtime.GOOS, GOOS: runtime.GOOS,
GOARCH: runtime.GOARCH, GOARCH: runtime.GOARCH,
@ -174,7 +172,7 @@ func (w *routerWalker) build(srcs []*source) (*Package, error) {
} }
bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0) 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) _, nogo := err.(*gobuild.NoGoError)
if err != nil { if err != nil {
if nogo { if nogo {
@ -272,9 +270,8 @@ func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, e
name = strings.TrimPrefix(name, "biogo.") name = strings.TrimPrefix(name, "biogo.")
// It's also common for the last element of the path to contain an // 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 // extra "go" prefix, but not always.
// detect when trimming the "go" prefix is appropriate. // TODO: examine unresolved ids to detect when trimming the "go" prefix is appropriate.
pkg = ast.NewObj(ast.Pkg, name) pkg = ast.NewObj(ast.Pkg, name)
pkg.Data = ast.NewScope(nil) pkg.Data = ast.NewScope(nil)
imports[path] = pkg imports[path] = pkg

39
bale.go
View File

@ -50,19 +50,19 @@ func runBale(cmd *Command, args []string) int {
err := loadConfig() err := loadConfig()
if err != nil { 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.RemoveAll("bale")
os.Mkdir("bale", os.ModePerm) os.Mkdir("bale", os.ModePerm)
// Pack and compress data. // Pack and compress data
for _, p := range conf.Bale.Dirs { for _, p := range conf.Bale.Dirs {
if !isExist(p) { if !isExist(p) {
ColorLog("[WARN] Skipped directory( %s )\n", p) logger.Warnf("Skipped directory: %s", p)
continue continue
} }
ColorLog("[INFO] Packaging directory( %s )\n", p) logger.Infof("Packaging directory: %s", p)
filepath.Walk(p, walkFn) filepath.Walk(p, walkFn)
} }
@ -74,22 +74,21 @@ func runBale(cmd *Command, args []string) int {
fw, err := os.Create("bale.go") fw, err := os.Create("bale.go")
if err != nil { if err != nil {
ColorLog("[ERRO] Fail to create file[ %s ]\n", err) logger.Fatalf("Failed to create file: %s", err)
os.Exit(2)
} }
defer fw.Close() defer fw.Close()
_, err = fw.Write(buf.Bytes()) _, err = fw.Write(buf.Bytes())
if err != nil { if err != nil {
ColorLog("[ERRO] Fail to write data[ %s ]\n", err) logger.Fatalf("Failed to write data: %s", err)
os.Exit(2)
} }
ColorLog("[SUCC] Baled resources successfully!\n") logger.Success("Baled resources successfully!")
return 0 return 0
} }
const ( const (
// BaleHeader ...
BaleHeader = `package main BaleHeader = `package main
import( import(
@ -150,14 +149,13 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
return nil return nil
} }
// Open resource files. // Open resource files
fr, err := os.Open(resPath) fr, err := os.Open(resPath)
if err != nil { if err != nil {
ColorLog("[ERRO] Fail to read file[ %s ]\n", err) logger.Fatalf("Failed to read file: %s", err)
os.Exit(2)
} }
// Convert path. // Convert path
resPath = strings.Replace(resPath, "_", "_0_", -1) resPath = strings.Replace(resPath, "_", "_0_", -1)
resPath = strings.Replace(resPath, ".", "_1_", -1) resPath = strings.Replace(resPath, ".", "_1_", -1)
resPath = strings.Replace(resPath, "-", "_2_", -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) resPath = strings.Replace(resPath, sep, "_4_", -1)
// Create corresponding Go source files. // Create corresponding Go source files
os.MkdirAll(path.Dir(resPath), os.ModePerm) os.MkdirAll(path.Dir(resPath), os.ModePerm)
fw, err := os.Create("bale/" + resPath + ".go") fw, err := os.Create("bale/" + resPath + ".go")
if err != nil { if err != nil {
ColorLog("[ERRO] Fail to create file[ %s ]\n", err) logger.Fatalf("Failed to create file: %s", err)
os.Exit(2)
} }
defer fw.Close() defer fw.Close()
// Write header. // Write header
fmt.Fprintf(fw, Header, resPath) fmt.Fprintf(fw, Header, resPath)
// Copy and compress data. // Copy and compress data
gz := gzip.NewWriter(&ByteWriter{Writer: fw}) gz := gzip.NewWriter(&ByteWriter{Writer: fw})
io.Copy(gz, fr) io.Copy(gz, fr)
gz.Close() gz.Close()
@ -202,6 +199,7 @@ func filterSuffix(name string) bool {
} }
const ( const (
// Header ...
Header = `package bale Header = `package bale
import( import(
@ -212,6 +210,7 @@ import(
func R%s() []byte { func R%s() []byte {
gz, err := gzip.NewReader(bytes.NewBuffer([]byte{` gz, err := gzip.NewReader(bytes.NewBuffer([]byte{`
// Footer ...
Footer = ` Footer = `
})) }))
@ -229,6 +228,7 @@ func R%s() []byte {
var newline = []byte{'\n'} var newline = []byte{'\n'}
// ByteWriter ...
type ByteWriter struct { type ByteWriter struct {
io.Writer io.Writer
c int c int
@ -244,12 +244,9 @@ func (w *ByteWriter) Write(p []byte) (n int, err error) {
w.Writer.Write(newline) w.Writer.Write(newline)
w.c = 0 w.c = 0
} }
fmt.Fprintf(w.Writer, "0x%02x,", p[n]) fmt.Fprintf(w.Writer, "0x%02x,", p[n])
w.c++ w.c++
} }
n++ n++
return return
} }

View File

@ -6,7 +6,6 @@ import (
"os" "os"
"runtime" "runtime"
"text/template" "text/template"
"time"
) )
type vars struct { type vars struct {
@ -21,25 +20,17 @@ type vars struct {
BeegoVersion string 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 // InitBanner loads the banner and prints it to output
// All errors are ignored, the application will not // All errors are ignored, the application will not
// print the banner in case of error. // print the banner in case of error.
func InitBanner(out io.Writer, in io.Reader) { func InitBanner(out io.Writer, in io.Reader) {
if in == nil { if in == nil {
ColorLog("[ERRO] The input is nil\n") logger.Fatal("The input is nil")
os.Exit(2)
} }
banner, err := ioutil.ReadAll(in) banner, err := ioutil.ReadAll(in)
if err != nil { if err != nil {
ColorLog("[ERRO] Error trying to read the banner\n") logger.Fatalf("Error while trying to read the banner: %s", err)
ColorLog("[HINT] %v\n", err)
os.Exit(2)
} }
show(out, string(banner)) show(out, string(banner))
@ -51,9 +42,7 @@ func show(out io.Writer, content string) {
Parse(content) Parse(content)
if err != nil { if err != nil {
ColorLog("[ERRO] Cannot parse the banner template\n") logger.Fatalf("Cannot parse the banner template: %s", err)
ColorLog("[HINT] %v\n", err)
os.Exit(2)
} }
err = t.Execute(out, vars{ err = t.Execute(out, vars{
@ -67,7 +56,5 @@ func show(out io.Writer, content string) {
version, version,
getBeegoVersion(), getBeegoVersion(),
}) })
if err != nil { MustCheck(err)
panic(err)
}
} }

2
bee.go
View File

@ -27,6 +27,7 @@ import (
const version = "1.5.2" const version = "1.5.2"
// Command is the unit of execution
type Command struct { type Command struct {
// Run runs the command. // Run runs the command.
// The args are the arguments after the command name. // The args are the arguments after the command name.
@ -60,6 +61,7 @@ func (c *Command) Name() string {
return name return name
} }
// Usage puts out the usage for the command.
func (c *Command) Usage() { func (c *Command) Usage() {
fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine) fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(string(c.Long))) fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(string(c.Long)))

View File

@ -109,12 +109,12 @@ func (v *annotationVisitor) Visit(n ast.Node) ast.Visitor {
v.ignoreName() v.ignoreName()
ast.Walk(v, n.Type) ast.Walk(v, n.Type)
case *ast.Field: case *ast.Field:
for _ = range n.Names { for range n.Names {
v.ignoreName() v.ignoreName()
} }
ast.Walk(v, n.Type) ast.Walk(v, n.Type)
case *ast.ValueSpec: case *ast.ValueSpec:
for _ = range n.Names { for range n.Names {
v.add(AnchorAnnotation, "") v.add(AnchorAnnotation, "")
} }
if n.Type != nil { if n.Type != nil {

View File

@ -14,7 +14,10 @@
package main package main
import "io" import (
"fmt"
"io"
)
type outputMode int type outputMode int
@ -49,3 +52,77 @@ func NewModeColorWriter(w io.Writer, mode outputMode) io.Writer {
} }
return w 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))
}

View File

@ -419,7 +419,7 @@ func (cw *colorWriter) Write(p []byte) (int, error) {
} }
if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode { 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 r += nw
} }

18
conf.go
View File

@ -25,7 +25,7 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
const ConfVer = 0 const confVer = 0
var defaultConf = `{ var defaultConf = `{
"version": 0, "version": 0,
@ -89,20 +89,20 @@ func loadConfig() (err error) {
} }
if fileInfo.Name() == "bee.json" { if fileInfo.Name() == "bee.json" {
ColorLog("[INFO] Loading configuration from 'bee.json'...\n") logger.Info("Loading configuration from 'bee.json'...")
err = parseJSON(path, conf) err = parseJSON(path, conf)
if err != nil { 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 err
} }
return io.EOF return io.EOF
} }
if fileInfo.Name() == "Beefile" { if fileInfo.Name() == "Beefile" {
ColorLog("[INFO] Loading configuration from 'Beefile'...\n") logger.Info("Loading configuration from 'Beefile'...")
err = parseYAML(path, conf) err = parseYAML(path, conf)
if err != nil { 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 err
} }
return io.EOF return io.EOF
@ -113,7 +113,7 @@ func loadConfig() (err error) {
// In case no configuration file found or an error different than io.EOF, // In case no configuration file found or an error different than io.EOF,
// fallback to default configuration // fallback to default configuration
if err != io.EOF { if err != io.EOF {
ColorLog("[INFO] Loading default configuration...\n") logger.Info("Loading default configuration...")
err = json.Unmarshal([]byte(defaultConf), &conf) err = json.Unmarshal([]byte(defaultConf), &conf)
if err != nil { if err != nil {
return return
@ -124,9 +124,9 @@ func loadConfig() (err error) {
err = nil err = nil
// Check format version // Check format version
if conf.Version != ConfVer { if conf.Version != confVer {
ColorLog("[WARN] Your bee.json is outdated. Please do consider updating it.\n") logger.Warn("Your configuration file is outdated. Please do consider updating it.")
ColorLog("[HINT] Compare bee.json under bee source code path and yours\n") logger.Hint("Check the latest version of bee's configuration file.")
} }
// Set variables // Set variables

21
fix.go
View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"go/parser" "go/parser"
"go/token" "go/token"
"io/ioutil" "io/ioutil"
@ -8,7 +9,6 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"fmt"
) )
var cmdFix = &Command{ var cmdFix = &Command{
@ -28,11 +28,12 @@ func init() {
func runFix(cmd *Command, args []string) int { func runFix(cmd *Command, args []string) int {
ShowShortVersionBanner() ShowShortVersionBanner()
ColorLog("[INFO] Upgrading the application...\n") logger.Info("Upgrading the application...")
dir, err := os.Getwd() dir, err := os.Getwd()
if err != nil { 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 { filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if info.IsDir() { if info.IsDir() {
if strings.HasPrefix(info.Name(), ".") { if strings.HasPrefix(info.Name(), ".") {
@ -49,11 +50,11 @@ func runFix(cmd *Command, args []string) int {
err = fixFile(path) err = fixFile(path)
fmt.Println("\tfix\t", path) fmt.Println("\tfix\t", path)
if err != nil { if err != nil {
ColorLog("[ERRO] Could not fix file: %s\n", err) logger.Errorf("Could not fix file: %s", err)
} }
return err return err
}) })
ColorLog("[INFO] Upgrade done!\n") logger.Success("Upgrade done!")
return 0 return 0
} }
@ -167,18 +168,18 @@ func fixFile(file string) error {
} }
fixed := rp.Replace(string(content)) fixed := rp.Replace(string(content))
// forword the RequestBody from the replace // Forword the RequestBody from the replace
// "Input.Request", "Input.Context.Request", // "Input.Request", "Input.Context.Request",
fixed = strings.Replace(fixed, "Input.Context.RequestBody", "Input.RequestBody", -1) fixed = strings.Replace(fixed, "Input.Context.RequestBody", "Input.RequestBody", -1)
// regexp replace // Regexp replace
pareg := regexp.MustCompile(`(Input.Params\[")(.*)("])`) pareg := regexp.MustCompile(`(Input.Params\[")(.*)("])`)
fixed = pareg.ReplaceAllString(fixed, "Input.Param(\"$2\")") fixed = pareg.ReplaceAllString(fixed, "Input.Param(\"$2\")")
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])(\s)(=)(\s)(.*)`) pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])(\s)(=)(\s)(.*)`)
fixed = pareg.ReplaceAllString(fixed, "Input.SetData(\"$2\", $7)") fixed = pareg.ReplaceAllString(fixed, "Input.SetData(\"$2\", $7)")
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])`) pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])`)
fixed = pareg.ReplaceAllString(fixed, "Input.Data(\"$2\")") 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*)([^\*.]*)(\))`) pareg = regexp.MustCompile(`(\.Put\(\")(.*)(\",)(\s)(.*)(,\s*)([^\*.]*)(\))`)
if pareg.MatchString(fixed) && strings.HasSuffix(file, ".go") { if pareg.MatchString(fixed) && strings.HasSuffix(file, ".go") {
fixed = pareg.ReplaceAllString(fixed, ".Put(\"$2\", $5, $7*time.Second)") 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) 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") { if strings.Contains(file, "docs.go") {
fixed = strings.Replace(fixed, "v.Apis", "v.APIs", -1) fixed = strings.Replace(fixed, "v.Apis", "v.APIs", -1)
} }
// replace the config file // Replace the config file
if strings.HasSuffix(file, ".conf") { if strings.HasSuffix(file, ".conf") {
fixed = strings.Replace(fixed, "HttpCertFile", "HTTPSCertFile", -1) fixed = strings.Replace(fixed, "HttpCertFile", "HTTPSCertFile", -1)
fixed = strings.Replace(fixed, "HttpKeyFile", "HTTPSKeyFile", -1) fixed = strings.Replace(fixed, "HttpKeyFile", "HTTPSKeyFile", -1)

63
g.go
View File

@ -81,29 +81,28 @@ func generateCode(cmd *Command, args []string) int {
currpath, _ := os.Getwd() currpath, _ := os.Getwd()
if len(args) < 1 { if len(args) < 1 {
ColorLog("[ERRO] command is missing\n") logger.Fatal("Command is missing")
os.Exit(2)
} }
gps := GetGOPATHs() gps := GetGOPATHs()
if len(gps) == 0 { if len(gps) == 0 {
ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty") logger.Fatal("GOPATH environment variable is not set or empty")
os.Exit(2)
} }
gopath := gps[0] gopath := gps[0]
Debugf("GOPATH: %s", gopath)
logger.Debugf("GOPATH: %s", gopath)
gcmd := args[0] gcmd := args[0]
switch gcmd { switch gcmd {
case "scaffold": case "scaffold":
if len(args) < 2 { if len(args) < 2 {
ColorLog("[ERRO] Wrong number of arguments\n") logger.Fatal("Wrong number of arguments. Run: bee help generate")
ColorLog("[HINT] Usage: bee generate scaffold [scaffoldname] [-fields=\"\"]\n")
os.Exit(2)
} }
// Load the configuration
err := loadConfig() err := loadConfig()
if err != nil { 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:]) cmd.Flag.Parse(args[2:])
if driver == "" { if driver == "" {
@ -119,19 +118,18 @@ func generateCode(cmd *Command, args []string) int {
} }
} }
if fields == "" { if fields == "" {
ColorLog("[ERRO] Wrong number of arguments\n") logger.Hint("fields option should not be empty, i.e. -fields=\"title:string,body:text\"")
ColorLog("[HINT] Usage: bee generate scaffold [scaffoldname] [-fields=\"title:string,body:text\"]\n") logger.Fatal("Wrong number of arguments. Run: bee help generate")
os.Exit(2)
} }
sname := args[1] sname := args[1]
generateScaffold(sname, fields.String(), currpath, driver.String(), conn.String()) generateScaffold(sname, fields.String(), currpath, driver.String(), conn.String())
case "docs": case "docs":
generateDocs(currpath) generateDocs(currpath)
case "appcode": case "appcode":
// load config // Load the configuration
err := loadConfig() err := loadConfig()
if err != nil { 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:]) cmd.Flag.Parse(args[1:])
if driver == "" { if driver == "" {
@ -153,20 +151,20 @@ func generateCode(cmd *Command, args []string) int {
if level == "" { if level == "" {
level = "3" level = "3"
} }
ColorLog("[INFO] Using '%s' as 'driver'\n", driver) logger.Infof("Using '%s' as 'driver'", driver)
ColorLog("[INFO] Using '%s' as 'conn'\n", conn) logger.Infof("Using '%s' as 'conn'", conn)
ColorLog("[INFO] Using '%s' as 'tables'\n", tables) logger.Infof("Using '%s' as 'tables'", tables)
ColorLog("[INFO] Using '%s' as 'level'\n", level) logger.Infof("Using '%s' as 'level'", level)
generateAppcode(driver.String(), conn.String(), level.String(), tables.String(), currpath) generateAppcode(driver.String(), conn.String(), level.String(), tables.String(), currpath)
case "migration": case "migration":
if len(args) < 2 { if len(args) < 2 {
ColorLog("[ERRO] Wrong number of arguments\n") logger.Fatal("Wrong number of arguments. Run: bee help generate")
ColorLog("[HINT] Usage: bee generate migration [migrationname] [-fields=\"\"]\n")
os.Exit(2)
} }
cmd.Flag.Parse(args[2:]) cmd.Flag.Parse(args[2:])
mname := args[1] mname := args[1]
ColorLog("[INFO] Using '%s' as migration name\n", mname)
logger.Infof("Using '%s' as migration name", mname)
upsql := "" upsql := ""
downsql := "" downsql := ""
if fields != "" { if fields != "" {
@ -180,21 +178,16 @@ func generateCode(cmd *Command, args []string) int {
cname := args[1] cname := args[1]
generateController(cname, currpath) generateController(cname, currpath)
} else { } else {
ColorLog("[ERRO] Wrong number of arguments\n") logger.Fatal("Wrong number of arguments. Run: bee help generate")
ColorLog("[HINT] Usage: bee generate controller [controllername]\n")
os.Exit(2)
} }
case "model": case "model":
if len(args) < 2 { if len(args) < 2 {
ColorLog("[ERRO] Wrong number of arguments\n") logger.Fatal("Wrong number of arguments. Run: bee help generate")
ColorLog("[HINT] Usage: bee generate model [modelname] [-fields=\"\"]\n")
os.Exit(2)
} }
cmd.Flag.Parse(args[2:]) cmd.Flag.Parse(args[2:])
if fields == "" { if fields == "" {
ColorLog("[ERRO] Wrong number of arguments\n") logger.Hint("fields option should not be empty, i.e. -fields=\"title:string,body:text\"")
ColorLog("[HINT] Usage: bee generate model [modelname] [-fields=\"title:string,body:text\"]\n") logger.Fatal("Wrong number of arguments. Run: bee help generate")
os.Exit(2)
} }
sname := args[1] sname := args[1]
generateModel(sname, fields.String(), currpath) generateModel(sname, fields.String(), currpath)
@ -203,13 +196,11 @@ func generateCode(cmd *Command, args []string) int {
cname := args[1] cname := args[1]
generateView(cname, currpath) generateView(cname, currpath)
} else { } else {
ColorLog("[ERRO] Wrong number of arguments\n") logger.Fatal("Wrong number of arguments. Run: bee help generate")
ColorLog("[HINT] Usage: bee generate view [viewpath]\n")
os.Exit(2)
} }
default: 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 return 0
} }

View File

@ -18,7 +18,6 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"os" "os"
"os/exec"
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
@ -39,7 +38,7 @@ type DbTransformer interface {
GetTableNames(conn *sql.DB) []string GetTableNames(conn *sql.DB) []string
GetConstraints(conn *sql.DB, table *Table, blackList map[string]bool) GetConstraints(conn *sql.DB, table *Table, blackList map[string]bool)
GetColumns(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 // MysqlDB is the MySQL version of DbTransformer
@ -265,9 +264,7 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
case "3": case "3":
mode = OModel | OController | ORouter mode = OModel | OController | ORouter
default: default:
ColorLog("[ERRO] Invalid 'level' option: %s\n", level) logger.Fatal("Invalid level value. Must be either \"1\", \"2\", or \"3\"")
ColorLog("[HINT] Level must be either 1, 2 or 3\n")
os.Exit(2)
} }
var selectedTables map[string]bool var selectedTables map[string]bool
if tables != "" { if tables != "" {
@ -280,12 +277,9 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
case "mysql": case "mysql":
case "postgres": case "postgres":
case "sqlite": case "sqlite":
ColorLog("[ERRO] Generating app code from SQLite database is not supported yet.\n") logger.Fatal("Generating app code from SQLite database is not supported yet.")
os.Exit(2)
default: default:
ColorLog("[ERRO] Unknown database driver: %s\n", driver) logger.Fatal("Unknown database driver. Must be either \"mysql\", \"postgres\" or \"sqlite\"")
ColorLog("[HINT] Driver must be one of mysql, postgres or sqlite\n")
os.Exit(2)
} }
gen(driver, connStr, mode, selectedTables, currpath) 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) { func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, apppath string) {
db, err := sql.Open(dbms, connStr) db, err := sql.Open(dbms, connStr)
if err != nil { if err != nil {
ColorLog("[ERRO] Could not connect to %s database: %s, %s\n", dbms, connStr, err) logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err)
os.Exit(2)
} }
defer db.Close() defer db.Close()
if trans, ok := dbDriver[dbms]; ok { if trans, ok := dbDriver[dbms]; ok {
ColorLog("[INFO] Analyzing database tables...\n") logger.Info("Analyzing database tables...")
tableNames := trans.GetTableNames(db) tableNames := trans.GetTableNames(db)
tables := getTableObjects(tableNames, db, trans) tables := getTableObjects(tableNames, db, trans)
mvcPath := new(MvcPath) mvcPath := new(MvcPath)
@ -311,25 +304,21 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, ap
pkgPath := getPackagePath(apppath) pkgPath := getPackagePath(apppath)
writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
} else { } else {
ColorLog("[ERRO] Generating app code from %s database is not supported yet.\n", dbms) logger.Fatalf("Generating app code from '%s' database is not supported yet.", dbms)
os.Exit(2)
} }
} }
// 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) { func (*MysqlDB) GetTableNames(db *sql.DB) (tables []string) {
rows, err := db.Query("SHOW TABLES") rows, err := db.Query("SHOW TABLES")
if err != nil { if err != nil {
ColorLog("[ERRO] Could not show tables\n") logger.Fatalf("Could not show tables: %s", err)
ColorLog("[HINT] Check your connection string\n")
os.Exit(2)
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var name string var name string
if err := rows.Scan(&name); err != nil { if err := rows.Scan(&name); err != nil {
ColorLog("[ERRO] Could not show tables\n") logger.Fatalf("Could not show tables: %s", err)
os.Exit(2)
} }
tables = append(tables, name) tables = append(tables, name)
} }
@ -358,8 +347,8 @@ func getTableObjects(tableNames []string, db *sql.DB, dbTransformer DbTransforme
return return
} }
// getConstraints gets primary key, unique key and foreign keys of a table from information_schema // GetConstraints gets primary key, unique key and foreign keys of a table from
// and fill in Table struct // information_schema and fill in the Table struct
func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bool) { func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bool) {
rows, err := db.Query( rows, err := db.Query(
`SELECT `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 = ?`, 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, table.Name, table.Name) // u.position_in_unique_constraint,
if err != nil { if err != nil {
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for PK/UK/FK information\n") logger.Fatal("Could not query INFORMATION_SCHEMA for PK/UK/FK information")
os.Exit(2)
} }
for rows.Next() { for rows.Next() {
var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte
if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil { 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") logger.Fatal("Could not read INFORMATION_SCHEMA for PK/UK/FK information")
os.Exit(2)
} }
constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos := constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos :=
string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes), 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 table.Pk = columnName
} else { } else {
table.Pk = "" 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 // registering blacklisted tables
blackList[table.Name] = true 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 // GetColumns retrieves columns details from
// and fill in the Column struct // information_schema and fill in the Column struct
func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[string]bool) { func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[string]bool) {
// retrieve columns // retrieve columns
colDefRows, _ := db.Query( colDefRows, err := db.Query(
`SELECT `SELECT
column_name, data_type, column_type, is_nullable, column_default, extra column_name, data_type, column_type, is_nullable, column_default, extra
FROM FROM
@ -418,20 +405,28 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
WHERE WHERE
table_schema = database() AND table_name = ?`, table_schema = database() AND table_name = ?`,
table.Name) table.Name)
if err != nil {
logger.Fatalf("Could not query the database: %s", err)
}
defer colDefRows.Close() defer colDefRows.Close()
for colDefRows.Next() { for colDefRows.Next() {
// datatype as bytes so that SQL <null> values can be retrieved // datatype as bytes so that SQL <null> values can be retrieved
var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte
if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil { if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil {
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for column information\n") logger.Fatal("Could not query INFORMATION_SCHEMA for column information")
os.Exit(2)
} }
colName, dataType, columnType, isNullable, columnDefault, extra := colName, dataType, columnType, isNullable, columnDefault, extra :=
string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes) string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes)
// create a column // create a column
col := new(Column) col := new(Column)
col.Name = camelCase(colName) 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 info
tag := new(OrmTag) tag := new(OrmTag)
tag.Column = colName tag.Column = colName
@ -466,7 +461,10 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
if isSQLSignedIntType(dataType) { if isSQLSignedIntType(dataType) {
sign := extractIntSignness(columnType) sign := extractIntSignness(columnType)
if sign == "unsigned" && extra != "auto_increment" { 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) { 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 // 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{} var typeMapping = map[string]string{}
typeMapping = typeMappingMysql typeMapping = typeMappingMysql
if v, ok := typeMapping[sqlType]; ok { if v, ok := typeMapping[sqlType]; ok {
return v return v, nil
} }
ColorLog("[ERRO] data type (%s) not found!\n", sqlType) return "", fmt.Errorf("data type '%s' not found", sqlType)
os.Exit(2)
return goType
} }
// GetTableNames for PostgreSQL // GetTableNames for PostgreSQL
@ -519,16 +515,14 @@ func (*PostgresDB) GetTableNames(db *sql.DB) (tables []string) {
table_type = 'BASE TABLE' AND table_type = 'BASE TABLE' AND
table_schema NOT IN ('pg_catalog', 'information_schema')`) table_schema NOT IN ('pg_catalog', 'information_schema')`)
if err != nil { if err != nil {
ColorLog("[ERRO] Could not show tables: %s\n", err) logger.Fatalf("Could not show tables: %s", err)
ColorLog("[HINT] Check your connection string\n")
os.Exit(2)
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var name string var name string
if err := rows.Scan(&name); err != nil { if err := rows.Scan(&name); err != nil {
ColorLog("[ERRO] Could not show tables\n") logger.Fatalf("Could not show tables: %s", err)
os.Exit(2)
} }
tables = append(tables, name) tables = append(tables, name)
} }
@ -558,14 +552,13 @@ func (*PostgresDB) GetConstraints(db *sql.DB, table *Table, blackList map[string
AND u.table_name = $2`, AND u.table_name = $2`,
table.Name, table.Name) // u.position_in_unique_constraint, table.Name, table.Name) // u.position_in_unique_constraint,
if err != nil { if err != nil {
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s\n", err) logger.Fatalf("Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s", err)
os.Exit(2)
} }
for rows.Next() { for rows.Next() {
var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte
if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil { 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") logger.Fatalf("Could not read INFORMATION_SCHEMA for PK/UK/FK information: %s", err)
os.Exit(2)
} }
constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos := constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos :=
string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes), string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes),
@ -595,7 +588,7 @@ func (*PostgresDB) GetConstraints(db *sql.DB, table *Table, blackList map[string
// GetColumns for PostgreSQL // GetColumns for PostgreSQL
func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map[string]bool) { func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map[string]bool) {
// retrieve columns // retrieve columns
colDefRows, _ := db.Query( colDefRows, err := db.Query(
`SELECT `SELECT
column_name, column_name,
data_type, 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') table_catalog = current_database() AND table_schema NOT IN ('pg_catalog', 'information_schema')
AND table_name = $1`, AND table_name = $1`,
table.Name) table.Name)
if err != nil {
logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err)
}
defer colDefRows.Close() defer colDefRows.Close()
for colDefRows.Next() { for colDefRows.Next() {
// datatype as bytes so that SQL <null> values can be retrieved // datatype as bytes so that SQL <null> values can be retrieved
var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte
if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil { if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil {
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for column information\n") logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err)
os.Exit(2)
} }
colName, dataType, columnType, isNullable, columnDefault, extra := colName, dataType, columnType, isNullable, columnDefault, extra :=
string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes) string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes)
// create a column // Create a column
col := new(Column) col := new(Column)
col.Name = camelCase(colName) 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 info
tag := new(OrmTag) tag := new(OrmTag)
tag.Column = colName 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 // 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 { if v, ok := typeMappingPostgres[sqlType]; ok {
return v return v, nil
} }
ColorLog("[ERRO] data type (%s) not found!\n", sqlType) return "", fmt.Errorf("data type '%s' not found", sqlType)
os.Exit(2)
return goType
} }
// deleteAndRecreatePaths removes several directories completely // deleteAndRecreatePaths removes several directories completely
@ -717,15 +715,15 @@ func createPaths(mode byte, paths *MvcPath) {
// Newly geneated files will be inside these folders. // Newly geneated files will be inside these folders.
func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
if (OModel & mode) == OModel { if (OModel & mode) == OModel {
ColorLog("[INFO] Creating model files...\n") logger.Info("Creating model files...")
writeModelFiles(tables, paths.ModelPath, selectedTables) writeModelFiles(tables, paths.ModelPath, selectedTables)
} }
if (OController & mode) == OController { if (OController & mode) == OController {
ColorLog("[INFO] Creating controller files...\n") logger.Info("Creating controller files...")
writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath) writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath)
} }
if (ORouter & mode) == ORouter { if (ORouter & mode) == ORouter {
ColorLog("[INFO] Creating router files...\n") logger.Info("Creating router files...")
writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath) 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 f *os.File
var err error var err error
if isExist(fpath) { 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() { if askForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil { if err != nil {
ColorLog("[WARN] %v\n", err) logger.Warnf("%s", err)
continue continue
} }
} else { } else {
ColorLog("[WARN] Skipped create file '%s'\n", fpath) logger.Warnf("Skipped create file '%s'", fpath)
continue continue
} }
} else { } else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil { if err != nil {
ColorLog("[WARN] %v\n", err) logger.Warnf("%s", err)
continue 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(template, "{{modelStruct}}", tb.String(), 1)
fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1) fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1)
fileStr = strings.Replace(fileStr, "{{tableName}}", 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 := "" timePkg := ""
importTimePkg := "" importTimePkg := ""
if tb.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, "{{timePkg}}", timePkg, -1)
fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1)
if _, err := f.WriteString(fileStr); err != nil { if _, err := f.WriteString(fileStr); err != nil {
ColorLog("[ERRO] Could not write model file to %s\n", fpath) logger.Fatalf("Could not write model file to '%s': %s", fpath, err)
os.Exit(2)
} }
CloseFile(f) CloseFile(f)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") 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) w := NewColorWriter(os.Stdout)
for _, tb := range tables { 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 selectedTables != nil {
if _, selected := selectedTables[tb.Name]; !selected { if _, selected := selectedTables[tb.Name]; !selected {
continue continue
@ -811,29 +809,28 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri
var f *os.File var f *os.File
var err error var err error
if isExist(fpath) { 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() { if askForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil { if err != nil {
ColorLog("[WARN] %v\n", err) logger.Warnf("%s", err)
continue continue
} }
} else { } else {
ColorLog("[WARN] Skipped create file '%s'\n", fpath) logger.Warnf("Skipped create file '%s'", fpath)
continue continue
} }
} else { } else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil { if err != nil {
ColorLog("[WARN] %v\n", err) logger.Warnf("%s", err)
continue continue
} }
} }
fileStr := strings.Replace(CtrlTPL, "{{ctrlName}}", camelCase(tb.Name), -1) fileStr := strings.Replace(CtrlTPL, "{{ctrlName}}", camelCase(tb.Name), -1)
fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1) fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1)
if _, err := f.WriteString(fileStr); err != nil { if _, err := f.WriteString(fileStr); err != nil {
ColorLog("[ERRO] Could not write controller file to %s\n", fpath) logger.Fatalf("Could not write controller file to '%s': %s", fpath, err)
os.Exit(2)
} }
CloseFile(f) CloseFile(f)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") 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 var nameSpaces []string
for _, tb := range tables { 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 selectedTables != nil {
if _, selected := selectedTables[tb.Name]; !selected { if _, selected := selectedTables[tb.Name]; !selected {
continue continue
@ -856,63 +853,44 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo
if tb.Pk == "" { if tb.Pk == "" {
continue continue
} }
// add namespaces // Add namespaces
nameSpace := strings.Replace(NamespaceTPL, "{{nameSpace}}", tb.Name, -1) nameSpace := strings.Replace(NamespaceTPL, "{{nameSpace}}", tb.Name, -1)
nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", camelCase(tb.Name), -1) nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", camelCase(tb.Name), -1)
nameSpaces = append(nameSpaces, nameSpace) nameSpaces = append(nameSpaces, nameSpace)
} }
// add export controller // Add export controller
fpath := path.Join(rPath, "router.go") fpath := path.Join(rPath, "router.go")
routerStr := strings.Replace(RouterTPL, "{{nameSpaces}}", strings.Join(nameSpaces, ""), 1) routerStr := strings.Replace(RouterTPL, "{{nameSpaces}}", strings.Join(nameSpaces, ""), 1)
routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1) routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1)
var f *os.File var f *os.File
var err error var err error
if isExist(fpath) { 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() { if askForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil { if err != nil {
ColorLog("[WARN] %v\n", err) logger.Warnf("%s", err)
return return
} }
} else { } else {
ColorLog("[WARN] Skipped create file '%s'\n", fpath) logger.Warnf("Skipped create file '%s'", fpath)
return return
} }
} else { } else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil { if err != nil {
ColorLog("[WARN] %v\n", err) logger.Warnf("%s", err)
return return
} }
} }
if _, err := f.WriteString(routerStr); err != nil { if _, err := f.WriteString(routerStr); err != nil {
ColorLog("[ERRO] Could not write router file to '%s'\n", fpath) logger.Fatalf("Could not write router file to '%s': %s", fpath, err)
os.Exit(2)
} }
CloseFile(f) CloseFile(f)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
formatSourceCode(fpath) 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 { func isSQLTemporalType(t string) bool {
return t == "date" || t == "datetime" || t == "timestamp" || t == "time" 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) { func getPackagePath(curpath string) (packpath string) {
gopath := os.Getenv("GOPATH") gopath := os.Getenv("GOPATH")
Debugf("gopath:%s", gopath)
if gopath == "" { if gopath == "" {
ColorLog("[ERRO] You should set GOPATH in the env") logger.Fatal("GOPATH environment variable is not set or empty")
os.Exit(2)
} }
logger.Debugf("GOPATH: %s", gopath)
appsrcpath := "" appsrcpath := ""
haspath := false haspath := false
wgopath := filepath.SplitList(gopath) wgopath := filepath.SplitList(gopath)
@ -993,13 +971,11 @@ func getPackagePath(curpath string) (packpath string) {
} }
if !haspath { if !haspath {
ColorLog("[ERRO] Can't generate application code outside of GOPATH '%s'\n", gopath) logger.Fatalf("Cannot generate application code outside of GOPATH '%s'", gopath)
os.Exit(2)
} }
if curpath == appsrcpath { if curpath == appsrcpath {
ColorLog("[ERRO] Can't generate application code outside of application PATH \n") logger.Fatal("Cannot generate application code outside of application path")
os.Exit(2)
} }
packpath = strings.Join(strings.Split(curpath[len(appsrcpath)+1:], string(filepath.Separator)), "/") packpath = strings.Join(strings.Split(curpath[len(appsrcpath)+1:], string(filepath.Separator)), "/")

View File

@ -21,9 +21,6 @@ import (
"strings" "strings"
) )
// article
// cms/article
//
func generateController(cname, currpath string) { func generateController(cname, currpath string) {
w := NewColorWriter(os.Stdout) w := NewColorWriter(os.Stdout)
@ -36,15 +33,14 @@ func generateController(cname, currpath string) {
packageName = p[i+1 : len(p)-1] packageName = p[i+1 : len(p)-1]
} }
ColorLog("[INFO] Using '%s' as controller name\n", controllerName) logger.Infof("Using '%s' as controller name", controllerName)
ColorLog("[INFO] Using '%s' as package name\n", packageName) logger.Infof("Using '%s' as package name", packageName)
fp := path.Join(currpath, "controllers", p) fp := path.Join(currpath, "controllers", p)
if _, err := os.Stat(fp); os.IsNotExist(err) { if _, err := os.Stat(fp); os.IsNotExist(err) {
// Create the controller's directory // Create the controller's directory
if err := os.MkdirAll(fp, 0777); err != nil { if err := os.MkdirAll(fp, 0777); err != nil {
ColorLog("[ERRO] Could not create controllers directory: %s\n", err) logger.Fatalf("Could not create controllers directory: %s", err)
os.Exit(2)
} }
} }
@ -56,7 +52,7 @@ func generateController(cname, currpath string) {
var content string var content string
if _, err := os.Stat(modelPath); err == nil { 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) content = strings.Replace(controllerModelTpl, "{{packageName}}", packageName, -1)
pkgPath := getPackagePath(currpath) pkgPath := getPackagePath(currpath)
content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1) content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1)
@ -71,8 +67,7 @@ func generateController(cname, currpath string) {
formatSourceCode(fpath) formatSourceCode(fpath)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
} else { } else {
ColorLog("[ERRO] Could not create controller file: %s\n", err) logger.Fatalf("Could not create controller file: %s", err)
os.Exit(2)
} }
} }

View File

@ -26,7 +26,6 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp" "regexp"
"runtime"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
@ -92,8 +91,7 @@ func parsePackagesFromDir(path string) {
parsePackageFromDir(path) parsePackageFromDir(path)
list, err := ioutil.ReadDir(path) list, err := ioutil.ReadDir(path)
if err != nil { if err != nil {
ColorLog("[ERRO] Can't read directory %s : %s\n", path, err) logger.Fatalf("Cannot read directory '%s': %s", path, err)
os.Exit(1)
} }
for _, item := range list { for _, item := range list {
if item.IsDir() && item.Name() != "vendor" { if item.IsDir() && item.Name() != "vendor" {
@ -108,11 +106,10 @@ func parsePackageFromDir(path string) {
name := info.Name() name := info.Name()
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}, parser.ParseComments) }, parser.ParseComments)
if err != nil { if err != nil {
ColorLog("[ERRO] the model %s parser.ParseDir error: %s\n", path, err) logger.Fatalf("Error while parsing dir at '%s': %s", path, err)
os.Exit(1)
} }
for k, v := range folderPkgs { for k, v := range folderPkgs {
astPkgs[k] = v 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) f, err := parser.ParseFile(fset, path.Join(curpath, "routers", "router.go"), nil, parser.ParseComments)
if err != nil { if err != nil {
ColorLog("[ERRO] parse router.go error\n") logger.Fatalf("Error while parsing router.go: %s", err)
os.Exit(2)
} }
rootapi.Infos = swagger.Information{} rootapi.Infos = swagger.Information{}
rootapi.SwaggerVersion = "2.0" rootapi.SwaggerVersion = "2.0"
//analysis API comments
// Analyse API comments
if f.Comments != nil { if f.Comments != nil {
for _, c := range f.Comments { for _, c := range f.Comments {
for _, s := range strings.Split(c.Text(), "\n") { 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 { for _, im := range f.Imports {
localName := "" localName := ""
if im.Name != nil { if im.Name != nil {
localName = im.Name.Name localName = im.Name.Name
} }
analisyscontrollerPkg(localName, im.Path.Value) analyseControllerPkg(localName, im.Path.Value)
} }
for _, d := range f.Decls { for _, d := range f.Decls {
switch specDecl := d.(type) { switch specDecl := d.(type) {
@ -184,11 +182,11 @@ func generateDocs(curpath string) {
case *ast.AssignStmt: case *ast.AssignStmt:
for _, l := range stmt.Rhs { for _, l := range stmt.Rhs {
if v, ok := l.(*ast.CallExpr); ok { 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" { if selName := v.Fun.(*ast.SelectorExpr).Sel.String(); selName != "NewNamespace" {
continue continue
} }
version, params := analisysNewNamespace(v) version, params := analyseNewNamespace(v)
if rootapi.BasePath == "" && version != "" { if rootapi.BasePath == "" && version != "" {
rootapi.BasePath = version rootapi.BasePath = version
} }
@ -197,12 +195,12 @@ func generateDocs(curpath string) {
case *ast.CallExpr: case *ast.CallExpr:
controllerName := "" controllerName := ""
if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" { if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" {
s, params := analisysNewNamespace(pp) s, params := analyseNewNamespace(pp)
for _, sp := range params { for _, sp := range params {
switch pp := sp.(type) { switch pp := sp.(type) {
case *ast.CallExpr: case *ast.CallExpr:
if pp.Fun.(*ast.SelectorExpr).Sel.String() == "NSInclude" { if pp.Fun.(*ast.SelectorExpr).Sel.String() == "NSInclude" {
controllerName = analisysNSInclude(s, pp) controllerName = analyseNSInclude(s, pp)
if v, ok := controllerComments[controllerName]; ok { if v, ok := controllerComments[controllerName]; ok {
rootapi.Tags = append(rootapi.Tags, swagger.Tag{ rootapi.Tags = append(rootapi.Tags, swagger.Tag{
Name: strings.Trim(s, "/"), Name: strings.Trim(s, "/"),
@ -213,7 +211,7 @@ func generateDocs(curpath string) {
} }
} }
} else if selname == "NSInclude" { } else if selname == "NSInclude" {
controllerName = analisysNSInclude("", pp) controllerName = analyseNSInclude("", pp)
if v, ok := controllerComments[controllerName]; ok { if v, ok := controllerComments[controllerName]; ok {
rootapi.Tags = append(rootapi.Tags, swagger.Tag{ rootapi.Tags = append(rootapi.Tags, swagger.Tag{
Name: controllerName, // if the NSInclude has no prefix, we use the controllername as the 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 // analyseNewNamespace returns version and the others params
func analisysNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) { func analyseNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
for i, p := range ce.Args { for i, p := range ce.Args {
if i == 0 { if i == 0 {
switch pp := p.(type) { switch pp := p.(type) {
@ -265,7 +263,7 @@ func analisysNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
return return
} }
func analisysNSInclude(baseurl string, ce *ast.CallExpr) string { func analyseNSInclude(baseurl string, ce *ast.CallExpr) string {
cname := "" cname := ""
for _, p := range ce.Args { for _, p := range ce.Args {
x := p.(*ast.UnaryExpr).X.(*ast.CompositeLit).Type.(*ast.SelectorExpr) x := p.(*ast.UnaryExpr).X.(*ast.CompositeLit).Type.(*ast.SelectorExpr)
@ -313,7 +311,7 @@ func analisysNSInclude(baseurl string, ce *ast.CallExpr) string {
return cname return cname
} }
func analisyscontrollerPkg(localName, pkgpath string) { func analyseControllerPkg(localName, pkgpath string) {
pkgpath = strings.Trim(pkgpath, "\"") pkgpath = strings.Trim(pkgpath, "\"")
if isSystemPackage(pkgpath) { if isSystemPackage(pkgpath) {
return return
@ -329,7 +327,7 @@ func analisyscontrollerPkg(localName, pkgpath string) {
} }
gopath := os.Getenv("GOPATH") gopath := os.Getenv("GOPATH")
if gopath == "" { if gopath == "" {
panic("please set gopath") logger.Fatal("GOPATH environment variable is not set or empty")
} }
pkgRealpath := "" pkgRealpath := ""
@ -347,18 +345,16 @@ func analisyscontrollerPkg(localName, pkgpath string) {
} }
pkgCache[pkgpath] = struct{}{} pkgCache[pkgpath] = struct{}{}
} else { } else {
ColorLog("[ERRO] the %s pkg not exist in gopath\n", pkgpath) logger.Fatalf("Package '%s' does not exist in the GOPATH", pkgpath)
os.Exit(1)
} }
fileSet := token.NewFileSet() fileSet := token.NewFileSet()
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool { astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
name := info.Name() name := info.Name()
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}, parser.ParseComments) }, parser.ParseComments)
if err != nil { if err != nil {
ColorLog("[ERRO] the %s pkg parser.ParseDir error: %s\n", pkgpath, err) logger.Fatalf("Error while parsing dir at '%s': %s", pkgpath, err)
os.Exit(1)
} }
for _, pkg := range astPkgs { for _, pkg := range astPkgs {
for _, fl := range pkg.Files { for _, fl := range pkg.Files {
@ -367,7 +363,7 @@ func analisyscontrollerPkg(localName, pkgpath string) {
case *ast.FuncDecl: case *ast.FuncDecl:
if specDecl.Recv != nil && len(specDecl.Recv.List) > 0 { if specDecl.Recv != nil && len(specDecl.Recv.List) > 0 {
if t, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr); ok { 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) 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) { switch tp := s.(*ast.TypeSpec).Type.(type) {
case *ast.StructType: case *ast.StructType:
_ = tp.Struct _ = tp.Struct
//parse controller definition comments // Parse controller definition comments
if strings.TrimSpace(specDecl.Doc.Text()) != "" { if strings.TrimSpace(specDecl.Doc.Text()) != "" {
controllerComments[pkgpath+s.(*ast.TypeSpec).Name.String()] = 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 { func isSystemPackage(pkgpath string) bool {
goroot := runtime.GOROOT() goroot := os.Getenv("GOROOT")
if 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)) wg, _ := filepath.EvalSymlinks(filepath.Join(goroot, "src", "pkg", pkgpath))
if utils.FileExists(wg) { if utils.FileExists(wg) {
return true return true
@ -460,8 +457,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
ss = strings.TrimSpace(ss[pos:]) ss = strings.TrimSpace(ss[pos:])
schemaName, pos := peekNextSplitString(ss) schemaName, pos := peekNextSplitString(ss)
if schemaName == "" { if schemaName == "" {
ColorLog("[ERRO][%s.%s] Schema must follow {object} or {array}\n", controllerName, funcName) logger.Fatalf("[%s.%s] Schema must follow {object} or {array}", controllerName, funcName)
os.Exit(-1)
} }
if strings.HasPrefix(schemaName, "[]") { if strings.HasPrefix(schemaName, "[]") {
schemaName = schemaName[2:] schemaName = schemaName[2:]
@ -498,7 +494,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
para := swagger.Parameter{} para := swagger.Parameter{}
p := getparams(strings.TrimSpace(t[len("@Param "):])) p := getparams(strings.TrimSpace(t[len("@Param "):]))
if len(p) < 4 { 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] para.Name = p[0]
switch p[1] { switch p[1] {
@ -513,7 +509,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
case "body": case "body":
break break
default: 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] para.In = p[1]
pp := strings.Split(p[2], ".") pp := strings.Split(p[2], ".")
@ -544,7 +540,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
paraType = typeFormat[0] paraType = typeFormat[0]
paraFormat = typeFormat[1] paraFormat = typeFormat[1]
} else { } 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 { if isArray {
para.Type = "array" para.Type = "array"
@ -697,7 +693,7 @@ func getModel(str string) (objectname string, m swagger.Schema, realTypes []stri
} }
} }
if m.Title == "" { 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 // TODO remove when all type have been supported
//os.Exit(1) //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) { 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) ts, ok := d.Decl.(*ast.TypeSpec)
if !ok { if !ok {
ColorLog("Unknown type without TypeSec: %v\n", d) logger.Fatalf("Unknown type without TypeSec: %v\n", d)
os.Exit(1)
} }
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc... // TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc...
st, ok := ts.Type.(*ast.StructType) 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) mp.Default = str2RealType(res[1], realType)
} else { } 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 { 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 return s
} }

View File

@ -38,9 +38,7 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
case "3": case "3":
mode = OModel | OController | ORouter mode = OModel | OController | ORouter
default: default:
ColorLog("[ERRO] Invalid 'level' option: %s\n", level) logger.Fatal("Invalid 'level' option. Level must be either \"1\", \"2\" or \"3\"")
ColorLog("[HINT] Level must be either 1, 2 or 3\n")
os.Exit(2)
} }
var selectedTables map[string]bool var selectedTables map[string]bool
if tables != "" { if tables != "" {
@ -53,12 +51,9 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
case "mysql": case "mysql":
case "postgres": case "postgres":
case "sqlite": case "sqlite":
ColorLog("[ERRO] Generating app code from SQLite database is not supported yet.\n") logger.Fatal("Generating app code from SQLite database is not supported yet")
os.Exit(2)
default: default:
ColorLog("[ERRO] Unknown database driver: %s\n", driver) logger.Fatalf("Unknown database driver '%s'. Driver must be one of mysql, postgres or sqlite", driver)
ColorLog("[HINT] Driver must be one of mysql, postgres or sqlite\n")
os.Exit(2)
} }
genHprose(driver, connStr, mode, selectedTables, currpath) 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) { func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bool, currpath string) {
db, err := sql.Open(dbms, connStr) db, err := sql.Open(dbms, connStr)
if err != nil { if err != nil {
ColorLog("[ERRO] Could not connect to %s database: %s, %s\n", dbms, connStr, err) logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err)
os.Exit(2)
} }
defer db.Close() defer db.Close()
if trans, ok := dbDriver[dbms]; ok { if trans, ok := dbDriver[dbms]; ok {
ColorLog("[INFO] Analyzing database tables...\n") logger.Info("Analyzing database tables...")
tableNames := trans.GetTableNames(db) tableNames := trans.GetTableNames(db)
tables := getTableObjects(tableNames, db, trans) tables := getTableObjects(tableNames, db, trans)
mvcPath := new(MvcPath) mvcPath := new(MvcPath)
@ -82,8 +76,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo
pkgPath := getPackagePath(currpath) pkgPath := getPackagePath(currpath)
writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
} else { } else {
ColorLog("[ERRO] Generating app code from %s database is not supported yet.\n", dbms) logger.Fatalf("Generating app code from '%s' database is not supported yet", dbms)
os.Exit(2)
} }
} }
@ -92,7 +85,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo
// Newly geneated files will be inside these folders. // Newly geneated files will be inside these folders.
func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
if (OModel & mode) == OModel { if (OModel & mode) == OModel {
ColorLog("[INFO] Creating model files...\n") logger.Info("Creating model files...")
writeHproseModelFiles(tables, paths.ModelPath, selectedTables) writeHproseModelFiles(tables, paths.ModelPath, selectedTables)
} }
} }
@ -113,21 +106,21 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
var f *os.File var f *os.File
var err error var err error
if isExist(fpath) { 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() { if askForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil { if err != nil {
ColorLog("[WARN] %v\n", err) logger.Warnf("%s", err)
continue continue
} }
} else { } else {
ColorLog("[WARN] Skipped create file '%s'\n", fpath) logger.Warnf("Skipped create file '%s'", fpath)
continue continue
} }
} else { } else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil { if err != nil {
ColorLog("[WARN] %v\n", err) logger.Warnf("%s", err)
continue 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, "{{timePkg}}", timePkg, -1)
fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1)
if _, err := f.WriteString(fileStr); err != nil { if _, err := f.WriteString(fileStr); err != nil {
ColorLog("[ERRO] Could not write model file to '%s'\n", fpath) logger.Fatalf("Could not write model file to '%s'", fpath)
os.Exit(2)
} }
CloseFile(f) CloseFile(f)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")

View File

@ -51,12 +51,12 @@ func (m mysqlDriver) generateSQLFromFields(fields string) string {
for i, v := range fds { for i, v := range fds {
kv := strings.SplitN(v, ":", 2) kv := strings.SplitN(v, ":", 2)
if len(kv) != 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 "" return ""
} }
typ, tag := m.getSQLType(kv[1]) typ, tag := m.getSQLType(kv[1])
if typ == "" { 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 "" return ""
} }
if i == 0 && strings.ToLower(kv[0]) != "id" { if i == 0 && strings.ToLower(kv[0]) != "id" {
@ -120,12 +120,12 @@ func (m postgresqlDriver) generateSQLFromFields(fields string) string {
for i, v := range fds { for i, v := range fds {
kv := strings.SplitN(v, ":", 2) kv := strings.SplitN(v, ":", 2)
if len(kv) != 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 "" return ""
} }
typ, tag := m.getSQLType(kv[1]) typ, tag := m.getSQLType(kv[1])
if typ == "" { 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 "" return ""
} }
if i == 0 && strings.ToLower(kv[0]) != "id" { if i == 0 && strings.ToLower(kv[0]) != "id" {
@ -177,7 +177,8 @@ func newDBDriver() DBDriver {
case "postgres": case "postgres":
return postgresqlDriver{} return postgresqlDriver{}
default: 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) { if _, err := os.Stat(migrationFilePath); os.IsNotExist(err) {
// create migrations directory // create migrations directory
if err := os.MkdirAll(migrationFilePath, 0777); err != nil { if err := os.MkdirAll(migrationFilePath, 0777); err != nil {
ColorLog("[ERRO] Could not create migration directory: %s\n", err) logger.Fatalf("Could not create migration directory: %s", err)
os.Exit(2)
} }
} }
// create file // create file
@ -208,8 +208,7 @@ func generateMigration(mname, upsql, downsql, curpath string) {
formatSourceCode(fpath) formatSourceCode(fpath)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
} else { } else {
ColorLog("[ERRO] Could not create migration file: %s\n", err) logger.Fatalf("Could not create migration file: %s", err)
os.Exit(2)
} }
} }

View File

@ -35,19 +35,17 @@ func generateModel(mname, fields, currpath string) {
modelStruct, hastime, err := getStruct(modelName, fields) modelStruct, hastime, err := getStruct(modelName, fields)
if err != nil { if err != nil {
ColorLog("[ERRO] Could not generate the model struct: %s\n", err) logger.Fatalf("Could not generate the model struct: %s", err)
os.Exit(2)
} }
ColorLog("[INFO] Using '%s' as model name\n", modelName) logger.Infof("Using '%s' as model name", modelName)
ColorLog("[INFO] Using '%s' as package name\n", packageName) logger.Infof("Using '%s' as package name", packageName)
fp := path.Join(currpath, "models", p) fp := path.Join(currpath, "models", p)
if _, err := os.Stat(fp); os.IsNotExist(err) { if _, err := os.Stat(fp); os.IsNotExist(err) {
// Create the model's directory // Create the model's directory
if err := os.MkdirAll(fp, 0777); err != nil { if err := os.MkdirAll(fp, 0777); err != nil {
ColorLog("[ERRO] Could not create the model directory: %s\n", err) logger.Fatalf("Could not create the model directory: %s", err)
os.Exit(2)
} }
} }
@ -67,8 +65,7 @@ func generateModel(mname, fields, currpath string) {
formatSourceCode(fpath) formatSourceCode(fpath)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
} else { } else {
ColorLog("[ERRO] Could not create model file: %s\n", err) logger.Fatalf("Could not create model file: %s", err)
os.Exit(2)
} }
} }

View File

@ -3,7 +3,7 @@ package main
import "strings" import "strings"
func generateScaffold(sname, fields, currpath, driver, conn string) { 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 // Generate the model
if askForConfirmation() { if askForConfirmation() {
@ -11,19 +11,19 @@ func generateScaffold(sname, fields, currpath, driver, conn string) {
} }
// Generate the controller // 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() { if askForConfirmation() {
generateController(sname, currpath) generateController(sname, currpath)
} }
// Generate the views // 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() { if askForConfirmation() {
generateView(sname, currpath) generateView(sname, currpath)
} }
// Generate a migration // 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() { if askForConfirmation() {
upsql := "" upsql := ""
downsql := "" downsql := ""
@ -40,9 +40,9 @@ func generateScaffold(sname, fields, currpath, driver, conn string) {
} }
// Run the migration // 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() { if askForConfirmation() {
migrateUpdate(currpath, driver, conn) 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))
} }

View File

@ -25,13 +25,12 @@ import (
func generateView(viewpath, currpath string) { func generateView(viewpath, currpath string) {
w := NewColorWriter(os.Stdout) w := NewColorWriter(os.Stdout)
ColorLog("[INFO] Generating view...\n") logger.Info("Generating view...")
absViewPath := path.Join(currpath, "views", viewpath) absViewPath := path.Join(currpath, "views", viewpath)
err := os.MkdirAll(absViewPath, os.ModePerm) err := os.MkdirAll(absViewPath, os.ModePerm)
if err != nil { if err != nil {
ColorLog("[ERRO] Could not create '%s' view: %s\n", viewpath, err) logger.Fatalf("Could not create '%s' view: %s", viewpath, err)
os.Exit(2)
} }
cfile := path.Join(absViewPath, "index.tpl") cfile := path.Join(absViewPath, "index.tpl")
@ -40,8 +39,7 @@ func generateView(viewpath, currpath string) {
f.WriteString(cfile) f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else { } else {
ColorLog("[ERRO] Could not create view file: %s\n", err) logger.Fatalf("Could not create view file: %s", err)
os.Exit(2)
} }
cfile = path.Join(absViewPath, "show.tpl") cfile = path.Join(absViewPath, "show.tpl")
@ -50,8 +48,7 @@ func generateView(viewpath, currpath string) {
f.WriteString(cfile) f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else { } else {
ColorLog("[ERRO] Could not create view file: %s\n", err) logger.Fatalf("Could not create view file: %s", err)
os.Exit(2)
} }
cfile = path.Join(absViewPath, "create.tpl") cfile = path.Join(absViewPath, "create.tpl")
@ -60,8 +57,7 @@ func generateView(viewpath, currpath string) {
f.WriteString(cfile) f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else { } else {
ColorLog("[ERRO] Could not create view file: %s\n", err) logger.Fatalf("Could not create view file: %s", err)
os.Exit(2)
} }
cfile = path.Join(absViewPath, "edit.tpl") cfile = path.Join(absViewPath, "edit.tpl")
@ -70,7 +66,6 @@ func generateView(viewpath, currpath string) {
f.WriteString(cfile) f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else { } else {
ColorLog("[ERRO] Could not create view file: %s\n", err) logger.Fatalf("Could not create view file: %s", err)
os.Exit(2)
} }
} }

View File

@ -65,7 +65,7 @@ var hproseMaingo = `package main
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"{{.Appname}}/models" "{{.Appname}}/models"
"github.com/hprose/hprose-golang/rpc" "github.com/hprose/hprose-golang/rpc"
@ -90,7 +90,7 @@ func main() {
// Create Http Server // Create Http Server
service := rpc.NewHTTPService() service := rpc.NewHTTPService()
// Use Logger Middleware // Use Logger Middleware
service.AddInvokeHandler(logInvokeHandler) service.AddInvokeHandler(logInvokeHandler)
// Publish Functions // Publish Functions
@ -139,7 +139,7 @@ func main() {
// Create Http Server // Create Http Server
service := rpc.NewHTTPService() service := rpc.NewHTTPService()
// Use Logger Middleware // Use Logger Middleware
service.AddInvokeHandler(logInvokeHandler) service.AddInvokeHandler(logInvokeHandler)
{{HproseFunctionList}} {{HproseFunctionList}}
@ -314,8 +314,7 @@ func createhprose(cmd *Command, args []string) int {
} }
apppath, packpath, err := checkEnv(args[0]) apppath, packpath, err := checkEnv(args[0])
if err != nil { if err != nil {
fmt.Println(err) logger.Fatalf("%s", err)
os.Exit(2)
} }
if driver == "" { if driver == "" {
driver = "mysql" driver = "mysql"
@ -323,7 +322,7 @@ func createhprose(cmd *Command, args []string) int {
if conn == "" { if conn == "" {
} }
ColorLog("[INFO] Creating Hprose application...\n") logger.Info("Creating Hprose application...")
os.MkdirAll(apppath, 0755) os.MkdirAll(apppath, 0755)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") fmt.Fprintf(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)) strings.Replace(hproseconf, "{{.Appname}}", args[0], -1))
if conn != "" { if conn != "" {
ColorLog("[INFO] Using '%s' as 'driver'\n", driver) logger.Infof("Using '%s' as 'driver'", driver)
ColorLog("[INFO] Using '%s' as 'conn'\n", conn) logger.Infof("Using '%s' as 'conn'", conn)
ColorLog("[INFO] Using '%s' as 'tables'\n", tables) logger.Infof("Using '%s' as 'tables'", tables)
generateHproseAppcode(string(driver), string(conn), "1", string(tables), path.Join(curpath, args[0])) generateHproseAppcode(string(driver), string(conn), "1", string(tables), path.Join(curpath, args[0]))
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") fmt.Fprintf(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) maingoContent := strings.Replace(hproseMainconngo, "{{.Appname}}", packpath, -1)
@ -369,6 +368,6 @@ func createhprose(cmd *Command, args []string) int {
WriteToFile(path.Join(apppath, "main.go"), WriteToFile(path.Join(apppath, "main.go"),
strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1)) strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1))
} }
ColorLog("[SUCC] New Hprose application successfully created!\n") logger.Success("New Hprose application successfully created!")
return 0 return 0
} }

260
logger.go Normal file
View File

@ -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 string
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: fmt.Sprintf("%04d", 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: fmt.Sprintf("%04d", 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...)
}

View File

@ -16,14 +16,13 @@ package main
import ( import (
"database/sql" "database/sql"
"fmt"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"runtime"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"runtime"
) )
var cmdMigrate = &Command{ var cmdMigrate = &Command{
@ -69,18 +68,20 @@ func runMigration(cmd *Command, args []string) int {
gps := GetGOPATHs() gps := GetGOPATHs()
if len(gps) == 0 { if len(gps) == 0 {
ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty") logger.Fatal("GOPATH environment variable is not set or empty")
os.Exit(2)
} }
gopath := gps[0]
Debugf("GOPATH: %s", gopath)
// load config gopath := gps[0]
logger.Debugf("GOPATH: %s", gopath)
// Load the configuration
err := loadConfig() err := loadConfig()
if err != nil { 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 { if len(args) != 0 {
cmd.Flag.Parse(args[1:]) 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" mConn = "root:@tcp(127.0.0.1:3306)/test"
} }
} }
ColorLog("[INFO] Using '%s' as 'driver'\n", mDriver) logger.Infof("Using '%s' as 'driver'", mDriver)
ColorLog("[INFO] Using '%s' as 'conn'\n", mConn) logger.Infof("Using '%s' as 'conn'", mConn)
driverStr, connStr := string(mDriver), string(mConn) driverStr, connStr := string(mDriver), string(mConn)
if len(args) == 0 { if len(args) == 0 {
// run all outstanding migrations // run all outstanding migrations
ColorLog("[INFO] Running all outstanding migrations\n") logger.Info("Running all outstanding migrations")
migrateUpdate(currpath, driverStr, connStr) migrateUpdate(currpath, driverStr, connStr)
} else { } else {
mcmd := args[0] mcmd := args[0]
switch mcmd { switch mcmd {
case "rollback": case "rollback":
ColorLog("[INFO] Rolling back the last migration operation\n") logger.Info("Rolling back the last migration operation")
migrateRollback(currpath, driverStr, connStr) migrateRollback(currpath, driverStr, connStr)
case "reset": case "reset":
ColorLog("[INFO] Reseting all migrations\n") logger.Info("Reseting all migrations")
migrateReset(currpath, driverStr, connStr) migrateReset(currpath, driverStr, connStr)
case "refresh": case "refresh":
ColorLog("[INFO] Refreshing all migrations\n") logger.Info("Refreshing all migrations")
migrateRefresh(currpath, driverStr, connStr) migrateRefresh(currpath, driverStr, connStr)
default: default:
ColorLog("[ERRO] Command is missing\n") logger.Fatal("Command is missing")
os.Exit(2)
} }
} }
ColorLog("[SUCC] Migration successful!\n") logger.Success("Migration successful!")
return 0 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 // migrate generates source code, build it, and invoke the binary who does the actual migration
func migrate(goal, currpath, driver, connStr string) { func migrate(goal, currpath, driver, connStr string) {
dir := path.Join(currpath, "database", "migrations") dir := path.Join(currpath, "database", "migrations")
postfix := "" postfix := ""
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
postfix = ".exe" postfix = ".exe"
} }
binary := "m" + postfix binary := "m" + postfix
source := binary + ".go" source := binary + ".go"
// connect to database
// Connect to database
db, err := sql.Open(driver, connStr) db, err := sql.Open(driver, connStr)
if err != nil { if err != nil {
ColorLog("[ERRO] Could not connect to %s: %s\n", driver, connStr) logger.Fatalf("Could not connect to database using '%s': %s", connStr, err)
ColorLog("[ERRO] Error: %v", err.Error())
os.Exit(2)
} }
defer db.Close() defer db.Close()
checkForSchemaUpdateTable(db, driver) checkForSchemaUpdateTable(db, driver)
latestName, latestTime := getLatestMigration(db, goal) latestName, latestTime := getLatestMigration(db, goal)
writeMigrationSourceFile(dir, source, driver, connStr, latestTime, latestName, 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) { func checkForSchemaUpdateTable(db *sql.DB, driver string) {
showTableSQL := showMigrationsTableSQL(driver) showTableSQL := showMigrationsTableSQL(driver)
if rows, err := db.Query(showTableSQL); err != nil { if rows, err := db.Query(showTableSQL); err != nil {
ColorLog("[ERRO] Could not show migrations table: %s\n", err) logger.Fatalf("Could not show migrations table: %s", err)
os.Exit(2)
} else if !rows.Next() { } else if !rows.Next() {
// no migrations table, create anew // No migrations table, create new ones
createTableSQL := createMigrationsTableSQL(driver) createTableSQL := createMigrationsTableSQL(driver)
ColorLog("[INFO] Creating 'migrations' table...\n")
logger.Infof("Creating 'migrations' table...")
if _, err := db.Query(createTableSQL); err != nil { if _, err := db.Query(createTableSQL); err != nil {
ColorLog("[ERRO] Could not create migrations table: %s\n", err) logger.Fatalf("Could not create migrations table: %s", err)
os.Exit(2)
} }
} }
// checking that migrations table schema are expected // Checking that migrations table schema are expected
selectTableSQL := selectMigrationsTableSQL(driver) selectTableSQL := selectMigrationsTableSQL(driver)
if rows, err := db.Query(selectTableSQL); err != nil { if rows, err := db.Query(selectTableSQL); err != nil {
ColorLog("[ERRO] Could not show columns of migrations table: %s\n", err) logger.Fatalf("Could not show columns of migrations table: %s", err)
os.Exit(2)
} else { } else {
for rows.Next() { for rows.Next() {
var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte
if err := rows.Scan(&fieldBytes, &typeBytes, &nullBytes, &keyBytes, &defaultBytes, &extraBytes); err != nil { if err := rows.Scan(&fieldBytes, &typeBytes, &nullBytes, &keyBytes, &defaultBytes, &extraBytes); err != nil {
ColorLog("[ERRO] Could not read column information: %s\n", err) logger.Fatalf("Could not read column information: %s", err)
os.Exit(2)
} }
fieldStr, typeStr, nullStr, keyStr, defaultStr, extraStr := fieldStr, typeStr, nullStr, keyStr, defaultStr, extraStr :=
string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes) string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes)
if fieldStr == "id_migration" { if fieldStr == "id_migration" {
if keyStr != "PRI" || extraStr != "auto_increment" { if keyStr != "PRI" || extraStr != "auto_increment" {
ColorLog("[ERRO] Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s\n", keyStr, extraStr) logger.Hint("Expecting KEY: PRI, EXTRA: auto_increment")
ColorLog("[HINT] Expecting KEY: PRI, EXTRA: auto_increment\n") logger.Fatalf("Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s", keyStr, extraStr)
os.Exit(2)
} }
} else if fieldStr == "name" { } else if fieldStr == "name" {
if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" { if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" {
ColorLog("[ERRO] Column migration.name type mismatch: TYPE: %s, NULL: %s\n", typeStr, nullStr) logger.Hint("Expecting TYPE: varchar, NULL: YES")
ColorLog("[HINT] Expecting TYPE: varchar, NULL: YES\n") logger.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr)
os.Exit(2)
} }
} else if fieldStr == "created_at" { } else if fieldStr == "created_at" {
if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" { if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" {
ColorLog("[ERRO] Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s\n", typeStr, defaultStr) logger.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP")
ColorLog("[HINT] Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP\n") logger.Fatalf("Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s", typeStr, defaultStr)
os.Exit(2)
} }
} }
} }
@ -262,26 +256,22 @@ func selectMigrationsTableSQL(driver string) string {
func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64) { 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" sql := "SELECT name FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1"
if rows, err := db.Query(sql); err != nil { if rows, err := db.Query(sql); err != nil {
ColorLog("[ERRO] Could not retrieve migrations: %s\n", err) logger.Fatalf("Could not retrieve migrations: %s", err)
os.Exit(2)
} else { } else {
if rows.Next() { if rows.Next() {
if err := rows.Scan(&file); err != nil { if err := rows.Scan(&file); err != nil {
ColorLog("[ERRO] Could not read migrations in database: %s\n", err) logger.Fatalf("Could not read migrations in database: %s", err)
os.Exit(2)
} }
createdAtStr := file[len(file)-15:] createdAtStr := file[len(file)-15:]
if t, err := time.Parse("20060102_150405", createdAtStr); err != nil { if t, err := time.Parse("20060102_150405", createdAtStr); err != nil {
ColorLog("[ERRO] Could not parse time: %s\n", err) logger.Fatalf("Could not parse time: %s", err)
os.Exit(2)
} else { } else {
createdAt = t.Unix() createdAt = t.Unix()
} }
} else { } else {
// migration table has no 'update' record, no point rolling back // migration table has no 'update' record, no point rolling back
if goal == "rollback" { if goal == "rollback" {
ColorLog("[ERRO] There is nothing to rollback\n") logger.Fatal("There is nothing to rollback")
os.Exit(2)
} }
file, createdAt = "", 0 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) { func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime int64, latestName string, task string) {
changeDir(dir) changeDir(dir)
if f, err := os.OpenFile(source, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err != nil { 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) logger.Fatalf("Could not create file: %s", err)
os.Exit(2)
} else { } else {
content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1) content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1)
content = strings.Replace(content, "{{ConnStr}}", connStr, -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, "{{LatestName}}", latestName, -1)
content = strings.Replace(content, "{{Task}}", task, -1) content = strings.Replace(content, "{{Task}}", task, -1)
if _, err := f.WriteString(content); err != nil { if _, err := f.WriteString(content); err != nil {
ColorLog("[ERRO] Could not write to file: %s\n", err) logger.Fatalf("Could not write to file: %s", err)
os.Exit(2)
} }
CloseFile(f) CloseFile(f)
} }
@ -314,7 +302,7 @@ func buildMigrationBinary(dir, binary string) {
changeDir(dir) changeDir(dir)
cmd := exec.Command("go", "build", "-o", binary) cmd := exec.Command("go", "build", "-o", binary)
if out, err := cmd.CombinedOutput(); err != nil { 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)) formatShellErrOutput(string(out))
removeTempFile(dir, binary) removeTempFile(dir, binary)
removeTempFile(dir, binary+".go") removeTempFile(dir, binary+".go")
@ -328,7 +316,7 @@ func runMigrationBinary(dir, binary string) {
cmd := exec.Command("./" + binary) cmd := exec.Command("./" + binary)
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
formatShellOutput(string(out)) 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)
removeTempFile(dir, binary+".go") removeTempFile(dir, binary+".go")
os.Exit(2) os.Exit(2)
@ -341,8 +329,7 @@ func runMigrationBinary(dir, binary string) {
// It exits the system when encouter an error // It exits the system when encouter an error
func changeDir(dir string) { func changeDir(dir string) {
if err := os.Chdir(dir); err != nil { if err := os.Chdir(dir); err != nil {
ColorLog("[ERRO] Could not find migration directory: %s\n", err) logger.Fatalf("Could not find migration directory: %s", err)
os.Exit(2)
} }
} }
@ -350,7 +337,7 @@ func changeDir(dir string) {
func removeTempFile(dir, file string) { func removeTempFile(dir, file string) {
changeDir(dir) changeDir(dir)
if err := os.Remove(file); err != nil { 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) { func formatShellErrOutput(o string) {
for _, line := range strings.Split(o, "\n") { for _, line := range strings.Split(o, "\n") {
if line != "" { if line != "" {
ColorLog("[ERRO] -| ") logger.Errorf("|> %s", line)
fmt.Println(line)
} }
} }
} }
@ -368,13 +354,13 @@ func formatShellErrOutput(o string) {
func formatShellOutput(o string) { func formatShellOutput(o string) {
for _, line := range strings.Split(o, "\n") { for _, line := range strings.Split(o, "\n") {
if line != "" { if line != "" {
ColorLog("[INFO] -| ") logger.Infof("|> %s", line)
fmt.Println(line)
} }
} }
} }
const ( const (
// MigrationMainTPL migration main template
MigrationMainTPL = `package main MigrationMainTPL = `package main
import( import(
@ -414,6 +400,7 @@ func main(){
} }
` `
// MYSQLMigrationDDL MySQL migration SQL
MYSQLMigrationDDL = ` MYSQLMigrationDDL = `
CREATE TABLE migrations ( CREATE TABLE migrations (
id_migration int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key', id_migration int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
@ -425,7 +412,7 @@ CREATE TABLE migrations (
PRIMARY KEY (id_migration) PRIMARY KEY (id_migration)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ) ENGINE=InnoDB DEFAULT CHARSET=utf8
` `
// POSTGRESMigrationDDL Postgres migration SQL
POSTGRESMigrationDDL = ` POSTGRESMigrationDDL = `
CREATE TYPE migrations_status AS ENUM('update', 'rollback'); CREATE TYPE migrations_status AS ENUM('update', 'rollback');

20
new.go
View File

@ -58,7 +58,7 @@ func createApp(cmd *Command, args []string) int {
ShowShortVersionBanner() ShowShortVersionBanner()
w := NewColorWriter(os.Stdout) w := NewColorWriter(os.Stdout)
if len(args) != 1 { if len(args) != 1 {
ColorLog("[ERRO] Argument [appname] is missing\n") logger.Error("Argument [appname] is missing")
os.Exit(2) os.Exit(2)
} }
apppath, packpath, err := checkEnv(args[0]) apppath, packpath, err := checkEnv(args[0])
@ -68,14 +68,14 @@ func createApp(cmd *Command, args []string) int {
} }
if isExist(apppath) { if isExist(apppath) {
ColorLog("[ERRO] Path (%s) already exists\n", apppath) logger.Errorf("Path (%s) already exists", apppath)
ColorLog("[WARN] Do you want to overwrite it? [Yes|No] ") logger.Warn("Do you want to overwrite it? [Yes|No] ")
if !askForConfirmation() { if !askForConfirmation() {
os.Exit(2) os.Exit(2)
} }
} }
ColorLog("[INFO] Creating application...\n") logger.Info("Creating application...")
os.MkdirAll(apppath, 0755) os.MkdirAll(apppath, 0755)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath+string(path.Separator), "\x1b[0m") fmt.Fprintf(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") 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)) 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 return 0
} }
@ -302,13 +302,3 @@ var indextpl = `<!DOCTYPE html>
</body> </body>
</html> </html>
` `
// 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)
}

35
pack.go
View File

@ -104,11 +104,6 @@ func init() {
w = NewColorWriter(os.Stdout) w = NewColorWriter(os.Stdout)
} }
func exitPrint(con string) {
fmt.Fprintln(os.Stderr, con)
os.Exit(2)
}
type walker interface { type walker interface {
isExclude(string) bool isExclude(string) bool
isEmpty(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, func packDirectory(excludePrefix []string, excludeSuffix []string,
excludeRegexp []*regexp.Regexp, includePath ...string) (err error) { excludeRegexp []*regexp.Regexp, includePath ...string) (err error) {
ColorLog("Excluding relpath prefix: %s\n", strings.Join(excludePrefix, ":")) logger.Infof("Excluding relpath prefix: %s", strings.Join(excludePrefix, ":"))
ColorLog("Excluding relpath suffix: %s\n", strings.Join(excludeSuffix, ":")) logger.Infof("Excluding relpath suffix: %s", strings.Join(excludeSuffix, ":"))
if len(excludeRegexp) > 0 { 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) 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) thePath, err := path.Abs(appPath)
if err != nil { 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 { 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 { 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) appName := path.Base(thePath)
@ -507,7 +502,7 @@ func packApp(cmd *Command, args []string) int {
os.Mkdir(tmpdir, 0700) os.Mkdir(tmpdir, 0700)
if build { if build {
ColorLog("Building application...\n") logger.Info("Building application...")
var envs []string var envs []string
for _, env := range buildEnvs { for _, env := range buildEnvs {
parts := strings.SplitN(env, "=", 2) parts := strings.SplitN(env, "=", 2)
@ -529,7 +524,7 @@ func packApp(cmd *Command, args []string) int {
os.Setenv("GOOS", goos) os.Setenv("GOOS", goos)
os.Setenv("GOARCH", goarch) 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) binPath := path.Join(tmpdir, appName)
if goos == "windows" { if goos == "windows" {
@ -552,10 +547,10 @@ func packApp(cmd *Command, args []string) int {
execmd.Dir = thePath execmd.Dir = thePath
err = execmd.Run() err = execmd.Run()
if err != nil { if err != nil {
exitPrint(err.Error()) logger.Fatal(err.Error())
} }
ColorLog("Build successful\n") logger.Success("Build successful!")
} }
switch format { switch format {
@ -573,7 +568,7 @@ func packApp(cmd *Command, args []string) int {
if _, err := os.Stat(outputP); err != nil { if _, err := os.Stat(outputP); err != nil {
err = os.MkdirAll(outputP, 0755) err = os.MkdirAll(outputP, 0755)
if err != nil { 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 { for _, r := range excludeR {
if len(r) > 0 { if len(r) > 0 {
if re, err := regexp.Compile(r); err != nil { if re, err := regexp.Compile(r); err != nil {
exitPrint(err.Error()) logger.Fatal(err.Error())
} else { } else {
exr = append(exr, re) exr = append(exr, re)
} }
@ -604,9 +599,9 @@ func packApp(cmd *Command, args []string) int {
err = packDirectory(exp, exs, exr, tmpdir, thePath) err = packDirectory(exp, exs, exr, tmpdir, thePath)
if err != nil { 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 return 0
} }

34
run.go
View File

@ -15,7 +15,6 @@
package main package main
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
path "path/filepath" path "path/filepath"
@ -77,9 +76,8 @@ func runApp(cmd *Command, args []string) int {
appname = path.Base(currpath) appname = path.Base(currpath)
currentGoPath = _gopath currentGoPath = _gopath
} else { } 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 { } else {
// Check if passed Bee application path/name exists in the GOPATH(s) // Check if passed Bee application path/name exists in the GOPATH(s)
if found, _gopath, _path := SearchGOPATHs(args[0]); found { if found, _gopath, _path := SearchGOPATHs(args[0]); found {
@ -87,35 +85,35 @@ func runApp(cmd *Command, args []string) int {
currentGoPath = _gopath currentGoPath = _gopath
appname = path.Base(currpath) appname = path.Base(currpath)
} else { } 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) { 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) logger.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname)
ColorLog("[INFO] Do you want to overwrite it? [yes|no]] ") logger.Info("Do you want to overwrite it? [yes|no] ")
if !askForConfirmation() { if !askForConfirmation() {
return 0 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" { if runmode == "prod" || runmode == "dev" {
os.Setenv("BEEGO_RUNMODE", runmode) 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 != "" { } else if runmode != "" {
os.Setenv("BEEGO_RUNMODE", 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") != "" { } 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() err := loadConfig()
if err != nil { if err != nil {
ColorLog("[ERRO] Failed to load configuration [ %s ]\n", err) logger.Fatalf("Failed to load configuration: %s", err)
} }
var paths []string var paths []string
@ -143,10 +141,10 @@ func runApp(cmd *Command, args []string) int {
} }
if gendoc == "true" { if gendoc == "true" {
NewWatcher(paths, files, true) NewWatcher(paths, files, true)
Autobuild(files, true) AutoBuild(files, true)
} else { } else {
NewWatcher(paths, files, false) NewWatcher(paths, files, false)
Autobuild(files, false) AutoBuild(files, false)
} }
for { for {
@ -202,16 +200,16 @@ func isExcluded(filePath string) bool {
for _, p := range excludedPaths { for _, p := range excludedPaths {
absP, err := path.Abs(p) absP, err := path.Abs(p)
if err != nil { if err != nil {
ColorLog("[ERROR] Can not get absolute path of [ %s ]\n", p) logger.Errorf("Cannot get absolute path of '%s'", p)
continue continue
} }
absFilePath, err := path.Abs(filePath) absFilePath, err := path.Abs(filePath)
if err != nil { if err != nil {
ColorLog("[ERROR] Can not get absolute path of [ %s ]\n", filePath) logger.Errorf("Cannot get absolute path of '%s'", filePath)
break break
} }
if strings.HasPrefix(absFilePath, absP) { if strings.HasPrefix(absFilePath, absP) {
ColorLog("[INFO] Excluding from watching [ %s ]\n", filePath) logger.Infof("'%s' is not being watched", filePath)
return true return true
} }
} }

View File

@ -17,7 +17,6 @@ import (
"archive/zip" "archive/zip"
"fmt" "fmt"
"io" "io"
"log"
"net/http" "net/http"
"os" "os"
"strings" "strings"
@ -63,18 +62,22 @@ func runDocs(cmd *Command, args []string) int {
downloadFromURL(swaggerlink, "swagger.zip") downloadFromURL(swaggerlink, "swagger.zip")
err := unzipAndDelete("swagger.zip") err := unzipAndDelete("swagger.zip")
if err != nil { if err != nil {
fmt.Println("has err exet unzipAndDelete", err) logger.Errorf("Error while unzipping 'swagger.zip' file: %s", err)
} }
} }
if docport == "" { if docport == "" {
docport = "8089" docport = "8089"
} }
if _, err := os.Stat("swagger"); err != nil && os.IsNotExist(err) { if _, err := os.Stat("swagger"); err != nil && os.IsNotExist(err) {
fmt.Println("there's no swagger, please use bee rundocs -isDownload=true downlaod first") logger.Fatal("No Swagger dist found. Run: bee rundocs -isDownload=true")
os.Exit(2) }
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 return 0
} }
@ -85,36 +88,36 @@ func downloadFromURL(url, fileName string) {
} else if fd.Size() == int64(0) { } else if fd.Size() == int64(0) {
down = true down = true
} else { } else {
ColorLog("[%s] Filename %s already exist\n", INFO, fileName) logger.Infof("'%s' already exists", fileName)
return return
} }
if down { 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) output, err := os.Create(fileName)
if err != nil { if err != nil {
ColorLog("[%s]Error while creating %s: %s\n", ERRO, fileName, err) logger.Errorf("Error while creating '%s': %s", fileName, err)
return return
} }
defer output.Close() defer output.Close()
response, err := http.Get(url) response, err := http.Get(url)
if err != nil { if err != nil {
ColorLog("[%s]Error while downloading %s:%s\n", ERRO, url, err) logger.Errorf("Error while downloading '%s': %s", url, err)
return return
} }
defer response.Body.Close() defer response.Body.Close()
n, err := io.Copy(output, response.Body) n, err := io.Copy(output, response.Body)
if err != nil { if err != nil {
ColorLog("[%s]Error while downloading %s:%s\n", ERRO, url, err) logger.Errorf("Error while downloading '%s': %s", url, err)
return return
} }
ColorLog("[%s] %d bytes downloaded.\n", SUCC, n) logger.Successf("%d bytes downloaded!", n)
} }
} }
func unzipAndDelete(src string) error { 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) r, err := zip.OpenReader(src)
if err != nil { if err != nil {
return err 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) return os.RemoveAll(src)
} }

26
test.go
View File

@ -51,19 +51,19 @@ var started = make(chan bool)
func testApp(cmd *Command, args []string) int { func testApp(cmd *Command, args []string) int {
if len(args) != 1 { if len(args) != 1 {
ColorLog("[ERRO] Cannot start running[ %s ]\n", logger.Fatalf("Failed to start: %s", "argument 'appname' is missing")
"argument 'appname' is missing")
os.Exit(2)
} }
crupath, _ := os.Getwd()
Debugf("current path:%s\n", crupath) currpath, _ := os.Getwd()
logger.Debugf("Current path: %s", currpath)
err := loadConfig() err := loadConfig()
if err != nil { if err != nil {
ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err) logger.Fatalf("Failed to load configuration: %s", err)
} }
var paths []string var paths []string
readAppDirectories(crupath, &paths) readAppDirectories(currpath, &paths)
NewWatcher(paths, nil, false) NewWatcher(paths, nil, false)
appname = args[0] appname = args[0]
@ -76,7 +76,7 @@ func testApp(cmd *Command, args []string) int {
} }
func runTest() { func runTest() {
ColorLog("[INFO] Start testing...\n") logger.Info("Start testing...")
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
crupwd, _ := os.Getwd() crupwd, _ := os.Getwd()
testDir := path.Join(crupwd, "tests") testDir := path.Join(crupwd, "tests")
@ -88,14 +88,14 @@ func runTest() {
icmd := exec.Command("go", "test") icmd := exec.Command("go", "test")
icmd.Stdout = os.Stdout icmd.Stdout = os.Stdout
icmd.Stderr = os.Stderr icmd.Stderr = os.Stderr
ColorLog("[TRAC] ============== Test Begin ===================\n") logger.Info("============== Test Begin ===================")
err = icmd.Run() err = icmd.Run()
ColorLog("[TRAC] ============== Test End ===================\n") logger.Info("============== Test End =====================")
if err != nil { if err != nil {
ColorLog("[ERRO] ============== Test failed ===================\n") logger.Error("============== Test failed ===================")
ColorLog("[ERRO] %s", err) logger.Errorf("Cause: %s", err)
return return
} }
ColorLog("[SUCC] Test finish\n") logger.Success("Test Completed")
} }

176
util.go
View File

@ -19,6 +19,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"os/exec"
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
@ -37,124 +38,14 @@ func Go(f func() error) chan error {
return ch return ch
} }
// Debugf outputs a formtted debug message, when os.env DEBUG is set. // Now returns the current local time in the specified layout
func Debugf(format string, a ...interface{}) { func Now(layout string) string {
if os.Getenv("DEBUG") != "" { return time.Now().Format(layout)
_, file, line, ok := runtime.Caller(1)
if !ok {
file = "<unknown>"
line = -1
} else {
file = filepath.Base(file)
}
fmt.Fprintf(os.Stderr, fmt.Sprintf("[debug] %s:%d %s\n", file, line, format), a...)
}
} }
const ( // EndLine returns the a newline escape character
Gray = uint8(iota + 90) func EndLine() string {
Red return "\n"
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: <level> <content [highlight][path]> [ 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
}
} }
// IsExist returns whether a file or directory exists. // IsExist returns whether a file or directory exists.
@ -210,8 +101,7 @@ func isBeegoProject(thePath string) bool {
func SearchGOPATHs(app string) (bool, string, string) { func SearchGOPATHs(app string) (bool, string, string) {
gps := GetGOPATHs() gps := GetGOPATHs()
if len(gps) == 0 { if len(gps) == 0 {
ColorLog("[ERRO] Fail to start [ %s ]\n", "GOPATH environment variable is not set or empty") logger.Fatal("GOPATH environment variable is not set or empty")
os.Exit(2)
} }
// Lookup the application inside the user workspace(s) // Lookup the application inside the user workspace(s)
@ -282,7 +172,7 @@ func snakeString(s string) string {
} }
data = append(data, d) data = append(data, d)
} }
return strings.ToLower(string(data[:len(data)])) return strings.ToLower(string(data[:]))
} }
func camelString(s string) string { func camelString(s string) string {
@ -306,7 +196,25 @@ func camelString(s string) string {
} }
data = append(data, d) 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 // 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 // CloseFile attempts to close the passed file
// or panics with the actual error // or panics with the actual error
func CloseFile(f *os.File) { 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) 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]
}

View File

@ -78,7 +78,7 @@ func getBeegoVersion() string {
return "" return ""
} }
if gopath == "" { if gopath == "" {
err = fmt.Errorf("You should set GOPATH env variable") err = fmt.Errorf("You need to set GOPATH environment variable")
return "" return ""
} }
wgopath := path.SplitList(gopath) wgopath := path.SplitList(gopath)
@ -90,11 +90,11 @@ func getBeegoVersion() string {
if os.IsNotExist(err) { if os.IsNotExist(err) {
continue continue
} }
ColorLog("[ERRO] Get `beego.go` has error\n") logger.Error("Error while getting stats of 'beego.go'")
} }
fd, err := os.Open(filename) fd, err := os.Open(filename)
if err != nil { if err != nil {
ColorLog("[ERRO] Open `beego.go` has error\n") logger.Error("Error while reading 'beego.go'")
continue continue
} }
reader := bufio.NewReader(fd) 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 { func getGoVersion() string {
@ -124,8 +124,7 @@ func getGoVersion() string {
) )
if cmdOut, err = exec.Command("go", "version").Output(); err != nil { if cmdOut, err = exec.Command("go", "version").Output(); err != nil {
fmt.Fprintln(os.Stderr, "There was an error running go version command:", err) logger.Fatalf("There was an error running go version command: %s", err)
os.Exit(2)
} }
return strings.Split(string(cmdOut), " ")[2] return strings.Split(string(cmdOut), " ")[2]
} }

View File

@ -16,7 +16,6 @@ package main
import ( import (
"bytes" "bytes"
"fmt"
"github.com/howeyc/fsnotify" "github.com/howeyc/fsnotify"
"os" "os"
"os/exec" "os/exec"
@ -34,11 +33,11 @@ var (
scheduleTime time.Time scheduleTime time.Time
) )
// NewWatcher starts an fsnotify Watcher on the specified paths
func NewWatcher(paths []string, files []string, isgenerate bool) { func NewWatcher(paths []string, files []string, isgenerate bool) {
watcher, err := fsnotify.NewWatcher() watcher, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
ColorLog("[ERRO] Fail to create new Watcher[ %s ]\n", err) logger.Fatalf("Failed to create watcher: %s", err)
os.Exit(2)
} }
go func() { go func() {
@ -57,14 +56,14 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
mt := getFileModTime(e.Name) mt := getFileModTime(e.Name)
if t := eventTime[e.Name]; mt == t { if t := eventTime[e.Name]; mt == t {
ColorLog("[SKIP] # %s #\n", e.String()) logger.Infof(bold("Skipping: ")+"%s", e.String())
isbuild = false isbuild = false
} }
eventTime[e.Name] = mt eventTime[e.Name] = mt
if isbuild { if isbuild {
ColorLog("[EVEN] %s\n", e) logger.Infof("Event fired: %s", e)
go func() { go func() {
// Wait 1s before autobuild util there is no file change. // Wait 1s before autobuild util there is no file change.
scheduleTime = time.Now().Add(1 * time.Second) scheduleTime = time.Now().Add(1 * time.Second)
@ -76,22 +75,21 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
return return
} }
Autobuild(files, isgenerate) AutoBuild(files, isgenerate)
}() }()
} }
case err := <-watcher.Error: 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 { for _, path := range paths {
ColorLog("[TRAC] Directory( %s )\n", path) logger.Infof(bold("Watching: ")+"%s", path)
err = watcher.Watch(path) err = watcher.Watch(path)
if err != nil { if err != nil {
ColorLog("[ERRO] Fail to watch directory[ %s ]\n", err) logger.Fatalf("Failed to watch directory: %s", err)
os.Exit(2)
} }
} }
@ -102,25 +100,26 @@ func getFileModTime(path string) int64 {
path = strings.Replace(path, "\\", "/", -1) path = strings.Replace(path, "\\", "/", -1)
f, err := os.Open(path) f, err := os.Open(path)
if err != nil { 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() return time.Now().Unix()
} }
defer f.Close() defer f.Close()
fi, err := f.Stat() fi, err := f.Stat()
if err != nil { 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 time.Now().Unix()
} }
return fi.ModTime().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() state.Lock()
defer state.Unlock() defer state.Unlock()
ColorLog("[INFO] Start building...\n") logger.Info("Start building...")
os.Chdir(currpath) os.Chdir(currpath)
@ -162,7 +161,7 @@ func Autobuild(files []string, isgenerate bool) {
icmd.Stdout = os.Stdout icmd.Stdout = os.Stdout
icmd.Stderr = os.Stderr icmd.Stderr = os.Stderr
icmd.Run() icmd.Run()
ColorLog("============== generate docs ===================\n") logger.Info("============== Generate Docs ===================")
} }
if err == nil { if err == nil {
@ -186,35 +185,38 @@ func Autobuild(files []string, isgenerate bool) {
} }
if err != nil { if err != nil {
ColorLog("[ERRO] ============== Build failed ===================\n") logger.Error("============== Build Failed ===================")
return return
} }
ColorLog("[SUCC] Build was successful\n") logger.Success("Built Successfully!")
Restart(appname) Restart(appname)
} }
// Kill kills the running command process
func Kill() { func Kill() {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
fmt.Println("Kill.recover -> ", e) logger.Infof("Kill recover: %s", e)
} }
}() }()
if cmd != nil && cmd.Process != nil { if cmd != nil && cmd.Process != nil {
err := cmd.Process.Kill() err := cmd.Process.Kill()
if err != nil { 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) { func Restart(appname string) {
Debugf("kill running process") logger.Debugf("Kill running process")
Kill() Kill()
go Start(appname) go Start(appname)
} }
// Start starts the command process
func Start(appname string) { func Start(appname string) {
ColorLog("[INFO] Restarting %s ...\n", appname) logger.Infof("Restarting '%s'...", appname)
if strings.Index(appname, "./") == -1 { if strings.Index(appname, "./") == -1 {
appname = "./" + appname appname = "./" + appname
} }
@ -226,17 +228,17 @@ func Start(appname string) {
cmd.Env = append(os.Environ(), conf.Envs...) cmd.Env = append(os.Environ(), conf.Envs...)
go cmd.Run() go cmd.Run()
ColorLog("[INFO] %s is running...\n", appname) logger.Successf("'%s' is running...", appname)
started <- true started <- true
} }
// Should ignore filenames generated by // shouldIgnoreFile ignores filenames generated by Emacs, Vim or SublimeText.
// Emacs, Vim or SublimeText // It returns true if the file should be ignored, false otherwise.
func shouldIgnoreFile(filename string) bool { func shouldIgnoreFile(filename string) bool {
for _, regex := range ignoredFilesRegExps { for _, regex := range ignoredFilesRegExps {
r, err := regexp.Compile(regex) r, err := regexp.Compile(regex)
if err != nil { if err != nil {
panic("Could not compile the regex: " + regex) logger.Fatalf("Could not compile regular expression: %s", err)
} }
if r.MatchString(filename) { if r.MatchString(filename) {
return true return true