mirror of
https://github.com/beego/bee.git
synced 2024-11-16 20:30:54 +00:00
Merge remote-tracking branch 'org-bee/develop' into develop
This commit is contained in:
commit
cda38e63f6
29
apiapp.go
29
apiapp.go
@ -538,19 +538,17 @@ func TestGet(t *testing.T) {
|
||||
|
||||
func init() {
|
||||
cmdApiapp.Run = createapi
|
||||
cmdApiapp.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
cmdApiapp.Flag.Var(&tables, "tables", "specify tables to generate model")
|
||||
cmdApiapp.Flag.Var(&driver, "driver", "database driver: mysql, postgresql, etc.")
|
||||
cmdApiapp.Flag.Var(&conn, "conn", "connection string used by the driver to connect to a database instance")
|
||||
}
|
||||
|
||||
func createapi(cmd *Command, args []string) int {
|
||||
ShowShortVersionBanner()
|
||||
|
||||
w := NewColorWriter(os.Stdout)
|
||||
|
||||
if len(args) < 1 {
|
||||
ColorLog("[ERRO] Argument [appname] is missing\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Argument [appname] is missing")
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
@ -559,8 +557,7 @@ func createapi(cmd *Command, args []string) int {
|
||||
|
||||
apppath, packpath, err := checkEnv(args[0])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("%s", err)
|
||||
}
|
||||
if driver == "" {
|
||||
driver = "mysql"
|
||||
@ -568,7 +565,7 @@ func createapi(cmd *Command, args []string) int {
|
||||
if conn == "" {
|
||||
}
|
||||
|
||||
ColorLog("[INFO] Creating API...\n")
|
||||
logger.Info("Creating API...")
|
||||
|
||||
os.MkdirAll(apppath, 0755)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m")
|
||||
@ -599,9 +596,9 @@ func createapi(cmd *Command, args []string) int {
|
||||
-1,
|
||||
),
|
||||
)
|
||||
ColorLog("[INFO] Using '%s' as 'driver'\n", driver)
|
||||
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
|
||||
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
|
||||
logger.Infof("Using '%s' as 'driver'", driver)
|
||||
logger.Infof("Using '%s' as 'conn'", conn)
|
||||
logger.Infof("Using '%s' as 'tables'", tables)
|
||||
generateAppcode(string(driver), string(conn), "3", string(tables), apppath)
|
||||
} else {
|
||||
os.Mkdir(path.Join(apppath, "models"), 0755)
|
||||
@ -635,15 +632,14 @@ func createapi(cmd *Command, args []string) int {
|
||||
WriteToFile(path.Join(apppath, "main.go"),
|
||||
strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1))
|
||||
}
|
||||
ColorLog("[SUCC] New API successfully created!\n")
|
||||
logger.Success("New API successfully created!")
|
||||
return 0
|
||||
}
|
||||
|
||||
func checkEnv(appname string) (apppath, packpath string, err error) {
|
||||
gps := GetGOPATHs()
|
||||
if len(gps) == 0 {
|
||||
ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty")
|
||||
os.Exit(2)
|
||||
logger.Fatal("GOPATH environment variable is not set or empty")
|
||||
}
|
||||
currpath, _ := os.Getwd()
|
||||
currpath = path.Join(currpath, appname)
|
||||
@ -658,15 +654,16 @@ func checkEnv(appname string) (apppath, packpath string, err error) {
|
||||
// In case of multiple paths in the GOPATH, by default
|
||||
// we use the first path
|
||||
gopath := gps[0]
|
||||
ColorLog("[%s]You current workdir is not a $GOPATH/src, bee will create the application in GOPATH: %s\n", WARN, gopath)
|
||||
Debugf("GOPATH: %s", gopath)
|
||||
|
||||
logger.Warn("You current workdir is not inside $GOPATH/src.")
|
||||
logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath)
|
||||
|
||||
gosrcpath := path.Join(gopath, "src")
|
||||
apppath = path.Join(gosrcpath, appname)
|
||||
|
||||
if _, e := os.Stat(apppath); os.IsNotExist(e) == false {
|
||||
err = fmt.Errorf("Cannot create application without removing '%s' first.", apppath)
|
||||
ColorLog("[ERRO] Path '%s' already exists\n", apppath)
|
||||
logger.Errorf("Path '%s' already exists", apppath)
|
||||
return
|
||||
}
|
||||
packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(path.Separator)), "/")
|
||||
|
@ -66,7 +66,7 @@ func getControllerInfo(path string) (map[string][]string, error) {
|
||||
|
||||
files := make([]*source, 0, len(fis))
|
||||
for _, fi := range fis {
|
||||
// Only load go files.
|
||||
// Only load Go files
|
||||
if strings.HasSuffix(fi.Name(), ".go") {
|
||||
f, err := os.Open(path + "/" + fi.Name())
|
||||
if err != nil {
|
||||
@ -107,7 +107,7 @@ func getControllerInfo(path string) (map[string][]string, error) {
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
// A source describles a source code file.
|
||||
// source represents a source code file.
|
||||
type source struct {
|
||||
name string
|
||||
data []byte
|
||||
@ -120,27 +120,25 @@ func (s *source) ModTime() time.Time { return time.Time{} }
|
||||
func (s *source) IsDir() bool { return false }
|
||||
func (s *source) Sys() interface{} { return nil }
|
||||
|
||||
// A routerWalker holds the state used when building the documentation.
|
||||
// routerWalker holds the state used when building the documentation.
|
||||
type routerWalker struct {
|
||||
pdoc *Package
|
||||
srcs map[string]*source // Source files.
|
||||
srcs map[string]*source // Source files
|
||||
fset *token.FileSet
|
||||
buf []byte // scratch space for printNode method.
|
||||
buf []byte // scratch space for printNode method
|
||||
}
|
||||
|
||||
// Package represents full information and documentation for a package.
|
||||
type Package struct {
|
||||
ImportPath string
|
||||
|
||||
// Top-level declarations.
|
||||
Types []*Type
|
||||
Types []*Type // Top-level declarations
|
||||
}
|
||||
|
||||
// Type represents structs and interfaces.
|
||||
type Type struct {
|
||||
Name string // Type name.
|
||||
Name string // Type name
|
||||
Decl string
|
||||
Methods []*Func // Exported methods.
|
||||
Methods []*Func // Exported methods
|
||||
}
|
||||
|
||||
// Func represents functions
|
||||
@ -150,7 +148,7 @@ type Func struct {
|
||||
|
||||
// build generates data from source files.
|
||||
func (w *routerWalker) build(srcs []*source) (*Package, error) {
|
||||
// Add source files to walker, I skipped references here.
|
||||
// Add source files to walker, I skipped references here
|
||||
w.srcs = make(map[string]*source)
|
||||
for _, src := range srcs {
|
||||
w.srcs[src.name] = src
|
||||
@ -158,7 +156,7 @@ func (w *routerWalker) build(srcs []*source) (*Package, error) {
|
||||
|
||||
w.fset = token.NewFileSet()
|
||||
|
||||
// Find the package and associated files.
|
||||
// Find the package and associated files
|
||||
ctxt := gobuild.Context{
|
||||
GOOS: runtime.GOOS,
|
||||
GOARCH: runtime.GOARCH,
|
||||
@ -174,7 +172,7 @@ func (w *routerWalker) build(srcs []*source) (*Package, error) {
|
||||
}
|
||||
|
||||
bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0)
|
||||
// Continue if there are no Go source files; we still want the directory info.
|
||||
// Continue if there are no Go source files; we still want the directory info
|
||||
_, nogo := err.(*gobuild.NoGoError)
|
||||
if err != nil {
|
||||
if nogo {
|
||||
@ -272,9 +270,8 @@ func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, e
|
||||
name = strings.TrimPrefix(name, "biogo.")
|
||||
|
||||
// It's also common for the last element of the path to contain an
|
||||
// extra "go" prefix, but not always. TODO: examine unresolved ids to
|
||||
// detect when trimming the "go" prefix is appropriate.
|
||||
|
||||
// extra "go" prefix, but not always.
|
||||
// TODO: examine unresolved ids to detect when trimming the "go" prefix is appropriate.
|
||||
pkg = ast.NewObj(ast.Pkg, name)
|
||||
pkg.Data = ast.NewScope(nil)
|
||||
imports[path] = pkg
|
||||
|
44
bale.go
44
bale.go
@ -32,7 +32,7 @@ var cmdBale = &Command{
|
||||
Long: `
|
||||
Bale command compress all the static files in to a single binary file.
|
||||
|
||||
This is usefull to not have to carry static files including js, css, images
|
||||
This is useful to not have to carry static files including js, css, images
|
||||
and views when publishing a project.
|
||||
|
||||
auto-generate unpack function to main package then run it during the runtime.
|
||||
@ -43,26 +43,25 @@ This is mainly used for zealots who are requiring 100% Go code.
|
||||
|
||||
func init() {
|
||||
cmdBale.Run = runBale
|
||||
cmdBale.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
}
|
||||
|
||||
func runBale(cmd *Command, args []string) int {
|
||||
ShowShortVersionBanner()
|
||||
|
||||
err := loadConfig()
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
|
||||
logger.Fatalf("Failed to load configuration: %s", err)
|
||||
}
|
||||
|
||||
os.RemoveAll("bale")
|
||||
os.Mkdir("bale", os.ModePerm)
|
||||
|
||||
// Pack and compress data.
|
||||
// Pack and compress data
|
||||
for _, p := range conf.Bale.Dirs {
|
||||
if !isExist(p) {
|
||||
ColorLog("[WARN] Skipped directory( %s )\n", p)
|
||||
logger.Warnf("Skipped directory: %s", p)
|
||||
continue
|
||||
}
|
||||
ColorLog("[INFO] Packaging directory( %s )\n", p)
|
||||
logger.Infof("Packaging directory: %s", p)
|
||||
filepath.Walk(p, walkFn)
|
||||
}
|
||||
|
||||
@ -74,22 +73,21 @@ func runBale(cmd *Command, args []string) int {
|
||||
|
||||
fw, err := os.Create("bale.go")
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to create file[ %s ]\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Failed to create file: %s", err)
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
_, err = fw.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to write data[ %s ]\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Failed to write data: %s", err)
|
||||
}
|
||||
|
||||
ColorLog("[SUCC] Baled resources successfully!\n")
|
||||
logger.Success("Baled resources successfully!")
|
||||
return 0
|
||||
}
|
||||
|
||||
const (
|
||||
// BaleHeader ...
|
||||
BaleHeader = `package main
|
||||
|
||||
import(
|
||||
@ -150,14 +148,13 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Open resource files.
|
||||
// Open resource files
|
||||
fr, err := os.Open(resPath)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to read file[ %s ]\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Failed to read file: %s", err)
|
||||
}
|
||||
|
||||
// Convert path.
|
||||
// Convert path
|
||||
resPath = strings.Replace(resPath, "_", "_0_", -1)
|
||||
resPath = strings.Replace(resPath, ".", "_1_", -1)
|
||||
resPath = strings.Replace(resPath, "-", "_2_", -1)
|
||||
@ -168,19 +165,18 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
|
||||
}
|
||||
resPath = strings.Replace(resPath, sep, "_4_", -1)
|
||||
|
||||
// Create corresponding Go source files.
|
||||
// Create corresponding Go source files
|
||||
os.MkdirAll(path.Dir(resPath), os.ModePerm)
|
||||
fw, err := os.Create("bale/" + resPath + ".go")
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to create file[ %s ]\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Failed to create file: %s", err)
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
// Write header.
|
||||
// Write header
|
||||
fmt.Fprintf(fw, Header, resPath)
|
||||
|
||||
// Copy and compress data.
|
||||
// Copy and compress data
|
||||
gz := gzip.NewWriter(&ByteWriter{Writer: fw})
|
||||
io.Copy(gz, fr)
|
||||
gz.Close()
|
||||
@ -202,6 +198,7 @@ func filterSuffix(name string) bool {
|
||||
}
|
||||
|
||||
const (
|
||||
// Header ...
|
||||
Header = `package bale
|
||||
|
||||
import(
|
||||
@ -212,6 +209,7 @@ import(
|
||||
|
||||
func R%s() []byte {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer([]byte{`
|
||||
// Footer ...
|
||||
Footer = `
|
||||
}))
|
||||
|
||||
@ -229,6 +227,7 @@ func R%s() []byte {
|
||||
|
||||
var newline = []byte{'\n'}
|
||||
|
||||
// ByteWriter ...
|
||||
type ByteWriter struct {
|
||||
io.Writer
|
||||
c int
|
||||
@ -244,12 +243,9 @@ func (w *ByteWriter) Write(p []byte) (n int, err error) {
|
||||
w.Writer.Write(newline)
|
||||
w.c = 0
|
||||
}
|
||||
|
||||
fmt.Fprintf(w.Writer, "0x%02x,", p[n])
|
||||
w.c++
|
||||
}
|
||||
|
||||
n++
|
||||
|
||||
return
|
||||
}
|
||||
|
23
banner.go
23
banner.go
@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
type vars struct {
|
||||
@ -21,25 +20,17 @@ type vars struct {
|
||||
BeegoVersion string
|
||||
}
|
||||
|
||||
// Now returns the current local time in the specified layout
|
||||
func Now(layout string) string {
|
||||
return time.Now().Format(layout)
|
||||
}
|
||||
|
||||
// InitBanner loads the banner and prints it to output
|
||||
// All errors are ignored, the application will not
|
||||
// print the banner in case of error.
|
||||
func InitBanner(out io.Writer, in io.Reader) {
|
||||
if in == nil {
|
||||
ColorLog("[ERRO] The input is nil\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("The input is nil")
|
||||
}
|
||||
|
||||
banner, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Error trying to read the banner\n")
|
||||
ColorLog("[HINT] %v\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Error while trying to read the banner: %s", err)
|
||||
}
|
||||
|
||||
show(out, string(banner))
|
||||
@ -51,13 +42,11 @@ func show(out io.Writer, content string) {
|
||||
Parse(content)
|
||||
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Cannot parse the banner template\n")
|
||||
ColorLog("[HINT] %v\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Cannot parse the banner template: %s", err)
|
||||
}
|
||||
|
||||
err = t.Execute(out, vars{
|
||||
runtime.Version(),
|
||||
getGoVersion(),
|
||||
runtime.GOOS,
|
||||
runtime.GOARCH,
|
||||
runtime.NumCPU(),
|
||||
@ -67,7 +56,5 @@ func show(out io.Writer, content string) {
|
||||
version,
|
||||
getBeegoVersion(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
MustCheck(err)
|
||||
}
|
||||
|
23
bee.go
23
bee.go
@ -25,13 +25,17 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const version = "1.5.1"
|
||||
const version = "1.5.2"
|
||||
|
||||
// Command is the unit of execution
|
||||
type Command struct {
|
||||
// Run runs the command.
|
||||
// The args are the arguments after the command name.
|
||||
Run func(cmd *Command, args []string) int
|
||||
|
||||
// PreRun performs an operation before running the command
|
||||
PreRun func(cmd *Command, args []string)
|
||||
|
||||
// UsageLine is the one-line usage message.
|
||||
// The first word in the line is taken to be the command name.
|
||||
UsageLine string
|
||||
@ -60,6 +64,7 @@ func (c *Command) Name() string {
|
||||
return name
|
||||
}
|
||||
|
||||
// Usage puts out the usage for the command.
|
||||
func (c *Command) Usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(string(c.Long)))
|
||||
@ -88,7 +93,11 @@ var commands = []*Command{
|
||||
cmdFix,
|
||||
}
|
||||
|
||||
var logger = GetBeeLogger(os.Stdout)
|
||||
|
||||
func main() {
|
||||
currentpath, _ := os.Getwd()
|
||||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
log.SetFlags(0)
|
||||
@ -112,6 +121,17 @@ func main() {
|
||||
cmd.Flag.Parse(args[1:])
|
||||
args = cmd.Flag.Args()
|
||||
}
|
||||
|
||||
if cmd.PreRun != nil {
|
||||
cmd.PreRun(cmd, args)
|
||||
}
|
||||
|
||||
// Check if current directory is inside the GOPATH,
|
||||
// if so parse the packages inside it.
|
||||
if strings.Contains(currentpath, GetGOPATHs()[0]+"/src") {
|
||||
parsePackagesFromDir(currentpath)
|
||||
}
|
||||
|
||||
os.Exit(cmd.Run(cmd, args))
|
||||
return
|
||||
}
|
||||
@ -138,7 +158,6 @@ Additional help topics:
|
||||
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
|
||||
|
||||
Use "bee help [topic]" for more information about that topic.
|
||||
|
||||
`
|
||||
|
||||
var helpTemplate = `{{if .Runnable}}usage: bee {{.UsageLine}}
|
||||
|
4
code.go
4
code.go
@ -109,12 +109,12 @@ func (v *annotationVisitor) Visit(n ast.Node) ast.Visitor {
|
||||
v.ignoreName()
|
||||
ast.Walk(v, n.Type)
|
||||
case *ast.Field:
|
||||
for _ = range n.Names {
|
||||
for range n.Names {
|
||||
v.ignoreName()
|
||||
}
|
||||
ast.Walk(v, n.Type)
|
||||
case *ast.ValueSpec:
|
||||
for _ = range n.Names {
|
||||
for range n.Names {
|
||||
v.add(AnchorAnnotation, "")
|
||||
}
|
||||
if n.Type != nil {
|
||||
|
79
color.go
79
color.go
@ -14,7 +14,10 @@
|
||||
|
||||
package main
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type outputMode int
|
||||
|
||||
@ -49,3 +52,77 @@ func NewModeColorWriter(w io.Writer, mode outputMode) io.Writer {
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func bold(message string) string {
|
||||
return fmt.Sprintf("\x1b[1m%s\x1b[21m", message)
|
||||
}
|
||||
|
||||
// Cyan returns a cyan string
|
||||
func Cyan(message string) string {
|
||||
return fmt.Sprintf("\x1b[36m%s\x1b[0m", message)
|
||||
}
|
||||
|
||||
// Blue returns a blue string
|
||||
func Blue(message string) string {
|
||||
return fmt.Sprintf("\x1b[34m%s\x1b[0m", message)
|
||||
}
|
||||
|
||||
// Red returns a red string
|
||||
func Red(message string) string {
|
||||
return fmt.Sprintf("\x1b[31m%s\x1b[0m", message)
|
||||
}
|
||||
|
||||
// Green returns a green string
|
||||
func Green(message string) string {
|
||||
return fmt.Sprintf("\x1b[32m%s\x1b[0m", message)
|
||||
}
|
||||
|
||||
// Yellow returns a yellow string
|
||||
func Yellow(message string) string {
|
||||
return fmt.Sprintf("\x1b[33m%s\x1b[0m", message)
|
||||
}
|
||||
|
||||
// Gray returns a gray string
|
||||
func Gray(message string) string {
|
||||
return fmt.Sprintf("\x1b[37m%s\x1b[0m", message)
|
||||
}
|
||||
|
||||
// Magenta returns a magenta string
|
||||
func Magenta(message string) string {
|
||||
return fmt.Sprintf("\x1b[35m%s\x1b[0m", message)
|
||||
}
|
||||
|
||||
// CyanBold returns a cyan bold string
|
||||
func CyanBold(message string) string {
|
||||
return fmt.Sprintf("\x1b[36m%s\x1b[0m", bold(message))
|
||||
}
|
||||
|
||||
// BlueBold returns a blue bold string
|
||||
func BlueBold(message string) string {
|
||||
return fmt.Sprintf("\x1b[34m%s\x1b[0m", bold(message))
|
||||
}
|
||||
|
||||
// RedBold returns a red bold string
|
||||
func RedBold(message string) string {
|
||||
return fmt.Sprintf("\x1b[31m%s\x1b[0m", bold(message))
|
||||
}
|
||||
|
||||
// GreenBold returns a green bold string
|
||||
func GreenBold(message string) string {
|
||||
return fmt.Sprintf("\x1b[32m%s\x1b[0m", bold(message))
|
||||
}
|
||||
|
||||
// YellowBold returns a yellow bold string
|
||||
func YellowBold(message string) string {
|
||||
return fmt.Sprintf("\x1b[33m%s\x1b[0m", bold(message))
|
||||
}
|
||||
|
||||
// GrayBold returns a gray bold string
|
||||
func GrayBold(message string) string {
|
||||
return fmt.Sprintf("\x1b[37m%s\x1b[0m", bold(message))
|
||||
}
|
||||
|
||||
// MagentaBold returns a magenta bold string
|
||||
func MagentaBold(message string) string {
|
||||
return fmt.Sprintf("\x1b[35m%s\x1b[0m", bold(message))
|
||||
}
|
||||
|
@ -419,7 +419,7 @@ func (cw *colorWriter) Write(p []byte) (int, error) {
|
||||
}
|
||||
|
||||
if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode {
|
||||
nw, err = cw.w.Write(p[first:len(p)])
|
||||
nw, err = cw.w.Write(p[first:])
|
||||
r += nw
|
||||
}
|
||||
|
||||
|
109
conf.go
109
conf.go
@ -19,10 +19,13 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const ConfVer = 0
|
||||
const confVer = 0
|
||||
|
||||
var defaultConf = `{
|
||||
"version": 0,
|
||||
@ -75,42 +78,58 @@ var conf struct {
|
||||
}
|
||||
|
||||
// loadConfig loads customized configuration.
|
||||
func loadConfig() error {
|
||||
foundConf := false
|
||||
f, err := os.Open("bee.json")
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
ColorLog("[INFO] Detected bee.json\n")
|
||||
d := json.NewDecoder(f)
|
||||
err = d.Decode(&conf)
|
||||
func loadConfig() (err error) {
|
||||
err = filepath.Walk(".", func(path string, fileInfo os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
foundConf = true
|
||||
}
|
||||
byml, erryml := ioutil.ReadFile("Beefile")
|
||||
if erryml == nil {
|
||||
ColorLog("[INFO] Detected Beefile\n")
|
||||
err = yaml.Unmarshal(byml, &conf)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if fileInfo.IsDir() {
|
||||
return nil
|
||||
}
|
||||
foundConf = true
|
||||
}
|
||||
if !foundConf {
|
||||
// Use default.
|
||||
|
||||
if fileInfo.Name() == "bee.json" {
|
||||
logger.Info("Loading configuration from 'bee.json'...")
|
||||
err = parseJSON(path, conf)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to parse JSON file: %s", err)
|
||||
return err
|
||||
}
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
if fileInfo.Name() == "Beefile" {
|
||||
logger.Info("Loading configuration from 'Beefile'...")
|
||||
err = parseYAML(path, conf)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to parse YAML file: %s", err)
|
||||
return err
|
||||
}
|
||||
return io.EOF
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// In case no configuration file found or an error different than io.EOF,
|
||||
// fallback to default configuration
|
||||
if err != io.EOF {
|
||||
logger.Info("Loading default configuration...")
|
||||
err = json.Unmarshal([]byte(defaultConf), &conf)
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
}
|
||||
// Check format version.
|
||||
if conf.Version != ConfVer {
|
||||
ColorLog("[WARN] Your bee.json is out-of-date, please update!\n")
|
||||
ColorLog("[HINT] Compare bee.json under bee source code path and yours\n")
|
||||
|
||||
// No need to return io.EOF error
|
||||
err = nil
|
||||
|
||||
// Check format version
|
||||
if conf.Version != confVer {
|
||||
logger.Warn("Your configuration file is outdated. Please do consider updating it.")
|
||||
logger.Hint("Check the latest version of bee's configuration file.")
|
||||
}
|
||||
|
||||
// Set variables.
|
||||
// Set variables
|
||||
if len(conf.DirStruct.Controllers) == 0 {
|
||||
conf.DirStruct.Controllers = "controllers"
|
||||
}
|
||||
@ -118,7 +137,39 @@ func loadConfig() error {
|
||||
conf.DirStruct.Models = "models"
|
||||
}
|
||||
|
||||
// Append watch exts.
|
||||
// Append watch exts
|
||||
watchExts = append(watchExts, conf.WatchExt...)
|
||||
return
|
||||
}
|
||||
|
||||
func parseJSON(path string, v interface{}) error {
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
data, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseYAML(path string, v interface{}) error {
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
data, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = yaml.Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
23
fix.go
23
fix.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
@ -8,7 +9,6 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var cmdFix = &Command{
|
||||
@ -23,16 +23,17 @@ bee fix help to upgrade the application to beego 1.6
|
||||
|
||||
func init() {
|
||||
cmdFix.Run = runFix
|
||||
cmdFix.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
}
|
||||
|
||||
func runFix(cmd *Command, args []string) int {
|
||||
ShowShortVersionBanner()
|
||||
logger.Info("Upgrading the application...")
|
||||
|
||||
ColorLog("[INFO] Upgrading the application...\n")
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] GetCurrent Path:%s\n", err)
|
||||
logger.Fatalf("Error while getting the current working directory: %s", err)
|
||||
}
|
||||
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
if strings.HasPrefix(info.Name(), ".") {
|
||||
@ -49,11 +50,11 @@ func runFix(cmd *Command, args []string) int {
|
||||
err = fixFile(path)
|
||||
fmt.Println("\tfix\t", path)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not fix file: %s\n", err)
|
||||
logger.Errorf("Could not fix file: %s", err)
|
||||
}
|
||||
return err
|
||||
})
|
||||
ColorLog("[INFO] Upgrade done!\n")
|
||||
logger.Success("Upgrade done!")
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -167,18 +168,18 @@ func fixFile(file string) error {
|
||||
}
|
||||
fixed := rp.Replace(string(content))
|
||||
|
||||
// forword the RequestBody from the replace
|
||||
// Forword the RequestBody from the replace
|
||||
// "Input.Request", "Input.Context.Request",
|
||||
fixed = strings.Replace(fixed, "Input.Context.RequestBody", "Input.RequestBody", -1)
|
||||
|
||||
// regexp replace
|
||||
// Regexp replace
|
||||
pareg := regexp.MustCompile(`(Input.Params\[")(.*)("])`)
|
||||
fixed = pareg.ReplaceAllString(fixed, "Input.Param(\"$2\")")
|
||||
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])(\s)(=)(\s)(.*)`)
|
||||
fixed = pareg.ReplaceAllString(fixed, "Input.SetData(\"$2\", $7)")
|
||||
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])`)
|
||||
fixed = pareg.ReplaceAllString(fixed, "Input.Data(\"$2\")")
|
||||
// fix the cache object Put method
|
||||
// Fix the cache object Put method
|
||||
pareg = regexp.MustCompile(`(\.Put\(\")(.*)(\",)(\s)(.*)(,\s*)([^\*.]*)(\))`)
|
||||
if pareg.MatchString(fixed) && strings.HasSuffix(file, ".go") {
|
||||
fixed = pareg.ReplaceAllString(fixed, ".Put(\"$2\", $5, $7*time.Second)")
|
||||
@ -199,11 +200,11 @@ func fixFile(file string) error {
|
||||
fixed = strings.Replace(fixed, "import (", "import (\n\t\"time\"", 1)
|
||||
}
|
||||
}
|
||||
// replace the v.Apis in docs.go
|
||||
// Replace the v.Apis in docs.go
|
||||
if strings.Contains(file, "docs.go") {
|
||||
fixed = strings.Replace(fixed, "v.Apis", "v.APIs", -1)
|
||||
}
|
||||
// replace the config file
|
||||
// Replace the config file
|
||||
if strings.HasSuffix(file, ".conf") {
|
||||
fixed = strings.Replace(fixed, "HttpCertFile", "HTTPSCertFile", -1)
|
||||
fixed = strings.Replace(fixed, "HttpKeyFile", "HTTPSKeyFile", -1)
|
||||
|
66
g.go
66
g.go
@ -69,6 +69,7 @@ var fields docValue
|
||||
|
||||
func init() {
|
||||
cmdGenerate.Run = generateCode
|
||||
cmdGenerate.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
cmdGenerate.Flag.Var(&tables, "tables", "specify tables to generate model")
|
||||
cmdGenerate.Flag.Var(&driver, "driver", "database driver: mysql, postgresql, etc.")
|
||||
cmdGenerate.Flag.Var(&conn, "conn", "connection string used by the driver to connect to a database instance")
|
||||
@ -77,33 +78,30 @@ func init() {
|
||||
}
|
||||
|
||||
func generateCode(cmd *Command, args []string) int {
|
||||
ShowShortVersionBanner()
|
||||
|
||||
currpath, _ := os.Getwd()
|
||||
if len(args) < 1 {
|
||||
ColorLog("[ERRO] command is missing\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Command is missing")
|
||||
}
|
||||
|
||||
gps := GetGOPATHs()
|
||||
if len(gps) == 0 {
|
||||
ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty")
|
||||
os.Exit(2)
|
||||
logger.Fatal("GOPATH environment variable is not set or empty")
|
||||
}
|
||||
|
||||
gopath := gps[0]
|
||||
Debugf("GOPATH: %s", gopath)
|
||||
|
||||
logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath)
|
||||
|
||||
gcmd := args[0]
|
||||
switch gcmd {
|
||||
case "scaffold":
|
||||
if len(args) < 2 {
|
||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||
ColorLog("[HINT] Usage: bee generate scaffold [scaffoldname] [-fields=\"\"]\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Wrong number of arguments. Run: bee help generate")
|
||||
}
|
||||
// Load the configuration
|
||||
err := loadConfig()
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
|
||||
logger.Fatalf("Failed to load configuration: %s", err)
|
||||
}
|
||||
cmd.Flag.Parse(args[2:])
|
||||
if driver == "" {
|
||||
@ -119,19 +117,18 @@ func generateCode(cmd *Command, args []string) int {
|
||||
}
|
||||
}
|
||||
if fields == "" {
|
||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||
ColorLog("[HINT] Usage: bee generate scaffold [scaffoldname] [-fields=\"title:string,body:text\"]\n")
|
||||
os.Exit(2)
|
||||
logger.Hint("fields option should not be empty, i.e. -fields=\"title:string,body:text\"")
|
||||
logger.Fatal("Wrong number of arguments. Run: bee help generate")
|
||||
}
|
||||
sname := args[1]
|
||||
generateScaffold(sname, fields.String(), currpath, driver.String(), conn.String())
|
||||
case "docs":
|
||||
generateDocs(currpath)
|
||||
case "appcode":
|
||||
// load config
|
||||
// Load the configuration
|
||||
err := loadConfig()
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
|
||||
logger.Fatalf("Failed to load configuration: %s", err)
|
||||
}
|
||||
cmd.Flag.Parse(args[1:])
|
||||
if driver == "" {
|
||||
@ -153,20 +150,20 @@ func generateCode(cmd *Command, args []string) int {
|
||||
if level == "" {
|
||||
level = "3"
|
||||
}
|
||||
ColorLog("[INFO] Using '%s' as 'driver'\n", driver)
|
||||
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
|
||||
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
|
||||
ColorLog("[INFO] Using '%s' as 'level'\n", level)
|
||||
logger.Infof("Using '%s' as 'driver'", driver)
|
||||
logger.Infof("Using '%s' as 'conn'", conn)
|
||||
logger.Infof("Using '%s' as 'tables'", tables)
|
||||
logger.Infof("Using '%s' as 'level'", level)
|
||||
generateAppcode(driver.String(), conn.String(), level.String(), tables.String(), currpath)
|
||||
case "migration":
|
||||
if len(args) < 2 {
|
||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||
ColorLog("[HINT] Usage: bee generate migration [migrationname] [-fields=\"\"]\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Wrong number of arguments. Run: bee help generate")
|
||||
}
|
||||
cmd.Flag.Parse(args[2:])
|
||||
mname := args[1]
|
||||
ColorLog("[INFO] Using '%s' as migration name\n", mname)
|
||||
|
||||
logger.Infof("Using '%s' as migration name", mname)
|
||||
|
||||
upsql := ""
|
||||
downsql := ""
|
||||
if fields != "" {
|
||||
@ -180,21 +177,16 @@ func generateCode(cmd *Command, args []string) int {
|
||||
cname := args[1]
|
||||
generateController(cname, currpath)
|
||||
} else {
|
||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||
ColorLog("[HINT] Usage: bee generate controller [controllername]\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Wrong number of arguments. Run: bee help generate")
|
||||
}
|
||||
case "model":
|
||||
if len(args) < 2 {
|
||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||
ColorLog("[HINT] Usage: bee generate model [modelname] [-fields=\"\"]\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Wrong number of arguments. Run: bee help generate")
|
||||
}
|
||||
cmd.Flag.Parse(args[2:])
|
||||
if fields == "" {
|
||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||
ColorLog("[HINT] Usage: bee generate model [modelname] [-fields=\"title:string,body:text\"]\n")
|
||||
os.Exit(2)
|
||||
logger.Hint("fields option should not be empty, i.e. -fields=\"title:string,body:text\"")
|
||||
logger.Fatal("Wrong number of arguments. Run: bee help generate")
|
||||
}
|
||||
sname := args[1]
|
||||
generateModel(sname, fields.String(), currpath)
|
||||
@ -203,13 +195,11 @@ func generateCode(cmd *Command, args []string) int {
|
||||
cname := args[1]
|
||||
generateView(cname, currpath)
|
||||
} else {
|
||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||
ColorLog("[HINT] Usage: bee generate view [viewpath]\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Wrong number of arguments. Run: bee help generate")
|
||||
}
|
||||
default:
|
||||
ColorLog("[ERRO] Command is missing\n")
|
||||
logger.Fatal("Command is missing")
|
||||
}
|
||||
ColorLog("[SUCC] %s successfully generated!\n", strings.Title(gcmd))
|
||||
logger.Successf("%s successfully generated!", strings.Title(gcmd))
|
||||
return 0
|
||||
}
|
||||
|
218
g_appcode.go
218
g_appcode.go
@ -18,7 +18,6 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@ -39,7 +38,7 @@ type DbTransformer interface {
|
||||
GetTableNames(conn *sql.DB) []string
|
||||
GetConstraints(conn *sql.DB, table *Table, blackList map[string]bool)
|
||||
GetColumns(conn *sql.DB, table *Table, blackList map[string]bool)
|
||||
GetGoDataType(sqlType string) string
|
||||
GetGoDataType(sqlType string) (string, error)
|
||||
}
|
||||
|
||||
// MysqlDB is the MySQL version of DbTransformer
|
||||
@ -265,9 +264,7 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
|
||||
case "3":
|
||||
mode = OModel | OController | ORouter
|
||||
default:
|
||||
ColorLog("[ERRO] Invalid 'level' option: %s\n", level)
|
||||
ColorLog("[HINT] Level must be either 1, 2 or 3\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Invalid level value. Must be either \"1\", \"2\", or \"3\"")
|
||||
}
|
||||
var selectedTables map[string]bool
|
||||
if tables != "" {
|
||||
@ -280,12 +277,9 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
|
||||
case "mysql":
|
||||
case "postgres":
|
||||
case "sqlite":
|
||||
ColorLog("[ERRO] Generating app code from SQLite database is not supported yet.\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Generating app code from SQLite database is not supported yet.")
|
||||
default:
|
||||
ColorLog("[ERRO] Unknown database driver: %s\n", driver)
|
||||
ColorLog("[HINT] Driver must be one of mysql, postgres or sqlite\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Unknown database driver. Must be either \"mysql\", \"postgres\" or \"sqlite\"")
|
||||
}
|
||||
gen(driver, connStr, mode, selectedTables, currpath)
|
||||
}
|
||||
@ -295,12 +289,11 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
|
||||
func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, apppath string) {
|
||||
db, err := sql.Open(dbms, connStr)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not connect to %s database: %s, %s\n", dbms, connStr, err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err)
|
||||
}
|
||||
defer db.Close()
|
||||
if trans, ok := dbDriver[dbms]; ok {
|
||||
ColorLog("[INFO] Analyzing database tables...\n")
|
||||
logger.Info("Analyzing database tables...")
|
||||
tableNames := trans.GetTableNames(db)
|
||||
tables := getTableObjects(tableNames, db, trans)
|
||||
mvcPath := new(MvcPath)
|
||||
@ -311,25 +304,21 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, ap
|
||||
pkgPath := getPackagePath(apppath)
|
||||
writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
|
||||
} else {
|
||||
ColorLog("[ERRO] Generating app code from %s database is not supported yet.\n", dbms)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Generating app code from '%s' database is not supported yet.", dbms)
|
||||
}
|
||||
}
|
||||
|
||||
// getTables gets a list table names in current database
|
||||
// GetTableNames returns a slice of table names in the current database
|
||||
func (*MysqlDB) GetTableNames(db *sql.DB) (tables []string) {
|
||||
rows, err := db.Query("SHOW TABLES")
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not show tables\n")
|
||||
ColorLog("[HINT] Check your connection string\n")
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not show tables: %s", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var name string
|
||||
if err := rows.Scan(&name); err != nil {
|
||||
ColorLog("[ERRO] Could not show tables\n")
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not show tables: %s", err)
|
||||
}
|
||||
tables = append(tables, name)
|
||||
}
|
||||
@ -358,8 +347,8 @@ func getTableObjects(tableNames []string, db *sql.DB, dbTransformer DbTransforme
|
||||
return
|
||||
}
|
||||
|
||||
// getConstraints gets primary key, unique key and foreign keys of a table from information_schema
|
||||
// and fill in Table struct
|
||||
// GetConstraints gets primary key, unique key and foreign keys of a table from
|
||||
// information_schema and fill in the Table struct
|
||||
func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bool) {
|
||||
rows, err := db.Query(
|
||||
`SELECT
|
||||
@ -372,14 +361,12 @@ func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bo
|
||||
c.table_schema = database() AND c.table_name = ? AND u.table_schema = database() AND u.table_name = ?`,
|
||||
table.Name, table.Name) // u.position_in_unique_constraint,
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for PK/UK/FK information\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Could not query INFORMATION_SCHEMA for PK/UK/FK information")
|
||||
}
|
||||
for rows.Next() {
|
||||
var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte
|
||||
if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil {
|
||||
ColorLog("[ERRO] Could not read INFORMATION_SCHEMA for PK/UK/FK information\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Could not read INFORMATION_SCHEMA for PK/UK/FK information")
|
||||
}
|
||||
constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos :=
|
||||
string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes),
|
||||
@ -389,7 +376,7 @@ func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bo
|
||||
table.Pk = columnName
|
||||
} else {
|
||||
table.Pk = ""
|
||||
// add table to blacklist so that other struct will not reference it, because we are not
|
||||
// Add table to blacklist so that other struct will not reference it, because we are not
|
||||
// registering blacklisted tables
|
||||
blackList[table.Name] = true
|
||||
}
|
||||
@ -406,11 +393,11 @@ func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bo
|
||||
}
|
||||
}
|
||||
|
||||
// getColumns retrieve columns details from information_schema
|
||||
// and fill in the Column struct
|
||||
// GetColumns retrieves columns details from
|
||||
// information_schema and fill in the Column struct
|
||||
func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[string]bool) {
|
||||
// retrieve columns
|
||||
colDefRows, _ := db.Query(
|
||||
colDefRows, err := db.Query(
|
||||
`SELECT
|
||||
column_name, data_type, column_type, is_nullable, column_default, extra
|
||||
FROM
|
||||
@ -418,20 +405,28 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
|
||||
WHERE
|
||||
table_schema = database() AND table_name = ?`,
|
||||
table.Name)
|
||||
if err != nil {
|
||||
logger.Fatalf("Could not query the database: %s", err)
|
||||
}
|
||||
defer colDefRows.Close()
|
||||
|
||||
for colDefRows.Next() {
|
||||
// datatype as bytes so that SQL <null> values can be retrieved
|
||||
var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte
|
||||
if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil {
|
||||
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for column information\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Could not query INFORMATION_SCHEMA for column information")
|
||||
}
|
||||
colName, dataType, columnType, isNullable, columnDefault, extra :=
|
||||
string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes)
|
||||
|
||||
// create a column
|
||||
col := new(Column)
|
||||
col.Name = camelCase(colName)
|
||||
col.Type = mysqlDB.GetGoDataType(dataType)
|
||||
col.Type, err = mysqlDB.GetGoDataType(dataType)
|
||||
if err != nil {
|
||||
logger.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
// Tag info
|
||||
tag := new(OrmTag)
|
||||
tag.Column = colName
|
||||
@ -466,7 +461,10 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
|
||||
if isSQLSignedIntType(dataType) {
|
||||
sign := extractIntSignness(columnType)
|
||||
if sign == "unsigned" && extra != "auto_increment" {
|
||||
col.Type = mysqlDB.GetGoDataType(dataType + " " + sign)
|
||||
col.Type, err = mysqlDB.GetGoDataType(dataType + " " + sign)
|
||||
if err != nil {
|
||||
logger.Fatalf("%s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if isSQLStringType(dataType) {
|
||||
@ -500,15 +498,13 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
|
||||
}
|
||||
|
||||
// GetGoDataType maps an SQL data type to Golang data type
|
||||
func (*MysqlDB) GetGoDataType(sqlType string) (goType string) {
|
||||
func (*MysqlDB) GetGoDataType(sqlType string) (string, error) {
|
||||
var typeMapping = map[string]string{}
|
||||
typeMapping = typeMappingMysql
|
||||
if v, ok := typeMapping[sqlType]; ok {
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
ColorLog("[ERRO] data type (%s) not found!\n", sqlType)
|
||||
os.Exit(2)
|
||||
return goType
|
||||
return "", fmt.Errorf("data type '%s' not found", sqlType)
|
||||
}
|
||||
|
||||
// GetTableNames for PostgreSQL
|
||||
@ -519,16 +515,14 @@ func (*PostgresDB) GetTableNames(db *sql.DB) (tables []string) {
|
||||
table_type = 'BASE TABLE' AND
|
||||
table_schema NOT IN ('pg_catalog', 'information_schema')`)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not show tables: %s\n", err)
|
||||
ColorLog("[HINT] Check your connection string\n")
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not show tables: %s", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var name string
|
||||
if err := rows.Scan(&name); err != nil {
|
||||
ColorLog("[ERRO] Could not show tables\n")
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not show tables: %s", err)
|
||||
}
|
||||
tables = append(tables, name)
|
||||
}
|
||||
@ -558,14 +552,13 @@ func (*PostgresDB) GetConstraints(db *sql.DB, table *Table, blackList map[string
|
||||
AND u.table_name = $2`,
|
||||
table.Name, table.Name) // u.position_in_unique_constraint,
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s", err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte
|
||||
if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil {
|
||||
ColorLog("[ERRO] Could not read INFORMATION_SCHEMA for PK/UK/FK information\n")
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not read INFORMATION_SCHEMA for PK/UK/FK information: %s", err)
|
||||
}
|
||||
constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos :=
|
||||
string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes),
|
||||
@ -595,7 +588,7 @@ func (*PostgresDB) GetConstraints(db *sql.DB, table *Table, blackList map[string
|
||||
// GetColumns for PostgreSQL
|
||||
func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map[string]bool) {
|
||||
// retrieve columns
|
||||
colDefRows, _ := db.Query(
|
||||
colDefRows, err := db.Query(
|
||||
`SELECT
|
||||
column_name,
|
||||
data_type,
|
||||
@ -614,20 +607,27 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map
|
||||
table_catalog = current_database() AND table_schema NOT IN ('pg_catalog', 'information_schema')
|
||||
AND table_name = $1`,
|
||||
table.Name)
|
||||
if err != nil {
|
||||
logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err)
|
||||
}
|
||||
defer colDefRows.Close()
|
||||
|
||||
for colDefRows.Next() {
|
||||
// datatype as bytes so that SQL <null> values can be retrieved
|
||||
var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte
|
||||
if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil {
|
||||
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for column information\n")
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err)
|
||||
}
|
||||
colName, dataType, columnType, isNullable, columnDefault, extra :=
|
||||
string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes)
|
||||
// create a column
|
||||
// Create a column
|
||||
col := new(Column)
|
||||
col.Name = camelCase(colName)
|
||||
col.Type = postgresDB.GetGoDataType(dataType)
|
||||
col.Type, err = postgresDB.GetGoDataType(dataType)
|
||||
if err != nil {
|
||||
logger.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
// Tag info
|
||||
tag := new(OrmTag)
|
||||
tag.Column = colName
|
||||
@ -690,13 +690,11 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map
|
||||
}
|
||||
|
||||
// GetGoDataType returns the Go type from the mapped Postgres type
|
||||
func (*PostgresDB) GetGoDataType(sqlType string) (goType string) {
|
||||
func (*PostgresDB) GetGoDataType(sqlType string) (string, error) {
|
||||
if v, ok := typeMappingPostgres[sqlType]; ok {
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
ColorLog("[ERRO] data type (%s) not found!\n", sqlType)
|
||||
os.Exit(2)
|
||||
return goType
|
||||
return "", fmt.Errorf("data type '%s' not found", sqlType)
|
||||
}
|
||||
|
||||
// deleteAndRecreatePaths removes several directories completely
|
||||
@ -717,15 +715,15 @@ func createPaths(mode byte, paths *MvcPath) {
|
||||
// Newly geneated files will be inside these folders.
|
||||
func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
|
||||
if (OModel & mode) == OModel {
|
||||
ColorLog("[INFO] Creating model files...\n")
|
||||
logger.Info("Creating model files...")
|
||||
writeModelFiles(tables, paths.ModelPath, selectedTables)
|
||||
}
|
||||
if (OController & mode) == OController {
|
||||
ColorLog("[INFO] Creating controller files...\n")
|
||||
logger.Info("Creating controller files...")
|
||||
writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath)
|
||||
}
|
||||
if (ORouter & mode) == ORouter {
|
||||
ColorLog("[INFO] Creating router files...\n")
|
||||
logger.Info("Creating router files...")
|
||||
writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath)
|
||||
}
|
||||
}
|
||||
@ -746,21 +744,21 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
|
||||
var f *os.File
|
||||
var err error
|
||||
if isExist(fpath) {
|
||||
ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||
logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||
if askForConfirmation() {
|
||||
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
ColorLog("[WARN] %v\n", err)
|
||||
logger.Warnf("%s", err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
ColorLog("[WARN] Skipped create file '%s'\n", fpath)
|
||||
logger.Warnf("Skipped create file '%s'", fpath)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
|
||||
if err != nil {
|
||||
ColorLog("[WARN] %v\n", err)
|
||||
logger.Warnf("%s", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -773,7 +771,8 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
|
||||
fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1)
|
||||
fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1)
|
||||
fileStr = strings.Replace(fileStr, "{{tableName}}", tb.Name, -1)
|
||||
// if table contains time field, import time.Time package
|
||||
|
||||
// If table contains time field, import time.Time package
|
||||
timePkg := ""
|
||||
importTimePkg := ""
|
||||
if tb.ImportTimePkg {
|
||||
@ -783,8 +782,7 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
|
||||
fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1)
|
||||
fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1)
|
||||
if _, err := f.WriteString(fileStr); err != nil {
|
||||
ColorLog("[ERRO] Could not write model file to %s\n", fpath)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not write model file to '%s': %s", fpath, err)
|
||||
}
|
||||
CloseFile(f)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||
@ -797,7 +795,7 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri
|
||||
w := NewColorWriter(os.Stdout)
|
||||
|
||||
for _, tb := range tables {
|
||||
// if selectedTables map is not nil and this table is not selected, ignore it
|
||||
// If selectedTables map is not nil and this table is not selected, ignore it
|
||||
if selectedTables != nil {
|
||||
if _, selected := selectedTables[tb.Name]; !selected {
|
||||
continue
|
||||
@ -811,29 +809,28 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri
|
||||
var f *os.File
|
||||
var err error
|
||||
if isExist(fpath) {
|
||||
ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||
logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||
if askForConfirmation() {
|
||||
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
ColorLog("[WARN] %v\n", err)
|
||||
logger.Warnf("%s", err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
ColorLog("[WARN] Skipped create file '%s'\n", fpath)
|
||||
logger.Warnf("Skipped create file '%s'", fpath)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
|
||||
if err != nil {
|
||||
ColorLog("[WARN] %v\n", err)
|
||||
logger.Warnf("%s", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
fileStr := strings.Replace(CtrlTPL, "{{ctrlName}}", camelCase(tb.Name), -1)
|
||||
fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1)
|
||||
if _, err := f.WriteString(fileStr); err != nil {
|
||||
ColorLog("[ERRO] Could not write controller file to %s\n", fpath)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not write controller file to '%s': %s", fpath, err)
|
||||
}
|
||||
CloseFile(f)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||
@ -847,7 +844,7 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo
|
||||
|
||||
var nameSpaces []string
|
||||
for _, tb := range tables {
|
||||
// if selectedTables map is not nil and this table is not selected, ignore it
|
||||
// If selectedTables map is not nil and this table is not selected, ignore it
|
||||
if selectedTables != nil {
|
||||
if _, selected := selectedTables[tb.Name]; !selected {
|
||||
continue
|
||||
@ -856,63 +853,44 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo
|
||||
if tb.Pk == "" {
|
||||
continue
|
||||
}
|
||||
// add namespaces
|
||||
// Add namespaces
|
||||
nameSpace := strings.Replace(NamespaceTPL, "{{nameSpace}}", tb.Name, -1)
|
||||
nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", camelCase(tb.Name), -1)
|
||||
nameSpaces = append(nameSpaces, nameSpace)
|
||||
}
|
||||
// add export controller
|
||||
// Add export controller
|
||||
fpath := path.Join(rPath, "router.go")
|
||||
routerStr := strings.Replace(RouterTPL, "{{nameSpaces}}", strings.Join(nameSpaces, ""), 1)
|
||||
routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1)
|
||||
var f *os.File
|
||||
var err error
|
||||
if isExist(fpath) {
|
||||
ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||
logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||
if askForConfirmation() {
|
||||
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
ColorLog("[WARN] %v\n", err)
|
||||
logger.Warnf("%s", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ColorLog("[WARN] Skipped create file '%s'\n", fpath)
|
||||
logger.Warnf("Skipped create file '%s'", fpath)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
|
||||
if err != nil {
|
||||
ColorLog("[WARN] %v\n", err)
|
||||
logger.Warnf("%s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if _, err := f.WriteString(routerStr); err != nil {
|
||||
ColorLog("[ERRO] Could not write router file to '%s'\n", fpath)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not write router file to '%s': %s", fpath, err)
|
||||
}
|
||||
CloseFile(f)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||
formatSourceCode(fpath)
|
||||
}
|
||||
|
||||
// formatSourceCode formats source files
|
||||
func formatSourceCode(filename string) {
|
||||
cmd := exec.Command("gofmt", "-w", filename)
|
||||
if err := cmd.Run(); err != nil {
|
||||
ColorLog("[WARN] gofmt err: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// camelCase converts a _ delimited string to camel case
|
||||
// e.g. very_important_person => VeryImportantPerson
|
||||
func camelCase(in string) string {
|
||||
tokens := strings.Split(in, "_")
|
||||
for i := range tokens {
|
||||
tokens[i] = strings.Title(strings.Trim(tokens[i], " "))
|
||||
}
|
||||
return strings.Join(tokens, "")
|
||||
}
|
||||
|
||||
func isSQLTemporalType(t string) bool {
|
||||
return t == "date" || t == "datetime" || t == "timestamp" || t == "time"
|
||||
}
|
||||
@ -972,12 +950,12 @@ func getFileName(tbName string) (filename string) {
|
||||
|
||||
func getPackagePath(curpath string) (packpath string) {
|
||||
gopath := os.Getenv("GOPATH")
|
||||
Debugf("gopath:%s", gopath)
|
||||
if gopath == "" {
|
||||
ColorLog("[ERRO] You should set GOPATH in the env")
|
||||
os.Exit(2)
|
||||
logger.Fatal("GOPATH environment variable is not set or empty")
|
||||
}
|
||||
|
||||
logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath)
|
||||
|
||||
appsrcpath := ""
|
||||
haspath := false
|
||||
wgopath := filepath.SplitList(gopath)
|
||||
@ -993,13 +971,11 @@ func getPackagePath(curpath string) (packpath string) {
|
||||
}
|
||||
|
||||
if !haspath {
|
||||
ColorLog("[ERRO] Can't generate application code outside of GOPATH '%s'\n", gopath)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Cannot generate application code outside of GOPATH '%s'", gopath)
|
||||
}
|
||||
|
||||
if curpath == appsrcpath {
|
||||
ColorLog("[ERRO] Can't generate application code outside of application PATH \n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Cannot generate application code outside of application path")
|
||||
}
|
||||
|
||||
packpath = strings.Join(strings.Split(curpath[len(appsrcpath)+1:], string(filepath.Separator)), "/")
|
||||
@ -1062,7 +1038,11 @@ func GetAll{{modelName}}(query map[string]string, fields []string, sortby []stri
|
||||
for k, v := range query {
|
||||
// rewrite dot-notation to Object__Attribute
|
||||
k = strings.Replace(k, ".", "__", -1)
|
||||
qs = qs.Filter(k, v)
|
||||
if strings.Contains(k, "isnull") {
|
||||
qs = qs.Filter(k, (v == "true" || v == "1"))
|
||||
} else {
|
||||
qs = qs.Filter(k, v)
|
||||
}
|
||||
}
|
||||
// order by:
|
||||
var sortFields []string
|
||||
@ -1168,11 +1148,12 @@ import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
// oprations for {{ctrlName}}
|
||||
// {{ctrlName}}Controller oprations for {{ctrlName}}
|
||||
type {{ctrlName}}Controller struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// URLMapping ...
|
||||
func (c *{{ctrlName}}Controller) URLMapping() {
|
||||
c.Mapping("Post", c.Post)
|
||||
c.Mapping("GetOne", c.GetOne)
|
||||
@ -1181,6 +1162,7 @@ func (c *{{ctrlName}}Controller) URLMapping() {
|
||||
c.Mapping("Delete", c.Delete)
|
||||
}
|
||||
|
||||
// Post ...
|
||||
// @Title Post
|
||||
// @Description create {{ctrlName}}
|
||||
// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content"
|
||||
@ -1202,7 +1184,8 @@ func (c *{{ctrlName}}Controller) Post() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title Get
|
||||
// GetOne ...
|
||||
// @Title Get One
|
||||
// @Description get {{ctrlName}} by id
|
||||
// @Param id path string true "The key for staticblock"
|
||||
// @Success 200 {object} models.{{ctrlName}}
|
||||
@ -1220,6 +1203,7 @@ func (c *{{ctrlName}}Controller) GetOne() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetAll ...
|
||||
// @Title Get All
|
||||
// @Description get {{ctrlName}}
|
||||
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
|
||||
@ -1235,9 +1219,9 @@ func (c *{{ctrlName}}Controller) GetAll() {
|
||||
var fields []string
|
||||
var sortby []string
|
||||
var order []string
|
||||
var query map[string]string = make(map[string]string)
|
||||
var query = make(map[string]string)
|
||||
var limit int64 = 10
|
||||
var offset int64 = 0
|
||||
var offset int64
|
||||
|
||||
// fields: col1,col2,entity.col3
|
||||
if v := c.GetString("fields"); v != "" {
|
||||
@ -1282,7 +1266,8 @@ func (c *{{ctrlName}}Controller) GetAll() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title Update
|
||||
// Put ...
|
||||
// @Title Put
|
||||
// @Description update the {{ctrlName}}
|
||||
// @Param id path string true "The id you want to update"
|
||||
// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content"
|
||||
@ -1305,6 +1290,7 @@ func (c *{{ctrlName}}Controller) Put() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
// @Title Delete
|
||||
// @Description delete the {{ctrlName}}
|
||||
// @Param id path string true "The id you want to delete"
|
||||
|
@ -21,9 +21,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// article
|
||||
// cms/article
|
||||
//
|
||||
func generateController(cname, currpath string) {
|
||||
w := NewColorWriter(os.Stdout)
|
||||
|
||||
@ -36,15 +33,14 @@ func generateController(cname, currpath string) {
|
||||
packageName = p[i+1 : len(p)-1]
|
||||
}
|
||||
|
||||
ColorLog("[INFO] Using '%s' as controller name\n", controllerName)
|
||||
ColorLog("[INFO] Using '%s' as package name\n", packageName)
|
||||
logger.Infof("Using '%s' as controller name", controllerName)
|
||||
logger.Infof("Using '%s' as package name", packageName)
|
||||
|
||||
fp := path.Join(currpath, "controllers", p)
|
||||
if _, err := os.Stat(fp); os.IsNotExist(err) {
|
||||
// Create the controller's directory
|
||||
if err := os.MkdirAll(fp, 0777); err != nil {
|
||||
ColorLog("[ERRO] Could not create controllers directory: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create controllers directory: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +52,7 @@ func generateController(cname, currpath string) {
|
||||
|
||||
var content string
|
||||
if _, err := os.Stat(modelPath); err == nil {
|
||||
ColorLog("[INFO] Using matching model '%s'\n", controllerName)
|
||||
logger.Infof("Using matching model '%s'", controllerName)
|
||||
content = strings.Replace(controllerModelTpl, "{{packageName}}", packageName, -1)
|
||||
pkgPath := getPackagePath(currpath)
|
||||
content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1)
|
||||
@ -71,8 +67,7 @@ func generateController(cname, currpath string) {
|
||||
formatSourceCode(fpath)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||
} else {
|
||||
ColorLog("[ERRO] Could not create controller file: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create controller file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,11 +77,12 @@ import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
// operations for {{controllerName}}
|
||||
// {{controllerName}}Controller operations for {{controllerName}}
|
||||
type {{controllerName}}Controller struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// URLMapping ...
|
||||
func (c *{{controllerName}}Controller) URLMapping() {
|
||||
c.Mapping("Post", c.Post)
|
||||
c.Mapping("GetOne", c.GetOne)
|
||||
@ -95,6 +91,7 @@ func (c *{{controllerName}}Controller) URLMapping() {
|
||||
c.Mapping("Delete", c.Delete)
|
||||
}
|
||||
|
||||
// Post ...
|
||||
// @Title Create
|
||||
// @Description create {{controllerName}}
|
||||
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
||||
@ -105,6 +102,7 @@ func (c *{{controllerName}}Controller) Post() {
|
||||
|
||||
}
|
||||
|
||||
// GetOne ...
|
||||
// @Title GetOne
|
||||
// @Description get {{controllerName}} by id
|
||||
// @Param id path string true "The key for staticblock"
|
||||
@ -115,6 +113,7 @@ func (c *{{controllerName}}Controller) GetOne() {
|
||||
|
||||
}
|
||||
|
||||
// GetAll ...
|
||||
// @Title GetAll
|
||||
// @Description get {{controllerName}}
|
||||
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
|
||||
@ -130,7 +129,8 @@ func (c *{{controllerName}}Controller) GetAll() {
|
||||
|
||||
}
|
||||
|
||||
// @Title Update
|
||||
// Put ...
|
||||
// @Title Put
|
||||
// @Description update the {{controllerName}}
|
||||
// @Param id path string true "The id you want to update"
|
||||
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
||||
@ -141,6 +141,7 @@ func (c *{{controllerName}}Controller) Put() {
|
||||
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
// @Title Delete
|
||||
// @Description delete the {{controllerName}}
|
||||
// @Param id path string true "The id you want to delete"
|
||||
@ -164,11 +165,12 @@ import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
// oprations for {{controllerName}}
|
||||
// {{controllerName}}Controller oprations for {{controllerName}}
|
||||
type {{controllerName}}Controller struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// URLMapping ...
|
||||
func (c *{{controllerName}}Controller) URLMapping() {
|
||||
c.Mapping("Post", c.Post)
|
||||
c.Mapping("GetOne", c.GetOne)
|
||||
@ -177,6 +179,7 @@ func (c *{{controllerName}}Controller) URLMapping() {
|
||||
c.Mapping("Delete", c.Delete)
|
||||
}
|
||||
|
||||
// Post ...
|
||||
// @Title Post
|
||||
// @Description create {{controllerName}}
|
||||
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
||||
@ -195,7 +198,8 @@ func (c *{{controllerName}}Controller) Post() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title Get
|
||||
// GetOne ...
|
||||
// @Title Get One
|
||||
// @Description get {{controllerName}} by id
|
||||
// @Param id path string true "The key for staticblock"
|
||||
// @Success 200 {object} models.{{controllerName}}
|
||||
@ -213,6 +217,7 @@ func (c *{{controllerName}}Controller) GetOne() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetAll ...
|
||||
// @Title Get All
|
||||
// @Description get {{controllerName}}
|
||||
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
|
||||
@ -228,9 +233,9 @@ func (c *{{controllerName}}Controller) GetAll() {
|
||||
var fields []string
|
||||
var sortby []string
|
||||
var order []string
|
||||
var query map[string]string = make(map[string]string)
|
||||
var query = make(map[string]string)
|
||||
var limit int64 = 10
|
||||
var offset int64 = 0
|
||||
var offset int64
|
||||
|
||||
// fields: col1,col2,entity.col3
|
||||
if v := c.GetString("fields"); v != "" {
|
||||
@ -275,7 +280,8 @@ func (c *{{controllerName}}Controller) GetAll() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title Update
|
||||
// Put ...
|
||||
// @Title Put
|
||||
// @Description update the {{controllerName}}
|
||||
// @Param id path string true "The id you want to update"
|
||||
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
||||
@ -295,6 +301,7 @@ func (c *{{controllerName}}Controller) Put() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
// @Title Delete
|
||||
// @Description delete the {{controllerName}}
|
||||
// @Param id path string true "The id you want to delete"
|
||||
|
324
g_docs.go
324
g_docs.go
@ -26,11 +26,12 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/astaxie/beego/swagger"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
@ -48,6 +49,30 @@ var importlist map[string]string
|
||||
var controllerList map[string]map[string]*swagger.Item //controllername Paths items
|
||||
var modelsList map[string]map[string]swagger.Schema
|
||||
var rootapi swagger.Swagger
|
||||
var astPkgs map[string]*ast.Package
|
||||
|
||||
// refer to builtin.go
|
||||
var basicTypes = map[string]string{
|
||||
"bool": "boolean:",
|
||||
"uint": "integer:int32",
|
||||
"uint8": "integer:int32",
|
||||
"uint16": "integer:int32",
|
||||
"uint32": "integer:int32",
|
||||
"uint64": "integer:int64",
|
||||
"int": "integer:int64",
|
||||
"int8": "integer:int32",
|
||||
"int16:int32": "integer:int32",
|
||||
"int32": "integer:int32",
|
||||
"int64": "integer:int64",
|
||||
"uintptr": "integer:int64",
|
||||
"float32": "number:float",
|
||||
"float64": "number:double",
|
||||
"string": "string:",
|
||||
"complex64": "number:float",
|
||||
"complex128": "number:double",
|
||||
"byte": "string:byte",
|
||||
"rune": "string:byte",
|
||||
}
|
||||
|
||||
func init() {
|
||||
pkgCache = make(map[string]struct{})
|
||||
@ -55,21 +80,68 @@ func init() {
|
||||
importlist = make(map[string]string)
|
||||
controllerList = make(map[string]map[string]*swagger.Item)
|
||||
modelsList = make(map[string]map[string]swagger.Schema)
|
||||
astPkgs = map[string]*ast.Package{}
|
||||
}
|
||||
|
||||
func parsePackagesFromDir(dirpath string) {
|
||||
c := make(chan error)
|
||||
|
||||
go func() {
|
||||
filepath.Walk(dirpath, func(fpath string, fileInfo os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !fileInfo.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fileInfo.Name() != "vendor" {
|
||||
err = parsePackageFromDir(fpath)
|
||||
if err != nil {
|
||||
// Send the error to through the channel and continue walking
|
||||
c <- fmt.Errorf("Error while parsing directory: %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
close(c)
|
||||
}()
|
||||
|
||||
for err := range c {
|
||||
logger.Warnf("%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func parsePackageFromDir(path string) error {
|
||||
fileSet := token.NewFileSet()
|
||||
folderPkgs, err := parser.ParseDir(fileSet, path, func(info os.FileInfo) bool {
|
||||
name := info.Name()
|
||||
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
|
||||
}, parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for k, v := range folderPkgs {
|
||||
astPkgs[k] = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateDocs(curpath string) {
|
||||
fset := token.NewFileSet()
|
||||
|
||||
f, err := parser.ParseFile(fset, path.Join(curpath, "routers", "router.go"), nil, parser.ParseComments)
|
||||
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] parse router.go error\n")
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Error while parsing router.go: %s", err)
|
||||
}
|
||||
|
||||
rootapi.Infos = swagger.Information{}
|
||||
rootapi.SwaggerVersion = "2.0"
|
||||
//analysis API comments
|
||||
|
||||
// Analyse API comments
|
||||
if f.Comments != nil {
|
||||
for _, c := range f.Comments {
|
||||
for _, s := range strings.Split(c.Text(), "\n") {
|
||||
@ -83,21 +155,38 @@ func generateDocs(curpath string) {
|
||||
rootapi.Infos.TermsOfService = strings.TrimSpace(s[len("@TermsOfServiceUrl"):])
|
||||
} else if strings.HasPrefix(s, "@Contact") {
|
||||
rootapi.Infos.Contact.EMail = strings.TrimSpace(s[len("@Contact"):])
|
||||
} else if strings.HasPrefix(s, "@License") {
|
||||
rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):])
|
||||
} else if strings.HasPrefix(s, "@Name") {
|
||||
rootapi.Infos.Contact.Name = strings.TrimSpace(s[len("@Name"):])
|
||||
} else if strings.HasPrefix(s, "@URL") {
|
||||
rootapi.Infos.Contact.URL = strings.TrimSpace(s[len("@URL"):])
|
||||
} else if strings.HasPrefix(s, "@LicenseUrl") {
|
||||
rootapi.Infos.License.URL = strings.TrimSpace(s[len("@LicenseUrl"):])
|
||||
if rootapi.Infos.License == nil {
|
||||
rootapi.Infos.License = &swagger.License{URL: strings.TrimSpace(s[len("@LicenseUrl"):])}
|
||||
} else {
|
||||
rootapi.Infos.License.URL = strings.TrimSpace(s[len("@LicenseUrl"):])
|
||||
}
|
||||
} else if strings.HasPrefix(s, "@License") {
|
||||
if rootapi.Infos.License == nil {
|
||||
rootapi.Infos.License = &swagger.License{Name: strings.TrimSpace(s[len("@License"):])}
|
||||
} else {
|
||||
rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):])
|
||||
}
|
||||
} else if strings.HasPrefix(s, "@Schemes") {
|
||||
rootapi.Schemes = strings.Split(strings.TrimSpace(s[len("@Schemes"):]), ",")
|
||||
} else if strings.HasPrefix(s, "@Host") {
|
||||
rootapi.Host = strings.TrimSpace(s[len("@Host"):])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// analisys controller package
|
||||
|
||||
// Analyse controller package
|
||||
for _, im := range f.Imports {
|
||||
localName := ""
|
||||
if im.Name != nil {
|
||||
localName = im.Name.Name
|
||||
}
|
||||
analisyscontrollerPkg(localName, im.Path.Value)
|
||||
analyseControllerPkg(localName, im.Path.Value)
|
||||
}
|
||||
for _, d := range f.Decls {
|
||||
switch specDecl := d.(type) {
|
||||
@ -107,11 +196,11 @@ func generateDocs(curpath string) {
|
||||
case *ast.AssignStmt:
|
||||
for _, l := range stmt.Rhs {
|
||||
if v, ok := l.(*ast.CallExpr); ok {
|
||||
// analisys NewNamespace, it will return version and the subfunction
|
||||
// Analyse NewNamespace, it will return version and the subfunction
|
||||
if selName := v.Fun.(*ast.SelectorExpr).Sel.String(); selName != "NewNamespace" {
|
||||
continue
|
||||
}
|
||||
version, params := analisysNewNamespace(v)
|
||||
version, params := analyseNewNamespace(v)
|
||||
if rootapi.BasePath == "" && version != "" {
|
||||
rootapi.BasePath = version
|
||||
}
|
||||
@ -120,12 +209,12 @@ func generateDocs(curpath string) {
|
||||
case *ast.CallExpr:
|
||||
controllerName := ""
|
||||
if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" {
|
||||
s, params := analisysNewNamespace(pp)
|
||||
s, params := analyseNewNamespace(pp)
|
||||
for _, sp := range params {
|
||||
switch pp := sp.(type) {
|
||||
case *ast.CallExpr:
|
||||
if pp.Fun.(*ast.SelectorExpr).Sel.String() == "NSInclude" {
|
||||
controllerName = analisysNSInclude(s, pp)
|
||||
controllerName = analyseNSInclude(s, pp)
|
||||
if v, ok := controllerComments[controllerName]; ok {
|
||||
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
|
||||
Name: strings.Trim(s, "/"),
|
||||
@ -136,7 +225,7 @@ func generateDocs(curpath string) {
|
||||
}
|
||||
}
|
||||
} else if selname == "NSInclude" {
|
||||
controllerName = analisysNSInclude("", pp)
|
||||
controllerName = analyseNSInclude("", pp)
|
||||
if v, ok := controllerComments[controllerName]; ok {
|
||||
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
|
||||
Name: controllerName, // if the NSInclude has no prefix, we use the controllername as the tag
|
||||
@ -155,22 +244,26 @@ func generateDocs(curpath string) {
|
||||
}
|
||||
os.Mkdir(path.Join(curpath, "swagger"), 0755)
|
||||
fd, err := os.Create(path.Join(curpath, "swagger", "swagger.json"))
|
||||
fdyml, err := os.Create(path.Join(curpath, "swagger", "swagger.yml"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fdyml.Close()
|
||||
defer fd.Close()
|
||||
dt, err := json.MarshalIndent(rootapi, "", " ")
|
||||
if err != nil {
|
||||
dtyml, erryml := yaml.Marshal(rootapi)
|
||||
if err != nil || erryml != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = fd.Write(dt)
|
||||
if err != nil {
|
||||
_, erryml = fdyml.Write(dtyml)
|
||||
if err != nil || erryml != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// return version and the others params
|
||||
func analisysNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
|
||||
// analyseNewNamespace returns version and the others params
|
||||
func analyseNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
|
||||
for i, p := range ce.Args {
|
||||
if i == 0 {
|
||||
switch pp := p.(type) {
|
||||
@ -184,7 +277,7 @@ func analisysNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
|
||||
return
|
||||
}
|
||||
|
||||
func analisysNSInclude(baseurl string, ce *ast.CallExpr) string {
|
||||
func analyseNSInclude(baseurl string, ce *ast.CallExpr) string {
|
||||
cname := ""
|
||||
for _, p := range ce.Args {
|
||||
x := p.(*ast.UnaryExpr).X.(*ast.CompositeLit).Type.(*ast.SelectorExpr)
|
||||
@ -232,7 +325,7 @@ func analisysNSInclude(baseurl string, ce *ast.CallExpr) string {
|
||||
return cname
|
||||
}
|
||||
|
||||
func analisyscontrollerPkg(localName, pkgpath string) {
|
||||
func analyseControllerPkg(localName, pkgpath string) {
|
||||
pkgpath = strings.Trim(pkgpath, "\"")
|
||||
if isSystemPackage(pkgpath) {
|
||||
return
|
||||
@ -248,7 +341,7 @@ func analisyscontrollerPkg(localName, pkgpath string) {
|
||||
}
|
||||
gopath := os.Getenv("GOPATH")
|
||||
if gopath == "" {
|
||||
panic("please set gopath")
|
||||
logger.Fatal("GOPATH environment variable is not set or empty")
|
||||
}
|
||||
pkgRealpath := ""
|
||||
|
||||
@ -266,18 +359,16 @@ func analisyscontrollerPkg(localName, pkgpath string) {
|
||||
}
|
||||
pkgCache[pkgpath] = struct{}{}
|
||||
} else {
|
||||
ColorLog("[ERRO] the %s pkg not exist in gopath\n", pkgpath)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("Package '%s' does not exist in the GOPATH", pkgpath)
|
||||
}
|
||||
|
||||
fileSet := token.NewFileSet()
|
||||
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
|
||||
name := info.Name()
|
||||
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
|
||||
}, parser.ParseComments)
|
||||
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] the %s pkg parser.ParseDir error\n", pkgpath)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("Error while parsing dir at '%s': %s", pkgpath, err)
|
||||
}
|
||||
for _, pkg := range astPkgs {
|
||||
for _, fl := range pkg.Files {
|
||||
@ -286,7 +377,7 @@ func analisyscontrollerPkg(localName, pkgpath string) {
|
||||
case *ast.FuncDecl:
|
||||
if specDecl.Recv != nil && len(specDecl.Recv.List) > 0 {
|
||||
if t, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr); ok {
|
||||
// parse controller method
|
||||
// Parse controller method
|
||||
parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(t.X), pkgpath)
|
||||
}
|
||||
}
|
||||
@ -296,7 +387,7 @@ func analisyscontrollerPkg(localName, pkgpath string) {
|
||||
switch tp := s.(*ast.TypeSpec).Type.(type) {
|
||||
case *ast.StructType:
|
||||
_ = tp.Struct
|
||||
//parse controller definition comments
|
||||
// Parse controller definition comments
|
||||
if strings.TrimSpace(specDecl.Doc.Text()) != "" {
|
||||
controllerComments[pkgpath+s.(*ast.TypeSpec).Name.String()] = specDecl.Doc.Text()
|
||||
}
|
||||
@ -310,10 +401,11 @@ func analisyscontrollerPkg(localName, pkgpath string) {
|
||||
}
|
||||
|
||||
func isSystemPackage(pkgpath string) bool {
|
||||
goroot := runtime.GOROOT()
|
||||
goroot := os.Getenv("GOROOT")
|
||||
if goroot == "" {
|
||||
panic("goroot is empty, do you install Go right?")
|
||||
logger.Fatalf("GOROOT environment variable is not set or empty")
|
||||
}
|
||||
|
||||
wg, _ := filepath.EvalSymlinks(filepath.Join(goroot, "src", "pkg", pkgpath))
|
||||
if utils.FileExists(wg) {
|
||||
return true
|
||||
@ -379,8 +471,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
||||
ss = strings.TrimSpace(ss[pos:])
|
||||
schemaName, pos := peekNextSplitString(ss)
|
||||
if schemaName == "" {
|
||||
ColorLog("[ERRO][%s.%s] Schema must follow {object} or {array}\n", controllerName, funcName)
|
||||
os.Exit(-1)
|
||||
logger.Fatalf("[%s.%s] Schema must follow {object} or {array}", controllerName, funcName)
|
||||
}
|
||||
if strings.HasPrefix(schemaName, "[]") {
|
||||
schemaName = schemaName[2:]
|
||||
@ -392,13 +483,13 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
||||
schema.Type = typeFormat[0]
|
||||
schema.Format = typeFormat[1]
|
||||
} else {
|
||||
cmpath, m, mod, realTypes := getModel(schemaName)
|
||||
m, mod, realTypes := getModel(schemaName)
|
||||
schema.Ref = "#/definitions/" + m
|
||||
if _, ok := modelsList[pkgpath+controllerName]; !ok {
|
||||
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0)
|
||||
}
|
||||
modelsList[pkgpath+controllerName][schemaName] = mod
|
||||
appendModels(cmpath, pkgpath, controllerName, realTypes)
|
||||
appendModels(pkgpath, controllerName, realTypes)
|
||||
}
|
||||
if isArray {
|
||||
rs.Schema = &swagger.Schema{
|
||||
@ -417,7 +508,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
||||
para := swagger.Parameter{}
|
||||
p := getparams(strings.TrimSpace(t[len("@Param "):]))
|
||||
if len(p) < 4 {
|
||||
panic(controllerName + "_" + funcName + "'s comments @Param at least should has 4 params")
|
||||
logger.Fatal(controllerName + "_" + funcName + "'s comments @Param should have at least 4 params")
|
||||
}
|
||||
para.Name = p[0]
|
||||
switch p[1] {
|
||||
@ -432,13 +523,13 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
||||
case "body":
|
||||
break
|
||||
default:
|
||||
ColorLog("[WARN][%s.%s] Unknow param location: %s, Possible values are `query`, `header`, `path`, `formData` or `body`.\n", controllerName, funcName, p[1])
|
||||
logger.Warnf("[%s.%s] Unknown param location: %s. Possible values are `query`, `header`, `path`, `formData` or `body`.\n", controllerName, funcName, p[1])
|
||||
}
|
||||
para.In = p[1]
|
||||
pp := strings.Split(p[2], ".")
|
||||
typ := pp[len(pp)-1]
|
||||
if len(pp) >= 2 {
|
||||
cmpath, m, mod, realTypes := getModel(p[2])
|
||||
m, mod, realTypes := getModel(p[2])
|
||||
para.Schema = &swagger.Schema{
|
||||
Ref: "#/definitions/" + m,
|
||||
}
|
||||
@ -446,7 +537,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
||||
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0)
|
||||
}
|
||||
modelsList[pkgpath+controllerName][typ] = mod
|
||||
appendModels(cmpath, pkgpath, controllerName, realTypes)
|
||||
appendModels(pkgpath, controllerName, realTypes)
|
||||
} else {
|
||||
isArray := false
|
||||
paraType := ""
|
||||
@ -463,7 +554,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
||||
paraType = typeFormat[0]
|
||||
paraFormat = typeFormat[1]
|
||||
} else {
|
||||
ColorLog("[WARN][%s.%s] Unknow param type: %s\n", controllerName, funcName, typ)
|
||||
logger.Warnf("[%s.%s] Unknown param type: %s\n", controllerName, funcName, typ)
|
||||
}
|
||||
if isArray {
|
||||
para.Type = "array"
|
||||
@ -476,10 +567,15 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
||||
para.Format = paraFormat
|
||||
}
|
||||
}
|
||||
if len(p) > 4 {
|
||||
switch len(p) {
|
||||
case 5:
|
||||
para.Required, _ = strconv.ParseBool(p[3])
|
||||
para.Description = strings.Trim(p[4], `" `)
|
||||
} else {
|
||||
case 6:
|
||||
para.Default = str2RealType(p[3], para.Type)
|
||||
para.Required, _ = strconv.ParseBool(p[4])
|
||||
para.Description = strings.Trim(p[5], `" `)
|
||||
default:
|
||||
para.Description = strings.Trim(p[3], `" `)
|
||||
}
|
||||
opts.Parameters = append(opts.Parameters, para)
|
||||
@ -565,16 +661,12 @@ func getparams(str string) []string {
|
||||
var j int
|
||||
var start bool
|
||||
var r []string
|
||||
for i, c := range []rune(str) {
|
||||
if unicode.IsSpace(c) {
|
||||
var quoted int8
|
||||
for _, c := range []rune(str) {
|
||||
if unicode.IsSpace(c) && quoted == 0 {
|
||||
if !start {
|
||||
continue
|
||||
} else {
|
||||
if j == 3 {
|
||||
r = append(r, string(s))
|
||||
r = append(r, strings.TrimSpace((str[i+1:])))
|
||||
break
|
||||
}
|
||||
start = false
|
||||
j++
|
||||
r = append(r, string(s))
|
||||
@ -582,28 +674,24 @@ func getparams(str string) []string {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
start = true
|
||||
if c == '"' {
|
||||
quoted ^= 1
|
||||
continue
|
||||
}
|
||||
s = append(s, c)
|
||||
}
|
||||
if len(s) > 0 {
|
||||
r = append(r, string(s))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func getModel(str string) (pkgpath, objectname string, m swagger.Schema, realTypes []string) {
|
||||
func getModel(str string) (objectname string, m swagger.Schema, realTypes []string) {
|
||||
strs := strings.Split(str, ".")
|
||||
objectname = strs[len(strs)-1]
|
||||
pkgpath = strings.Join(strs[:len(strs)-1], "/")
|
||||
curpath, _ := os.Getwd()
|
||||
pkgRealpath := path.Join(curpath, pkgpath)
|
||||
fileSet := token.NewFileSet()
|
||||
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
|
||||
name := info.Name()
|
||||
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
|
||||
}, parser.ParseComments)
|
||||
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] the model %s parser.ParseDir error\n", str)
|
||||
os.Exit(1)
|
||||
}
|
||||
packageName := ""
|
||||
m.Type = "object"
|
||||
for _, pkg := range astPkgs {
|
||||
for _, fl := range pkg.Files {
|
||||
@ -612,28 +700,29 @@ func getModel(str string) (pkgpath, objectname string, m swagger.Schema, realTyp
|
||||
if k != objectname {
|
||||
continue
|
||||
}
|
||||
parseObject(d, k, &m, &realTypes, astPkgs)
|
||||
packageName = pkg.Name
|
||||
parseObject(d, k, &m, &realTypes, astPkgs, pkg.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if m.Title == "" {
|
||||
ColorLog("[WARN]can't find the object: %s\n", str)
|
||||
logger.Warnf("Cannot find the object: %s", str)
|
||||
// TODO remove when all type have been supported
|
||||
//os.Exit(1)
|
||||
}
|
||||
if len(rootapi.Definitions) == 0 {
|
||||
rootapi.Definitions = make(map[string]swagger.Schema)
|
||||
}
|
||||
objectname = packageName + "." + objectname
|
||||
rootapi.Definitions[objectname] = m
|
||||
return
|
||||
}
|
||||
|
||||
func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package) {
|
||||
func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package, packageName string) {
|
||||
ts, ok := d.Decl.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
ColorLog("Unknown type without TypeSec: %v\n", d)
|
||||
os.Exit(1)
|
||||
logger.Fatalf("Unknown type without TypeSec: %v\n", d)
|
||||
}
|
||||
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc...
|
||||
st, ok := ts.Type.(*ast.StructType)
|
||||
@ -644,7 +733,18 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
|
||||
if st.Fields.List != nil {
|
||||
m.Properties = make(map[string]swagger.Propertie)
|
||||
for _, field := range st.Fields.List {
|
||||
realType := ""
|
||||
isSlice, realType, sType := typeAnalyser(field)
|
||||
if (isSlice && isBasicType(realType)) || sType == "object" {
|
||||
if len(strings.Split(realType, " ")) > 1 {
|
||||
realType = strings.Replace(realType, " ", ".", -1)
|
||||
realType = strings.Replace(realType, "&", "", -1)
|
||||
realType = strings.Replace(realType, "{", "", -1)
|
||||
realType = strings.Replace(realType, "}", "", -1)
|
||||
} else {
|
||||
realType = packageName + "." + realType
|
||||
}
|
||||
}
|
||||
*realTypes = append(*realTypes, realType)
|
||||
mp := swagger.Propertie{}
|
||||
if isSlice {
|
||||
@ -661,12 +761,18 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if isBasicType(realType) {
|
||||
if sType == "object" {
|
||||
mp.Ref = "#/definitions/" + realType
|
||||
} else if isBasicType(realType) {
|
||||
typeFormat := strings.Split(sType, ":")
|
||||
mp.Type = typeFormat[0]
|
||||
mp.Format = typeFormat[1]
|
||||
} else if sType == "object" {
|
||||
mp.Ref = "#/definitions/" + realType
|
||||
} else if realType == "map" {
|
||||
typeFormat := strings.Split(sType, ":")
|
||||
mp.AdditionalProperties = &swagger.Propertie{
|
||||
Type: typeFormat[0],
|
||||
Format: typeFormat[1],
|
||||
}
|
||||
}
|
||||
}
|
||||
if field.Names != nil {
|
||||
@ -681,7 +787,21 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
|
||||
}
|
||||
|
||||
var tagValues []string
|
||||
|
||||
stag := reflect.StructTag(strings.Trim(field.Tag.Value, "`"))
|
||||
|
||||
defaultValue := stag.Get("doc")
|
||||
if defaultValue != "" {
|
||||
r, _ := regexp.Compile(`default\((.*)\)`)
|
||||
if r.MatchString(defaultValue) {
|
||||
res := r.FindStringSubmatch(defaultValue)
|
||||
mp.Default = str2RealType(res[1], realType)
|
||||
|
||||
} else {
|
||||
logger.Warnf("Invalid default value: %s", defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
tag := stag.Get("json")
|
||||
|
||||
if tag != "" {
|
||||
@ -719,7 +839,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
|
||||
for _, fl := range pkg.Files {
|
||||
for nameOfObj, obj := range fl.Scope.Objects {
|
||||
if obj.Name == fmt.Sprint(field.Type) {
|
||||
parseObject(obj, nameOfObj, m, realTypes, astPkgs)
|
||||
parseObject(obj, nameOfObj, m, realTypes, astPkgs, pkg.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -746,7 +866,11 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
|
||||
case *ast.StarExpr:
|
||||
return false, fmt.Sprint(t.X), "object"
|
||||
case *ast.MapType:
|
||||
return false, fmt.Sprint(t.Value), "object"
|
||||
val := fmt.Sprintf("%v", t.Value)
|
||||
if isBasicType(val) {
|
||||
return false, "map", basicTypes[val]
|
||||
}
|
||||
return false, val, "object"
|
||||
}
|
||||
if k, ok := basicTypes[fmt.Sprint(f.Type)]; ok {
|
||||
return false, fmt.Sprint(f.Type), k
|
||||
@ -761,18 +885,6 @@ func isBasicType(Type string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// refer to builtin.go
|
||||
var basicTypes = map[string]string{
|
||||
"bool": "boolean:",
|
||||
"uint": "integer:int32", "uint8": "integer:int32", "uint16": "integer:int32", "uint32": "integer:int32", "uint64": "integer:int64",
|
||||
"int": "integer:int64", "int8": "integer:int32", "int16:int32": "integer:int32", "int32": "integer:int32", "int64": "integer:int64",
|
||||
"uintptr": "integer:int64",
|
||||
"float32": "number:float", "float64": "number:double",
|
||||
"string": "string:",
|
||||
"complex64": "number:float", "complex128": "number:double",
|
||||
"byte": "string:byte", "rune": "string:byte",
|
||||
}
|
||||
|
||||
// regexp get json tag
|
||||
func grepJSONTag(tag string) string {
|
||||
r, _ := regexp.Compile(`json:"([^"]*)"`)
|
||||
@ -784,23 +896,16 @@ func grepJSONTag(tag string) string {
|
||||
}
|
||||
|
||||
// append models
|
||||
func appendModels(cmpath, pkgpath, controllerName string, realTypes []string) {
|
||||
var p string
|
||||
if cmpath != "" {
|
||||
p = strings.Join(strings.Split(cmpath, "/"), ".") + "."
|
||||
} else {
|
||||
p = ""
|
||||
}
|
||||
func appendModels(pkgpath, controllerName string, realTypes []string) {
|
||||
for _, realType := range realTypes {
|
||||
if realType != "" && !isBasicType(strings.TrimLeft(realType, "[]")) &&
|
||||
!strings.HasPrefix(realType, "map") && !strings.HasPrefix(realType, "&") {
|
||||
if _, ok := modelsList[pkgpath+controllerName][p+realType]; ok {
|
||||
if _, ok := modelsList[pkgpath+controllerName][realType]; ok {
|
||||
continue
|
||||
}
|
||||
//fmt.Printf(pkgpath + ":" + controllerName + ":" + cmpath + ":" + realType + "\n")
|
||||
_, _, mod, newRealTypes := getModel(p + realType)
|
||||
modelsList[pkgpath+controllerName][p+realType] = mod
|
||||
appendModels(cmpath, pkgpath, controllerName, newRealTypes)
|
||||
_, mod, newRealTypes := getModel(realType)
|
||||
modelsList[pkgpath+controllerName][realType] = mod
|
||||
appendModels(pkgpath, controllerName, newRealTypes)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -818,3 +923,28 @@ func urlReplace(src string) string {
|
||||
}
|
||||
return strings.Join(pt, "/")
|
||||
}
|
||||
|
||||
func str2RealType(s string, typ string) interface{} {
|
||||
var err error
|
||||
var ret interface{}
|
||||
|
||||
switch typ {
|
||||
case "int", "int64", "int32", "int16", "int8":
|
||||
ret, err = strconv.Atoi(s)
|
||||
case "bool":
|
||||
ret, err = strconv.ParseBool(s)
|
||||
case "float64":
|
||||
ret, err = strconv.ParseFloat(s, 64)
|
||||
case "float32":
|
||||
ret, err = strconv.ParseFloat(s, 32)
|
||||
default:
|
||||
return s
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Warnf("Invalid default value type '%s': %s", typ, s)
|
||||
return s
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
@ -38,9 +38,7 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
|
||||
case "3":
|
||||
mode = OModel | OController | ORouter
|
||||
default:
|
||||
ColorLog("[ERRO] Invalid 'level' option: %s\n", level)
|
||||
ColorLog("[HINT] Level must be either 1, 2 or 3\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Invalid 'level' option. Level must be either \"1\", \"2\" or \"3\"")
|
||||
}
|
||||
var selectedTables map[string]bool
|
||||
if tables != "" {
|
||||
@ -53,12 +51,9 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
|
||||
case "mysql":
|
||||
case "postgres":
|
||||
case "sqlite":
|
||||
ColorLog("[ERRO] Generating app code from SQLite database is not supported yet.\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Generating app code from SQLite database is not supported yet")
|
||||
default:
|
||||
ColorLog("[ERRO] Unknown database driver: %s\n", driver)
|
||||
ColorLog("[HINT] Driver must be one of mysql, postgres or sqlite\n")
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Unknown database driver '%s'. Driver must be one of mysql, postgres or sqlite", driver)
|
||||
}
|
||||
genHprose(driver, connStr, mode, selectedTables, currpath)
|
||||
}
|
||||
@ -68,12 +63,11 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
|
||||
func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bool, currpath string) {
|
||||
db, err := sql.Open(dbms, connStr)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not connect to %s database: %s, %s\n", dbms, connStr, err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err)
|
||||
}
|
||||
defer db.Close()
|
||||
if trans, ok := dbDriver[dbms]; ok {
|
||||
ColorLog("[INFO] Analyzing database tables...\n")
|
||||
logger.Info("Analyzing database tables...")
|
||||
tableNames := trans.GetTableNames(db)
|
||||
tables := getTableObjects(tableNames, db, trans)
|
||||
mvcPath := new(MvcPath)
|
||||
@ -82,8 +76,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo
|
||||
pkgPath := getPackagePath(currpath)
|
||||
writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
|
||||
} else {
|
||||
ColorLog("[ERRO] Generating app code from %s database is not supported yet.\n", dbms)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Generating app code from '%s' database is not supported yet", dbms)
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +85,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo
|
||||
// Newly geneated files will be inside these folders.
|
||||
func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
|
||||
if (OModel & mode) == OModel {
|
||||
ColorLog("[INFO] Creating model files...\n")
|
||||
logger.Info("Creating model files...")
|
||||
writeHproseModelFiles(tables, paths.ModelPath, selectedTables)
|
||||
}
|
||||
}
|
||||
@ -113,21 +106,21 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
|
||||
var f *os.File
|
||||
var err error
|
||||
if isExist(fpath) {
|
||||
ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||
logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||
if askForConfirmation() {
|
||||
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
ColorLog("[WARN] %v\n", err)
|
||||
logger.Warnf("%s", err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
ColorLog("[WARN] Skipped create file '%s'\n", fpath)
|
||||
logger.Warnf("Skipped create file '%s'", fpath)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
|
||||
if err != nil {
|
||||
ColorLog("[WARN] %v\n", err)
|
||||
logger.Warnf("%s", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -150,8 +143,7 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
|
||||
fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1)
|
||||
fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1)
|
||||
if _, err := f.WriteString(fileStr); err != nil {
|
||||
ColorLog("[ERRO] Could not write model file to '%s'\n", fpath)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not write model file to '%s'", fpath)
|
||||
}
|
||||
CloseFile(f)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||
|
@ -51,12 +51,12 @@ func (m mysqlDriver) generateSQLFromFields(fields string) string {
|
||||
for i, v := range fds {
|
||||
kv := strings.SplitN(v, ":", 2)
|
||||
if len(kv) != 2 {
|
||||
ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n")
|
||||
logger.Error("Fields format is wrong. Should be: key:type,key:type " + v)
|
||||
return ""
|
||||
}
|
||||
typ, tag := m.getSQLType(kv[1])
|
||||
if typ == "" {
|
||||
ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n")
|
||||
logger.Error("Fields format is wrong. Should be: key:type,key:type " + v)
|
||||
return ""
|
||||
}
|
||||
if i == 0 && strings.ToLower(kv[0]) != "id" {
|
||||
@ -120,16 +120,16 @@ func (m postgresqlDriver) generateSQLFromFields(fields string) string {
|
||||
for i, v := range fds {
|
||||
kv := strings.SplitN(v, ":", 2)
|
||||
if len(kv) != 2 {
|
||||
ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n")
|
||||
logger.Error("Fields format is wrong. Should be: key:type,key:type " + v)
|
||||
return ""
|
||||
}
|
||||
typ, tag := m.getSQLType(kv[1])
|
||||
if typ == "" {
|
||||
ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n")
|
||||
logger.Error("Fields format is wrong. Should be: key:type,key:type " + v)
|
||||
return ""
|
||||
}
|
||||
if i == 0 && strings.ToLower(kv[0]) != "id" {
|
||||
sql += "id interger serial primary key,"
|
||||
sql += "id serial primary key,"
|
||||
}
|
||||
sql += snakeString(kv[0]) + " " + typ + ","
|
||||
if tag != "" {
|
||||
@ -177,7 +177,8 @@ func newDBDriver() DBDriver {
|
||||
case "postgres":
|
||||
return postgresqlDriver{}
|
||||
default:
|
||||
panic("driver not supported")
|
||||
logger.Fatal("Driver not supported")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,8 +191,7 @@ func generateMigration(mname, upsql, downsql, curpath string) {
|
||||
if _, err := os.Stat(migrationFilePath); os.IsNotExist(err) {
|
||||
// create migrations directory
|
||||
if err := os.MkdirAll(migrationFilePath, 0777); err != nil {
|
||||
ColorLog("[ERRO] Could not create migration directory: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create migration directory: %s", err)
|
||||
}
|
||||
}
|
||||
// create file
|
||||
@ -208,8 +208,7 @@ func generateMigration(mname, upsql, downsql, curpath string) {
|
||||
formatSourceCode(fpath)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||
} else {
|
||||
ColorLog("[ERRO] Could not create migration file: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create migration file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
13
g_model.go
13
g_model.go
@ -35,19 +35,17 @@ func generateModel(mname, fields, currpath string) {
|
||||
|
||||
modelStruct, hastime, err := getStruct(modelName, fields)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not generate the model struct: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not generate the model struct: %s", err)
|
||||
}
|
||||
|
||||
ColorLog("[INFO] Using '%s' as model name\n", modelName)
|
||||
ColorLog("[INFO] Using '%s' as package name\n", packageName)
|
||||
logger.Infof("Using '%s' as model name", modelName)
|
||||
logger.Infof("Using '%s' as package name", packageName)
|
||||
|
||||
fp := path.Join(currpath, "models", p)
|
||||
if _, err := os.Stat(fp); os.IsNotExist(err) {
|
||||
// Create the model's directory
|
||||
if err := os.MkdirAll(fp, 0777); err != nil {
|
||||
ColorLog("[ERRO] Could not create the model directory: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create the model directory: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,8 +65,7 @@ func generateModel(mname, fields, currpath string) {
|
||||
formatSourceCode(fpath)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||
} else {
|
||||
ColorLog("[ERRO] Could not create model file: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create model file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ package main
|
||||
import "strings"
|
||||
|
||||
func generateScaffold(sname, fields, currpath, driver, conn string) {
|
||||
ColorLog("[INFO] Do you want to create a '%v' model? [Yes|No] ", sname)
|
||||
logger.Infof("Do you want to create a '%s' model? [Yes|No] ", sname)
|
||||
|
||||
// Generate the model
|
||||
if askForConfirmation() {
|
||||
@ -11,19 +11,19 @@ func generateScaffold(sname, fields, currpath, driver, conn string) {
|
||||
}
|
||||
|
||||
// Generate the controller
|
||||
ColorLog("[INFO] Do you want to create a '%v' controller? [Yes|No] ", sname)
|
||||
logger.Infof("Do you want to create a '%s' controller? [Yes|No] ", sname)
|
||||
if askForConfirmation() {
|
||||
generateController(sname, currpath)
|
||||
}
|
||||
|
||||
// Generate the views
|
||||
ColorLog("[INFO] Do you want to create views for this '%v' resource? [Yes|No] ", sname)
|
||||
logger.Infof("Do you want to create views for this '%s' resource? [Yes|No] ", sname)
|
||||
if askForConfirmation() {
|
||||
generateView(sname, currpath)
|
||||
}
|
||||
|
||||
// Generate a migration
|
||||
ColorLog("[INFO] Do you want to create a '%v' migration and schema for this resource? [Yes|No] ", sname)
|
||||
logger.Infof("Do you want to create a '%s' migration and schema for this resource? [Yes|No] ", sname)
|
||||
if askForConfirmation() {
|
||||
upsql := ""
|
||||
downsql := ""
|
||||
@ -40,9 +40,9 @@ func generateScaffold(sname, fields, currpath, driver, conn string) {
|
||||
}
|
||||
|
||||
// Run the migration
|
||||
ColorLog("[INFO] Do you want to migrate the database? [Yes|No] ")
|
||||
logger.Infof("Do you want to migrate the database? [Yes|No] ")
|
||||
if askForConfirmation() {
|
||||
migrateUpdate(currpath, driver, conn)
|
||||
}
|
||||
ColorLog("[INFO] All done! Don't forget to add beego.Router(\"/%v\" ,&controllers.%vController{}) to routers/route.go\n", sname, strings.Title(sname))
|
||||
logger.Successf("All done! Don't forget to add beego.Router(\"/%s\" ,&controllers.%sController{}) to routers/route.go\n", sname, strings.Title(sname))
|
||||
}
|
||||
|
17
g_views.go
17
g_views.go
@ -25,13 +25,12 @@ import (
|
||||
func generateView(viewpath, currpath string) {
|
||||
w := NewColorWriter(os.Stdout)
|
||||
|
||||
ColorLog("[INFO] Generating view...\n")
|
||||
logger.Info("Generating view...")
|
||||
|
||||
absViewPath := path.Join(currpath, "views", viewpath)
|
||||
err := os.MkdirAll(absViewPath, os.ModePerm)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not create '%s' view: %s\n", viewpath, err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create '%s' view: %s", viewpath, err)
|
||||
}
|
||||
|
||||
cfile := path.Join(absViewPath, "index.tpl")
|
||||
@ -40,8 +39,7 @@ func generateView(viewpath, currpath string) {
|
||||
f.WriteString(cfile)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
|
||||
} else {
|
||||
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create view file: %s", err)
|
||||
}
|
||||
|
||||
cfile = path.Join(absViewPath, "show.tpl")
|
||||
@ -50,8 +48,7 @@ func generateView(viewpath, currpath string) {
|
||||
f.WriteString(cfile)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
|
||||
} else {
|
||||
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create view file: %s", err)
|
||||
}
|
||||
|
||||
cfile = path.Join(absViewPath, "create.tpl")
|
||||
@ -60,8 +57,7 @@ func generateView(viewpath, currpath string) {
|
||||
f.WriteString(cfile)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
|
||||
} else {
|
||||
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create view file: %s", err)
|
||||
}
|
||||
|
||||
cfile = path.Join(absViewPath, "edit.tpl")
|
||||
@ -70,7 +66,6 @@ func generateView(viewpath, currpath string) {
|
||||
f.WriteString(cfile)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
|
||||
} else {
|
||||
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create view file: %s", err)
|
||||
}
|
||||
}
|
||||
|
75
hproseapp.go
75
hproseapp.go
@ -10,7 +10,7 @@
|
||||
* *
|
||||
* Build rpc application use Hprose base on beego *
|
||||
* *
|
||||
* LastModified: Oct 13, 2014 *
|
||||
* LastModified: Oct 31, 2016 *
|
||||
* Author: Liu jian <laoliu@lanmv.com> *
|
||||
* *
|
||||
\**********************************************************/
|
||||
@ -63,16 +63,41 @@ EnableDocs = true
|
||||
var hproseMaingo = `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"{{.Appname}}/models"
|
||||
"github.com/hprose/hprose-go/hprose"
|
||||
"github.com/hprose/hprose-golang/rpc"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
func logInvokeHandler(
|
||||
name string,
|
||||
args []reflect.Value,
|
||||
context rpc.Context,
|
||||
next rpc.NextInvokeHandler) (results []reflect.Value, err error) {
|
||||
fmt.Printf("%s(%v) = ", name, args)
|
||||
results, err = next(name, args, context)
|
||||
fmt.Printf("%v %v\r\n", results, err)
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
service := hprose.NewHttpService()
|
||||
// Create WebSocketServer
|
||||
// service := rpc.NewWebSocketService()
|
||||
|
||||
// Create Http Server
|
||||
service := rpc.NewHTTPService()
|
||||
|
||||
// Use Logger Middleware
|
||||
service.AddInvokeHandler(logInvokeHandler)
|
||||
|
||||
// Publish Functions
|
||||
service.AddFunction("AddOne", models.AddOne)
|
||||
service.AddFunction("GetOne", models.GetOne)
|
||||
|
||||
// Start Service
|
||||
beego.Handler("/", service)
|
||||
beego.Run()
|
||||
}
|
||||
@ -81,8 +106,11 @@ func main() {
|
||||
var hproseMainconngo = `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"{{.Appname}}/models"
|
||||
"github.com/hprose/hprose-go/hprose"
|
||||
"github.com/hprose/hprose-golang/rpc"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/orm"
|
||||
@ -93,9 +121,30 @@ func init() {
|
||||
orm.RegisterDataBase("default", "{{.DriverName}}", "{{.conn}}")
|
||||
}
|
||||
|
||||
func logInvokeHandler(
|
||||
name string,
|
||||
args []reflect.Value,
|
||||
context rpc.Context,
|
||||
next rpc.NextInvokeHandler) (results []reflect.Value, err error) {
|
||||
fmt.Printf("%s(%v) = ", name, args)
|
||||
results, err = next(name, args, context)
|
||||
fmt.Printf("%v %v\r\n", results, err)
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
service := hprose.NewHttpService()
|
||||
// Create WebSocketServer
|
||||
// service := rpc.NewWebSocketService()
|
||||
|
||||
// Create Http Server
|
||||
service := rpc.NewHTTPService()
|
||||
|
||||
// Use Logger Middleware
|
||||
service.AddInvokeHandler(logInvokeHandler)
|
||||
|
||||
{{HproseFunctionList}}
|
||||
|
||||
// Start Service
|
||||
beego.Handler("/", service)
|
||||
beego.Run()
|
||||
}
|
||||
@ -249,14 +298,13 @@ var hproseAddFunctions = []string{}
|
||||
|
||||
func init() {
|
||||
cmdHproseapp.Run = createhprose
|
||||
cmdHproseapp.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
cmdHproseapp.Flag.Var(&tables, "tables", "specify tables to generate model")
|
||||
cmdHproseapp.Flag.Var(&driver, "driver", "database driver: mysql, postgresql, etc.")
|
||||
cmdHproseapp.Flag.Var(&conn, "conn", "connection string used by the driver to connect to a database instance")
|
||||
}
|
||||
|
||||
func createhprose(cmd *Command, args []string) int {
|
||||
ShowShortVersionBanner()
|
||||
|
||||
w := NewColorWriter(os.Stdout)
|
||||
|
||||
curpath, _ := os.Getwd()
|
||||
@ -265,8 +313,7 @@ func createhprose(cmd *Command, args []string) int {
|
||||
}
|
||||
apppath, packpath, err := checkEnv(args[0])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("%s", err)
|
||||
}
|
||||
if driver == "" {
|
||||
driver = "mysql"
|
||||
@ -274,7 +321,7 @@ func createhprose(cmd *Command, args []string) int {
|
||||
if conn == "" {
|
||||
}
|
||||
|
||||
ColorLog("[INFO] Creating Hprose application...\n")
|
||||
logger.Info("Creating Hprose application...")
|
||||
|
||||
os.MkdirAll(apppath, 0755)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m")
|
||||
@ -285,9 +332,9 @@ func createhprose(cmd *Command, args []string) int {
|
||||
strings.Replace(hproseconf, "{{.Appname}}", args[0], -1))
|
||||
|
||||
if conn != "" {
|
||||
ColorLog("[INFO] Using '%s' as 'driver'\n", driver)
|
||||
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
|
||||
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
|
||||
logger.Infof("Using '%s' as 'driver'", driver)
|
||||
logger.Infof("Using '%s' as 'conn'", conn)
|
||||
logger.Infof("Using '%s' as 'tables'", tables)
|
||||
generateHproseAppcode(string(driver), string(conn), "1", string(tables), path.Join(curpath, args[0]))
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
|
||||
maingoContent := strings.Replace(hproseMainconngo, "{{.Appname}}", packpath, -1)
|
||||
@ -320,6 +367,6 @@ func createhprose(cmd *Command, args []string) int {
|
||||
WriteToFile(path.Join(apppath, "main.go"),
|
||||
strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1))
|
||||
}
|
||||
ColorLog("[SUCC] New Hprose application successfully created!\n")
|
||||
logger.Success("New Hprose application successfully created!")
|
||||
return 0
|
||||
}
|
||||
|
265
logger.go
Normal file
265
logger.go
Normal file
@ -0,0 +1,265 @@
|
||||
// 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"
|
||||
"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
|
||||
instance *BeeLogger
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
// GetBeeLogger initializes the logger instance with a NewColorWriter output
|
||||
// and returns a singleton
|
||||
func GetBeeLogger(w io.Writer) *BeeLogger {
|
||||
once.Do(func() {
|
||||
var (
|
||||
err error
|
||||
simpleLogFormat = `{{Now "2006/01/02 15:04:05"}} {{.Level}} ▶ {{.ID}} {{.Message}}{{EndLine}}`
|
||||
debugLogFormat = `{{Now "2006/01/02 15:04:05"}} {{.Level}} ▶ {{.ID}} {{.Filename}}:{{.LineNo}} {{.Message}}{{EndLine}}`
|
||||
)
|
||||
|
||||
// Initialize and parse logging templates
|
||||
funcs := template.FuncMap{
|
||||
"Now": Now,
|
||||
"EndLine": EndLine,
|
||||
}
|
||||
logRecordTemplate, err = template.New("simpleLogFormat").Funcs(funcs).Parse(simpleLogFormat)
|
||||
MustCheck(err)
|
||||
debugLogRecordTemplate, err = template.New("debugLogFormat").Funcs(funcs).Parse(debugLogFormat)
|
||||
MustCheck(err)
|
||||
|
||||
instance = &BeeLogger{output: NewColorWriter(w)}
|
||||
})
|
||||
return instance
|
||||
}
|
||||
|
||||
// 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{}) {
|
||||
// Acquire the lock
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
// Create the logging record and pass into the output
|
||||
record := LogRecord{
|
||||
ID: fmt.Sprintf("%04d", atomic.AddUint64(&sequenceNo, 1)),
|
||||
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, file string, line int, args ...interface{}) {
|
||||
if !IsDebugEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
// Change the output to Stderr
|
||||
l.SetOutput(os.Stderr)
|
||||
|
||||
// Create the log record
|
||||
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, file string, line int) {
|
||||
l.mustLogDebug(message, file, line)
|
||||
}
|
||||
|
||||
// Debugf outputs a formatted debug log message
|
||||
func (l *BeeLogger) Debugf(message string, file string, line int, vars ...interface{}) {
|
||||
l.mustLogDebug(message, file, line, 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...)
|
||||
}
|
125
migrate.go
125
migrate.go
@ -16,10 +16,10 @@ package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -56,30 +56,31 @@ var mConn docValue
|
||||
|
||||
func init() {
|
||||
cmdMigrate.Run = runMigration
|
||||
cmdMigrate.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
cmdMigrate.Flag.Var(&mDriver, "driver", "database driver: mysql, postgres, sqlite, etc.")
|
||||
cmdMigrate.Flag.Var(&mConn, "conn", "connection string used by the driver to connect to a database instance")
|
||||
}
|
||||
|
||||
// runMigration is the entry point for starting a migration
|
||||
func runMigration(cmd *Command, args []string) int {
|
||||
ShowShortVersionBanner()
|
||||
|
||||
currpath, _ := os.Getwd()
|
||||
|
||||
gps := GetGOPATHs()
|
||||
if len(gps) == 0 {
|
||||
ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty")
|
||||
os.Exit(2)
|
||||
logger.Fatal("GOPATH environment variable is not set or empty")
|
||||
}
|
||||
gopath := gps[0]
|
||||
Debugf("GOPATH: %s", gopath)
|
||||
|
||||
// load config
|
||||
gopath := gps[0]
|
||||
|
||||
logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath)
|
||||
|
||||
// Load the configuration
|
||||
err := loadConfig()
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
|
||||
logger.Errorf("Failed to load configuration: %s", err)
|
||||
}
|
||||
// getting command line arguments
|
||||
|
||||
// Getting command line arguments
|
||||
if len(args) != 0 {
|
||||
cmd.Flag.Parse(args[1:])
|
||||
}
|
||||
@ -95,31 +96,30 @@ func runMigration(cmd *Command, args []string) int {
|
||||
mConn = "root:@tcp(127.0.0.1:3306)/test"
|
||||
}
|
||||
}
|
||||
ColorLog("[INFO] Using '%s' as 'driver'\n", mDriver)
|
||||
ColorLog("[INFO] Using '%s' as 'conn'\n", mConn)
|
||||
logger.Infof("Using '%s' as 'driver'", mDriver)
|
||||
logger.Infof("Using '%s' as 'conn'", mConn)
|
||||
driverStr, connStr := string(mDriver), string(mConn)
|
||||
if len(args) == 0 {
|
||||
// run all outstanding migrations
|
||||
ColorLog("[INFO] Running all outstanding migrations\n")
|
||||
logger.Info("Running all outstanding migrations")
|
||||
migrateUpdate(currpath, driverStr, connStr)
|
||||
} else {
|
||||
mcmd := args[0]
|
||||
switch mcmd {
|
||||
case "rollback":
|
||||
ColorLog("[INFO] Rolling back the last migration operation\n")
|
||||
logger.Info("Rolling back the last migration operation")
|
||||
migrateRollback(currpath, driverStr, connStr)
|
||||
case "reset":
|
||||
ColorLog("[INFO] Reseting all migrations\n")
|
||||
logger.Info("Reseting all migrations")
|
||||
migrateReset(currpath, driverStr, connStr)
|
||||
case "refresh":
|
||||
ColorLog("[INFO] Refreshing all migrations\n")
|
||||
logger.Info("Refreshing all migrations")
|
||||
migrateRefresh(currpath, driverStr, connStr)
|
||||
default:
|
||||
ColorLog("[ERRO] Command is missing\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Command is missing")
|
||||
}
|
||||
}
|
||||
ColorLog("[SUCC] Migration successful!\n")
|
||||
logger.Success("Migration successful!")
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -146,16 +146,20 @@ func migrateRefresh(currpath, driver, connStr string) {
|
||||
// migrate generates source code, build it, and invoke the binary who does the actual migration
|
||||
func migrate(goal, currpath, driver, connStr string) {
|
||||
dir := path.Join(currpath, "database", "migrations")
|
||||
binary := "m"
|
||||
postfix := ""
|
||||
if runtime.GOOS == "windows" {
|
||||
postfix = ".exe"
|
||||
}
|
||||
binary := "m" + postfix
|
||||
source := binary + ".go"
|
||||
// connect to database
|
||||
|
||||
// Connect to database
|
||||
db, err := sql.Open(driver, connStr)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Could not connect to %s: %s\n", driver, connStr)
|
||||
ColorLog("[ERRO] Error: %v", err.Error())
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not connect to database using '%s': %s", connStr, err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
checkForSchemaUpdateTable(db, driver)
|
||||
latestName, latestTime := getLatestMigration(db, goal)
|
||||
writeMigrationSourceFile(dir, source, driver, connStr, latestTime, latestName, goal)
|
||||
@ -170,50 +174,44 @@ func migrate(goal, currpath, driver, connStr string) {
|
||||
func checkForSchemaUpdateTable(db *sql.DB, driver string) {
|
||||
showTableSQL := showMigrationsTableSQL(driver)
|
||||
if rows, err := db.Query(showTableSQL); err != nil {
|
||||
ColorLog("[ERRO] Could not show migrations table: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not show migrations table: %s", err)
|
||||
} else if !rows.Next() {
|
||||
// no migrations table, create anew
|
||||
// No migrations table, create new ones
|
||||
createTableSQL := createMigrationsTableSQL(driver)
|
||||
ColorLog("[INFO] Creating 'migrations' table...\n")
|
||||
|
||||
logger.Infof("Creating 'migrations' table...")
|
||||
|
||||
if _, err := db.Query(createTableSQL); err != nil {
|
||||
ColorLog("[ERRO] Could not create migrations table: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create migrations table: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// checking that migrations table schema are expected
|
||||
// Checking that migrations table schema are expected
|
||||
selectTableSQL := selectMigrationsTableSQL(driver)
|
||||
if rows, err := db.Query(selectTableSQL); err != nil {
|
||||
ColorLog("[ERRO] Could not show columns of migrations table: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not show columns of migrations table: %s", err)
|
||||
} else {
|
||||
for rows.Next() {
|
||||
var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte
|
||||
if err := rows.Scan(&fieldBytes, &typeBytes, &nullBytes, &keyBytes, &defaultBytes, &extraBytes); err != nil {
|
||||
ColorLog("[ERRO] Could not read column information: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not read column information: %s", err)
|
||||
}
|
||||
fieldStr, typeStr, nullStr, keyStr, defaultStr, extraStr :=
|
||||
string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes)
|
||||
if fieldStr == "id_migration" {
|
||||
if keyStr != "PRI" || extraStr != "auto_increment" {
|
||||
ColorLog("[ERRO] Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s\n", keyStr, extraStr)
|
||||
ColorLog("[HINT] Expecting KEY: PRI, EXTRA: auto_increment\n")
|
||||
os.Exit(2)
|
||||
logger.Hint("Expecting KEY: PRI, EXTRA: auto_increment")
|
||||
logger.Fatalf("Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s", keyStr, extraStr)
|
||||
}
|
||||
} else if fieldStr == "name" {
|
||||
if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" {
|
||||
ColorLog("[ERRO] Column migration.name type mismatch: TYPE: %s, NULL: %s\n", typeStr, nullStr)
|
||||
ColorLog("[HINT] Expecting TYPE: varchar, NULL: YES\n")
|
||||
os.Exit(2)
|
||||
logger.Hint("Expecting TYPE: varchar, NULL: YES")
|
||||
logger.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr)
|
||||
}
|
||||
|
||||
} else if fieldStr == "created_at" {
|
||||
if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" {
|
||||
ColorLog("[ERRO] Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s\n", typeStr, defaultStr)
|
||||
ColorLog("[HINT] Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP\n")
|
||||
os.Exit(2)
|
||||
logger.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP")
|
||||
logger.Fatalf("Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s", typeStr, defaultStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -257,26 +255,22 @@ func selectMigrationsTableSQL(driver string) string {
|
||||
func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64) {
|
||||
sql := "SELECT name FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1"
|
||||
if rows, err := db.Query(sql); err != nil {
|
||||
ColorLog("[ERRO] Could not retrieve migrations: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not retrieve migrations: %s", err)
|
||||
} else {
|
||||
if rows.Next() {
|
||||
if err := rows.Scan(&file); err != nil {
|
||||
ColorLog("[ERRO] Could not read migrations in database: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not read migrations in database: %s", err)
|
||||
}
|
||||
createdAtStr := file[len(file)-15:]
|
||||
if t, err := time.Parse("20060102_150405", createdAtStr); err != nil {
|
||||
ColorLog("[ERRO] Could not parse time: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not parse time: %s", err)
|
||||
} else {
|
||||
createdAt = t.Unix()
|
||||
}
|
||||
} else {
|
||||
// migration table has no 'update' record, no point rolling back
|
||||
if goal == "rollback" {
|
||||
ColorLog("[ERRO] There is nothing to rollback\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("There is nothing to rollback")
|
||||
}
|
||||
file, createdAt = "", 0
|
||||
}
|
||||
@ -288,8 +282,7 @@ func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64)
|
||||
func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime int64, latestName string, task string) {
|
||||
changeDir(dir)
|
||||
if f, err := os.OpenFile(source, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err != nil {
|
||||
ColorLog("[ERRO] Could not create file: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not create file: %s", err)
|
||||
} else {
|
||||
content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1)
|
||||
content = strings.Replace(content, "{{ConnStr}}", connStr, -1)
|
||||
@ -297,8 +290,7 @@ func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime in
|
||||
content = strings.Replace(content, "{{LatestName}}", latestName, -1)
|
||||
content = strings.Replace(content, "{{Task}}", task, -1)
|
||||
if _, err := f.WriteString(content); err != nil {
|
||||
ColorLog("[ERRO] Could not write to file: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not write to file: %s", err)
|
||||
}
|
||||
CloseFile(f)
|
||||
}
|
||||
@ -309,7 +301,7 @@ func buildMigrationBinary(dir, binary string) {
|
||||
changeDir(dir)
|
||||
cmd := exec.Command("go", "build", "-o", binary)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
ColorLog("[ERRO] Could not build migration binary: %s\n", err)
|
||||
logger.Errorf("Could not build migration binary: %s", err)
|
||||
formatShellErrOutput(string(out))
|
||||
removeTempFile(dir, binary)
|
||||
removeTempFile(dir, binary+".go")
|
||||
@ -323,7 +315,7 @@ func runMigrationBinary(dir, binary string) {
|
||||
cmd := exec.Command("./" + binary)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
formatShellOutput(string(out))
|
||||
ColorLog("[ERRO] Could not run migration binary: %s\n", err)
|
||||
logger.Errorf("Could not run migration binary: %s", err)
|
||||
removeTempFile(dir, binary)
|
||||
removeTempFile(dir, binary+".go")
|
||||
os.Exit(2)
|
||||
@ -336,8 +328,7 @@ func runMigrationBinary(dir, binary string) {
|
||||
// It exits the system when encouter an error
|
||||
func changeDir(dir string) {
|
||||
if err := os.Chdir(dir); err != nil {
|
||||
ColorLog("[ERRO] Could not find migration directory: %s\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Could not find migration directory: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,7 +336,7 @@ func changeDir(dir string) {
|
||||
func removeTempFile(dir, file string) {
|
||||
changeDir(dir)
|
||||
if err := os.Remove(file); err != nil {
|
||||
ColorLog("[WARN] Could not remove temporary file: %s\n", err)
|
||||
logger.Warnf("Could not remove temporary file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,8 +344,7 @@ func removeTempFile(dir, file string) {
|
||||
func formatShellErrOutput(o string) {
|
||||
for _, line := range strings.Split(o, "\n") {
|
||||
if line != "" {
|
||||
ColorLog("[ERRO] -| ")
|
||||
fmt.Println(line)
|
||||
logger.Errorf("|> %s", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -363,13 +353,13 @@ func formatShellErrOutput(o string) {
|
||||
func formatShellOutput(o string) {
|
||||
for _, line := range strings.Split(o, "\n") {
|
||||
if line != "" {
|
||||
ColorLog("[INFO] -| ")
|
||||
fmt.Println(line)
|
||||
logger.Infof("|> %s", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// MigrationMainTPL migration main template
|
||||
MigrationMainTPL = `package main
|
||||
|
||||
import(
|
||||
@ -409,6 +399,7 @@ func main(){
|
||||
}
|
||||
|
||||
`
|
||||
// MYSQLMigrationDDL MySQL migration SQL
|
||||
MYSQLMigrationDDL = `
|
||||
CREATE TABLE migrations (
|
||||
id_migration int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
|
||||
@ -420,7 +411,7 @@ CREATE TABLE migrations (
|
||||
PRIMARY KEY (id_migration)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
||||
`
|
||||
|
||||
// POSTGRESMigrationDDL Postgres migration SQL
|
||||
POSTGRESMigrationDDL = `
|
||||
CREATE TYPE migrations_status AS ENUM('update', 'rollback');
|
||||
|
||||
|
34
new.go
34
new.go
@ -52,30 +52,30 @@ the following files/directories structure:
|
||||
|
||||
func init() {
|
||||
cmdNew.Run = createApp
|
||||
cmdNew.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
}
|
||||
|
||||
func createApp(cmd *Command, args []string) int {
|
||||
ShowShortVersionBanner()
|
||||
w := NewColorWriter(os.Stdout)
|
||||
|
||||
if len(args) != 1 {
|
||||
ColorLog("[ERRO] Argument [appname] is missing\n")
|
||||
os.Exit(2)
|
||||
logger.Fatal("Argument [appname] is missing")
|
||||
}
|
||||
|
||||
apppath, packpath, err := checkEnv(args[0])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
if isExist(apppath) {
|
||||
ColorLog("[ERRO] Path (%s) already exists\n", apppath)
|
||||
ColorLog("[WARN] Do you want to overwrite it? [Yes|No] ")
|
||||
logger.Errorf(bold("Application '%s' already exists"), apppath)
|
||||
logger.Warn(bold("Do you want to overwrite it? [Yes|No] "))
|
||||
if !askForConfirmation() {
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
ColorLog("[INFO] Creating application...\n")
|
||||
logger.Info("Creating application...")
|
||||
|
||||
os.MkdirAll(apppath, 0755)
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath+string(path.Separator), "\x1b[0m")
|
||||
@ -117,7 +117,7 @@ func createApp(cmd *Command, args []string) int {
|
||||
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
|
||||
WriteToFile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", packpath, -1))
|
||||
|
||||
ColorLog("[SUCC] New application successfully created!\n")
|
||||
logger.Success("New application successfully created!")
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -171,13 +171,13 @@ func init() {
|
||||
}
|
||||
|
||||
|
||||
// TestMain is a sample to run an endpoint test
|
||||
func TestMain(t *testing.T) {
|
||||
// TestBeego is a sample to run an endpoint test
|
||||
func TestBeego(t *testing.T) {
|
||||
r, _ := http.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
||||
|
||||
beego.Trace("testing", "TestMain", "Code[%d]\n%s", w.Code, w.Body.String())
|
||||
beego.Trace("testing", "TestBeego", "Code[%d]\n%s", w.Code, w.Body.String())
|
||||
|
||||
Convey("Subject: Test Station Endpoint\n", t, func() {
|
||||
Convey("Status Code Should Be 200", func() {
|
||||
@ -302,13 +302,3 @@ var indextpl = `<!DOCTYPE html>
|
||||
</body>
|
||||
</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)
|
||||
}
|
||||
|
61
pack.go
61
pack.go
@ -21,7 +21,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
path "path/filepath"
|
||||
@ -102,14 +101,10 @@ func init() {
|
||||
fs.BoolVar(&verbose, "v", false, "verbose")
|
||||
cmdPack.Flag = *fs
|
||||
cmdPack.Run = packApp
|
||||
cmdPack.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
w = NewColorWriter(os.Stdout)
|
||||
}
|
||||
|
||||
func exitPrint(con string) {
|
||||
fmt.Fprintln(os.Stderr, con)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
type walker interface {
|
||||
isExclude(string) bool
|
||||
isEmpty(string) bool
|
||||
@ -398,10 +393,10 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) {
|
||||
func packDirectory(excludePrefix []string, excludeSuffix []string,
|
||||
excludeRegexp []*regexp.Regexp, includePath ...string) (err error) {
|
||||
|
||||
ColorLog("Excluding relpath prefix: %s\n", strings.Join(excludePrefix, ":"))
|
||||
ColorLog("Excluding relpath suffix: %s\n", strings.Join(excludeSuffix, ":"))
|
||||
logger.Infof("Excluding relpath prefix: %s", strings.Join(excludePrefix, ":"))
|
||||
logger.Infof("Excluding relpath suffix: %s", strings.Join(excludeSuffix, ":"))
|
||||
if len(excludeRegexp) > 0 {
|
||||
ColorLog("Excluding filename regex: `%s`\n", strings.Join(excludeR, "`, `"))
|
||||
logger.Infof("Excluding filename regex: `%s`", strings.Join(excludeR, "`, `"))
|
||||
}
|
||||
|
||||
w, err := os.OpenFile(outputP, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
@ -454,27 +449,7 @@ func packDirectory(excludePrefix []string, excludeSuffix []string,
|
||||
return
|
||||
}
|
||||
|
||||
func isBeegoProject(thePath string) bool {
|
||||
fh, _ := os.Open(thePath)
|
||||
fis, _ := fh.Readdir(-1)
|
||||
regex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`)
|
||||
for _, fi := range fis {
|
||||
if fi.IsDir() == false && strings.HasSuffix(fi.Name(), ".go") {
|
||||
data, err := ioutil.ReadFile(path.Join(thePath, fi.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(regex.Find(data)) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func packApp(cmd *Command, args []string) int {
|
||||
ShowShortVersionBanner()
|
||||
|
||||
curPath, _ := os.Getwd()
|
||||
thePath := ""
|
||||
|
||||
@ -496,17 +471,13 @@ func packApp(cmd *Command, args []string) int {
|
||||
|
||||
thePath, err := path.Abs(appPath)
|
||||
if err != nil {
|
||||
exitPrint(fmt.Sprintf("Wrong app path: %s", thePath))
|
||||
logger.Fatalf("Wrong application path: %s", thePath)
|
||||
}
|
||||
if stat, err := os.Stat(thePath); os.IsNotExist(err) || stat.IsDir() == false {
|
||||
exitPrint(fmt.Sprintf("App path does not exist: %s", thePath))
|
||||
logger.Fatalf("Application path does not exist: %s", thePath)
|
||||
}
|
||||
|
||||
if isBeegoProject(thePath) == false {
|
||||
exitPrint(fmt.Sprintf("Bee does not support non Beego project"))
|
||||
}
|
||||
|
||||
ColorLog("Packaging application: %s\n", thePath)
|
||||
logger.Infof("Packaging application on '%s'...", thePath)
|
||||
|
||||
appName := path.Base(thePath)
|
||||
|
||||
@ -526,7 +497,7 @@ func packApp(cmd *Command, args []string) int {
|
||||
os.Mkdir(tmpdir, 0700)
|
||||
|
||||
if build {
|
||||
ColorLog("Building application...\n")
|
||||
logger.Info("Building application...")
|
||||
var envs []string
|
||||
for _, env := range buildEnvs {
|
||||
parts := strings.SplitN(env, "=", 2)
|
||||
@ -548,7 +519,7 @@ func packApp(cmd *Command, args []string) int {
|
||||
os.Setenv("GOOS", goos)
|
||||
os.Setenv("GOARCH", goarch)
|
||||
|
||||
ColorLog("Env: GOOS=%s GOARCH=%s\n", goos, goarch)
|
||||
logger.Infof("Using: GOOS=%s GOARCH=%s", goos, goarch)
|
||||
|
||||
binPath := path.Join(tmpdir, appName)
|
||||
if goos == "windows" {
|
||||
@ -571,10 +542,10 @@ func packApp(cmd *Command, args []string) int {
|
||||
execmd.Dir = thePath
|
||||
err = execmd.Run()
|
||||
if err != nil {
|
||||
exitPrint(err.Error())
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
|
||||
ColorLog("Build successful\n")
|
||||
logger.Success("Build successful!")
|
||||
}
|
||||
|
||||
switch format {
|
||||
@ -592,7 +563,7 @@ func packApp(cmd *Command, args []string) int {
|
||||
if _, err := os.Stat(outputP); err != nil {
|
||||
err = os.MkdirAll(outputP, 0755)
|
||||
if err != nil {
|
||||
exitPrint(err.Error())
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@ -614,18 +585,20 @@ func packApp(cmd *Command, args []string) int {
|
||||
for _, r := range excludeR {
|
||||
if len(r) > 0 {
|
||||
if re, err := regexp.Compile(r); err != nil {
|
||||
exitPrint(err.Error())
|
||||
logger.Fatal(err.Error())
|
||||
} else {
|
||||
exr = append(exr, re)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.Infof("Writing to output: %s", outputP)
|
||||
|
||||
err = packDirectory(exp, exs, exr, tmpdir, thePath)
|
||||
if err != nil {
|
||||
exitPrint(err.Error())
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
|
||||
ColorLog("Writing to output: `%s`\n", outputP)
|
||||
logger.Success("Application packed!")
|
||||
return 0
|
||||
}
|
||||
|
43
run.go
43
run.go
@ -15,7 +15,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
path "path/filepath"
|
||||
@ -57,6 +56,7 @@ var (
|
||||
|
||||
func init() {
|
||||
cmdRun.Run = runApp
|
||||
cmdRun.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
cmdRun.Flag.Var(&mainFiles, "main", "specify main go files")
|
||||
cmdRun.Flag.Var(&gendoc, "gendoc", "auto generate the docs")
|
||||
cmdRun.Flag.Var(&downdoc, "downdoc", "auto download swagger file when not exist")
|
||||
@ -68,8 +68,6 @@ func init() {
|
||||
}
|
||||
|
||||
func runApp(cmd *Command, args []string) int {
|
||||
ShowShortVersionBanner()
|
||||
|
||||
if len(args) == 0 || args[0] == "watchall" {
|
||||
currpath, _ = os.Getwd()
|
||||
|
||||
@ -77,9 +75,8 @@ func runApp(cmd *Command, args []string) int {
|
||||
appname = path.Base(currpath)
|
||||
currentGoPath = _gopath
|
||||
} else {
|
||||
exitPrint(fmt.Sprintf("Bee does not support non Beego project: %s", currpath))
|
||||
logger.Fatalf("No application '%s' found in your GOPATH", currpath)
|
||||
}
|
||||
ColorLog("[INFO] Using '%s' as 'appname'\n", appname)
|
||||
} else {
|
||||
// Check if passed Bee application path/name exists in the GOPATH(s)
|
||||
if found, _gopath, _path := SearchGOPATHs(args[0]); found {
|
||||
@ -87,35 +84,35 @@ func runApp(cmd *Command, args []string) int {
|
||||
currentGoPath = _gopath
|
||||
appname = path.Base(currpath)
|
||||
} else {
|
||||
panic(fmt.Sprintf("No Beego application '%s' found in your GOPATH", args[0]))
|
||||
logger.Fatalf("No application '%s' found in your GOPATH", args[0])
|
||||
}
|
||||
|
||||
ColorLog("[INFO] Using '%s' as 'appname'\n", appname)
|
||||
|
||||
if strings.HasSuffix(appname, ".go") && isExist(currpath) {
|
||||
ColorLog("[WARN] The appname is in conflict with currpath's file, do you want to build appname as %s\n", appname)
|
||||
ColorLog("[INFO] Do you want to overwrite it? [yes|no]] ")
|
||||
logger.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname)
|
||||
logger.Info("Do you want to overwrite it? [yes|no] ")
|
||||
if !askForConfirmation() {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debugf("current path:%s\n", currpath)
|
||||
logger.Infof("Using '%s' as 'appname'", appname)
|
||||
|
||||
if runmode == "prod" || runmode == "dev"{
|
||||
logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath)
|
||||
|
||||
if runmode == "prod" || runmode == "dev" {
|
||||
os.Setenv("BEEGO_RUNMODE", runmode)
|
||||
ColorLog("[INFO] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE"))
|
||||
}else if runmode != ""{
|
||||
logger.Infof("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
|
||||
} else if runmode != "" {
|
||||
os.Setenv("BEEGO_RUNMODE", runmode)
|
||||
ColorLog("[WARN] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE"))
|
||||
}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"))
|
||||
} else if os.Getenv("BEEGO_RUNMODE") != "" {
|
||||
logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
|
||||
}
|
||||
|
||||
err := loadConfig()
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
|
||||
logger.Fatalf("Failed to load configuration: %s", err)
|
||||
}
|
||||
|
||||
var paths []string
|
||||
@ -143,10 +140,10 @@ func runApp(cmd *Command, args []string) int {
|
||||
}
|
||||
if gendoc == "true" {
|
||||
NewWatcher(paths, files, true)
|
||||
Autobuild(files, true)
|
||||
AutoBuild(files, true)
|
||||
} else {
|
||||
NewWatcher(paths, files, false)
|
||||
Autobuild(files, false)
|
||||
AutoBuild(files, false)
|
||||
}
|
||||
|
||||
for {
|
||||
@ -202,16 +199,16 @@ func isExcluded(filePath string) bool {
|
||||
for _, p := range excludedPaths {
|
||||
absP, err := path.Abs(p)
|
||||
if err != nil {
|
||||
ColorLog("[ERROR] Can not get absolute path of [ %s ]\n", p)
|
||||
logger.Errorf("Cannot get absolute path of '%s'", p)
|
||||
continue
|
||||
}
|
||||
absFilePath, err := path.Abs(filePath)
|
||||
if err != nil {
|
||||
ColorLog("[ERROR] Can not get absolute path of [ %s ]\n", filePath)
|
||||
logger.Errorf("Cannot get absolute path of '%s'", filePath)
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(absFilePath, absP) {
|
||||
ColorLog("[INFO] Excluding from watching [ %s ]\n", filePath)
|
||||
logger.Infof("'%s' is not being watched", filePath)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
32
rundocs.go
32
rundocs.go
@ -17,7 +17,6 @@ import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
@ -54,6 +53,7 @@ var docport docValue
|
||||
|
||||
func init() {
|
||||
cmdRundocs.Run = runDocs
|
||||
cmdRundocs.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
cmdRundocs.Flag.Var(&isDownload, "isDownload", "weather download the Swagger Docs")
|
||||
cmdRundocs.Flag.Var(&docport, "docport", "doc server port")
|
||||
}
|
||||
@ -63,18 +63,22 @@ func runDocs(cmd *Command, args []string) int {
|
||||
downloadFromURL(swaggerlink, "swagger.zip")
|
||||
err := unzipAndDelete("swagger.zip")
|
||||
if err != nil {
|
||||
fmt.Println("has err exet unzipAndDelete", err)
|
||||
logger.Errorf("Error while unzipping 'swagger.zip' file: %s", err)
|
||||
}
|
||||
}
|
||||
if docport == "" {
|
||||
docport = "8089"
|
||||
}
|
||||
if _, err := os.Stat("swagger"); err != nil && os.IsNotExist(err) {
|
||||
fmt.Println("there's no swagger, please use bee rundocs -isDownload=true downlaod first")
|
||||
os.Exit(2)
|
||||
logger.Fatal("No Swagger dist found. Run: bee rundocs -isDownload=true")
|
||||
}
|
||||
|
||||
logger.Infof("Starting the docs server on: http://127.0.0.1:%s", docport)
|
||||
|
||||
err := http.ListenAndServe(":"+string(docport), http.FileServer(http.Dir("swagger")))
|
||||
if err != nil {
|
||||
logger.Fatalf("%s", err)
|
||||
}
|
||||
fmt.Println("start the docs server on: http://127.0.0.1:" + docport)
|
||||
log.Fatal(http.ListenAndServe(":"+string(docport), http.FileServer(http.Dir("swagger"))))
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -85,36 +89,36 @@ func downloadFromURL(url, fileName string) {
|
||||
} else if fd.Size() == int64(0) {
|
||||
down = true
|
||||
} else {
|
||||
ColorLog("[%s] Filename %s already exist\n", INFO, fileName)
|
||||
logger.Infof("'%s' already exists", fileName)
|
||||
return
|
||||
}
|
||||
if down {
|
||||
ColorLog("[%s]Downloading %s to %s\n", SUCC, url, fileName)
|
||||
logger.Infof("Downloading '%s' to '%s'...", url, fileName)
|
||||
output, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
ColorLog("[%s]Error while creating %s: %s\n", ERRO, fileName, err)
|
||||
logger.Errorf("Error while creating '%s': %s", fileName, err)
|
||||
return
|
||||
}
|
||||
defer output.Close()
|
||||
|
||||
response, err := http.Get(url)
|
||||
if err != nil {
|
||||
ColorLog("[%s]Error while downloading %s:%s\n", ERRO, url, err)
|
||||
logger.Errorf("Error while downloading '%s': %s", url, err)
|
||||
return
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
n, err := io.Copy(output, response.Body)
|
||||
if err != nil {
|
||||
ColorLog("[%s]Error while downloading %s:%s\n", ERRO, url, err)
|
||||
logger.Errorf("Error while downloading '%s': %s", url, err)
|
||||
return
|
||||
}
|
||||
ColorLog("[%s] %d bytes downloaded.\n", SUCC, n)
|
||||
logger.Successf("%d bytes downloaded!", n)
|
||||
}
|
||||
}
|
||||
|
||||
func unzipAndDelete(src string) error {
|
||||
ColorLog("[%s]start to unzip file from %s\n", INFO, src)
|
||||
logger.Infof("Unzipping '%s'...", src)
|
||||
r, err := zip.OpenReader(src)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -146,6 +150,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)
|
||||
}
|
||||
|
27
test.go
27
test.go
@ -31,6 +31,7 @@ var cmdTest = &Command{
|
||||
|
||||
func init() {
|
||||
cmdTest.Run = testApp
|
||||
cmdTest.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
|
||||
}
|
||||
|
||||
func safePathAppend(arr []string, paths ...string) []string {
|
||||
@ -51,19 +52,19 @@ var started = make(chan bool)
|
||||
|
||||
func testApp(cmd *Command, args []string) int {
|
||||
if len(args) != 1 {
|
||||
ColorLog("[ERRO] Cannot start running[ %s ]\n",
|
||||
"argument 'appname' is missing")
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Failed to start: %s", "argument 'appname' is missing")
|
||||
}
|
||||
crupath, _ := os.Getwd()
|
||||
Debugf("current path:%s\n", crupath)
|
||||
|
||||
currpath, _ := os.Getwd()
|
||||
|
||||
logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath)
|
||||
|
||||
err := loadConfig()
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
|
||||
logger.Fatalf("Failed to load configuration: %s", err)
|
||||
}
|
||||
var paths []string
|
||||
readAppDirectories(crupath, &paths)
|
||||
readAppDirectories(currpath, &paths)
|
||||
|
||||
NewWatcher(paths, nil, false)
|
||||
appname = args[0]
|
||||
@ -76,7 +77,7 @@ func testApp(cmd *Command, args []string) int {
|
||||
}
|
||||
|
||||
func runTest() {
|
||||
ColorLog("[INFO] Start testing...\n")
|
||||
logger.Info("Start testing...")
|
||||
time.Sleep(time.Second * 1)
|
||||
crupwd, _ := os.Getwd()
|
||||
testDir := path.Join(crupwd, "tests")
|
||||
@ -88,14 +89,14 @@ func runTest() {
|
||||
icmd := exec.Command("go", "test")
|
||||
icmd.Stdout = os.Stdout
|
||||
icmd.Stderr = os.Stderr
|
||||
ColorLog("[TRAC] ============== Test Begin ===================\n")
|
||||
logger.Info("============== Test Begin ===================")
|
||||
err = icmd.Run()
|
||||
ColorLog("[TRAC] ============== Test End ===================\n")
|
||||
logger.Info("============== Test End =====================")
|
||||
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] ============== Test failed ===================\n")
|
||||
ColorLog("[ERRO] %s", err)
|
||||
logger.Error("============== Test failed ===================")
|
||||
logger.Errorf("Cause: %s", err)
|
||||
return
|
||||
}
|
||||
ColorLog("[SUCC] Test finish\n")
|
||||
logger.Success("Test Completed")
|
||||
}
|
||||
|
242
util.go
242
util.go
@ -15,14 +15,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
"path"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Go is a basic promise implementation: it wraps calls a function in a goroutine
|
||||
@ -35,124 +37,14 @@ func Go(f func() error) chan error {
|
||||
return ch
|
||||
}
|
||||
|
||||
// if os.env DEBUG set, debug is on
|
||||
func Debugf(format string, a ...interface{}) {
|
||||
if os.Getenv("DEBUG") != "" {
|
||||
_, 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...)
|
||||
}
|
||||
// Now returns the current local time in the specified layout
|
||||
func Now(layout string) string {
|
||||
return time.Now().Format(layout)
|
||||
}
|
||||
|
||||
const (
|
||||
Gray = uint8(iota + 90)
|
||||
Red
|
||||
Green
|
||||
Yellow
|
||||
Blue
|
||||
Magenta
|
||||
//NRed = uint8(31) // Normal
|
||||
EndColor = "\033[0m"
|
||||
|
||||
INFO = "INFO"
|
||||
TRAC = "TRAC"
|
||||
ERRO = "ERRO"
|
||||
WARN = "WARN"
|
||||
SUCC = "SUCC"
|
||||
)
|
||||
|
||||
// ColorLog colors log and print to stdout.
|
||||
// See color rules in function 'ColorLogS'.
|
||||
func ColorLog(format string, a ...interface{}) {
|
||||
fmt.Print(ColorLogS(format, a...))
|
||||
}
|
||||
|
||||
// ColorLogS colors log and return colored content.
|
||||
// Log format: <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
|
||||
}
|
||||
// EndLine returns the a newline escape character
|
||||
func EndLine() string {
|
||||
return "\n"
|
||||
}
|
||||
|
||||
// IsExist returns whether a file or directory exists.
|
||||
@ -174,11 +66,52 @@ func GetGOPATHs() []string {
|
||||
return paths
|
||||
}
|
||||
|
||||
// IsBeegoProject checks whether the current path is a Beego application or not
|
||||
func IsBeegoProject(thePath string) bool {
|
||||
mainFiles := []string{}
|
||||
hasBeegoRegex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`)
|
||||
c := make(chan error)
|
||||
// Walk the application path tree to look for main files.
|
||||
// Main files must satisfy the 'hasBeegoRegex' regular expression.
|
||||
go func() {
|
||||
filepath.Walk(thePath, func(fpath string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// Skip sub-directories
|
||||
if !f.IsDir() {
|
||||
var data []byte
|
||||
data, err = ioutil.ReadFile(fpath)
|
||||
if err != nil {
|
||||
c <- err
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(hasBeegoRegex.Find(data)) > 0 {
|
||||
mainFiles = append(mainFiles, fpath)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
close(c)
|
||||
}()
|
||||
|
||||
if err := <-c; err != nil {
|
||||
logger.Fatalf("Unable to walk '%s' tree: %s", thePath, err)
|
||||
}
|
||||
|
||||
if len(mainFiles) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SearchGOPATHs searchs the user GOPATH(s) for the specified application name.
|
||||
// It returns a boolean, the application's GOPATH and its full path.
|
||||
func SearchGOPATHs(app string) (bool, string, string) {
|
||||
gps := GetGOPATHs()
|
||||
if len(gps) == 0 {
|
||||
ColorLog("[ERRO] Fail to start [ %s ]\n", "GOPATH environment variable is not set or empty")
|
||||
os.Exit(2)
|
||||
logger.Fatal("GOPATH environment variable is not set or empty")
|
||||
}
|
||||
|
||||
// Lookup the application inside the user workspace(s)
|
||||
@ -193,9 +126,6 @@ func SearchGOPATHs(app string) (bool, string, string) {
|
||||
}
|
||||
|
||||
if isExist(currentPath) {
|
||||
if !isBeegoProject(currentPath) {
|
||||
continue
|
||||
}
|
||||
return true, gopath, currentPath
|
||||
}
|
||||
}
|
||||
@ -211,7 +141,7 @@ func askForConfirmation() bool {
|
||||
var response string
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
logger.Fatalf("%s", err)
|
||||
}
|
||||
okayResponses := []string{"y", "Y", "yes", "Yes", "YES"}
|
||||
nokayResponses := []string{"n", "N", "no", "No", "NO"}
|
||||
@ -249,7 +179,7 @@ func snakeString(s string) string {
|
||||
}
|
||||
data = append(data, d)
|
||||
}
|
||||
return strings.ToLower(string(data[:len(data)]))
|
||||
return strings.ToLower(string(data[:]))
|
||||
}
|
||||
|
||||
func camelString(s string) string {
|
||||
@ -273,7 +203,25 @@ func camelString(s string) string {
|
||||
}
|
||||
data = append(data, d)
|
||||
}
|
||||
return string(data[:len(data)])
|
||||
return string(data[:])
|
||||
}
|
||||
|
||||
// camelCase converts a _ delimited string to camel case
|
||||
// e.g. very_important_person => VeryImportantPerson
|
||||
func camelCase(in string) string {
|
||||
tokens := strings.Split(in, "_")
|
||||
for i := range tokens {
|
||||
tokens[i] = strings.Title(strings.Trim(tokens[i], " "))
|
||||
}
|
||||
return strings.Join(tokens, "")
|
||||
}
|
||||
|
||||
// formatSourceCode formats source files
|
||||
func formatSourceCode(filename string) {
|
||||
cmd := exec.Command("gofmt", "-w", filename)
|
||||
if err := cmd.Run(); err != nil {
|
||||
logger.Warnf("Error while running gofmt: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// The string flag list, implemented flag.Value interface
|
||||
@ -291,7 +239,45 @@ func (s *strFlags) Set(value string) error {
|
||||
// CloseFile attempts to close the passed file
|
||||
// or panics with the actual error
|
||||
func CloseFile(f *os.File) {
|
||||
if err := f.Close(); err != nil {
|
||||
err := f.Close()
|
||||
MustCheck(err)
|
||||
}
|
||||
|
||||
// MustCheck panics when the error is not nil
|
||||
func MustCheck(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func exitPrint(con string) {
|
||||
fmt.Fprintln(os.Stderr, con)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// WriteToFile creates a file and writes content to it
|
||||
func WriteToFile(filename, content string) {
|
||||
f, err := os.Create(filename)
|
||||
MustCheck(err)
|
||||
defer CloseFile(f)
|
||||
_, err = f.WriteString(content)
|
||||
MustCheck(err)
|
||||
}
|
||||
|
||||
// IsDebugEnabled checks if DEBUG_ENABLED is set or not
|
||||
func IsDebugEnabled() bool {
|
||||
debugMode := os.Getenv("DEBUG_ENABLED")
|
||||
return map[string]bool{"1": true, "0": false}[debugMode]
|
||||
}
|
||||
|
||||
// __FILE__ returns the file name in which the function was invoked
|
||||
func __FILE__() string {
|
||||
_, file, _, _ := runtime.Caller(1)
|
||||
return file
|
||||
}
|
||||
|
||||
// __LINE__ returns the line number at which the function was invoked
|
||||
func __LINE__() int {
|
||||
_, _, line, _ := runtime.Caller(1)
|
||||
return line
|
||||
}
|
||||
|
22
version.go
22
version.go
@ -6,8 +6,10 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
path "path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var cmdVersion = &Command{
|
||||
@ -76,7 +78,7 @@ func getBeegoVersion() string {
|
||||
return ""
|
||||
}
|
||||
if gopath == "" {
|
||||
err = fmt.Errorf("You should set GOPATH env variable")
|
||||
err = fmt.Errorf("You need to set GOPATH environment variable")
|
||||
return ""
|
||||
}
|
||||
wgopath := path.SplitList(gopath)
|
||||
@ -88,11 +90,11 @@ func getBeegoVersion() string {
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
ColorLog("[ERRO] Get `beego.go` has error\n")
|
||||
logger.Error("Error while getting stats of 'beego.go'")
|
||||
}
|
||||
fd, err := os.Open(filename)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Open `beego.go` has error\n")
|
||||
logger.Error("Error while reading 'beego.go'")
|
||||
continue
|
||||
}
|
||||
reader := bufio.NewReader(fd)
|
||||
@ -112,5 +114,17 @@ 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 {
|
||||
var (
|
||||
cmdOut []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if cmdOut, err = exec.Command("go", "version").Output(); err != nil {
|
||||
logger.Fatalf("There was an error running 'go version' command: %s", err)
|
||||
}
|
||||
return strings.Split(string(cmdOut), " ")[2]
|
||||
}
|
||||
|
60
watch.go
60
watch.go
@ -16,8 +16,6 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/howeyc/fsnotify"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
@ -25,6 +23,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/howeyc/fsnotify"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -34,11 +34,11 @@ var (
|
||||
scheduleTime time.Time
|
||||
)
|
||||
|
||||
// NewWatcher starts an fsnotify Watcher on the specified paths
|
||||
func NewWatcher(paths []string, files []string, isgenerate bool) {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to create new Watcher[ %s ]\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Failed to create watcher: %s", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
@ -57,14 +57,14 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
|
||||
|
||||
mt := getFileModTime(e.Name)
|
||||
if t := eventTime[e.Name]; mt == t {
|
||||
ColorLog("[SKIP] # %s #\n", e.String())
|
||||
logger.Infof(bold("Skipping: ")+"%s", e.String())
|
||||
isbuild = false
|
||||
}
|
||||
|
||||
eventTime[e.Name] = mt
|
||||
|
||||
if isbuild {
|
||||
ColorLog("[EVEN] %s\n", e)
|
||||
logger.Infof("Event fired: %s", e)
|
||||
go func() {
|
||||
// Wait 1s before autobuild util there is no file change.
|
||||
scheduleTime = time.Now().Add(1 * time.Second)
|
||||
@ -76,22 +76,21 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
|
||||
return
|
||||
}
|
||||
|
||||
Autobuild(files, isgenerate)
|
||||
AutoBuild(files, isgenerate)
|
||||
}()
|
||||
}
|
||||
case err := <-watcher.Error:
|
||||
ColorLog("[WARN] %s\n", err.Error()) // No need to exit here
|
||||
logger.Warnf("Watcher error: %s", err.Error()) // No need to exit here
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
ColorLog("[INFO] Initializing watcher...\n")
|
||||
logger.Info("Initializing watcher...")
|
||||
for _, path := range paths {
|
||||
ColorLog("[TRAC] Directory( %s )\n", path)
|
||||
logger.Infof(bold("Watching: ")+"%s", path)
|
||||
err = watcher.Watch(path)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to watch directory[ %s ]\n", err)
|
||||
os.Exit(2)
|
||||
logger.Fatalf("Failed to watch directory: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,25 +101,26 @@ func getFileModTime(path string) int64 {
|
||||
path = strings.Replace(path, "\\", "/", -1)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to open file[ %s ]\n", err)
|
||||
logger.Errorf("Failed to open file on '%s': %s", path, err)
|
||||
return time.Now().Unix()
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] Fail to get file information[ %s ]\n", err)
|
||||
logger.Errorf("Failed to get file stats: %s", err)
|
||||
return time.Now().Unix()
|
||||
}
|
||||
|
||||
return fi.ModTime().Unix()
|
||||
}
|
||||
|
||||
func Autobuild(files []string, isgenerate bool) {
|
||||
// AutoBuild builds the specified set of files
|
||||
func AutoBuild(files []string, isgenerate bool) {
|
||||
state.Lock()
|
||||
defer state.Unlock()
|
||||
|
||||
ColorLog("[INFO] Start building...\n")
|
||||
logger.Info("Start building...")
|
||||
|
||||
os.Chdir(currpath)
|
||||
|
||||
@ -162,7 +162,7 @@ func Autobuild(files []string, isgenerate bool) {
|
||||
icmd.Stdout = os.Stdout
|
||||
icmd.Stderr = os.Stderr
|
||||
icmd.Run()
|
||||
ColorLog("============== generate docs ===================\n")
|
||||
logger.Info("============== Generate Docs ===================")
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
@ -186,35 +186,38 @@ func Autobuild(files []string, isgenerate bool) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ColorLog("[ERRO] ============== Build failed ===================\n")
|
||||
logger.Error("============== Build Failed ===================")
|
||||
return
|
||||
}
|
||||
ColorLog("[SUCC] Build was successful\n")
|
||||
logger.Success("Built Successfully!")
|
||||
Restart(appname)
|
||||
}
|
||||
|
||||
// Kill kills the running command process
|
||||
func Kill() {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
fmt.Println("Kill.recover -> ", e)
|
||||
logger.Infof("Kill recover: %s", e)
|
||||
}
|
||||
}()
|
||||
if cmd != nil && cmd.Process != nil {
|
||||
err := cmd.Process.Kill()
|
||||
if err != nil {
|
||||
fmt.Println("Kill -> ", err)
|
||||
logger.Errorf("Error while killing cmd process: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restart kills the running command process and starts it again
|
||||
func Restart(appname string) {
|
||||
Debugf("kill running process")
|
||||
logger.Debugf("Kill running process", __FILE__(), __LINE__())
|
||||
Kill()
|
||||
go Start(appname)
|
||||
}
|
||||
|
||||
// Start starts the command process
|
||||
func Start(appname string) {
|
||||
ColorLog("[INFO] Restarting %s ...\n", appname)
|
||||
logger.Infof("Restarting '%s'...", appname)
|
||||
if strings.Index(appname, "./") == -1 {
|
||||
appname = "./" + appname
|
||||
}
|
||||
@ -226,23 +229,22 @@ func Start(appname string) {
|
||||
cmd.Env = append(os.Environ(), conf.Envs...)
|
||||
|
||||
go cmd.Run()
|
||||
ColorLog("[INFO] %s is running...\n", appname)
|
||||
logger.Successf("'%s' is running...", appname)
|
||||
started <- true
|
||||
}
|
||||
|
||||
// Should ignore filenames generated by
|
||||
// Emacs, Vim or SublimeText
|
||||
// shouldIgnoreFile ignores filenames generated by Emacs, Vim or SublimeText.
|
||||
// It returns true if the file should be ignored, false otherwise.
|
||||
func shouldIgnoreFile(filename string) bool {
|
||||
for _, regex := range ignoredFilesRegExps {
|
||||
r, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
panic("Could not compile the regex: " + regex)
|
||||
logger.Fatalf("Could not compile regular expression: %s", err)
|
||||
}
|
||||
if r.MatchString(filename) {
|
||||
return true
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user