1
0
mirror of https://github.com/beego/bee.git synced 2024-11-25 20:10:55 +00:00

Merge pull request #362 from sergeylanzman/refactor

Refactor
This commit is contained in:
astaxie 2017-03-10 18:58:13 +08:00 committed by GitHub
commit 6dce4df2cb
48 changed files with 1839 additions and 2467 deletions

View File

@ -3,3 +3,4 @@ language: go
go: go:
- 1.6.3 - 1.6.3
- 1.7.3 - 1.7.3
- 1.8

View File

@ -1,280 +0,0 @@
// 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 (
"bytes"
"errors"
"fmt"
"go/ast"
gobuild "go/build"
"go/doc"
"go/parser"
"go/token"
"io"
"io/ioutil"
"os"
"path"
"runtime"
"strings"
"time"
)
var cmdRouter = &Command{
UsageLine: "router",
Short: "auto-generate routers for the app controllers",
Long: `
`,
}
func init() {
cmdRouter.Run = autoRouter
}
func autoRouter(cmd *Command, args []string) int {
fmt.Println("[INFO] Starting auto-generating routers...")
return 0
}
// getControllerInfo returns controllers that embeded "beego.controller"
// and their methods of package in given path.
func getControllerInfo(path string) (map[string][]string, error) {
now := time.Now()
path = strings.TrimSuffix(path, "/")
dir, err := os.Open(path)
if err != nil {
return nil, err
}
fis, err := dir.Readdir(0)
if err != nil {
return nil, err
}
files := make([]*source, 0, len(fis))
for _, fi := range fis {
// Only load Go files
if strings.HasSuffix(fi.Name(), ".go") {
f, err := os.Open(path + "/" + fi.Name())
if err != nil {
return nil, err
}
p := make([]byte, fi.Size())
_, err = f.Read(p)
if err != nil {
return nil, err
}
f.Close()
files = append(files, &source{
name: path + "/" + fi.Name(),
data: p,
})
}
}
rw := &routerWalker{
pdoc: &Package{
ImportPath: path,
},
}
cm := make(map[string][]string)
pdoc, err := rw.build(files)
for _, t := range pdoc.Types {
// Check if embeded "beego.Controller".
if strings.Index(t.Decl, "beego.Controller") > -1 {
for _, f := range t.Methods {
cm[t.Name] = append(cm[t.Name], f.Name)
}
}
}
fmt.Println(time.Since(now))
return cm, nil
}
// source represents a source code file.
type source struct {
name string
data []byte
}
func (s *source) Name() string { return s.name }
func (s *source) Size() int64 { return int64(len(s.data)) }
func (s *source) Mode() os.FileMode { return 0 }
func (s *source) ModTime() time.Time { return time.Time{} }
func (s *source) IsDir() bool { return false }
func (s *source) Sys() interface{} { return nil }
// routerWalker holds the state used when building the documentation.
type routerWalker struct {
pdoc *Package
srcs map[string]*source // Source files
fset *token.FileSet
buf []byte // scratch space for printNode method
}
// Package represents full information and documentation for a package.
type Package struct {
ImportPath string
Types []*Type // Top-level declarations
}
// Type represents structs and interfaces.
type Type struct {
Name string // Type name
Decl string
Methods []*Func // Exported methods
}
// Func represents functions
type Func struct {
Name string
}
// build generates data from source files.
func (w *routerWalker) build(srcs []*source) (*Package, error) {
// Add source files to walker, I skipped references here
w.srcs = make(map[string]*source)
for _, src := range srcs {
w.srcs[src.name] = src
}
w.fset = token.NewFileSet()
// Find the package and associated files
ctxt := gobuild.Context{
GOOS: runtime.GOOS,
GOARCH: runtime.GOARCH,
CgoEnabled: true,
JoinPath: path.Join,
IsAbsPath: path.IsAbs,
SplitPathList: func(list string) []string { return strings.Split(list, ":") },
IsDir: func(path string) bool { panic("unexpected") },
HasSubdir: func(root, dir string) (rel string, ok bool) { panic("unexpected") },
ReadDir: func(dir string) (fi []os.FileInfo, err error) { return w.readDir(dir) },
OpenFile: func(path string) (r io.ReadCloser, err error) { return w.openFile(path) },
Compiler: "gc",
}
bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0)
// Continue if there are no Go source files; we still want the directory info
_, nogo := err.(*gobuild.NoGoError)
if err != nil {
if nogo {
err = nil
} else {
return nil, errors.New("routerWalker.build -> " + err.Error())
}
}
// Parse the Go files
files := make(map[string]*ast.File)
for _, name := range append(bpkg.GoFiles, bpkg.CgoFiles...) {
file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments)
if err != nil {
return nil, errors.New("routerWalker.build -> parse go files: " + err.Error())
}
files[name] = file
}
apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil)
mode := doc.Mode(0)
if w.pdoc.ImportPath == "builtin" {
mode |= doc.AllDecls
}
pdoc := doc.New(apkg, w.pdoc.ImportPath, mode)
w.pdoc.Types = w.types(pdoc.Types)
return w.pdoc, err
}
func (w *routerWalker) funcs(fdocs []*doc.Func) []*Func {
var result []*Func
for _, d := range fdocs {
result = append(result, &Func{
Name: d.Name,
})
}
return result
}
func (w *routerWalker) types(tdocs []*doc.Type) []*Type {
var result []*Type
for _, d := range tdocs {
result = append(result, &Type{
Decl: w.printDecl(d.Decl),
Name: d.Name,
Methods: w.funcs(d.Methods),
})
}
return result
}
func (w *routerWalker) printDecl(decl ast.Node) string {
var d Code
d, w.buf = printDecl(decl, w.fset, w.buf)
return d.Text
}
func (w *routerWalker) readDir(dir string) ([]os.FileInfo, error) {
if dir != w.pdoc.ImportPath {
panic("unexpected")
}
fis := make([]os.FileInfo, 0, len(w.srcs))
for _, src := range w.srcs {
fis = append(fis, src)
}
return fis, nil
}
func (w *routerWalker) openFile(path string) (io.ReadCloser, error) {
if strings.HasPrefix(path, w.pdoc.ImportPath+"/") {
if src, ok := w.srcs[path[len(w.pdoc.ImportPath)+1:]]; ok {
return ioutil.NopCloser(bytes.NewReader(src.data)), nil
}
}
panic("unexpected")
}
func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) {
pkg := imports[path]
if pkg == nil {
// Guess the package name without importing it. Start with the last
// element of the path.
name := path[strings.LastIndex(path, "/")+1:]
// Trim commonly used prefixes and suffixes containing illegal name
// runes.
name = strings.TrimSuffix(name, ".go")
name = strings.TrimSuffix(name, "-go")
name = strings.TrimPrefix(name, "go.")
name = strings.TrimPrefix(name, "go-")
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.
pkg = ast.NewObj(ast.Pkg, name)
pkg.Data = ast.NewScope(nil)
imports[path] = pkg
}
return pkg, nil
}

View File

@ -1,9 +0,0 @@
package main
import (
"testing"
)
func TestGetControllerInfo(t *testing.T) {
getControllerInfo("testdata/router/")
}

258
bee.go
View File

@ -1,258 +0,0 @@
// 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.
// Bee is a tool for developing applications based on beego framework.
package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"strings"
"text/template"
)
const version = "1.8.0"
// 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
// Short is the short description shown in the 'go help' output.
Short string
// Long is the long message shown in the 'go help <this-command>' output.
Long string
// Flag is a set of flags specific to this command.
Flag flag.FlagSet
// CustomFlags indicates that the command will do its own
// flag parsing.
CustomFlags bool
// output out writer if set in SetOutput(w)
output *io.Writer
}
// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
name := c.UsageLine
i := strings.Index(name, " ")
if i >= 0 {
name = name[:i]
}
return name
}
// SetOutput sets the destination for usage and error messages.
// If output is nil, os.Stderr is used.
func (c *Command) SetOutput(output io.Writer) {
c.output = &output
}
// Out returns the out writer of the current command.
// If cmd.output is nil, os.Stderr is used.
func (c *Command) Out() io.Writer {
if c.output != nil {
return *c.output
}
return NewColorWriter(os.Stderr)
}
// Usage puts out the usage for the command.
func (c *Command) Usage() {
tmpl(cmdUsage, c)
os.Exit(2)
}
// Runnable reports whether the command can be run; otherwise
// it is a documentation pseudo-command such as import path.
func (c *Command) Runnable() bool {
return c.Run != nil
}
func (c *Command) Options() map[string]string {
options := make(map[string]string)
c.Flag.VisitAll(func(f *flag.Flag) {
defaultVal := f.DefValue
if len(defaultVal) > 0 {
options[f.Name+"="+defaultVal] = f.Usage
} else {
options[f.Name] = f.Usage
}
})
return options
}
var availableCommands = []*Command{
cmdNew,
cmdRun,
cmdPack,
cmdApiapp,
cmdHproseapp,
//cmdRouter,
//cmdTest,
cmdBale,
cmdVersion,
cmdGenerate,
//cmdRundocs,
cmdMigrate,
cmdFix,
cmdDockerize,
}
var logger = GetBeeLogger(os.Stdout)
func main() {
currentpath, _ := os.Getwd()
flag.Usage = usage
flag.Parse()
log.SetFlags(0)
args := flag.Args()
if len(args) < 1 {
usage()
}
if args[0] == "help" {
help(args[1:])
return
}
for _, cmd := range availableCommands {
if cmd.Name() == args[0] && cmd.Run != nil {
cmd.Flag.Usage = func() { cmd.Usage() }
if cmd.CustomFlags {
args = args[1:]
} else {
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") && isGenerateDocs(cmd.Name(), args) {
parsePackagesFromDir(currentpath)
}
os.Exit(cmd.Run(cmd, args))
return
}
}
printErrorAndExit("Unknown subcommand")
}
func isGenerateDocs(name string, args []string) bool {
if name != "generate" {
return false
}
for _, a := range args {
if a == "docs" {
return true
}
}
return false
}
var usageTemplate = `Bee is a Fast and Flexible tool for managing your Beego Web Application.
{{"USAGE" | headline}}
{{"bee command [arguments]" | bold}}
{{"AVAILABLE COMMANDS" | headline}}
{{range .}}{{if .Runnable}}
{{.Name | printf "%-11s" | bold}} {{.Short}}{{end}}{{end}}
Use {{"bee help [command]" | bold}} for more information about a command.
{{"ADDITIONAL HELP TOPICS" | headline}}
{{range .}}{{if not .Runnable}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
Use {{"bee help [topic]" | bold}} for more information about that topic.
`
var helpTemplate = `{{"USAGE" | headline}}
{{.UsageLine | printf "bee %s" | bold}}
{{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}}
{{$k | printf "-%s" | bold}}
{{$v}}
{{end}}{{end}}
{{"DESCRIPTION" | headline}}
{{tmpltostr .Long . | trim}}
`
var errorTemplate = `bee: %s.
Use {{"bee help" | bold}} for more information.
`
var cmdUsage = `Use {{printf "bee help %s" .Name | bold}} for more information.{{endline}}`
func usage() {
tmpl(usageTemplate, availableCommands)
os.Exit(2)
}
func tmpl(text string, data interface{}) {
output := NewColorWriter(os.Stderr)
t := template.New("usage").Funcs(BeeFuncMap())
template.Must(t.Parse(text))
err := t.Execute(output, data)
MustCheck(err)
}
func help(args []string) {
if len(args) == 0 {
usage()
}
if len(args) != 1 {
printErrorAndExit("Too many arguments")
}
arg := args[0]
for _, cmd := range availableCommands {
if cmd.Name() == arg {
tmpl(helpTemplate, cmd)
return
}
}
printErrorAndExit("Unknown help topic")
}
func printErrorAndExit(message string) {
tmpl(fmt.Sprintf(errorTemplate, message), nil)
os.Exit(2)
}

102
cmd/bee.go Normal file
View File

@ -0,0 +1,102 @@
// 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.
// Bee is a tool for developing applications based on beego framework.
package cmd
import (
"os"
"github.com/beego/bee/cmd/commands"
_ "github.com/beego/bee/cmd/commands/api"
_ "github.com/beego/bee/cmd/commands/bale"
_ "github.com/beego/bee/cmd/commands/beefix"
_ "github.com/beego/bee/cmd/commands/dockerize"
_ "github.com/beego/bee/cmd/commands/generate"
_ "github.com/beego/bee/cmd/commands/hprose"
_ "github.com/beego/bee/cmd/commands/migrate"
_ "github.com/beego/bee/cmd/commands/new"
_ "github.com/beego/bee/cmd/commands/pack"
_ "github.com/beego/bee/cmd/commands/run"
_ "github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/utils"
)
func IfGenerateDocs(name string, args []string) bool {
if name != "generate" {
return false
}
for _, a := range args {
if a == "docs" {
return true
}
}
return false
}
var usageTemplate = `Bee is a Fast and Flexible tool for managing your Beego Web Application.
{{"USAGE" | headline}}
{{"bee command [arguments]" | bold}}
{{"AVAILABLE COMMANDS" | headline}}
{{range .}}{{if .Runnable}}
{{.Name | printf "%-11s" | bold}} {{.Short}}{{end}}{{end}}
Use {{"bee help [command]" | bold}} for more information about a command.
{{"ADDITIONAL HELP TOPICS" | headline}}
{{range .}}{{if not .Runnable}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
Use {{"bee help [topic]" | bold}} for more information about that topic.
`
var helpTemplate = `{{"USAGE" | headline}}
{{.UsageLine | printf "bee %s" | bold}}
{{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}}
{{$k | printf "-%s" | bold}}
{{$v}}
{{end}}{{end}}
{{"DESCRIPTION" | headline}}
{{tmpltostr .Long . | trim}}
`
var ErrorTemplate = `bee: %s.
Use {{"bee help" | bold}} for more information.
`
func Usage() {
utils.Tmpl(usageTemplate, commands.AvailableCommands)
os.Exit(2)
}
func Help(args []string) {
if len(args) == 0 {
Usage()
}
if len(args) != 1 {
utils.PrintErrorAndExit("Too many arguments", ErrorTemplate)
}
arg := args[0]
for _, cmd := range commands.AvailableCommands {
if cmd.Name() == arg {
utils.Tmpl(helpTemplate, cmd)
return
}
}
utils.PrintErrorAndExit("Unknown help topic", ErrorTemplate)
}

View File

@ -12,16 +12,22 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package apiapp
import ( import (
"fmt" "fmt"
"os" "os"
path "path/filepath" path "path/filepath"
"strings" "strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/generate"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
) )
var cmdApiapp = &Command{ var CmdApiapp = &commands.Command{
// CustomFlags: true, // CustomFlags: true,
UsageLine: "api [appname]", UsageLine: "api [appname]",
Short: "Creates a Beego API application", Short: "Creates a Beego API application",
@ -50,10 +56,9 @@ var cmdApiapp = &Command{
object.go object.go
user.go user.go
`, `,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: createapi, Run: createapi,
} }
var apiconf = `appname = {{.Appname}} var apiconf = `appname = {{.Appname}}
httpport = 8080 httpport = 8080
runmode = dev runmode = dev
@ -134,7 +139,7 @@ func init() {
} }
` `
var apiModels = `package models var ApiModels = `package models
import ( import (
"errors" "errors"
@ -189,7 +194,7 @@ func Delete(ObjectId string) {
` `
var apiModels2 = `package models var ApiModels2 = `package models
import ( import (
"errors" "errors"
@ -532,33 +537,32 @@ func TestGet(t *testing.T) {
` `
func init() { func init() {
cmdApiapp.Flag.Var(&tables, "tables", "List of table names separated by a comma.") CmdApiapp.Flag.Var(&generate.Tables, "tables", "List of table names separated by a comma.")
cmdApiapp.Flag.Var(&driver, "driver", "Database driver. Either mysql, postgres or sqlite.") CmdApiapp.Flag.Var(&generate.SQLDriver, "driver", "Database driver. Either mysql, postgres or sqlite.")
cmdApiapp.Flag.Var(&conn, "conn", "Connection string used by the driver to connect to a database instance.") CmdApiapp.Flag.Var(&generate.SQLConn, "conn", "Connection string used by the driver to connect to a database instance.")
commands.AvailableCommands = append(commands.AvailableCommands, CmdApiapp)
} }
func createapi(cmd *Command, args []string) int { func createapi(cmd *commands.Command, args []string) int {
output := cmd.Out() output := cmd.Out()
if len(args) < 1 { if len(args) < 1 {
logger.Fatal("Argument [appname] is missing") beeLogger.Log.Fatal("Argument [appname] is missing")
} }
if len(args) > 1 { if len(args) > 1 {
cmd.Flag.Parse(args[1:]) cmd.Flag.Parse(args[1:])
} }
apppath, packpath, err := checkEnv(args[0]) apppath, packpath, err := utils.CheckEnv(args[0])
if err != nil { if err != nil {
logger.Fatalf("%s", err) beeLogger.Log.Fatalf("%s", err)
} }
if driver == "" { if generate.SQLDriver == "" {
driver = "mysql" generate.SQLDriver = "mysql"
}
if conn == "" {
} }
logger.Info("Creating API...") beeLogger.Log.Info("Creating API...")
os.MkdirAll(apppath, 0755) os.MkdirAll(apppath, 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m")
@ -569,30 +573,30 @@ func createapi(cmd *Command, args []string) int {
os.Mkdir(path.Join(apppath, "tests"), 0755) os.Mkdir(path.Join(apppath, "tests"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests"), "\x1b[0m")
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m")
WriteToFile(path.Join(apppath, "conf", "app.conf"), utils.WriteToFile(path.Join(apppath, "conf", "app.conf"),
strings.Replace(apiconf, "{{.Appname}}", path.Base(args[0]), -1)) strings.Replace(apiconf, "{{.Appname}}", path.Base(args[0]), -1))
if conn != "" { if generate.SQLConn != "" {
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
maingoContent := strings.Replace(apiMainconngo, "{{.Appname}}", packpath, -1) maingoContent := strings.Replace(apiMainconngo, "{{.Appname}}", packpath, -1)
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1) maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(generate.SQLDriver), -1)
if driver == "mysql" { if generate.SQLDriver == "mysql" {
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/go-sql-driver/mysql"`, -1) maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/go-sql-driver/mysql"`, -1)
} else if driver == "postgres" { } else if generate.SQLDriver == "postgres" {
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1) maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1)
} }
WriteToFile(path.Join(apppath, "main.go"), utils.WriteToFile(path.Join(apppath, "main.go"),
strings.Replace( strings.Replace(
maingoContent, maingoContent,
"{{.conn}}", "{{.conn}}",
conn.String(), generate.SQLConn.String(),
-1, -1,
), ),
) )
logger.Infof("Using '%s' as 'driver'", driver) beeLogger.Log.Infof("Using '%s' as 'driver'", generate.SQLDriver)
logger.Infof("Using '%s' as 'conn'", conn) beeLogger.Log.Infof("Using '%s' as 'conn'", generate.SQLConn)
logger.Infof("Using '%s' as 'tables'", tables) beeLogger.Log.Infof("Using '%s' as 'tables'", generate.Tables)
generateAppcode(string(driver), string(conn), "3", string(tables), apppath) generate.GenerateAppcode(string(generate.SQLDriver), string(generate.SQLConn), "3", string(generate.Tables), apppath)
} else { } else {
os.Mkdir(path.Join(apppath, "models"), 0755) os.Mkdir(path.Join(apppath, "models"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m")
@ -600,65 +604,31 @@ func createapi(cmd *Command, args []string) int {
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m")
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "object.go"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "object.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "controllers", "object.go"), utils.WriteToFile(path.Join(apppath, "controllers", "object.go"),
strings.Replace(apiControllers, "{{.Appname}}", packpath, -1)) strings.Replace(apiControllers, "{{.Appname}}", packpath, -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "user.go"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "user.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "controllers", "user.go"), utils.WriteToFile(path.Join(apppath, "controllers", "user.go"),
strings.Replace(apiControllers2, "{{.Appname}}", packpath, -1)) strings.Replace(apiControllers2, "{{.Appname}}", packpath, -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "tests", "default_test.go"), utils.WriteToFile(path.Join(apppath, "tests", "default_test.go"),
strings.Replace(apiTests, "{{.Appname}}", packpath, -1)) strings.Replace(apiTests, "{{.Appname}}", packpath, -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "routers", "router.go"), utils.WriteToFile(path.Join(apppath, "routers", "router.go"),
strings.Replace(apirouter, "{{.Appname}}", packpath, -1)) strings.Replace(apirouter, "{{.Appname}}", packpath, -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "models", "object.go"), apiModels) utils.WriteToFile(path.Join(apppath, "models", "object.go"), ApiModels)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "models", "user.go"), apiModels2) utils.WriteToFile(path.Join(apppath, "models", "user.go"), ApiModels2)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "main.go"), utils.WriteToFile(path.Join(apppath, "main.go"),
strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1)) strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1))
} }
logger.Success("New API successfully created!") beeLogger.Log.Success("New API successfully created!")
return 0 return 0
} }
func checkEnv(appname string) (apppath, packpath string, err error) {
gps := GetGOPATHs()
if len(gps) == 0 {
logger.Fatal("GOPATH environment variable is not set or empty")
}
currpath, _ := os.Getwd()
currpath = path.Join(currpath, appname)
for _, gpath := range gps {
gsrcpath := path.Join(gpath, "src")
if strings.HasPrefix(currpath, gsrcpath) {
packpath = strings.Replace(currpath[len(gsrcpath)+1:], string(path.Separator), "/", -1)
return currpath, packpath, nil
}
}
// In case of multiple paths in the GOPATH, by default
// we use the first path
gopath := gps[0]
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)
logger.Errorf("Path '%s' already exists", apppath)
return
}
packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(path.Separator)), "/")
return
}

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package bale
import ( import (
"bytes" "bytes"
@ -24,9 +24,15 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/config"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
) )
var cmdBale = &Command{ var CmdBale = &commands.Command{
UsageLine: "bale", UsageLine: "bale",
Short: "Transforms non-Go files to Go source files", Short: "Transforms non-Go files to Go source files",
Long: `Bale command compress all the static files in to a single binary file. Long: `Bale command compress all the static files in to a single binary file.
@ -37,47 +43,46 @@ var cmdBale = &Command{
It will auto-generate an unpack function to the main package then run it during the runtime. It will auto-generate an unpack function to the main package then run it during the runtime.
This is mainly used for zealots who are requiring 100% Go code. This is mainly used for zealots who are requiring 100% Go code.
`, `,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: runBale, Run: runBale,
} }
func runBale(cmd *Command, args []string) int { func init() {
err := loadConfig() commands.AvailableCommands = append(commands.AvailableCommands, CmdBale)
if err != nil {
logger.Fatalf("Failed to load configuration: %s", err)
} }
func runBale(cmd *commands.Command, args []string) int {
os.RemoveAll("bale") os.RemoveAll("bale")
os.Mkdir("bale", os.ModePerm) os.Mkdir("bale", os.ModePerm)
// Pack and compress data // Pack and compress data
for _, p := range conf.Bale.Dirs { for _, p := range config.Conf.Bale.Dirs {
if !isExist(p) { if !utils.IsExist(p) {
logger.Warnf("Skipped directory: %s", p) beeLogger.Log.Warnf("Skipped directory: %s", p)
continue continue
} }
logger.Infof("Packaging directory: %s", p) beeLogger.Log.Infof("Packaging directory: %s", p)
filepath.Walk(p, walkFn) filepath.Walk(p, walkFn)
} }
// Generate auto-uncompress function. // Generate auto-uncompress function.
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.WriteString(fmt.Sprintf(BaleHeader, conf.Bale.Import, buf.WriteString(fmt.Sprintf(BaleHeader, config.Conf.Bale.Import,
strings.Join(resFiles, "\",\n\t\t\""), strings.Join(resFiles, "\",\n\t\t\""),
strings.Join(resFiles, ",\n\t\tbale.R"))) strings.Join(resFiles, ",\n\t\tbale.R")))
fw, err := os.Create("bale.go") fw, err := os.Create("bale.go")
if err != nil { if err != nil {
logger.Fatalf("Failed to create file: %s", err) beeLogger.Log.Fatalf("Failed to create file: %s", err)
} }
defer fw.Close() defer fw.Close()
_, err = fw.Write(buf.Bytes()) _, err = fw.Write(buf.Bytes())
if err != nil { if err != nil {
logger.Fatalf("Failed to write data: %s", err) beeLogger.Log.Fatalf("Failed to write data: %s", err)
} }
logger.Success("Baled resources successfully!") beeLogger.Log.Success("Baled resources successfully!")
return 0 return 0
} }
@ -146,7 +151,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
// Open resource files // Open resource files
fr, err := os.Open(resPath) fr, err := os.Open(resPath)
if err != nil { if err != nil {
logger.Fatalf("Failed to read file: %s", err) beeLogger.Log.Fatalf("Failed to read file: %s", err)
} }
// Convert path // Convert path
@ -164,7 +169,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
os.MkdirAll(path.Dir(resPath), os.ModePerm) os.MkdirAll(path.Dir(resPath), os.ModePerm)
fw, err := os.Create("bale/" + resPath + ".go") fw, err := os.Create("bale/" + resPath + ".go")
if err != nil { if err != nil {
logger.Fatalf("Failed to create file: %s", err) beeLogger.Log.Fatalf("Failed to create file: %s", err)
} }
defer fw.Close() defer fw.Close()
@ -184,7 +189,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
} }
func filterSuffix(name string) bool { func filterSuffix(name string) bool {
for _, s := range conf.Bale.IngExt { for _, s := range config.Conf.Bale.IngExt {
if strings.HasSuffix(name, s) { if strings.HasSuffix(name, s) {
return true return true
} }

View File

@ -1,4 +1,4 @@
package main package beefix
import ( import (
"fmt" "fmt"
@ -9,9 +9,14 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
) )
var cmdFix = &Command{ var CmdFix = &commands.Command{
UsageLine: "fix", UsageLine: "fix",
Short: "Fixes your application by making it compatible with newer versions of Beego", Short: "Fixes your application by making it compatible with newer versions of Beego",
Long: `As of {{"Beego 1.6"|bold}}, there are some backward compatibility issues. Long: `As of {{"Beego 1.6"|bold}}, there are some backward compatibility issues.
@ -22,18 +27,19 @@ var cmdFix = &Command{
} }
func init() { func init() {
cmdFix.Run = runFix CmdFix.Run = runFix
cmdFix.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } CmdFix.PreRun = func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }
commands.AvailableCommands = append(commands.AvailableCommands, CmdFix)
} }
func runFix(cmd *Command, args []string) int { func runFix(cmd *commands.Command, args []string) int {
output := cmd.Out() output := cmd.Out()
logger.Info("Upgrading the application...") beeLogger.Log.Info("Upgrading the application...")
dir, err := os.Getwd() dir, err := os.Getwd()
if err != nil { if err != nil {
logger.Fatalf("Error while getting the current working directory: %s", err) beeLogger.Log.Fatalf("Error while getting the current working directory: %s", err)
} }
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
@ -50,13 +56,13 @@ func runFix(cmd *Command, args []string) int {
return nil return nil
} }
err = fixFile(path) err = fixFile(path)
fmt.Fprintf(output, GreenBold("\tfix\t")+"%s\n", path) fmt.Fprintf(output, colors.GreenBold("\tfix\t")+"%s\n", path)
if err != nil { if err != nil {
logger.Errorf("Could not fix file: %s", err) beeLogger.Log.Errorf("Could not fix file: %s", err)
} }
return err return err
}) })
logger.Success("Upgrade Done!") beeLogger.Log.Success("Upgrade Done!")
return 0 return 0
} }

94
cmd/commands/command.go Normal file
View File

@ -0,0 +1,94 @@
package commands
import (
"flag"
"io"
"os"
"strings"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
)
// 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
// Short is the short description shown in the 'go help' output.
Short string
// Long is the long message shown in the 'go help <this-command>' output.
Long string
// Flag is a set of flags specific to this command.
Flag flag.FlagSet
// CustomFlags indicates that the command will do its own
// flag parsing.
CustomFlags bool
// output out writer if set in SetOutput(w)
output *io.Writer
}
var AvailableCommands = []*Command{}
var cmdUsage = `Use {{printf "bee help %s" .Name | bold}} for more information.{{endline}}`
// Name returns the command's name: the first word in the Usage line.
func (c *Command) Name() string {
name := c.UsageLine
i := strings.Index(name, " ")
if i >= 0 {
name = name[:i]
}
return name
}
// SetOutput sets the destination for Usage and error messages.
// If output is nil, os.Stderr is used.
func (c *Command) SetOutput(output io.Writer) {
c.output = &output
}
// Out returns the out writer of the current command.
// If cmd.output is nil, os.Stderr is used.
func (c *Command) Out() io.Writer {
if c.output != nil {
return *c.output
}
return colors.NewColorWriter(os.Stderr)
}
// Usage puts out the Usage for the command.
func (c *Command) Usage() {
utils.Tmpl(cmdUsage, c)
os.Exit(2)
}
// Runnable reports whether the command can be run; otherwise
// it is a documentation pseudo-command such as import path.
func (c *Command) Runnable() bool {
return c.Run != nil
}
func (c *Command) Options() map[string]string {
options := make(map[string]string)
c.Flag.VisitAll(func(f *flag.Flag) {
defaultVal := f.DefValue
if len(defaultVal) > 0 {
options[f.Name+"="+defaultVal] = f.Usage
} else {
options[f.Name] = f.Usage
}
})
return options
}

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package dockerize
import ( import (
"flag" "flag"
@ -21,22 +21,13 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"text/template" "text/template"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
) )
var cmdDockerize = &Command{
CustomFlags: true,
UsageLine: "dockerize",
Short: "Generates a Dockerfile for your Beego application",
Long: `Dockerize generates a Dockerfile for your Beego Web Application.
The Dockerfile will compile, get the dependencies with {{"godep"|bold}}, and set the entrypoint.
{{"Example:"|bold}}
$ bee dockerize -expose="3000,80,25"
`,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() },
Run: dockerizeApp,
}
const dockerBuildTemplate = `FROM {{.BaseImage}} const dockerBuildTemplate = `FROM {{.BaseImage}}
# Godep for vendoring # Godep for vendoring
@ -67,6 +58,20 @@ type Dockerfile struct {
Expose string Expose string
} }
var CmdDockerize = &commands.Command{
CustomFlags: true,
UsageLine: "dockerize",
Short: "Generates a Dockerfile for your Beego application",
Long: `Dockerize generates a Dockerfile for your Beego Web Application.
The Dockerfile will compile, get the dependencies with {{"godep"|bold}}, and set the entrypoint.
{{"Example:"|bold}}
$ bee dockerize -expose="3000,80,25"
`,
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: dockerizeApp,
}
var ( var (
expose string expose string
baseImage string baseImage string
@ -76,19 +81,22 @@ func init() {
fs := flag.NewFlagSet("dockerize", flag.ContinueOnError) fs := flag.NewFlagSet("dockerize", flag.ContinueOnError)
fs.StringVar(&baseImage, "image", "library/golang", "Set the base image of the Docker container.") fs.StringVar(&baseImage, "image", "library/golang", "Set the base image of the Docker container.")
fs.StringVar(&expose, "expose", "8080", "Port(s) to expose in the Docker container.") fs.StringVar(&expose, "expose", "8080", "Port(s) to expose in the Docker container.")
cmdDockerize.Flag = *fs CmdDockerize.Flag = *fs
commands.AvailableCommands = append(commands.AvailableCommands, CmdDockerize)
} }
func dockerizeApp(cmd *Command, args []string) int { func dockerizeApp(cmd *commands.Command, args []string) int {
if err := cmd.Flag.Parse(args); err != nil { if err := cmd.Flag.Parse(args); err != nil {
logger.Fatalf("Error parsing flags: %v", err.Error()) beeLogger.Log.Fatalf("Error parsing flags: %v", err.Error())
} }
logger.Info("Generating Dockerfile...") beeLogger.Log.Info("Generating Dockerfile...")
gopath := os.Getenv("GOPATH") gopath := os.Getenv("GOPATH")
dir, err := filepath.Abs(".") dir, err := filepath.Abs(".")
MustCheck(err) if err != nil {
beeLogger.Log.Error(err.Error())
}
appdir := strings.Replace(dir, gopath, "", 1) appdir := strings.Replace(dir, gopath, "", 1)
@ -112,15 +120,15 @@ func dockerizeApp(cmd *Command, args []string) int {
} }
func generateDockerfile(df Dockerfile) { func generateDockerfile(df Dockerfile) {
t := template.Must(template.New("dockerBuildTemplate").Parse(dockerBuildTemplate)).Funcs(BeeFuncMap()) t := template.Must(template.New("dockerBuildTemplate").Parse(dockerBuildTemplate)).Funcs(utils.BeeFuncMap())
f, err := os.Create("Dockerfile") f, err := os.Create("Dockerfile")
if err != nil { if err != nil {
logger.Fatalf("Error writing Dockerfile: %v", err.Error()) beeLogger.Log.Fatalf("Error writing Dockerfile: %v", err.Error())
} }
defer CloseFile(f) defer utils.CloseFile(f)
t.Execute(f, df) t.Execute(f, df)
logger.Success("Dockerfile generated.") beeLogger.Log.Success("Dockerfile generated.")
} }

View File

@ -0,0 +1,203 @@
package generate
import (
"os"
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/config"
"github.com/beego/bee/generate"
"github.com/beego/bee/generate/swaggergen"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
)
var CmdGenerate = &commands.Command{
UsageLine: "generate [command]",
Short: "Source code generator",
Long: ` {{"To scaffold out your entire application:"|bold}}
$ bee generate scaffold [scaffoldname] [-fields="title:string,body:text"] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
{{"To generate a Model based on fields:"|bold}}
$ bee generate model [modelname] [-fields="name:type"]
{{"To generate a controller:"|bold}}
$ bee generate controller [controllerfile]
{{"To generate a CRUD view:"|bold}}
$ bee generate view [viewpath]
{{"To generate a migration file for making database schema updates:"|bold}}
$ bee generate migration [migrationfile] [-fields="name:type"]
{{"To generate swagger doc file:"|bold}}
$ bee generate docs
{{"To generate a test case:"|bold}}
$ bee generate test [routerfile]
{{"To generate appcode based on an existing database:"|bold}}
$ bee generate appcode [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-level=3]
`,
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: GenerateCode,
}
func init() {
CmdGenerate.Flag.Var(&generate.Tables, "Tables", "List of table names separated by a comma.")
CmdGenerate.Flag.Var(&generate.SQLDriver, "SQLDriver", "Database SQLDriver. Either mysql, postgres or sqlite.")
CmdGenerate.Flag.Var(&generate.SQLConn, "SQLConn", "Connection string used by the SQLDriver to connect to a database instance.")
CmdGenerate.Flag.Var(&generate.Level, "Level", "Either 1, 2 or 3. i.e. 1=models; 2=models and controllers; 3=models, controllers and routers.")
CmdGenerate.Flag.Var(&generate.Fields, "Fields", "List of table Fields.")
commands.AvailableCommands = append(commands.AvailableCommands, CmdGenerate)
}
func GenerateCode(cmd *commands.Command, args []string) int {
currpath, _ := os.Getwd()
if len(args) < 1 {
beeLogger.Log.Fatal("Command is missing")
}
gps := utils.GetGOPATHs()
if len(gps) == 0 {
beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
}
gopath := gps[0]
beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath)
gcmd := args[0]
switch gcmd {
case "scaffold":
scaffold(cmd, args, currpath)
case "docs":
swaggergen.GenerateDocs(currpath)
case "appcode":
appCode(cmd, args, currpath)
case "migration":
migration(cmd, args, currpath)
case "controller":
controller(args, currpath)
case "model":
model(cmd, args, currpath)
case "view":
view(args, currpath)
default:
beeLogger.Log.Fatal("Command is missing")
}
beeLogger.Log.Successf("%s successfully generated!", strings.Title(gcmd))
return 0
}
func scaffold(cmd *commands.Command, args []string, currpath string) {
if len(args) < 2 {
beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate")
}
cmd.Flag.Parse(args[2:])
if generate.SQLDriver == "" {
generate.SQLDriver = utils.DocValue(config.Conf.Database.Driver)
if generate.SQLDriver == "" {
generate.SQLDriver = "mysql"
}
}
if generate.SQLConn == "" {
generate.SQLConn = utils.DocValue(config.Conf.Database.Conn)
if generate.SQLConn == "" {
generate.SQLConn = "root:@tcp(127.0.0.1:3306)/test"
}
}
if generate.Fields == "" {
beeLogger.Log.Hint("Fields option should not be empty, i.e. -Fields=\"title:string,body:text\"")
beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate")
}
sname := args[1]
generate.GenerateScaffold(sname, generate.Fields.String(), currpath, generate.SQLDriver.String(), generate.SQLConn.String())
}
func appCode(cmd *commands.Command, args []string, currpath string) {
cmd.Flag.Parse(args[1:])
if generate.SQLDriver == "" {
generate.SQLDriver = utils.DocValue(config.Conf.Database.Driver)
if generate.SQLDriver == "" {
generate.SQLDriver = "mysql"
}
}
if generate.SQLConn == "" {
generate.SQLConn = utils.DocValue(config.Conf.Database.Conn)
if generate.SQLConn == "" {
if generate.SQLDriver == "mysql" {
generate.SQLConn = "root:@tcp(127.0.0.1:3306)/test"
} else if generate.SQLDriver == "postgres" {
generate.SQLConn = "postgres://postgres:postgres@127.0.0.1:5432/postgres"
}
}
}
if generate.Level == "" {
generate.Level = "3"
}
beeLogger.Log.Infof("Using '%s' as 'SQLDriver'", generate.SQLDriver)
beeLogger.Log.Infof("Using '%s' as 'SQLConn'", generate.SQLConn)
beeLogger.Log.Infof("Using '%s' as 'Tables'", generate.Tables)
beeLogger.Log.Infof("Using '%s' as 'Level'", generate.Level)
generate.GenerateAppcode(generate.SQLDriver.String(), generate.SQLConn.String(), generate.Level.String(), generate.Tables.String(), currpath)
}
func migration(cmd *commands.Command, args []string, currpath string) {
if len(args) < 2 {
beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate")
}
cmd.Flag.Parse(args[2:])
mname := args[1]
beeLogger.Log.Infof("Using '%s' as migration name", mname)
upsql := ""
downsql := ""
if generate.Fields != "" {
dbMigrator := generate.NewDBDriver()
upsql = dbMigrator.GenerateCreateUp(mname)
downsql = dbMigrator.GenerateCreateDown(mname)
}
generate.GenerateMigration(mname, upsql, downsql, currpath)
}
func controller(args []string, currpath string) {
if len(args) == 2 {
cname := args[1]
generate.GenerateController(cname, currpath)
} else {
beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate")
}
}
func model(cmd *commands.Command, args []string, currpath string) {
if len(args) < 2 {
beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate")
}
cmd.Flag.Parse(args[2:])
if generate.Fields == "" {
beeLogger.Log.Hint("Fields option should not be empty, i.e. -Fields=\"title:string,body:text\"")
beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate")
}
sname := args[1]
generate.GenerateModel(sname, generate.Fields.String(), currpath)
}
func view(args []string, currpath string) {
if len(args) == 2 {
cname := args[1]
generate.GenerateView(cname, currpath)
} else {
beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate")
}
}

View File

@ -0,0 +1,119 @@
package hprose
import (
"os"
"fmt"
"path"
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/api"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/generate"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
)
var CmdHproseapp = &commands.Command{
// CustomFlags: true,
UsageLine: "hprose [appname]",
Short: "Creates an RPC application based on Hprose and Beego frameworks",
Long: `
The command 'hprose' creates an RPC application based on both Beego and Hprose (http://hprose.com/).
{{"To scaffold out your application, use:"|bold}}
$ bee hprose [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test]
If 'conn' is empty, the command will generate a sample application. Otherwise the command
will connect to your database and generate models based on the existing tables.
The command 'hprose' creates a folder named [appname] with the following structure:
main.go
{{"conf"|foldername}}
app.conf
{{"models"|foldername}}
object.go
user.go
`,
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: createhprose,
}
func init() {
CmdHproseapp.Flag.Var(&generate.Tables, "tables", "List of table names separated by a comma.")
CmdHproseapp.Flag.Var(&generate.SQLDriver, "driver", "Database driver. Either mysql, postgres or sqlite.")
CmdHproseapp.Flag.Var(&generate.SQLConn, "conn", "Connection string used by the driver to connect to a database instance.")
commands.AvailableCommands = append(commands.AvailableCommands, CmdHproseapp)
}
func createhprose(cmd *commands.Command, args []string) int {
output := cmd.Out()
if len(args) != 1 {
beeLogger.Log.Fatal("Argument [appname] is missing")
}
curpath, _ := os.Getwd()
if len(args) > 1 {
cmd.Flag.Parse(args[1:])
}
apppath, packpath, err := utils.CheckEnv(args[0])
if err != nil {
beeLogger.Log.Fatalf("%s", err)
}
if generate.SQLDriver == "" {
generate.SQLDriver = "mysql"
}
beeLogger.Log.Info("Creating Hprose application...")
os.MkdirAll(apppath, 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m")
os.Mkdir(path.Join(apppath, "conf"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m")
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "conf", "app.conf"),
strings.Replace(generate.Hproseconf, "{{.Appname}}", args[0], -1))
if generate.SQLConn != "" {
beeLogger.Log.Infof("Using '%s' as 'driver'", generate.SQLDriver)
beeLogger.Log.Infof("Using '%s' as 'conn'", generate.SQLConn)
beeLogger.Log.Infof("Using '%s' as 'tables'", generate.Tables)
generate.GenerateHproseAppcode(string(generate.SQLDriver), string(generate.SQLConn), "1", string(generate.Tables), path.Join(curpath, args[0]))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
maingoContent := strings.Replace(generate.HproseMainconngo, "{{.Appname}}", packpath, -1)
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(generate.SQLDriver), -1)
maingoContent = strings.Replace(maingoContent, "{{HproseFunctionList}}", strings.Join(generate.HproseAddFunctions, ""), -1)
if generate.SQLDriver == "mysql" {
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/go-sql-driver/mysql"`, -1)
} else if generate.SQLDriver == "postgres" {
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1)
}
utils.WriteToFile(path.Join(apppath, "main.go"),
strings.Replace(
maingoContent,
"{{.conn}}",
generate.SQLConn.String(),
-1,
),
)
} else {
os.Mkdir(path.Join(apppath, "models"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m")
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "models", "object.go"), apiapp.ApiModels)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "models", "user.go"), apiapp.ApiModels2)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "main.go"),
strings.Replace(generate.HproseMaingo, "{{.Appname}}", packpath, -1))
}
beeLogger.Log.Success("New Hprose application successfully created!")
return 0
}

View File

@ -1,18 +1,4 @@
// Copyright 2013 bee authors package migrate
//
// 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 ( import (
"database/sql" "database/sql"
@ -23,9 +9,16 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/config"
"github.com/beego/bee/utils"
beeLogger "github.com/beego/bee/logger"
) )
var cmdMigrate = &Command{ var CmdMigrate = &commands.Command{
UsageLine: "migrate [Command]", UsageLine: "migrate [Command]",
Short: "Runs database migrations", Short: "Runs database migrations",
Long: `The command 'migrate' allows you to run database migrations to keep it up-to-date. Long: `The command 'migrate' allows you to run database migrations to keep it up-to-date.
@ -46,100 +39,75 @@ var cmdMigrate = &Command{
$ bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] $ bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
`, `,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: runMigration, Run: RunMigration,
} }
var mDriver docValue var mDriver utils.DocValue
var mConn docValue var mConn utils.DocValue
func init() { func init() {
cmdMigrate.Flag.Var(&mDriver, "driver", "Database driver. Either mysql, postgres or sqlite.") CmdMigrate.Flag.Var(&mDriver, "driver", "Database driver. Either mysql, postgres or sqlite.")
cmdMigrate.Flag.Var(&mConn, "conn", "Connection string used by the driver to connect to a database instance.") CmdMigrate.Flag.Var(&mConn, "conn", "Connection string used by the driver to connect to a database instance.")
commands.AvailableCommands = append(commands.AvailableCommands, CmdMigrate)
} }
// runMigration is the entry point for starting a migration // runMigration is the entry point for starting a migration
func runMigration(cmd *Command, args []string) int { func RunMigration(cmd *commands.Command, args []string) int {
currpath, _ := os.Getwd() currpath, _ := os.Getwd()
gps := GetGOPATHs() gps := utils.GetGOPATHs()
if len(gps) == 0 { if len(gps) == 0 {
logger.Fatal("GOPATH environment variable is not set or empty") beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
} }
gopath := gps[0] gopath := gps[0]
logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath)
// Load the configuration
err := loadConfig()
if err != nil {
logger.Errorf("Failed to load configuration: %s", err)
}
// Getting command line arguments // Getting command line arguments
if len(args) != 0 { if len(args) != 0 {
cmd.Flag.Parse(args[1:]) cmd.Flag.Parse(args[1:])
} }
if mDriver == "" { if mDriver == "" {
mDriver = docValue(conf.Database.Driver) mDriver = utils.DocValue(config.Conf.Database.Driver)
if mDriver == "" { if mDriver == "" {
mDriver = "mysql" mDriver = "mysql"
} }
} }
if mConn == "" { if mConn == "" {
mConn = docValue(conf.Database.Conn) mConn = utils.DocValue(config.Conf.Database.Conn)
if mConn == "" { if mConn == "" {
mConn = "root:@tcp(127.0.0.1:3306)/test" mConn = "root:@tcp(127.0.0.1:3306)/test"
} }
} }
logger.Infof("Using '%s' as 'driver'", mDriver) beeLogger.Log.Infof("Using '%s' as 'driver'", mDriver)
logger.Infof("Using '%s' as 'conn'", mConn) beeLogger.Log.Infof("Using '%s' as 'conn'", mConn)
driverStr, connStr := string(mDriver), string(mConn) driverStr, connStr := string(mDriver), string(mConn)
if len(args) == 0 { if len(args) == 0 {
// run all outstanding migrations // run all outstanding migrations
logger.Info("Running all outstanding migrations") beeLogger.Log.Info("Running all outstanding migrations")
migrateUpdate(currpath, driverStr, connStr) MigrateUpdate(currpath, driverStr, connStr)
} else { } else {
mcmd := args[0] mcmd := args[0]
switch mcmd { switch mcmd {
case "rollback": case "rollback":
logger.Info("Rolling back the last migration operation") beeLogger.Log.Info("Rolling back the last migration operation")
migrateRollback(currpath, driverStr, connStr) MigrateRollback(currpath, driverStr, connStr)
case "reset": case "reset":
logger.Info("Reseting all migrations") beeLogger.Log.Info("Reseting all migrations")
migrateReset(currpath, driverStr, connStr) MigrateReset(currpath, driverStr, connStr)
case "refresh": case "refresh":
logger.Info("Refreshing all migrations") beeLogger.Log.Info("Refreshing all migrations")
migrateRefresh(currpath, driverStr, connStr) MigrateRefresh(currpath, driverStr, connStr)
default: default:
logger.Fatal("Command is missing") beeLogger.Log.Fatal("Command is missing")
} }
} }
logger.Success("Migration successful!") beeLogger.Log.Success("Migration successful!")
return 0 return 0
} }
// migrateUpdate does the schema update
func migrateUpdate(currpath, driver, connStr string) {
migrate("upgrade", currpath, driver, connStr)
}
// migrateRollback rolls back the latest migration
func migrateRollback(currpath, driver, connStr string) {
migrate("rollback", currpath, driver, connStr)
}
// migrateReset rolls back all migrations
func migrateReset(currpath, driver, connStr string) {
migrate("reset", currpath, driver, connStr)
}
// migrationRefresh rolls back all migrations and start over again
func migrateRefresh(currpath, driver, connStr string) {
migrate("refresh", currpath, driver, connStr)
}
// migrate generates source code, build it, and invoke the binary who does the actual migration // migrate generates source code, build it, and invoke the binary who does the actual migration
func migrate(goal, currpath, driver, connStr string) { func migrate(goal, currpath, driver, connStr string) {
dir := path.Join(currpath, "database", "migrations") dir := path.Join(currpath, "database", "migrations")
@ -153,7 +121,7 @@ func migrate(goal, currpath, driver, connStr string) {
// Connect to database // Connect to database
db, err := sql.Open(driver, connStr) db, err := sql.Open(driver, connStr)
if err != nil { if err != nil {
logger.Fatalf("Could not connect to database using '%s': %s", connStr, err) beeLogger.Log.Fatalf("Could not connect to database using '%s': %s", connStr, err)
} }
defer db.Close() defer db.Close()
@ -171,44 +139,44 @@ func migrate(goal, currpath, driver, connStr string) {
func checkForSchemaUpdateTable(db *sql.DB, driver string) { func checkForSchemaUpdateTable(db *sql.DB, driver string) {
showTableSQL := showMigrationsTableSQL(driver) showTableSQL := showMigrationsTableSQL(driver)
if rows, err := db.Query(showTableSQL); err != nil { if rows, err := db.Query(showTableSQL); err != nil {
logger.Fatalf("Could not show migrations table: %s", err) beeLogger.Log.Fatalf("Could not show migrations table: %s", err)
} else if !rows.Next() { } else if !rows.Next() {
// No migrations table, create new ones // No migrations table, create new ones
createTableSQL := createMigrationsTableSQL(driver) createTableSQL := createMigrationsTableSQL(driver)
logger.Infof("Creating 'migrations' table...") beeLogger.Log.Infof("Creating 'migrations' table...")
if _, err := db.Query(createTableSQL); err != nil { if _, err := db.Query(createTableSQL); err != nil {
logger.Fatalf("Could not create migrations table: %s", err) beeLogger.Log.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) selectTableSQL := selectMigrationsTableSQL(driver)
if rows, err := db.Query(selectTableSQL); err != nil { if rows, err := db.Query(selectTableSQL); err != nil {
logger.Fatalf("Could not show columns of migrations table: %s", err) beeLogger.Log.Fatalf("Could not show columns of migrations table: %s", err)
} else { } else {
for rows.Next() { for rows.Next() {
var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte
if err := rows.Scan(&fieldBytes, &typeBytes, &nullBytes, &keyBytes, &defaultBytes, &extraBytes); err != nil { if err := rows.Scan(&fieldBytes, &typeBytes, &nullBytes, &keyBytes, &defaultBytes, &extraBytes); err != nil {
logger.Fatalf("Could not read column information: %s", err) beeLogger.Log.Fatalf("Could not read column information: %s", err)
} }
fieldStr, typeStr, nullStr, keyStr, defaultStr, extraStr := fieldStr, typeStr, nullStr, keyStr, defaultStr, extraStr :=
string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes) string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes)
if fieldStr == "id_migration" { if fieldStr == "id_migration" {
if keyStr != "PRI" || extraStr != "auto_increment" { if keyStr != "PRI" || extraStr != "auto_increment" {
logger.Hint("Expecting KEY: PRI, EXTRA: auto_increment") beeLogger.Log.Hint("Expecting KEY: PRI, EXTRA: auto_increment")
logger.Fatalf("Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s", keyStr, extraStr) beeLogger.Log.Fatalf("Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s", keyStr, extraStr)
} }
} else if fieldStr == "name" { } else if fieldStr == "name" {
if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" { if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" {
logger.Hint("Expecting TYPE: varchar, NULL: YES") beeLogger.Log.Hint("Expecting TYPE: varchar, NULL: YES")
logger.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr) beeLogger.Log.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr)
} }
} else if fieldStr == "created_at" { } else if fieldStr == "created_at" {
if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" { if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" {
logger.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP") beeLogger.Log.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP")
logger.Fatalf("Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s", typeStr, defaultStr) beeLogger.Log.Fatalf("Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s", typeStr, defaultStr)
} }
} }
} }
@ -252,22 +220,22 @@ func selectMigrationsTableSQL(driver string) string {
func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64) { func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64) {
sql := "SELECT name FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1" sql := "SELECT name FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1"
if rows, err := db.Query(sql); err != nil { if rows, err := db.Query(sql); err != nil {
logger.Fatalf("Could not retrieve migrations: %s", err) beeLogger.Log.Fatalf("Could not retrieve migrations: %s", err)
} else { } else {
if rows.Next() { if rows.Next() {
if err := rows.Scan(&file); err != nil { if err := rows.Scan(&file); err != nil {
logger.Fatalf("Could not read migrations in database: %s", err) beeLogger.Log.Fatalf("Could not read migrations in database: %s", err)
} }
createdAtStr := file[len(file)-15:] createdAtStr := file[len(file)-15:]
if t, err := time.Parse("20060102_150405", createdAtStr); err != nil { if t, err := time.Parse("20060102_150405", createdAtStr); err != nil {
logger.Fatalf("Could not parse time: %s", err) beeLogger.Log.Fatalf("Could not parse time: %s", err)
} else { } else {
createdAt = t.Unix() createdAt = t.Unix()
} }
} else { } else {
// migration table has no 'update' record, no point rolling back // migration table has no 'update' record, no point rolling back
if goal == "rollback" { if goal == "rollback" {
logger.Fatal("There is nothing to rollback") beeLogger.Log.Fatal("There is nothing to rollback")
} }
file, createdAt = "", 0 file, createdAt = "", 0
} }
@ -279,7 +247,7 @@ func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64)
func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime int64, latestName string, task string) { func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime int64, latestName string, task string) {
changeDir(dir) changeDir(dir)
if f, err := os.OpenFile(source, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err != nil { if f, err := os.OpenFile(source, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err != nil {
logger.Fatalf("Could not create file: %s", err) beeLogger.Log.Fatalf("Could not create file: %s", err)
} else { } else {
content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1) content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1)
content = strings.Replace(content, "{{ConnStr}}", connStr, -1) content = strings.Replace(content, "{{ConnStr}}", connStr, -1)
@ -287,9 +255,9 @@ func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime in
content = strings.Replace(content, "{{LatestName}}", latestName, -1) content = strings.Replace(content, "{{LatestName}}", latestName, -1)
content = strings.Replace(content, "{{Task}}", task, -1) content = strings.Replace(content, "{{Task}}", task, -1)
if _, err := f.WriteString(content); err != nil { if _, err := f.WriteString(content); err != nil {
logger.Fatalf("Could not write to file: %s", err) beeLogger.Log.Fatalf("Could not write to file: %s", err)
} }
CloseFile(f) utils.CloseFile(f)
} }
} }
@ -298,7 +266,7 @@ func buildMigrationBinary(dir, binary string) {
changeDir(dir) changeDir(dir)
cmd := exec.Command("go", "build", "-o", binary) cmd := exec.Command("go", "build", "-o", binary)
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
logger.Errorf("Could not build migration binary: %s", err) beeLogger.Log.Errorf("Could not build migration binary: %s", err)
formatShellErrOutput(string(out)) formatShellErrOutput(string(out))
removeTempFile(dir, binary) removeTempFile(dir, binary)
removeTempFile(dir, binary+".go") removeTempFile(dir, binary+".go")
@ -312,7 +280,7 @@ func runMigrationBinary(dir, binary string) {
cmd := exec.Command("./" + binary) cmd := exec.Command("./" + binary)
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
formatShellOutput(string(out)) formatShellOutput(string(out))
logger.Errorf("Could not run migration binary: %s", err) beeLogger.Log.Errorf("Could not run migration binary: %s", err)
removeTempFile(dir, binary) removeTempFile(dir, binary)
removeTempFile(dir, binary+".go") removeTempFile(dir, binary+".go")
os.Exit(2) os.Exit(2)
@ -325,7 +293,7 @@ func runMigrationBinary(dir, binary string) {
// It exits the system when encouter an error // It exits the system when encouter an error
func changeDir(dir string) { func changeDir(dir string) {
if err := os.Chdir(dir); err != nil { if err := os.Chdir(dir); err != nil {
logger.Fatalf("Could not find migration directory: %s", err) beeLogger.Log.Fatalf("Could not find migration directory: %s", err)
} }
} }
@ -333,7 +301,7 @@ func changeDir(dir string) {
func removeTempFile(dir, file string) { func removeTempFile(dir, file string) {
changeDir(dir) changeDir(dir)
if err := os.Remove(file); err != nil { if err := os.Remove(file); err != nil {
logger.Warnf("Could not remove temporary file: %s", err) beeLogger.Log.Warnf("Could not remove temporary file: %s", err)
} }
} }
@ -341,7 +309,7 @@ func removeTempFile(dir, file string) {
func formatShellErrOutput(o string) { func formatShellErrOutput(o string) {
for _, line := range strings.Split(o, "\n") { for _, line := range strings.Split(o, "\n") {
if line != "" { if line != "" {
logger.Errorf("|> %s", line) beeLogger.Log.Errorf("|> %s", line)
} }
} }
} }
@ -350,7 +318,7 @@ func formatShellErrOutput(o string) {
func formatShellOutput(o string) { func formatShellOutput(o string) {
for _, line := range strings.Split(o, "\n") { for _, line := range strings.Split(o, "\n") {
if line != "" { if line != "" {
logger.Infof("|> %s", line) beeLogger.Log.Infof("|> %s", line)
} }
} }
} }
@ -421,3 +389,23 @@ CREATE TABLE migrations (
status migrations_status status migrations_status
)` )`
) )
// MigrateUpdate does the schema update
func MigrateUpdate(currpath, driver, connStr string) {
migrate("upgrade", currpath, driver, connStr)
}
// MigrateRollback rolls back the latest migration
func MigrateRollback(currpath, driver, connStr string) {
migrate("rollback", currpath, driver, connStr)
}
// MigrateReset rolls back all migrations
func MigrateReset(currpath, driver, connStr string) {
migrate("reset", currpath, driver, connStr)
}
// migrationRefresh rolls back all migrations and start over again
func MigrateRefresh(currpath, driver, connStr string) {
migrate("refresh", currpath, driver, connStr)
}

View File

@ -12,16 +12,22 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package new
import ( import (
"fmt" "fmt"
"os" "os"
path "path/filepath" path "path/filepath"
"strings" "strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
) )
var cmdNew = &Command{ var CmdNew = &commands.Command{
UsageLine: "new [appname]", UsageLine: "new [appname]",
Short: "Creates a Beego application", Short: "Creates a Beego application",
Long: ` Long: `
@ -47,74 +53,8 @@ Creates a Beego application for the given app name in the current directory.
index.tpl index.tpl
`, `,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: createApp, Run: CreateApp,
}
func createApp(cmd *Command, args []string) int {
output := cmd.Out()
if len(args) != 1 {
logger.Fatal("Argument [appname] is missing")
}
apppath, packpath, err := checkEnv(args[0])
if err != nil {
logger.Fatalf("%s", err)
}
if isExist(apppath) {
logger.Errorf(bold("Application '%s' already exists"), apppath)
logger.Warn(bold("Do you want to overwrite it? [Yes|No] "))
if !askForConfirmation() {
os.Exit(2)
}
}
logger.Info("Creating application...")
os.MkdirAll(apppath, 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "conf"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "controllers"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "models"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "routers"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "tests"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static", "js"), 0755)
WriteToFile(path.Join(apppath, "static", "js", "reload.min.js"), reloadJsClient)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "js")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static", "css"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "css")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static", "img"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "img")+string(path.Separator), "\x1b[0m")
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "views"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m")
WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", path.Base(args[0]), -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "default.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "controllers", "default.go"), controllers)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views", "index.tpl"), "\x1b[0m")
WriteToFile(path.Join(apppath, "views", "index.tpl"), indextpl)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "routers", "router.go"), strings.Replace(router, "{{.Appname}}", packpath, -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "tests", "default_test.go"), strings.Replace(test, "{{.Appname}}", packpath, -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", packpath, -1))
logger.Success("New application successfully created!")
return 0
} }
var appconf = `appname = {{.Appname}} var appconf = `appname = {{.Appname}}
@ -303,3 +243,73 @@ var indextpl = `<!DOCTYPE html>
var reloadJsClient = `function b(a){var c=new WebSocket(a);c.onclose=function(){setTimeout(function(){b(a)},2E3)};c.onmessage=function(){location.reload()}}try{if(window.WebSocket)try{b("ws://localhost:12450/reload")}catch(a){console.error(a)}else console.log("Your browser does not support WebSockets.")}catch(a){console.error("Exception during connecting to Reload:",a)}; var reloadJsClient = `function b(a){var c=new WebSocket(a);c.onclose=function(){setTimeout(function(){b(a)},2E3)};c.onmessage=function(){location.reload()}}try{if(window.WebSocket)try{b("ws://localhost:12450/reload")}catch(a){console.error(a)}else console.log("Your browser does not support WebSockets.")}catch(a){console.error("Exception during connecting to Reload:",a)};
` `
func init() {
commands.AvailableCommands = append(commands.AvailableCommands, CmdNew)
}
func CreateApp(cmd *commands.Command, args []string) int {
output := cmd.Out()
if len(args) != 1 {
beeLogger.Log.Fatal("Argument [appname] is missing")
}
apppath, packpath, err := utils.CheckEnv(args[0])
if err != nil {
beeLogger.Log.Fatalf("%s", err)
}
if utils.IsExist(apppath) {
beeLogger.Log.Errorf(colors.Bold("Application '%s' already exists"), apppath)
beeLogger.Log.Warn(colors.Bold("Do you want to overwrite it? [Yes|No] "))
if !utils.AskForConfirmation() {
os.Exit(2)
}
}
beeLogger.Log.Info("Creating application...")
os.MkdirAll(apppath, 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "conf"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "controllers"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "models"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "routers"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "tests"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static", "js"), 0755)
utils.WriteToFile(path.Join(apppath, "static", "js", "reload.min.js"), reloadJsClient)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "js")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static", "css"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "css")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static", "img"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "img")+string(path.Separator), "\x1b[0m")
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "views"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", path.Base(args[0]), -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "default.go"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "controllers", "default.go"), controllers)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views", "index.tpl"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "views", "index.tpl"), indextpl)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "routers", "router.go"), strings.Replace(router, "{{.Appname}}", packpath, -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "tests", "default_test.go"), strings.Replace(test, "{{.Appname}}", packpath, -1))
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", packpath, -1))
beeLogger.Log.Success("New application successfully created!")
return 0
}

View File

@ -1,18 +1,4 @@
// Copyright 2013 bee authors package pack
//
// 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 ( import (
"archive/tar" "archive/tar"
@ -31,9 +17,14 @@ import (
"strings" "strings"
"syscall" "syscall"
"time" "time"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
) )
var cmdPack = &Command{ var CmdPack = &commands.Command{
CustomFlags: true, CustomFlags: true,
UsageLine: "pack", UsageLine: "pack",
Short: "Compresses a Beego application into a single file", Short: "Compresses a Beego application into a single file",
@ -43,7 +34,7 @@ var cmdPack = &Command{
{{"Example:"|bold}} {{"Example:"|bold}}
$ bee pack -v -ba="-ldflags '-s -w'" $ bee pack -v -ba="-ldflags '-s -w'"
`, `,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: packApp, Run: packApp,
} }
@ -52,27 +43,16 @@ var (
excludeP string excludeP string
excludeS string excludeS string
outputP string outputP string
excludeR ListOpts excludeR utils.ListOpts
fsym bool fsym bool
ssym bool ssym bool
build bool build bool
buildArgs string buildArgs string
buildEnvs ListOpts buildEnvs utils.ListOpts
verbose bool verbose bool
format string format string
) )
type ListOpts []string
func (opts *ListOpts) String() string {
return fmt.Sprint(*opts)
}
func (opts *ListOpts) Set(value string) error {
*opts = append(*opts, value)
return nil
}
func init() { func init() {
fs := flag.NewFlagSet("pack", flag.ContinueOnError) fs := flag.NewFlagSet("pack", flag.ContinueOnError)
fs.StringVar(&appPath, "p", "", "Set the application path. Defaults to the current path.") fs.StringVar(&appPath, "p", "", "Set the application path. Defaults to the current path.")
@ -87,7 +67,8 @@ func init() {
fs.BoolVar(&fsym, "fs", false, "Tell the command to follow symlinks. Defaults to false.") fs.BoolVar(&fsym, "fs", false, "Tell the command to follow symlinks. Defaults to false.")
fs.BoolVar(&ssym, "ss", false, "Tell the command to skip symlinks. Defaults to false.") fs.BoolVar(&ssym, "ss", false, "Tell the command to skip symlinks. Defaults to false.")
fs.BoolVar(&verbose, "v", false, "Be more verbose during the operation. Defaults to false.") fs.BoolVar(&verbose, "v", false, "Be more verbose during the operation. Defaults to false.")
cmdPack.Flag = *fs CmdPack.Flag = *fs
commands.AvailableCommands = append(commands.AvailableCommands, CmdPack)
} }
type walker interface { type walker interface {
@ -115,10 +96,6 @@ type walkFileTree struct {
output *io.Writer output *io.Writer
} }
func (wft *walkFileTree) setPrefix(prefix string) {
wft.prefix = prefix
}
func (wft *walkFileTree) isExclude(fPath string) bool { func (wft *walkFileTree) isExclude(fPath string) bool {
if fPath == "" { if fPath == "" {
return true return true
@ -316,12 +293,12 @@ func (wft *tarWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) {
return false, err return false, err
} }
if isSym == false { if !isSym {
fr, err := os.Open(fpath) fr, err := os.Open(fpath)
if err != nil { if err != nil {
return false, err return false, err
} }
defer CloseFile(fr) defer utils.CloseFile(fr)
_, err = io.Copy(tw, fr) _, err = io.Copy(tw, fr)
if err != nil { if err != nil {
return false, err return false, err
@ -352,12 +329,12 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) {
return false, err return false, err
} }
if isSym == false { if !isSym {
fr, err := os.Open(fpath) fr, err := os.Open(fpath)
if err != nil { if err != nil {
return false, err return false, err
} }
defer CloseFile(fr) defer utils.CloseFile(fr)
_, err = io.Copy(w, fr) _, err = io.Copy(w, fr)
if err != nil { if err != nil {
return false, err return false, err
@ -379,10 +356,10 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) {
func packDirectory(output io.Writer, excludePrefix []string, excludeSuffix []string, func packDirectory(output io.Writer, excludePrefix []string, excludeSuffix []string,
excludeRegexp []*regexp.Regexp, includePath ...string) (err error) { excludeRegexp []*regexp.Regexp, includePath ...string) (err error) {
logger.Infof("Excluding relpath prefix: %s", strings.Join(excludePrefix, ":")) beeLogger.Log.Infof("Excluding relpath prefix: %s", strings.Join(excludePrefix, ":"))
logger.Infof("Excluding relpath suffix: %s", strings.Join(excludeSuffix, ":")) beeLogger.Log.Infof("Excluding relpath suffix: %s", strings.Join(excludeSuffix, ":"))
if len(excludeRegexp) > 0 { if len(excludeRegexp) > 0 {
logger.Infof("Excluding filename regex: `%s`", strings.Join(excludeR, "`, `")) beeLogger.Log.Infof("Excluding filename regex: `%s`", strings.Join(excludeR, "`, `"))
} }
w, err := os.OpenFile(outputP, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) w, err := os.OpenFile(outputP, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
@ -437,7 +414,7 @@ func packDirectory(output io.Writer, excludePrefix []string, excludeSuffix []str
return return
} }
func packApp(cmd *Command, args []string) int { func packApp(cmd *commands.Command, args []string) int {
output := cmd.Out() output := cmd.Out()
curPath, _ := os.Getwd() curPath, _ := os.Getwd()
thePath := "" thePath := ""
@ -454,19 +431,19 @@ func packApp(cmd *Command, args []string) int {
} }
cmd.Flag.Parse(nArgs) cmd.Flag.Parse(nArgs)
if path.IsAbs(appPath) == false { if !path.IsAbs(appPath) {
appPath = path.Join(curPath, appPath) appPath = path.Join(curPath, appPath)
} }
thePath, err := path.Abs(appPath) thePath, err := path.Abs(appPath)
if err != nil { if err != nil {
logger.Fatalf("Wrong application path: %s", thePath) beeLogger.Log.Fatalf("Wrong application path: %s", thePath)
} }
if stat, err := os.Stat(thePath); os.IsNotExist(err) || stat.IsDir() == false { if stat, err := os.Stat(thePath); os.IsNotExist(err) || !stat.IsDir() {
logger.Fatalf("Application path does not exist: %s", thePath) beeLogger.Log.Fatalf("Application path does not exist: %s", thePath)
} }
logger.Infof("Packaging application on '%s'...", thePath) beeLogger.Log.Infof("Packaging application on '%s'...", thePath)
appName := path.Base(thePath) appName := path.Base(thePath)
@ -488,12 +465,12 @@ func packApp(cmd *Command, args []string) int {
// Remove the tmpdir once bee pack is done // Remove the tmpdir once bee pack is done
err := os.RemoveAll(tmpdir) err := os.RemoveAll(tmpdir)
if err != nil { if err != nil {
logger.Error("Failed to remove the generated temp dir") beeLogger.Log.Error("Failed to remove the generated temp dir")
} }
}() }()
if build { if build {
logger.Info("Building application...") beeLogger.Log.Info("Building application...")
var envs []string var envs []string
for _, env := range buildEnvs { for _, env := range buildEnvs {
parts := strings.SplitN(env, "=", 2) parts := strings.SplitN(env, "=", 2)
@ -515,7 +492,7 @@ func packApp(cmd *Command, args []string) int {
os.Setenv("GOOS", goos) os.Setenv("GOOS", goos)
os.Setenv("GOARCH", goarch) os.Setenv("GOARCH", goarch)
logger.Infof("Using: GOOS=%s GOARCH=%s", goos, goarch) beeLogger.Log.Infof("Using: GOOS=%s GOARCH=%s", goos, goarch)
binPath := path.Join(tmpdir, appName) binPath := path.Join(tmpdir, appName)
if goos == "windows" { if goos == "windows" {
@ -538,10 +515,10 @@ func packApp(cmd *Command, args []string) int {
execmd.Dir = thePath execmd.Dir = thePath
err = execmd.Run() err = execmd.Run()
if err != nil { if err != nil {
logger.Fatal(err.Error()) beeLogger.Log.Fatal(err.Error())
} }
logger.Success("Build Successful!") beeLogger.Log.Success("Build Successful!")
} }
switch format { switch format {
@ -552,14 +529,14 @@ func packApp(cmd *Command, args []string) int {
outputN := appName + "." + format outputN := appName + "." + format
if outputP == "" || path.IsAbs(outputP) == false { if outputP == "" || !path.IsAbs(outputP) {
outputP = path.Join(curPath, outputP) outputP = path.Join(curPath, outputP)
} }
if _, err := os.Stat(outputP); err != nil { if _, err := os.Stat(outputP); err != nil {
err = os.MkdirAll(outputP, 0755) err = os.MkdirAll(outputP, 0755)
if err != nil { if err != nil {
logger.Fatal(err.Error()) beeLogger.Log.Fatal(err.Error())
} }
} }
@ -581,20 +558,20 @@ func packApp(cmd *Command, args []string) int {
for _, r := range excludeR { for _, r := range excludeR {
if len(r) > 0 { if len(r) > 0 {
if re, err := regexp.Compile(r); err != nil { if re, err := regexp.Compile(r); err != nil {
logger.Fatal(err.Error()) beeLogger.Log.Fatal(err.Error())
} else { } else {
exr = append(exr, re) exr = append(exr, re)
} }
} }
} }
logger.Infof("Writing to output: %s", outputP) beeLogger.Log.Infof("Writing to output: %s", outputP)
err = packDirectory(output, exp, exs, exr, tmpdir, thePath) err = packDirectory(output, exp, exs, exr, tmpdir, thePath)
if err != nil { if err != nil {
logger.Fatal(err.Error()) beeLogger.Log.Fatal(err.Error())
} }
logger.Success("Application packed!") beeLogger.Log.Success("Application packed!")
return 0 return 0
} }

88
cmd/commands/run/docs.go Normal file
View File

@ -0,0 +1,88 @@
package run
import (
"archive/zip"
"io"
"net/http"
"os"
"strings"
beeLogger "github.com/beego/bee/logger"
)
var (
swaggerVersion = "2"
swaggerlink = "https://github.com/beego/swagger/archive/v" + swaggerVersion + ".zip"
)
func downloadFromURL(url, fileName string) {
var down bool
if fd, err := os.Stat(fileName); err != nil && os.IsNotExist(err) {
down = true
} else if fd.Size() == int64(0) {
down = true
} else {
beeLogger.Log.Infof("'%s' already exists", fileName)
return
}
if down {
beeLogger.Log.Infof("Downloading '%s' to '%s'...", url, fileName)
output, err := os.Create(fileName)
if err != nil {
beeLogger.Log.Errorf("Error while creating '%s': %s", fileName, err)
return
}
defer output.Close()
response, err := http.Get(url)
if err != nil {
beeLogger.Log.Errorf("Error while downloading '%s': %s", url, err)
return
}
defer response.Body.Close()
n, err := io.Copy(output, response.Body)
if err != nil {
beeLogger.Log.Errorf("Error while downloading '%s': %s", url, err)
return
}
beeLogger.Log.Successf("%d bytes downloaded!", n)
}
}
func unzipAndDelete(src string) error {
beeLogger.Log.Infof("Unzipping '%s'...", src)
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer r.Close()
rp := strings.NewReplacer("swagger-"+swaggerVersion, "swagger")
for _, f := range r.File {
rc, err := f.Open()
if err != nil {
return err
}
defer rc.Close()
fname := rp.Replace(f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(fname, f.Mode())
} else {
f, err := os.OpenFile(
fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, rc)
if err != nil {
return err
}
}
}
beeLogger.Log.Successf("Done! Deleting '%s'...", src)
return os.RemoveAll(src)
}

View File

@ -11,13 +11,14 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package run
import ( import (
"bytes" "bytes"
"net/http" "net/http"
"time" "time"
beeLogger "github.com/beego/bee/logger"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
@ -70,7 +71,7 @@ func (c *wsClient) readPump() {
_, _, err := c.conn.ReadMessage() _, _, err := c.conn.ReadMessage()
if err != nil { if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
logger.Errorf("An error happened when reading from the Websocket client: %v", err) beeLogger.Log.Errorf("An error happened when reading from the Websocket client: %v", err)
} }
break break
} }
@ -109,7 +110,7 @@ func (c *wsClient) writePump() {
n := len(c.send) n := len(c.send)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
w.Write(newline) w.Write([]byte("/n"))
w.Write(<-c.send) w.Write(<-c.send)
} }
@ -155,13 +156,13 @@ func startReloadServer() {
}) })
go startServer() go startServer()
logger.Infof("Reload server listening at %s", reloadAddress) beeLogger.Log.Infof("Reload server listening at %s", reloadAddress)
} }
func startServer() { func startServer() {
err := http.ListenAndServe(reloadAddress, nil) err := http.ListenAndServe(reloadAddress, nil)
if err != nil { if err != nil {
logger.Errorf("Failed to start up the Reload server: %v", err) beeLogger.Log.Errorf("Failed to start up the Reload server: %v", err)
return return
} }
} }
@ -175,7 +176,7 @@ func sendReload(payload string) {
func handleWsRequest(broker *wsBroker, w http.ResponseWriter, r *http.Request) { func handleWsRequest(broker *wsBroker, w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil) conn, err := upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
logger.Errorf("error while upgrading server connection: %v", err) beeLogger.Log.Errorf("error while upgrading server connection: %v", err)
return return
} }

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package run
import ( import (
"io/ioutil" "io/ioutil"
@ -20,25 +20,31 @@ import (
path "path/filepath" path "path/filepath"
"runtime" "runtime"
"strings" "strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/config"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
) )
var cmdRun = &Command{ var CmdRun = &commands.Command{
UsageLine: "run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true] [-vendor=true] [-e=folderToExclude] [-ex=extraPackageToWatch] [-tags=goBuildTags] [-runmode=BEEGO_RUNMODE]", UsageLine: "run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true] [-vendor=true] [-e=folderToExclude] [-ex=extraPackageToWatch] [-tags=goBuildTags] [-runmode=BEEGO_RUNMODE]",
Short: "Run the application by starting a local development server", Short: "Run the application by starting a local development server",
Long: ` Long: `
Run command will supervise the filesystem of the application for any changes, and recompile/restart it. Run command will supervise the filesystem of the application for any changes, and recompile/restart it.
`, `,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: runApp, Run: RunApp,
} }
var ( var (
mainFiles ListOpts mainFiles utils.ListOpts
downdoc docValue downdoc utils.DocValue
gendoc docValue gendoc utils.DocValue
// The flags list of the paths excluded from watching // The flags list of the paths excluded from watching
excludedPaths strFlags excludedPaths utils.StrFlags
// Pass through to -tags arg of "go build" // Pass through to -tags arg of "go build"
buildTags string buildTags string
// Application path // Application path
@ -54,66 +60,63 @@ var (
// Current runmode // Current runmode
runmode string runmode string
// Extra directories // Extra directories
extraPackages strFlags extraPackages utils.StrFlags
) )
var started = make(chan bool)
func init() { func init() {
cmdRun.Flag.Var(&mainFiles, "main", "Specify main go files.") CmdRun.Flag.Var(&mainFiles, "main", "Specify main go files.")
cmdRun.Flag.Var(&gendoc, "gendoc", "Enable auto-generate the docs.") CmdRun.Flag.Var(&gendoc, "gendoc", "Enable auto-generate the docs.")
cmdRun.Flag.Var(&downdoc, "downdoc", "Enable auto-download of the swagger file if it does not exist.") CmdRun.Flag.Var(&downdoc, "downdoc", "Enable auto-download of the swagger file if it does not exist.")
cmdRun.Flag.Var(&excludedPaths, "e", "List of paths to exclude.") CmdRun.Flag.Var(&excludedPaths, "e", "List of paths to exclude.")
cmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.") CmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.")
cmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/") CmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/")
cmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.") CmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.")
cmdRun.Flag.Var(&extraPackages, "ex", "List of extra package to watch.") CmdRun.Flag.Var(&extraPackages, "ex", "List of extra package to watch.")
exit = make(chan bool) exit = make(chan bool)
commands.AvailableCommands = append(commands.AvailableCommands, CmdRun)
} }
func runApp(cmd *Command, args []string) int { func RunApp(cmd *commands.Command, args []string) int {
if len(args) == 0 || args[0] == "watchall" { if len(args) == 0 || args[0] == "watchall" {
currpath, _ = os.Getwd() currpath, _ = os.Getwd()
if found, _gopath, _ := SearchGOPATHs(currpath); found { if found, _gopath, _ := utils.SearchGOPATHs(currpath); found {
appname = path.Base(currpath) appname = path.Base(currpath)
currentGoPath = _gopath currentGoPath = _gopath
} else { } else {
logger.Fatalf("No application '%s' found in your GOPATH", currpath) beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", currpath)
} }
} else { } else {
// Check if passed Bee application path/name exists in the GOPATH(s) // Check if passed Bee application path/name exists in the GOPATH(s)
if found, _gopath, _path := SearchGOPATHs(args[0]); found { if found, _gopath, _path := utils.SearchGOPATHs(args[0]); found {
currpath = _path currpath = _path
currentGoPath = _gopath currentGoPath = _gopath
appname = path.Base(currpath) appname = path.Base(currpath)
} else { } else {
logger.Fatalf("No application '%s' found in your GOPATH", args[0]) beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", args[0])
} }
if strings.HasSuffix(appname, ".go") && isExist(currpath) { if strings.HasSuffix(appname, ".go") && utils.IsExist(currpath) {
logger.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname) beeLogger.Log.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] ") beeLogger.Log.Info("Do you want to overwrite it? [yes|no] ")
if !askForConfirmation() { if !utils.AskForConfirmation() {
return 0 return 0
} }
} }
} }
logger.Infof("Using '%s' as 'appname'", appname) beeLogger.Log.Infof("Using '%s' as 'appname'", appname)
logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath) beeLogger.Log.Debugf("Current path: %s", utils.FILE(), utils.LINE(), currpath)
if runmode == "prod" || runmode == "dev" { if runmode == "prod" || runmode == "dev" {
os.Setenv("BEEGO_RUNMODE", runmode) os.Setenv("BEEGO_RUNMODE", runmode)
logger.Infof("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) beeLogger.Log.Infof("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
} else if runmode != "" { } else if runmode != "" {
os.Setenv("BEEGO_RUNMODE", runmode) os.Setenv("BEEGO_RUNMODE", runmode)
logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) beeLogger.Log.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
} else if os.Getenv("BEEGO_RUNMODE") != "" { } else if os.Getenv("BEEGO_RUNMODE") != "" {
logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) beeLogger.Log.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
}
err := loadConfig()
if err != nil {
logger.Fatalf("Failed to load configuration: %s", err)
} }
var paths []string var paths []string
@ -121,17 +124,17 @@ func runApp(cmd *Command, args []string) int {
// Because monitor files has some issues, we watch current directory // Because monitor files has some issues, we watch current directory
// and ignore non-go files. // and ignore non-go files.
for _, p := range conf.DirStruct.Others { for _, p := range config.Conf.DirStruct.Others {
paths = append(paths, strings.Replace(p, "$GOPATH", currentGoPath, -1)) paths = append(paths, strings.Replace(p, "$GOPATH", currentGoPath, -1))
} }
if len(extraPackages) > 0 { if len(extraPackages) > 0 {
// get the full path // get the full path
for _, packagePath := range extraPackages { for _, packagePath := range extraPackages {
if found, _, _fullPath := SearchGOPATHs(packagePath); found { if found, _, _fullPath := utils.SearchGOPATHs(packagePath); found {
readAppDirectories(_fullPath, &paths) readAppDirectories(_fullPath, &paths)
} else { } else {
logger.Warnf("No extra package '%s' found in your GOPATH", packagePath) beeLogger.Log.Warnf("No extra package '%s' found in your GOPATH", packagePath)
} }
} }
// let paths unique // let paths unique
@ -163,7 +166,7 @@ func runApp(cmd *Command, args []string) int {
} }
// Start the Reload server (if enabled) // Start the Reload server (if enabled)
if conf.EnableReload { if config.Conf.EnableReload {
startReloadServer() startReloadServer()
} }
if gendoc == "true" { if gendoc == "true" {
@ -205,16 +208,16 @@ func readAppDirectories(directory string, paths *[]string) {
continue continue
} }
if fileInfo.IsDir() == true && fileInfo.Name()[0] != '.' { if fileInfo.IsDir() && fileInfo.Name()[0] != '.' {
readAppDirectories(directory+"/"+fileInfo.Name(), paths) readAppDirectories(directory+"/"+fileInfo.Name(), paths)
continue continue
} }
if useDirectory == true { if useDirectory {
continue continue
} }
if path.Ext(fileInfo.Name()) == ".go" || (ifStaticFile(fileInfo.Name()) && conf.EnableReload) { if path.Ext(fileInfo.Name()) == ".go" || (ifStaticFile(fileInfo.Name()) && config.Conf.EnableReload) {
*paths = append(*paths, directory) *paths = append(*paths, directory)
useDirectory = true useDirectory = true
} }
@ -227,16 +230,16 @@ func isExcluded(filePath string) bool {
for _, p := range excludedPaths { for _, p := range excludedPaths {
absP, err := path.Abs(p) absP, err := path.Abs(p)
if err != nil { if err != nil {
logger.Errorf("Cannot get absolute path of '%s'", p) beeLogger.Log.Errorf("Cannot get absolute path of '%s'", p)
continue continue
} }
absFilePath, err := path.Abs(filePath) absFilePath, err := path.Abs(filePath)
if err != nil { if err != nil {
logger.Errorf("Cannot get absolute path of '%s'", filePath) beeLogger.Log.Errorf("Cannot get absolute path of '%s'", filePath)
break break
} }
if strings.HasPrefix(absFilePath, absP) { if strings.HasPrefix(absFilePath, absP) {
logger.Infof("'%s' is not being watched", filePath) beeLogger.Log.Infof("'%s' is not being watched", filePath)
return true return true
} }
} }

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package run
import ( import (
"bytes" "bytes"
@ -24,6 +24,10 @@ import (
"sync" "sync"
"time" "time"
"github.com/beego/bee/config"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
) )
@ -46,7 +50,7 @@ var (
func NewWatcher(paths []string, files []string, isgenerate bool) { func NewWatcher(paths []string, files []string, isgenerate bool) {
watcher, err := fsnotify.NewWatcher() watcher, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
logger.Fatalf("Failed to create watcher: %s", err) beeLogger.Log.Fatalf("Failed to create watcher: %s", err)
} }
go func() { go func() {
@ -55,7 +59,7 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
case e := <-watcher.Events: case e := <-watcher.Events:
isBuild := true isBuild := true
if ifStaticFile(e.Name) && conf.EnableReload { if ifStaticFile(e.Name) && config.Conf.EnableReload {
sendReload(e.String()) sendReload(e.String())
continue continue
} }
@ -69,14 +73,14 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
mt := getFileModTime(e.Name) mt := getFileModTime(e.Name)
if t := eventTime[e.Name]; mt == t { if t := eventTime[e.Name]; mt == t {
logger.Infof(bold("Skipping: ")+"%s", e.String()) beeLogger.Log.Infof(colors.Bold("Skipping: ")+"%s", e.String())
isBuild = false isBuild = false
} }
eventTime[e.Name] = mt eventTime[e.Name] = mt
if isBuild { if isBuild {
logger.Infof("Event fired: %s", e) beeLogger.Log.Infof("Event fired: %s", e)
go func() { go func() {
// Wait 1s before autobuild until there is no file change. // Wait 1s before autobuild until there is no file change.
scheduleTime = time.Now().Add(1 * time.Second) scheduleTime = time.Now().Add(1 * time.Second)
@ -85,17 +89,17 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
}() }()
} }
case err := <-watcher.Errors: case err := <-watcher.Errors:
logger.Warnf("Watcher error: %s", err.Error()) // No need to exit here beeLogger.Log.Warnf("Watcher error: %s", err.Error()) // No need to exit here
} }
} }
}() }()
logger.Info("Initializing watcher...") beeLogger.Log.Info("Initializing watcher...")
for _, path := range paths { for _, path := range paths {
logger.Infof(bold("Watching: ")+"%s", path) beeLogger.Log.Infof(colors.Bold("Watching: ")+"%s", path)
err = watcher.Add(path) err = watcher.Add(path)
if err != nil { if err != nil {
logger.Fatalf("Failed to watch directory: %s", err) beeLogger.Log.Fatalf("Failed to watch directory: %s", err)
} }
} }
} }
@ -105,14 +109,14 @@ func getFileModTime(path string) int64 {
path = strings.Replace(path, "\\", "/", -1) path = strings.Replace(path, "\\", "/", -1)
f, err := os.Open(path) f, err := os.Open(path)
if err != nil { if err != nil {
logger.Errorf("Failed to open file on '%s': %s", path, err) beeLogger.Log.Errorf("Failed to open file on '%s': %s", path, err)
return time.Now().Unix() return time.Now().Unix()
} }
defer f.Close() defer f.Close()
fi, err := f.Stat() fi, err := f.Stat()
if err != nil { if err != nil {
logger.Errorf("Failed to get file stats: %s", err) beeLogger.Log.Errorf("Failed to get file stats: %s", err)
return time.Now().Unix() return time.Now().Unix()
} }
@ -127,7 +131,7 @@ func AutoBuild(files []string, isgenerate bool) {
os.Chdir(currpath) os.Chdir(currpath)
cmdName := "go" cmdName := "go"
if conf.Gopm.Enable { if config.Conf.Gopm.Enable {
cmdName = "gopm" cmdName = "gopm"
} }
@ -138,14 +142,14 @@ func AutoBuild(files []string, isgenerate bool) {
) )
// For applications use full import path like "github.com/.../.." // For applications use full import path like "github.com/.../.."
// are able to use "go install" to reduce build time. // are able to use "go install" to reduce build time.
if conf.GoInstall { if config.Conf.GoInstall {
icmd := exec.Command(cmdName, "install", "-v") icmd := exec.Command(cmdName, "install", "-v")
icmd.Stdout = os.Stdout icmd.Stdout = os.Stdout
icmd.Stderr = os.Stderr icmd.Stderr = os.Stderr
icmd.Env = append(os.Environ(), "GOGC=off") icmd.Env = append(os.Environ(), "GOGC=off")
icmd.Run() icmd.Run()
} }
if conf.Gopm.Install { if config.Conf.Gopm.Install {
icmd := exec.Command("go", "list", "./...") icmd := exec.Command("go", "list", "./...")
icmd.Stdout = &stdout icmd.Stdout = &stdout
icmd.Env = append(os.Environ(), "GOGC=off") icmd.Env = append(os.Environ(), "GOGC=off")
@ -169,15 +173,15 @@ func AutoBuild(files []string, isgenerate bool) {
} }
if isgenerate { if isgenerate {
logger.Info("Generating the docs...") beeLogger.Log.Info("Generating the docs...")
icmd := exec.Command("bee", "generate", "docs") icmd := exec.Command("bee", "generate", "docs")
icmd.Env = append(os.Environ(), "GOGC=off") icmd.Env = append(os.Environ(), "GOGC=off")
err = icmd.Run() err = icmd.Run()
if err != nil { if err != nil {
logger.Errorf("Failed to generate the docs.") beeLogger.Log.Errorf("Failed to generate the docs.")
return return
} }
logger.Success("Docs generated!") beeLogger.Log.Success("Docs generated!")
} }
if err == nil { if err == nil {
@ -198,12 +202,12 @@ func AutoBuild(files []string, isgenerate bool) {
bcmd.Stderr = &stderr bcmd.Stderr = &stderr
err = bcmd.Run() err = bcmd.Run()
if err != nil { if err != nil {
logger.Errorf("Failed to build the application: %s", stderr.String()) beeLogger.Log.Errorf("Failed to build the application: %s", stderr.String())
return return
} }
} }
logger.Success("Built Successfully!") beeLogger.Log.Success("Built Successfully!")
Restart(appname) Restart(appname)
} }
@ -211,39 +215,39 @@ func AutoBuild(files []string, isgenerate bool) {
func Kill() { func Kill() {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
logger.Infof("Kill recover: %s", e) beeLogger.Log.Infof("Kill recover: %s", e)
} }
}() }()
if cmd != nil && cmd.Process != nil { if cmd != nil && cmd.Process != nil {
err := cmd.Process.Kill() err := cmd.Process.Kill()
if err != nil { if err != nil {
logger.Errorf("Error while killing cmd process: %s", err) beeLogger.Log.Errorf("Error while killing cmd process: %s", err)
} }
} }
} }
// Restart kills the running command process and starts it again // Restart kills the running command process and starts it again
func Restart(appname string) { func Restart(appname string) {
logger.Debugf("Kill running process", __FILE__(), __LINE__()) beeLogger.Log.Debugf("Kill running process", utils.FILE(), utils.LINE())
Kill() Kill()
go Start(appname) go Start(appname)
} }
// Start starts the command process // Start starts the command process
func Start(appname string) { func Start(appname string) {
logger.Infof("Restarting '%s'...", appname) beeLogger.Log.Infof("Restarting '%s'...", appname)
if strings.Index(appname, "./") == -1 { if !strings.Contains(appname, "./") {
appname = "./" + appname appname = "./" + appname
} }
cmd = exec.Command(appname) cmd = exec.Command(appname)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Args = append([]string{appname}, conf.CmdArgs...) cmd.Args = append([]string{appname}, config.Conf.CmdArgs...)
cmd.Env = append(os.Environ(), conf.Envs...) cmd.Env = append(os.Environ(), config.Conf.Envs...)
go cmd.Run() go cmd.Run()
logger.Successf("'%s' is running...", appname) beeLogger.Log.Successf("'%s' is running...", appname)
started <- true started <- true
} }
@ -262,7 +266,7 @@ func shouldIgnoreFile(filename string) bool {
for _, regex := range ignoredFilesRegExps { for _, regex := range ignoredFilesRegExps {
r, err := regexp.Compile(regex) r, err := regexp.Compile(regex)
if err != nil { if err != nil {
logger.Fatalf("Could not compile regular expression: %s", err) beeLogger.Log.Fatalf("Could not compile regular expression: %s", err)
} }
if r.MatchString(filename) { if r.MatchString(filename) {
return true return true

View File

@ -1,4 +1,4 @@
package main package version
import ( import (
"io" "io"
@ -6,6 +6,10 @@ import (
"os" "os"
"runtime" "runtime"
"text/template" "text/template"
"time"
beeLogger "github.com/beego/bee/logger"
) )
// RuntimeInfo holds information about the current runtime. // RuntimeInfo holds information about the current runtime.
@ -26,12 +30,12 @@ type RuntimeInfo struct {
// print the banner in case of error. // print the banner in case of error.
func InitBanner(out io.Writer, in io.Reader) { func InitBanner(out io.Writer, in io.Reader) {
if in == nil { if in == nil {
logger.Fatal("The input is nil") beeLogger.Log.Fatal("The input is nil")
} }
banner, err := ioutil.ReadAll(in) banner, err := ioutil.ReadAll(in)
if err != nil { if err != nil {
logger.Fatalf("Error while trying to read the banner: %s", err) beeLogger.Log.Fatalf("Error while trying to read the banner: %s", err)
} }
show(out, string(banner)) show(out, string(banner))
@ -43,11 +47,11 @@ func show(out io.Writer, content string) {
Parse(content) Parse(content)
if err != nil { if err != nil {
logger.Fatalf("Cannot parse the banner template: %s", err) beeLogger.Log.Fatalf("Cannot parse the banner template: %s", err)
} }
err = t.Execute(out, RuntimeInfo{ err = t.Execute(out, RuntimeInfo{
getGoVersion(), GetGoVersion(),
runtime.GOOS, runtime.GOOS,
runtime.GOARCH, runtime.GOARCH,
runtime.NumCPU(), runtime.NumCPU(),
@ -55,7 +59,14 @@ func show(out io.Writer, content string) {
runtime.GOROOT(), runtime.GOROOT(),
runtime.Compiler, runtime.Compiler,
version, version,
getBeegoVersion(), GetBeegoVersion(),
}) })
MustCheck(err) if err != nil {
beeLogger.Log.Error(err.Error())
}
}
// Now returns the current local time in the specified layout
func Now(layout string) string {
return time.Now().Format(layout)
} }

View File

@ -1,4 +1,4 @@
package main package version
import ( import (
"bufio" "bufio"
@ -14,18 +14,12 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/beego/bee/cmd/commands"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
var cmdVersion = &Command{
UsageLine: "version",
Short: "Prints the current Bee version",
Long: `
Prints the current Bee, Beego and Go version alongside the platform information.
`,
Run: versionCmd,
}
const verboseVersionBanner string = `%s%s______ const verboseVersionBanner string = `%s%s______
| ___ \ | ___ \
| |_/ / ___ ___ | |_/ / ___ ___
@ -52,21 +46,33 @@ const shortVersionBanner = `______
\____/ \___| \___| v{{ .BeeVersion }} \____/ \___| \___| v{{ .BeeVersion }}
` `
var CmdVersion = &commands.Command{
UsageLine: "version",
Short: "Prints the current Bee version",
Long: `
Prints the current Bee, Beego and Go version alongside the platform information.
`,
Run: versionCmd,
}
var outputFormat string var outputFormat string
const version = "1.8.0"
func init() { func init() {
fs := flag.NewFlagSet("version", flag.ContinueOnError) fs := flag.NewFlagSet("version", flag.ContinueOnError)
fs.StringVar(&outputFormat, "o", "", "Set the output format. Either json or yaml.") fs.StringVar(&outputFormat, "o", "", "Set the output format. Either json or yaml.")
cmdVersion.Flag = *fs CmdVersion.Flag = *fs
commands.AvailableCommands = append(commands.AvailableCommands, CmdVersion)
} }
func versionCmd(cmd *Command, args []string) int { func versionCmd(cmd *commands.Command, args []string) int {
cmd.Flag.Parse(args) cmd.Flag.Parse(args)
stdout := cmd.Out() stdout := cmd.Out()
if outputFormat != "" { if outputFormat != "" {
runtimeInfo := RuntimeInfo{ runtimeInfo := RuntimeInfo{
getGoVersion(), GetGoVersion(),
runtime.GOOS, runtime.GOOS,
runtime.GOARCH, runtime.GOARCH,
runtime.NumCPU(), runtime.NumCPU(),
@ -74,20 +80,24 @@ func versionCmd(cmd *Command, args []string) int {
runtime.GOROOT(), runtime.GOROOT(),
runtime.Compiler, runtime.Compiler,
version, version,
getBeegoVersion(), GetBeegoVersion(),
} }
switch outputFormat { switch outputFormat {
case "json": case "json":
{ {
b, err := json.MarshalIndent(runtimeInfo, "", " ") b, err := json.MarshalIndent(runtimeInfo, "", " ")
MustCheck(err) if err != nil {
beeLogger.Log.Error(err.Error())
}
fmt.Println(string(b)) fmt.Println(string(b))
return 0 return 0
} }
case "yaml": case "yaml":
{ {
b, err := yaml.Marshal(&runtimeInfo) b, err := yaml.Marshal(&runtimeInfo)
MustCheck(err) if err != nil {
beeLogger.Log.Error(err.Error())
}
fmt.Println(string(b)) fmt.Println(string(b))
return 0 return 0
} }
@ -102,11 +112,11 @@ func versionCmd(cmd *Command, args []string) int {
// ShowShortVersionBanner prints the short version banner. // ShowShortVersionBanner prints the short version banner.
func ShowShortVersionBanner() { func ShowShortVersionBanner() {
output := NewColorWriter(os.Stdout) output := colors.NewColorWriter(os.Stdout)
InitBanner(output, bytes.NewBufferString(MagentaBold(shortVersionBanner))) InitBanner(output, bytes.NewBufferString(colors.MagentaBold(shortVersionBanner)))
} }
func getBeegoVersion() string { func GetBeegoVersion() string {
gopath := os.Getenv("GOPATH") gopath := os.Getenv("GOPATH")
re, err := regexp.Compile(`VERSION = "([0-9.]+)"`) re, err := regexp.Compile(`VERSION = "([0-9.]+)"`)
if err != nil { if err != nil {
@ -125,11 +135,11 @@ func getBeegoVersion() string {
if os.IsNotExist(err) { if os.IsNotExist(err) {
continue continue
} }
logger.Error("Error while getting stats of 'beego.go'") beeLogger.Log.Error("Error while getting stats of 'beego.go'")
} }
fd, err := os.Open(filename) fd, err := os.Open(filename)
if err != nil { if err != nil {
logger.Error("Error while reading 'beego.go'") beeLogger.Log.Error("Error while reading 'beego.go'")
continue continue
} }
reader := bufio.NewReader(fd) reader := bufio.NewReader(fd)
@ -152,14 +162,14 @@ func getBeegoVersion() string {
return "Beego is not installed. Please do consider installing it first: https://github.com/astaxie/beego" return "Beego is not installed. Please do consider installing it first: https://github.com/astaxie/beego"
} }
func getGoVersion() string { func GetGoVersion() string {
var ( var (
cmdOut []byte cmdOut []byte
err error err error
) )
if cmdOut, err = exec.Command("go", "version").Output(); err != nil { if cmdOut, err = exec.Command("go", "version").Output(); err != nil {
logger.Fatalf("There was an error running 'go version' command: %s", err) beeLogger.Log.Fatalf("There was an error running 'go version' command: %s", err)
} }
return strings.Split(string(cmdOut), " ")[2] return strings.Split(string(cmdOut), " ")[2]
} }

261
code.go
View File

@ -1,261 +0,0 @@
// Copyright 2011 Gary Burd
// Copyright 2013 Unknown
//
// 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 (
"go/ast"
"go/printer"
"go/scanner"
"go/token"
"math"
"strconv"
)
const (
notPredeclared = iota
predeclaredType
predeclaredConstant
predeclaredFunction
)
// predeclared represents the set of all predeclared identifiers.
var predeclared = map[string]int{
"bool": predeclaredType,
"byte": predeclaredType,
"complex128": predeclaredType,
"complex64": predeclaredType,
"error": predeclaredType,
"float32": predeclaredType,
"float64": predeclaredType,
"int16": predeclaredType,
"int32": predeclaredType,
"int64": predeclaredType,
"int8": predeclaredType,
"int": predeclaredType,
"rune": predeclaredType,
"string": predeclaredType,
"uint16": predeclaredType,
"uint32": predeclaredType,
"uint64": predeclaredType,
"uint8": predeclaredType,
"uint": predeclaredType,
"uintptr": predeclaredType,
"true": predeclaredConstant,
"false": predeclaredConstant,
"iota": predeclaredConstant,
"nil": predeclaredConstant,
"append": predeclaredFunction,
"cap": predeclaredFunction,
"close": predeclaredFunction,
"complex": predeclaredFunction,
"copy": predeclaredFunction,
"delete": predeclaredFunction,
"imag": predeclaredFunction,
"len": predeclaredFunction,
"make": predeclaredFunction,
"new": predeclaredFunction,
"panic": predeclaredFunction,
"print": predeclaredFunction,
"println": predeclaredFunction,
"real": predeclaredFunction,
"recover": predeclaredFunction,
}
const (
ExportLinkAnnotation AnnotationKind = iota
AnchorAnnotation
CommentAnnotation
PackageLinkAnnotation
BuiltinAnnotation
)
// annotationVisitor collects annotations.
type annotationVisitor struct {
annotations []Annotation
}
func (v *annotationVisitor) add(kind AnnotationKind, importPath string) {
v.annotations = append(v.annotations, Annotation{Kind: kind, ImportPath: importPath})
}
func (v *annotationVisitor) ignoreName() {
v.add(-1, "")
}
func (v *annotationVisitor) Visit(n ast.Node) ast.Visitor {
switch n := n.(type) {
case *ast.TypeSpec:
v.ignoreName()
ast.Walk(v, n.Type)
case *ast.FuncDecl:
if n.Recv != nil {
ast.Walk(v, n.Recv)
}
v.ignoreName()
ast.Walk(v, n.Type)
case *ast.Field:
for range n.Names {
v.ignoreName()
}
ast.Walk(v, n.Type)
case *ast.ValueSpec:
for range n.Names {
v.add(AnchorAnnotation, "")
}
if n.Type != nil {
ast.Walk(v, n.Type)
}
for _, x := range n.Values {
ast.Walk(v, x)
}
case *ast.Ident:
switch {
case n.Obj == nil && predeclared[n.Name] != notPredeclared:
v.add(BuiltinAnnotation, "")
case n.Obj != nil && ast.IsExported(n.Name):
v.add(ExportLinkAnnotation, "")
default:
v.ignoreName()
}
case *ast.SelectorExpr:
if x, _ := n.X.(*ast.Ident); x != nil {
if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
if path, err := strconv.Unquote(spec.Path.Value); err == nil {
v.add(PackageLinkAnnotation, path)
if path == "C" {
v.ignoreName()
} else {
v.add(ExportLinkAnnotation, path)
}
return nil
}
}
}
}
ast.Walk(v, n.X)
v.ignoreName()
default:
return v
}
return nil
}
func printDecl(decl ast.Node, fset *token.FileSet, buf []byte) (Code, []byte) {
v := &annotationVisitor{}
ast.Walk(v, decl)
buf = buf[:0]
err := (&printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}).Fprint(sliceWriter{&buf}, fset, decl)
if err != nil {
return Code{Text: err.Error()}, buf
}
var annotations []Annotation
var s scanner.Scanner
fset = token.NewFileSet()
file := fset.AddFile("", fset.Base(), len(buf))
s.Init(file, buf, nil, scanner.ScanComments)
loop:
for {
pos, tok, lit := s.Scan()
switch tok {
case token.EOF:
break loop
case token.COMMENT:
p := file.Offset(pos)
e := p + len(lit)
if p > math.MaxInt16 || e > math.MaxInt16 {
break loop
}
annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int16(p), End: int16(e)})
case token.IDENT:
if len(v.annotations) == 0 {
// Oops!
break loop
}
annotation := v.annotations[0]
v.annotations = v.annotations[1:]
if annotation.Kind == -1 {
continue
}
p := file.Offset(pos)
e := p + len(lit)
if p > math.MaxInt16 || e > math.MaxInt16 {
break loop
}
annotation.Pos = int16(p)
annotation.End = int16(e)
if len(annotations) > 0 && annotation.Kind == ExportLinkAnnotation {
prev := annotations[len(annotations)-1]
if prev.Kind == PackageLinkAnnotation &&
prev.ImportPath == annotation.ImportPath &&
prev.End+1 == annotation.Pos {
// merge with previous
annotation.Pos = prev.Pos
annotations[len(annotations)-1] = annotation
continue loop
}
}
annotations = append(annotations, annotation)
}
}
return Code{Text: string(buf), Annotations: annotations}, buf
}
type AnnotationKind int16
type Annotation struct {
Pos, End int16
Kind AnnotationKind
ImportPath string
}
type Code struct {
Text string
Annotations []Annotation
}
func commentAnnotations(src string) []Annotation {
var annotations []Annotation
var s scanner.Scanner
fset := token.NewFileSet()
file := fset.AddFile("", fset.Base(), len(src))
s.Init(file, []byte(src), nil, scanner.ScanComments)
for {
pos, tok, lit := s.Scan()
switch tok {
case token.EOF:
return annotations
case token.COMMENT:
p := file.Offset(pos)
e := p + len(lit)
if p > math.MaxInt16 || e > math.MaxInt16 {
return annotations
}
annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int16(p), End: int16(e)})
}
}
}
type sliceWriter struct{ p *[]byte }
func (w sliceWriter) Write(p []byte) (int, error) {
*w.p = append(*w.p, p...)
return len(p), nil
}

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package config
import ( import (
"encoding/json" "encoding/json"
@ -22,6 +22,7 @@ import (
"io" "io"
"path/filepath" "path/filepath"
beeLogger "github.com/beego/bee/logger"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -49,7 +50,7 @@ var defaultConf = `{
"enable_reload": false "enable_reload": false
} }
` `
var conf struct { var Conf struct {
Version int Version int
// gopm support // gopm support
Gopm struct { Gopm struct {
@ -79,6 +80,10 @@ var conf struct {
EnableReload bool `json:"enable_reload" yaml:"enable_reload"` EnableReload bool `json:"enable_reload" yaml:"enable_reload"`
} }
func init() {
loadConfig()
}
// loadConfig loads customized configuration. // loadConfig loads customized configuration.
func loadConfig() (err error) { func loadConfig() (err error) {
err = filepath.Walk(".", func(path string, fileInfo os.FileInfo, err error) error { err = filepath.Walk(".", func(path string, fileInfo os.FileInfo, err error) error {
@ -91,20 +96,20 @@ func loadConfig() (err error) {
} }
if fileInfo.Name() == "bee.json" { if fileInfo.Name() == "bee.json" {
logger.Info("Loading configuration from 'bee.json'...") beeLogger.Log.Info("Loading configuration from 'bee.json'...")
err = parseJSON(path, &conf) err = parseJSON(path, &Conf)
if err != nil { if err != nil {
logger.Errorf("Failed to parse JSON file: %s", err) beeLogger.Log.Errorf("Failed to parse JSON file: %s", err)
return err return err
} }
return io.EOF return io.EOF
} }
if fileInfo.Name() == "Beefile" { if fileInfo.Name() == "Beefile" {
logger.Info("Loading configuration from 'Beefile'...") beeLogger.Log.Info("Loading configuration from 'Beefile'...")
err = parseYAML(path, &conf) err = parseYAML(path, &Conf)
if err != nil { if err != nil {
logger.Errorf("Failed to parse YAML file: %s", err) beeLogger.Log.Errorf("Failed to parse YAML file: %s", err)
return err return err
} }
return io.EOF return io.EOF
@ -115,8 +120,8 @@ func loadConfig() (err error) {
// In case no configuration file found or an error different than io.EOF, // In case no configuration file found or an error different than io.EOF,
// fallback to default configuration // fallback to default configuration
if err != io.EOF { if err != io.EOF {
logger.Info("Loading default configuration...") beeLogger.Log.Info("Loading default configuration...")
err = json.Unmarshal([]byte(defaultConf), &conf) err = json.Unmarshal([]byte(defaultConf), &Conf)
if err != nil { if err != nil {
return return
} }
@ -126,21 +131,21 @@ func loadConfig() (err error) {
err = nil err = nil
// Check format version // Check format version
if conf.Version != confVer { if Conf.Version != confVer {
logger.Warn("Your configuration file is outdated. Please do consider updating it.") beeLogger.Log.Warn("Your configuration file is outdated. Please do consider updating it.")
logger.Hint("Check the latest version of bee's configuration file.") beeLogger.Log.Hint("Check the latest version of bee's configuration file.")
} }
// Set variables // Set variables
if len(conf.DirStruct.Controllers) == 0 { if len(Conf.DirStruct.Controllers) == 0 {
conf.DirStruct.Controllers = "controllers" Conf.DirStruct.Controllers = "controllers"
} }
if len(conf.DirStruct.Models) == 0 { if len(Conf.DirStruct.Models) == 0 {
conf.DirStruct.Models = "models" Conf.DirStruct.Models = "models"
} }
// Append watch exts // Append watch exts
watchExts = append(watchExts, conf.WatchExt...) //watchExts = append(watchExts, Conf.WatchExt...)
return return
} }
@ -154,11 +159,8 @@ func parseJSON(path string, v interface{}) error {
return err return err
} }
err = json.Unmarshal(data, v) err = json.Unmarshal(data, v)
if err != nil {
return err return err
} }
return nil
}
func parseYAML(path string, v interface{}) error { func parseYAML(path string, v interface{}) error {
var ( var (
@ -170,8 +172,5 @@ func parseYAML(path string, v interface{}) error {
return err return err
} }
err = yaml.Unmarshal(data, v) err = yaml.Unmarshal(data, v)
if err != nil {
return err return err
} }
return nil
}

200
g.go
View File

@ -1,200 +0,0 @@
// 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 (
"os"
"strings"
)
var cmdGenerate = &Command{
UsageLine: "generate [command]",
Short: "Source code generator",
Long: ` {{"To scaffold out your entire application:"|bold}}
$ bee generate scaffold [scaffoldname] [-fields="title:string,body:text"] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
{{"To generate a Model based on fields:"|bold}}
$ bee generate model [modelname] [-fields="name:type"]
{{"To generate a controller:"|bold}}
$ bee generate controller [controllerfile]
{{"To generate a CRUD view:"|bold}}
$ bee generate view [viewpath]
{{"To generate a migration file for making database schema updates:"|bold}}
$ bee generate migration [migrationfile] [-fields="name:type"]
{{"To generate swagger doc file:"|bold}}
$ bee generate docs
{{"To generate a test case:"|bold}}
$ bee generate test [routerfile]
{{"To generate appcode based on an existing database:"|bold}}
$ bee generate appcode [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-level=3]
`,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() },
Run: generateCode,
}
var driver docValue
var conn docValue
var level docValue
var tables docValue
var fields docValue
func init() {
cmdGenerate.Flag.Var(&tables, "tables", "List of table names separated by a comma.")
cmdGenerate.Flag.Var(&driver, "driver", "Database driver. Either mysql, postgres or sqlite.")
cmdGenerate.Flag.Var(&conn, "conn", "Connection string used by the driver to connect to a database instance.")
cmdGenerate.Flag.Var(&level, "level", "Either 1, 2 or 3. i.e. 1=models; 2=models and controllers; 3=models, controllers and routers.")
cmdGenerate.Flag.Var(&fields, "fields", "List of table fields.")
}
func generateCode(cmd *Command, args []string) int {
currpath, _ := os.Getwd()
if len(args) < 1 {
logger.Fatal("Command is missing")
}
gps := GetGOPATHs()
if len(gps) == 0 {
logger.Fatal("GOPATH environment variable is not set or empty")
}
gopath := gps[0]
logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath)
gcmd := args[0]
switch gcmd {
case "scaffold":
if len(args) < 2 {
logger.Fatal("Wrong number of arguments. Run: bee help generate")
}
// Load the configuration
err := loadConfig()
if err != nil {
logger.Fatalf("Failed to load configuration: %s", err)
}
cmd.Flag.Parse(args[2:])
if driver == "" {
driver = docValue(conf.Database.Driver)
if driver == "" {
driver = "mysql"
}
}
if conn == "" {
conn = docValue(conf.Database.Conn)
if conn == "" {
conn = "root:@tcp(127.0.0.1:3306)/test"
}
}
if fields == "" {
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 the configuration
err := loadConfig()
if err != nil {
logger.Fatalf("Failed to load configuration: %s", err)
}
cmd.Flag.Parse(args[1:])
if driver == "" {
driver = docValue(conf.Database.Driver)
if driver == "" {
driver = "mysql"
}
}
if conn == "" {
conn = docValue(conf.Database.Conn)
if conn == "" {
if driver == "mysql" {
conn = "root:@tcp(127.0.0.1:3306)/test"
} else if driver == "postgres" {
conn = "postgres://postgres:postgres@127.0.0.1:5432/postgres"
}
}
}
if level == "" {
level = "3"
}
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 {
logger.Fatal("Wrong number of arguments. Run: bee help generate")
}
cmd.Flag.Parse(args[2:])
mname := args[1]
logger.Infof("Using '%s' as migration name", mname)
upsql := ""
downsql := ""
if fields != "" {
dbMigrator := newDBDriver()
upsql = dbMigrator.generateCreateUp(mname)
downsql = dbMigrator.generateCreateDown(mname)
}
generateMigration(mname, upsql, downsql, currpath)
case "controller":
if len(args) == 2 {
cname := args[1]
generateController(cname, currpath)
} else {
logger.Fatal("Wrong number of arguments. Run: bee help generate")
}
case "model":
if len(args) < 2 {
logger.Fatal("Wrong number of arguments. Run: bee help generate")
}
cmd.Flag.Parse(args[2:])
if fields == "" {
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)
case "view":
if len(args) == 2 {
cname := args[1]
generateView(cname, currpath)
} else {
logger.Fatal("Wrong number of arguments. Run: bee help generate")
}
default:
logger.Fatal("Command is missing")
}
logger.Successf("%s successfully generated!", strings.Title(gcmd))
return 0
}

View File

@ -1,48 +0,0 @@
package main
import "strings"
func generateScaffold(sname, fields, currpath, driver, conn string) {
logger.Infof("Do you want to create a '%s' model? [Yes|No] ", sname)
// Generate the model
if askForConfirmation() {
generateModel(sname, fields, currpath)
}
// Generate the controller
logger.Infof("Do you want to create a '%s' controller? [Yes|No] ", sname)
if askForConfirmation() {
generateController(sname, currpath)
}
// Generate the views
logger.Infof("Do you want to create views for this '%s' resource? [Yes|No] ", sname)
if askForConfirmation() {
generateView(sname, currpath)
}
// Generate a migration
logger.Infof("Do you want to create a '%s' migration and schema for this resource? [Yes|No] ", sname)
if askForConfirmation() {
upsql := ""
downsql := ""
if fields != "" {
dbMigrator := newDBDriver()
upsql = dbMigrator.generateCreateUp(sname)
downsql = dbMigrator.generateCreateDown(sname)
//todo remove
//if driver == "" {
// downsql = strings.Replace(downsql, "`", "", -1)
//}
}
generateMigration(sname, upsql, downsql, currpath)
}
// Run the migration
logger.Infof("Do you want to migrate the database? [Yes|No] ")
if askForConfirmation() {
migrateUpdate(currpath, driver, conn)
}
logger.Successf("All done! Don't forget to add beego.Router(\"/%s\" ,&controllers.%sController{}) to routers/route.go\n", sname, strings.Title(sname))
}

23
generate/g.go Normal file
View File

@ -0,0 +1,23 @@
// 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 generate
import "github.com/beego/bee/utils"
var SQLDriver utils.DocValue
var SQLConn utils.DocValue
var Level utils.DocValue
var Tables utils.DocValue
var Fields utils.DocValue

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package generate
import ( import (
"database/sql" "database/sql"
@ -23,6 +23,9 @@ import (
"regexp" "regexp"
"strings" "strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq" _ "github.com/lib/pq"
) )
@ -183,7 +186,7 @@ type OrmTag struct {
// String returns the source code string for the Table struct // String returns the source code string for the Table struct
func (tb *Table) String() string { func (tb *Table) String() string {
rv := fmt.Sprintf("type %s struct {\n", camelCase(tb.Name)) rv := fmt.Sprintf("type %s struct {\n", utils.CamelCase(tb.Name))
for _, v := range tb.Columns { for _, v := range tb.Columns {
rv += v.String() + "\n" rv += v.String() + "\n"
} }
@ -255,7 +258,7 @@ func (tag *OrmTag) String() string {
return fmt.Sprintf("`orm:\"%s\"`", strings.Join(ormOptions, ";")) return fmt.Sprintf("`orm:\"%s\"`", strings.Join(ormOptions, ";"))
} }
func generateAppcode(driver, connStr, level, tables, currpath string) { func GenerateAppcode(driver, connStr, level, tables, currpath string) {
var mode byte var mode byte
switch level { switch level {
case "1": case "1":
@ -265,7 +268,7 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
case "3": case "3":
mode = OModel | OController | ORouter mode = OModel | OController | ORouter
default: default:
logger.Fatal("Invalid level value. Must be either \"1\", \"2\", or \"3\"") beeLogger.Log.Fatal("Invalid level value. Must be either \"1\", \"2\", or \"3\"")
} }
var selectedTables map[string]bool var selectedTables map[string]bool
if tables != "" { if tables != "" {
@ -278,9 +281,9 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
case "mysql": case "mysql":
case "postgres": case "postgres":
case "sqlite": case "sqlite":
logger.Fatal("Generating app code from SQLite database is not supported yet.") beeLogger.Log.Fatal("Generating app code from SQLite database is not supported yet.")
default: default:
logger.Fatal("Unknown database driver. Must be either \"mysql\", \"postgres\" or \"sqlite\"") beeLogger.Log.Fatal("Unknown database driver. Must be either \"mysql\", \"postgres\" or \"sqlite\"")
} }
gen(driver, connStr, mode, selectedTables, currpath) gen(driver, connStr, mode, selectedTables, currpath)
} }
@ -290,11 +293,11 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, apppath string) { func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, apppath string) {
db, err := sql.Open(dbms, connStr) db, err := sql.Open(dbms, connStr)
if err != nil { if err != nil {
logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err) beeLogger.Log.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err)
} }
defer db.Close() defer db.Close()
if trans, ok := dbDriver[dbms]; ok { if trans, ok := dbDriver[dbms]; ok {
logger.Info("Analyzing database tables...") beeLogger.Log.Info("Analyzing database tables...")
tableNames := trans.GetTableNames(db) tableNames := trans.GetTableNames(db)
tables := getTableObjects(tableNames, db, trans) tables := getTableObjects(tableNames, db, trans)
mvcPath := new(MvcPath) mvcPath := new(MvcPath)
@ -305,7 +308,7 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, ap
pkgPath := getPackagePath(apppath) pkgPath := getPackagePath(apppath)
writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
} else { } else {
logger.Fatalf("Generating app code from '%s' database is not supported yet.", dbms) beeLogger.Log.Fatalf("Generating app code from '%s' database is not supported yet.", dbms)
} }
} }
@ -313,13 +316,13 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, ap
func (*MysqlDB) GetTableNames(db *sql.DB) (tables []string) { func (*MysqlDB) GetTableNames(db *sql.DB) (tables []string) {
rows, err := db.Query("SHOW TABLES") rows, err := db.Query("SHOW TABLES")
if err != nil { if err != nil {
logger.Fatalf("Could not show tables: %s", err) beeLogger.Log.Fatalf("Could not show tables: %s", err)
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var name string var name string
if err := rows.Scan(&name); err != nil { if err := rows.Scan(&name); err != nil {
logger.Fatalf("Could not show tables: %s", err) beeLogger.Log.Fatalf("Could not show tables: %s", err)
} }
tables = append(tables, name) tables = append(tables, name)
} }
@ -362,12 +365,12 @@ func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bo
c.table_schema = database() AND c.table_name = ? AND u.table_schema = database() AND u.table_name = ?`, c.table_schema = database() AND c.table_name = ? AND u.table_schema = database() AND u.table_name = ?`,
table.Name, table.Name) // u.position_in_unique_constraint, table.Name, table.Name) // u.position_in_unique_constraint,
if err != nil { if err != nil {
logger.Fatal("Could not query INFORMATION_SCHEMA for PK/UK/FK information") beeLogger.Log.Fatal("Could not query INFORMATION_SCHEMA for PK/UK/FK information")
} }
for rows.Next() { for rows.Next() {
var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte
if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil { if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil {
logger.Fatal("Could not read INFORMATION_SCHEMA for PK/UK/FK information") beeLogger.Log.Fatal("Could not read INFORMATION_SCHEMA for PK/UK/FK information")
} }
constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos := constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos :=
string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes), string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes),
@ -407,7 +410,7 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
table_schema = database() AND table_name = ?`, table_schema = database() AND table_name = ?`,
table.Name) table.Name)
if err != nil { if err != nil {
logger.Fatalf("Could not query the database: %s", err) beeLogger.Log.Fatalf("Could not query the database: %s", err)
} }
defer colDefRows.Close() defer colDefRows.Close()
@ -415,17 +418,17 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
// datatype as bytes so that SQL <null> values can be retrieved // datatype as bytes so that SQL <null> values can be retrieved
var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte
if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil { if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil {
logger.Fatal("Could not query INFORMATION_SCHEMA for column information") beeLogger.Log.Fatal("Could not query INFORMATION_SCHEMA for column information")
} }
colName, dataType, columnType, isNullable, columnDefault, extra := colName, dataType, columnType, isNullable, columnDefault, extra :=
string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes) string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes)
// create a column // create a column
col := new(Column) col := new(Column)
col.Name = camelCase(colName) col.Name = utils.CamelCase(colName)
col.Type, err = mysqlDB.GetGoDataType(dataType) col.Type, err = mysqlDB.GetGoDataType(dataType)
if err != nil { if err != nil {
logger.Fatalf("%s", err) beeLogger.Log.Fatalf("%s", err)
} }
// Tag info // Tag info
@ -449,8 +452,8 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
if isFk && !isBl { if isFk && !isBl {
tag.RelFk = true tag.RelFk = true
refStructName := fkCol.RefTable refStructName := fkCol.RefTable
col.Name = camelCase(colName) col.Name = utils.CamelCase(colName)
col.Type = "*" + camelCase(refStructName) col.Type = "*" + utils.CamelCase(refStructName)
} else { } else {
// if the name of column is Id, and it's not primary key // if the name of column is Id, and it's not primary key
if colName == "id" { if colName == "id" {
@ -464,7 +467,7 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
if sign == "unsigned" && extra != "auto_increment" { if sign == "unsigned" && extra != "auto_increment" {
col.Type, err = mysqlDB.GetGoDataType(dataType + " " + sign) col.Type, err = mysqlDB.GetGoDataType(dataType + " " + sign)
if err != nil { if err != nil {
logger.Fatalf("%s", err) beeLogger.Log.Fatalf("%s", err)
} }
} }
} }
@ -516,14 +519,14 @@ func (*PostgresDB) GetTableNames(db *sql.DB) (tables []string) {
table_type = 'BASE TABLE' AND table_type = 'BASE TABLE' AND
table_schema NOT IN ('pg_catalog', 'information_schema')`) table_schema NOT IN ('pg_catalog', 'information_schema')`)
if err != nil { if err != nil {
logger.Fatalf("Could not show tables: %s", err) beeLogger.Log.Fatalf("Could not show tables: %s", err)
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var name string var name string
if err := rows.Scan(&name); err != nil { if err := rows.Scan(&name); err != nil {
logger.Fatalf("Could not show tables: %s", err) beeLogger.Log.Fatalf("Could not show tables: %s", err)
} }
tables = append(tables, name) tables = append(tables, name)
} }
@ -553,13 +556,13 @@ func (*PostgresDB) GetConstraints(db *sql.DB, table *Table, blackList map[string
AND u.table_name = $2`, AND u.table_name = $2`,
table.Name, table.Name) // u.position_in_unique_constraint, table.Name, table.Name) // u.position_in_unique_constraint,
if err != nil { if err != nil {
logger.Fatalf("Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s", err) beeLogger.Log.Fatalf("Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s", err)
} }
for rows.Next() { for rows.Next() {
var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte
if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil { if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil {
logger.Fatalf("Could not read INFORMATION_SCHEMA for PK/UK/FK information: %s", err) beeLogger.Log.Fatalf("Could not read INFORMATION_SCHEMA for PK/UK/FK information: %s", err)
} }
constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos := constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos :=
string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes), string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes),
@ -609,7 +612,7 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map
AND table_name = $1`, AND table_name = $1`,
table.Name) table.Name)
if err != nil { if err != nil {
logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err) beeLogger.Log.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err)
} }
defer colDefRows.Close() defer colDefRows.Close()
@ -617,16 +620,16 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map
// datatype as bytes so that SQL <null> values can be retrieved // datatype as bytes so that SQL <null> values can be retrieved
var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte
if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil { if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil {
logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err) beeLogger.Log.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err)
} }
colName, dataType, columnType, isNullable, columnDefault, extra := colName, dataType, columnType, isNullable, columnDefault, extra :=
string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes) string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes)
// Create a column // Create a column
col := new(Column) col := new(Column)
col.Name = camelCase(colName) col.Name = utils.CamelCase(colName)
col.Type, err = postgresDB.GetGoDataType(dataType) col.Type, err = postgresDB.GetGoDataType(dataType)
if err != nil { if err != nil {
logger.Fatalf("%s", err) beeLogger.Log.Fatalf("%s", err)
} }
// Tag info // Tag info
@ -650,8 +653,8 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map
if isFk && !isBl { if isFk && !isBl {
tag.RelFk = true tag.RelFk = true
refStructName := fkCol.RefTable refStructName := fkCol.RefTable
col.Name = camelCase(colName) col.Name = utils.CamelCase(colName)
col.Type = "*" + camelCase(refStructName) col.Type = "*" + utils.CamelCase(refStructName)
} else { } else {
// if the name of column is Id, and it's not primary key // if the name of column is Id, and it's not primary key
if colName == "id" { if colName == "id" {
@ -716,22 +719,22 @@ func createPaths(mode byte, paths *MvcPath) {
// Newly geneated files will be inside these folders. // Newly geneated files will be inside these folders.
func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
if (OModel & mode) == OModel { if (OModel & mode) == OModel {
logger.Info("Creating model files...") beeLogger.Log.Info("Creating model files...")
writeModelFiles(tables, paths.ModelPath, selectedTables) writeModelFiles(tables, paths.ModelPath, selectedTables)
} }
if (OController & mode) == OController { if (OController & mode) == OController {
logger.Info("Creating controller files...") beeLogger.Log.Info("Creating controller files...")
writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath) writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath)
} }
if (ORouter & mode) == ORouter { if (ORouter & mode) == ORouter {
logger.Info("Creating router files...") beeLogger.Log.Info("Creating router files...")
writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath) writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath)
} }
} }
// writeModelFiles generates model files // writeModelFiles generates model files
func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) { func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) {
w := NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
for _, tb := range tables { for _, tb := range tables {
// if selectedTables map is not nil and this table is not selected, ignore it // if selectedTables map is not nil and this table is not selected, ignore it
@ -744,22 +747,22 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
fpath := path.Join(mPath, filename+".go") fpath := path.Join(mPath, filename+".go")
var f *os.File var f *os.File
var err error var err error
if isExist(fpath) { if utils.IsExist(fpath) {
logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) beeLogger.Log.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
if askForConfirmation() { if utils.AskForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil { if err != nil {
logger.Warnf("%s", err) beeLogger.Log.Warnf("%s", err)
continue continue
} }
} else { } else {
logger.Warnf("Skipped create file '%s'", fpath) beeLogger.Log.Warnf("Skipped create file '%s'", fpath)
continue continue
} }
} else { } else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil { if err != nil {
logger.Warnf("%s", err) beeLogger.Log.Warnf("%s", err)
continue continue
} }
} }
@ -770,7 +773,7 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
template = ModelTPL template = ModelTPL
} }
fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1) fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1)
fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1) fileStr = strings.Replace(fileStr, "{{modelName}}", utils.CamelCase(tb.Name), -1)
fileStr = strings.Replace(fileStr, "{{tableName}}", tb.Name, -1) fileStr = strings.Replace(fileStr, "{{tableName}}", tb.Name, -1)
// If table contains time field, import time.Time package // If table contains time field, import time.Time package
@ -783,17 +786,17 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1) fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1)
fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1)
if _, err := f.WriteString(fileStr); err != nil { if _, err := f.WriteString(fileStr); err != nil {
logger.Fatalf("Could not write model file to '%s': %s", fpath, err) beeLogger.Log.Fatalf("Could not write model file to '%s': %s", fpath, err)
} }
CloseFile(f) utils.CloseFile(f)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
formatSourceCode(fpath) utils.FormatSourceCode(fpath)
} }
} }
// writeControllerFiles generates controller files // writeControllerFiles generates controller files
func writeControllerFiles(tables []*Table, cPath string, selectedTables map[string]bool, pkgPath string) { func writeControllerFiles(tables []*Table, cPath string, selectedTables map[string]bool, pkgPath string) {
w := NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
for _, tb := range tables { for _, tb := range tables {
// If selectedTables map is not nil and this table is not selected, ignore it // If selectedTables map is not nil and this table is not selected, ignore it
@ -809,39 +812,39 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri
fpath := path.Join(cPath, filename+".go") fpath := path.Join(cPath, filename+".go")
var f *os.File var f *os.File
var err error var err error
if isExist(fpath) { if utils.IsExist(fpath) {
logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) beeLogger.Log.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
if askForConfirmation() { if utils.AskForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil { if err != nil {
logger.Warnf("%s", err) beeLogger.Log.Warnf("%s", err)
continue continue
} }
} else { } else {
logger.Warnf("Skipped create file '%s'", fpath) beeLogger.Log.Warnf("Skipped create file '%s'", fpath)
continue continue
} }
} else { } else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil { if err != nil {
logger.Warnf("%s", err) beeLogger.Log.Warnf("%s", err)
continue continue
} }
} }
fileStr := strings.Replace(CtrlTPL, "{{ctrlName}}", camelCase(tb.Name), -1) fileStr := strings.Replace(CtrlTPL, "{{ctrlName}}", utils.CamelCase(tb.Name), -1)
fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1) fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1)
if _, err := f.WriteString(fileStr); err != nil { if _, err := f.WriteString(fileStr); err != nil {
logger.Fatalf("Could not write controller file to '%s': %s", fpath, err) beeLogger.Log.Fatalf("Could not write controller file to '%s': %s", fpath, err)
} }
CloseFile(f) utils.CloseFile(f)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
formatSourceCode(fpath) utils.FormatSourceCode(fpath)
} }
} }
// writeRouterFile generates router file // writeRouterFile generates router file
func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bool, pkgPath string) { func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bool, pkgPath string) {
w := NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
var nameSpaces []string var nameSpaces []string
for _, tb := range tables { for _, tb := range tables {
@ -856,7 +859,7 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo
} }
// Add namespaces // Add namespaces
nameSpace := strings.Replace(NamespaceTPL, "{{nameSpace}}", tb.Name, -1) nameSpace := strings.Replace(NamespaceTPL, "{{nameSpace}}", tb.Name, -1)
nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", camelCase(tb.Name), -1) nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", utils.CamelCase(tb.Name), -1)
nameSpaces = append(nameSpaces, nameSpace) nameSpaces = append(nameSpaces, nameSpace)
} }
// Add export controller // Add export controller
@ -865,31 +868,31 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo
routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1) routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1)
var f *os.File var f *os.File
var err error var err error
if isExist(fpath) { if utils.IsExist(fpath) {
logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) beeLogger.Log.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
if askForConfirmation() { if utils.AskForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil { if err != nil {
logger.Warnf("%s", err) beeLogger.Log.Warnf("%s", err)
return return
} }
} else { } else {
logger.Warnf("Skipped create file '%s'", fpath) beeLogger.Log.Warnf("Skipped create file '%s'", fpath)
return return
} }
} else { } else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil { if err != nil {
logger.Warnf("%s", err) beeLogger.Log.Warnf("%s", err)
return return
} }
} }
if _, err := f.WriteString(routerStr); err != nil { if _, err := f.WriteString(routerStr); err != nil {
logger.Fatalf("Could not write router file to '%s': %s", fpath, err) beeLogger.Log.Fatalf("Could not write router file to '%s': %s", fpath, err)
} }
CloseFile(f) utils.CloseFile(f)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
formatSourceCode(fpath) utils.FormatSourceCode(fpath)
} }
func isSQLTemporalType(t string) bool { func isSQLTemporalType(t string) bool {
@ -952,10 +955,10 @@ func getFileName(tbName string) (filename string) {
func getPackagePath(curpath string) (packpath string) { func getPackagePath(curpath string) (packpath string) {
gopath := os.Getenv("GOPATH") gopath := os.Getenv("GOPATH")
if gopath == "" { if gopath == "" {
logger.Fatal("GOPATH environment variable is not set or empty") beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
} }
logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath)
appsrcpath := "" appsrcpath := ""
haspath := false haspath := false
@ -964,7 +967,7 @@ func getPackagePath(curpath string) (packpath string) {
for _, wg := range wgopath { for _, wg := range wgopath {
wg, _ = filepath.EvalSymlinks(path.Join(wg, "src")) wg, _ = filepath.EvalSymlinks(path.Join(wg, "src"))
if filepath.HasPrefix(strings.ToLower(curpath), strings.ToLower(wg)) { if strings.HasPrefix(strings.ToLower(curpath), strings.ToLower(wg)) {
haspath = true haspath = true
appsrcpath = wg appsrcpath = wg
break break
@ -972,11 +975,11 @@ func getPackagePath(curpath string) (packpath string) {
} }
if !haspath { if !haspath {
logger.Fatalf("Cannot generate application code outside of GOPATH '%s'", gopath) beeLogger.Log.Fatalf("Cannot generate application code outside of GOPATH '%s'", gopath)
} }
if curpath == appsrcpath { if curpath == appsrcpath {
logger.Fatal("Cannot generate application code outside of application path") beeLogger.Log.Fatal("Cannot generate application code outside of application path")
} }
packpath = strings.Join(strings.Split(curpath[len(appsrcpath)+1:], string(filepath.Separator)), "/") packpath = strings.Join(strings.Split(curpath[len(appsrcpath)+1:], string(filepath.Separator)), "/")

View File

@ -12,17 +12,21 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package generate
import ( import (
"fmt" "fmt"
"os" "os"
"path" "path"
"strings" "strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
) )
func generateController(cname, currpath string) { func GenerateController(cname, currpath string) {
w := NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
p, f := path.Split(cname) p, f := path.Split(cname)
controllerName := strings.Title(f) controllerName := strings.Title(f)
@ -33,26 +37,26 @@ func generateController(cname, currpath string) {
packageName = p[i+1 : len(p)-1] packageName = p[i+1 : len(p)-1]
} }
logger.Infof("Using '%s' as controller name", controllerName) beeLogger.Log.Infof("Using '%s' as controller name", controllerName)
logger.Infof("Using '%s' as package name", packageName) beeLogger.Log.Infof("Using '%s' as package name", packageName)
fp := path.Join(currpath, "controllers", p) fp := path.Join(currpath, "controllers", p)
if _, err := os.Stat(fp); os.IsNotExist(err) { if _, err := os.Stat(fp); os.IsNotExist(err) {
// Create the controller's directory // Create the controller's directory
if err := os.MkdirAll(fp, 0777); err != nil { if err := os.MkdirAll(fp, 0777); err != nil {
logger.Fatalf("Could not create controllers directory: %s", err) beeLogger.Log.Fatalf("Could not create controllers directory: %s", err)
} }
} }
fpath := path.Join(fp, strings.ToLower(controllerName)+".go") fpath := path.Join(fp, strings.ToLower(controllerName)+".go")
if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer CloseFile(f) defer utils.CloseFile(f)
modelPath := path.Join(currpath, "models", strings.ToLower(controllerName)+".go") modelPath := path.Join(currpath, "models", strings.ToLower(controllerName)+".go")
var content string var content string
if _, err := os.Stat(modelPath); err == nil { if _, err := os.Stat(modelPath); err == nil {
logger.Infof("Using matching model '%s'", controllerName) beeLogger.Log.Infof("Using matching model '%s'", controllerName)
content = strings.Replace(controllerModelTpl, "{{packageName}}", packageName, -1) content = strings.Replace(controllerModelTpl, "{{packageName}}", packageName, -1)
pkgPath := getPackagePath(currpath) pkgPath := getPackagePath(currpath)
content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1) content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1)
@ -64,10 +68,10 @@ func generateController(cname, currpath string) {
f.WriteString(content) f.WriteString(content)
// Run 'gofmt' on the generated source code // Run 'gofmt' on the generated source code
formatSourceCode(fpath) utils.FormatSourceCode(fpath)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
} else { } else {
logger.Fatalf("Could not create controller file: %s", err) beeLogger.Log.Fatalf("Could not create controller file: %s", err)
} }
} }

View File

@ -15,7 +15,7 @@
* * * *
\**********************************************************/ \**********************************************************/
package main package generate
import ( import (
"database/sql" "database/sql"
@ -24,11 +24,256 @@ import (
"path" "path"
"strings" "strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq" _ "github.com/lib/pq"
) )
func generateHproseAppcode(driver, connStr, level, tables, currpath string) { var Hproseconf = `appname = {{.Appname}}
httpport = 8080
runmode = dev
autorender = false
copyrequestbody = true
EnableDocs = true
`
var HproseMaingo = `package main
import (
"fmt"
"reflect"
"{{.Appname}}/models"
"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() {
// 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()
}
`
var HproseMainconngo = `package main
import (
"fmt"
"reflect"
"{{.Appname}}/models"
"github.com/hprose/hprose-golang/rpc"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
{{.DriverPkg}}
)
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() {
// 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()
}
`
var HproseModels = `package models
import (
"errors"
"strconv"
"time"
)
var (
Objects map[string]*Object
)
type Object struct {
ObjectId string
Score int64
PlayerName string
}
func init() {
Objects = make(map[string]*Object)
Objects["hjkhsbnmn123"] = &Object{"hjkhsbnmn123", 100, "astaxie"}
Objects["mjjkxsxsaa23"] = &Object{"mjjkxsxsaa23", 101, "someone"}
}
func AddOne(object Object) (ObjectId string) {
object.ObjectId = "astaxie" + strconv.FormatInt(time.Now().UnixNano(), 10)
Objects[object.ObjectId] = &object
return object.ObjectId
}
func GetOne(ObjectId string) (object *Object, err error) {
if v, ok := Objects[ObjectId]; ok {
return v, nil
}
return nil, errors.New("ObjectId Not Exist")
}
func GetAll() map[string]*Object {
return Objects
}
func Update(ObjectId string, Score int64) (err error) {
if v, ok := Objects[ObjectId]; ok {
v.Score = Score
return nil
}
return errors.New("ObjectId Not Exist")
}
func Delete(ObjectId string) {
delete(Objects, ObjectId)
}
`
var HproseModels2 = `package models
import (
"errors"
"strconv"
"time"
)
var (
UserList map[string]*User
)
func init() {
UserList = make(map[string]*User)
u := User{"user_11111", "astaxie", "11111", Profile{"male", 20, "Singapore", "astaxie@gmail.com"}}
UserList["user_11111"] = &u
}
type User struct {
Id string
Username string
Password string
Profile Profile
}
type Profile struct {
Gender string
Age int
Address string
Email string
}
func AddUser(u User) string {
u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10)
UserList[u.Id] = &u
return u.Id
}
func GetUser(uid string) (u *User, err error) {
if u, ok := UserList[uid]; ok {
return u, nil
}
return nil, errors.New("User not exists")
}
func GetAllUsers() map[string]*User {
return UserList
}
func UpdateUser(uid string, uu *User) (a *User, err error) {
if u, ok := UserList[uid]; ok {
if uu.Username != "" {
u.Username = uu.Username
}
if uu.Password != "" {
u.Password = uu.Password
}
if uu.Profile.Age != 0 {
u.Profile.Age = uu.Profile.Age
}
if uu.Profile.Address != "" {
u.Profile.Address = uu.Profile.Address
}
if uu.Profile.Gender != "" {
u.Profile.Gender = uu.Profile.Gender
}
if uu.Profile.Email != "" {
u.Profile.Email = uu.Profile.Email
}
return u, nil
}
return nil, errors.New("User Not Exist")
}
func Login(username, password string) bool {
for _, u := range UserList {
if u.Username == username && u.Password == password {
return true
}
}
return false
}
func DeleteUser(uid string) {
delete(UserList, uid)
}
`
var HproseAddFunctions = []string{}
func GenerateHproseAppcode(driver, connStr, level, tables, currpath string) {
var mode byte var mode byte
switch level { switch level {
case "1": case "1":
@ -38,7 +283,7 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
case "3": case "3":
mode = OModel | OController | ORouter mode = OModel | OController | ORouter
default: default:
logger.Fatal("Invalid 'level' option. Level must be either \"1\", \"2\" or \"3\"") beeLogger.Log.Fatal("Invalid 'level' option. Level must be either \"1\", \"2\" or \"3\"")
} }
var selectedTables map[string]bool var selectedTables map[string]bool
if tables != "" { if tables != "" {
@ -51,9 +296,9 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
case "mysql": case "mysql":
case "postgres": case "postgres":
case "sqlite": case "sqlite":
logger.Fatal("Generating app code from SQLite database is not supported yet") beeLogger.Log.Fatal("Generating app code from SQLite database is not supported yet")
default: default:
logger.Fatalf("Unknown database driver '%s'. Driver must be one of mysql, postgres or sqlite", driver) beeLogger.Log.Fatalf("Unknown database driver '%s'. Driver must be one of mysql, postgres or sqlite", driver)
} }
genHprose(driver, connStr, mode, selectedTables, currpath) genHprose(driver, connStr, mode, selectedTables, currpath)
} }
@ -63,11 +308,11 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bool, currpath string) { func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bool, currpath string) {
db, err := sql.Open(dbms, connStr) db, err := sql.Open(dbms, connStr)
if err != nil { if err != nil {
logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err) beeLogger.Log.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err)
} }
defer db.Close() defer db.Close()
if trans, ok := dbDriver[dbms]; ok { if trans, ok := dbDriver[dbms]; ok {
logger.Info("Analyzing database tables...") beeLogger.Log.Info("Analyzing database tables...")
tableNames := trans.GetTableNames(db) tableNames := trans.GetTableNames(db)
tables := getTableObjects(tableNames, db, trans) tables := getTableObjects(tableNames, db, trans)
mvcPath := new(MvcPath) mvcPath := new(MvcPath)
@ -76,7 +321,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo
pkgPath := getPackagePath(currpath) pkgPath := getPackagePath(currpath)
writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
} else { } else {
logger.Fatalf("Generating app code from '%s' database is not supported yet", dbms) beeLogger.Log.Fatalf("Generating app code from '%s' database is not supported yet", dbms)
} }
} }
@ -85,14 +330,14 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo
// Newly geneated files will be inside these folders. // Newly geneated files will be inside these folders.
func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
if (OModel & mode) == OModel { if (OModel & mode) == OModel {
logger.Info("Creating model files...") beeLogger.Log.Info("Creating model files...")
writeHproseModelFiles(tables, paths.ModelPath, selectedTables) writeHproseModelFiles(tables, paths.ModelPath, selectedTables)
} }
} }
// writeHproseModelFiles generates model files // writeHproseModelFiles generates model files
func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) { func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) {
w := NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
for _, tb := range tables { for _, tb := range tables {
// if selectedTables map is not nil and this table is not selected, ignore it // if selectedTables map is not nil and this table is not selected, ignore it
@ -105,22 +350,22 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
fpath := path.Join(mPath, filename+".go") fpath := path.Join(mPath, filename+".go")
var f *os.File var f *os.File
var err error var err error
if isExist(fpath) { if utils.IsExist(fpath) {
logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) beeLogger.Log.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
if askForConfirmation() { if utils.AskForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil { if err != nil {
logger.Warnf("%s", err) beeLogger.Log.Warnf("%s", err)
continue continue
} }
} else { } else {
logger.Warnf("Skipped create file '%s'", fpath) beeLogger.Log.Warnf("Skipped create file '%s'", fpath)
continue continue
} }
} else { } else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil { if err != nil {
logger.Warnf("%s", err) beeLogger.Log.Warnf("%s", err)
continue continue
} }
} }
@ -129,10 +374,10 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
template = HproseStructModelTPL template = HproseStructModelTPL
} else { } else {
template = HproseModelTPL template = HproseModelTPL
hproseAddFunctions = append(hproseAddFunctions, strings.Replace(HproseAddFunction, "{{modelName}}", camelCase(tb.Name), -1)) HproseAddFunctions = append(HproseAddFunctions, strings.Replace(HproseAddFunction, "{{modelName}}", utils.CamelCase(tb.Name), -1))
} }
fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1) fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1)
fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1) fileStr = strings.Replace(fileStr, "{{modelName}}", utils.CamelCase(tb.Name), -1)
// if table contains time field, import time.Time package // if table contains time field, import time.Time package
timePkg := "" timePkg := ""
importTimePkg := "" importTimePkg := ""
@ -143,11 +388,11 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1) fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1)
fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1)
if _, err := f.WriteString(fileStr); err != nil { if _, err := f.WriteString(fileStr); err != nil {
logger.Fatalf("Could not write model file to '%s'", fpath) beeLogger.Log.Fatalf("Could not write model file to '%s'", fpath)
} }
CloseFile(f) utils.CloseFile(f)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
formatSourceCode(fpath) utils.FormatSourceCode(fpath)
} }
} }

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package generate
import ( import (
"fmt" "fmt"
@ -20,6 +20,10 @@ import (
"path" "path"
"strings" "strings"
"time" "time"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
) )
const ( const (
@ -29,18 +33,18 @@ const (
) )
type DBDriver interface { type DBDriver interface {
generateCreateUp(tableName string) string GenerateCreateUp(tableName string) string
generateCreateDown(tableName string) string GenerateCreateDown(tableName string) string
} }
type mysqlDriver struct{} type mysqlDriver struct{}
func (m mysqlDriver) generateCreateUp(tableName string) string { func (m mysqlDriver) GenerateCreateUp(tableName string) string {
upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(fields.String()) + `)");` upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(Fields.String()) + `)");`
return upsql return upsql
} }
func (m mysqlDriver) generateCreateDown(tableName string) string { func (m mysqlDriver) GenerateCreateDown(tableName string) string {
downsql := `m.SQL("DROP TABLE ` + "`" + tableName + "`" + `")` downsql := `m.SQL("DROP TABLE ` + "`" + tableName + "`" + `")`
return downsql return downsql
} }
@ -51,21 +55,21 @@ func (m mysqlDriver) generateSQLFromFields(fields string) string {
for i, v := range fds { for i, v := range fds {
kv := strings.SplitN(v, ":", 2) kv := strings.SplitN(v, ":", 2)
if len(kv) != 2 { if len(kv) != 2 {
logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v)
return "" return ""
} }
typ, tag := m.getSQLType(kv[1]) typ, tag := m.getSQLType(kv[1])
if typ == "" { if typ == "" {
logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v)
return "" return ""
} }
if i == 0 && strings.ToLower(kv[0]) != "id" { if i == 0 && strings.ToLower(kv[0]) != "id" {
sql += "`id` int(11) NOT NULL AUTO_INCREMENT," sql += "`id` int(11) NOT NULL AUTO_INCREMENT,"
tags = tags + "PRIMARY KEY (`id`)," tags = tags + "PRIMARY KEY (`id`),"
} }
sql += "`" + snakeString(kv[0]) + "` " + typ + "," sql += "`" + utils.SnakeString(kv[0]) + "` " + typ + ","
if tag != "" { if tag != "" {
tags = tags + fmt.Sprintf(tag, "`"+snakeString(kv[0])+"`") + "," tags = tags + fmt.Sprintf(tag, "`"+utils.SnakeString(kv[0])+"`") + ","
} }
} }
sql = strings.TrimRight(sql+tags, ",") sql = strings.TrimRight(sql+tags, ",")
@ -104,12 +108,12 @@ func (m mysqlDriver) getSQLType(ktype string) (tp, tag string) {
type postgresqlDriver struct{} type postgresqlDriver struct{}
func (m postgresqlDriver) generateCreateUp(tableName string) string { func (m postgresqlDriver) GenerateCreateUp(tableName string) string {
upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(fields.String()) + `)");` upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(Fields.String()) + `)");`
return upsql return upsql
} }
func (m postgresqlDriver) generateCreateDown(tableName string) string { func (m postgresqlDriver) GenerateCreateDown(tableName string) string {
downsql := `m.SQL("DROP TABLE ` + tableName + `")` downsql := `m.SQL("DROP TABLE ` + tableName + `")`
return downsql return downsql
} }
@ -120,20 +124,20 @@ func (m postgresqlDriver) generateSQLFromFields(fields string) string {
for i, v := range fds { for i, v := range fds {
kv := strings.SplitN(v, ":", 2) kv := strings.SplitN(v, ":", 2)
if len(kv) != 2 { if len(kv) != 2 {
logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v)
return "" return ""
} }
typ, tag := m.getSQLType(kv[1]) typ, tag := m.getSQLType(kv[1])
if typ == "" { if typ == "" {
logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v)
return "" return ""
} }
if i == 0 && strings.ToLower(kv[0]) != "id" { if i == 0 && strings.ToLower(kv[0]) != "id" {
sql += "id serial primary key," sql += "id serial primary key,"
} }
sql += snakeString(kv[0]) + " " + typ + "," sql += utils.SnakeString(kv[0]) + " " + typ + ","
if tag != "" { if tag != "" {
tags = tags + fmt.Sprintf(tag, snakeString(kv[0])) + "," tags = tags + fmt.Sprintf(tag, utils.SnakeString(kv[0])) + ","
} }
} }
if tags != "" { if tags != "" {
@ -170,14 +174,14 @@ func (m postgresqlDriver) getSQLType(ktype string) (tp, tag string) {
return "", "" return "", ""
} }
func newDBDriver() DBDriver { func NewDBDriver() DBDriver {
switch driver { switch SQLDriver {
case "mysql": case "mysql":
return mysqlDriver{} return mysqlDriver{}
case "postgres": case "postgres":
return postgresqlDriver{} return postgresqlDriver{}
default: default:
logger.Fatal("Driver not supported") beeLogger.Log.Fatal("Driver not supported")
return nil return nil
} }
} }
@ -185,30 +189,30 @@ func newDBDriver() DBDriver {
// generateMigration generates migration file template for database schema update. // generateMigration generates migration file template for database schema update.
// The generated file template consists of an up() method for updating schema and // The generated file template consists of an up() method for updating schema and
// a down() method for reverting the update. // a down() method for reverting the update.
func generateMigration(mname, upsql, downsql, curpath string) { func GenerateMigration(mname, upsql, downsql, curpath string) {
w := NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
migrationFilePath := path.Join(curpath, DBPath, MPath) migrationFilePath := path.Join(curpath, DBPath, MPath)
if _, err := os.Stat(migrationFilePath); os.IsNotExist(err) { if _, err := os.Stat(migrationFilePath); os.IsNotExist(err) {
// create migrations directory // create migrations directory
if err := os.MkdirAll(migrationFilePath, 0777); err != nil { if err := os.MkdirAll(migrationFilePath, 0777); err != nil {
logger.Fatalf("Could not create migration directory: %s", err) beeLogger.Log.Fatalf("Could not create migration directory: %s", err)
} }
} }
// create file // create file
today := time.Now().Format(MDateFormat) today := time.Now().Format(MDateFormat)
fpath := path.Join(migrationFilePath, fmt.Sprintf("%s_%s.go", today, mname)) fpath := path.Join(migrationFilePath, fmt.Sprintf("%s_%s.go", today, mname))
if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer CloseFile(f) defer utils.CloseFile(f)
content := strings.Replace(MigrationTPL, "{{StructName}}", camelCase(mname)+"_"+today, -1) content := strings.Replace(MigrationTPL, "{{StructName}}", utils.CamelCase(mname)+"_"+today, -1)
content = strings.Replace(content, "{{CurrTime}}", today, -1) content = strings.Replace(content, "{{CurrTime}}", today, -1)
content = strings.Replace(content, "{{UpSQL}}", upsql, -1) content = strings.Replace(content, "{{UpSQL}}", upsql, -1)
content = strings.Replace(content, "{{DownSQL}}", downsql, -1) content = strings.Replace(content, "{{DownSQL}}", downsql, -1)
f.WriteString(content) f.WriteString(content)
// Run 'gofmt' on the generated source code // Run 'gofmt' on the generated source code
formatSourceCode(fpath) utils.FormatSourceCode(fpath)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
} else { } else {
logger.Fatalf("Could not create migration file: %s", err) beeLogger.Log.Fatalf("Could not create migration file: %s", err)
} }
} }

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package generate
import ( import (
"errors" "errors"
@ -20,10 +20,14 @@ import (
"os" "os"
"path" "path"
"strings" "strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
) )
func generateModel(mname, fields, currpath string) { func GenerateModel(mname, fields, currpath string) {
w := NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
p, f := path.Split(mname) p, f := path.Split(mname)
modelName := strings.Title(f) modelName := strings.Title(f)
@ -35,23 +39,23 @@ func generateModel(mname, fields, currpath string) {
modelStruct, hastime, err := getStruct(modelName, fields) modelStruct, hastime, err := getStruct(modelName, fields)
if err != nil { if err != nil {
logger.Fatalf("Could not generate the model struct: %s", err) beeLogger.Log.Fatalf("Could not generate the model struct: %s", err)
} }
logger.Infof("Using '%s' as model name", modelName) beeLogger.Log.Infof("Using '%s' as model name", modelName)
logger.Infof("Using '%s' as package name", packageName) beeLogger.Log.Infof("Using '%s' as package name", packageName)
fp := path.Join(currpath, "models", p) fp := path.Join(currpath, "models", p)
if _, err := os.Stat(fp); os.IsNotExist(err) { if _, err := os.Stat(fp); os.IsNotExist(err) {
// Create the model's directory // Create the model's directory
if err := os.MkdirAll(fp, 0777); err != nil { if err := os.MkdirAll(fp, 0777); err != nil {
logger.Fatalf("Could not create the model directory: %s", err) beeLogger.Log.Fatalf("Could not create the model directory: %s", err)
} }
} }
fpath := path.Join(fp, strings.ToLower(modelName)+".go") fpath := path.Join(fp, strings.ToLower(modelName)+".go")
if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer CloseFile(f) defer utils.CloseFile(f)
content := strings.Replace(modelTpl, "{{packageName}}", packageName, -1) content := strings.Replace(modelTpl, "{{packageName}}", packageName, -1)
content = strings.Replace(content, "{{modelName}}", modelName, -1) content = strings.Replace(content, "{{modelName}}", modelName, -1)
content = strings.Replace(content, "{{modelStruct}}", modelStruct, -1) content = strings.Replace(content, "{{modelStruct}}", modelStruct, -1)
@ -62,10 +66,10 @@ func generateModel(mname, fields, currpath string) {
} }
f.WriteString(content) f.WriteString(content)
// Run 'gofmt' on the generated source code // Run 'gofmt' on the generated source code
formatSourceCode(fpath) utils.FormatSourceCode(fpath)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
} else { } else {
logger.Fatalf("Could not create model file: %s", err) beeLogger.Log.Fatalf("Could not create model file: %s", err)
} }
} }
@ -95,7 +99,7 @@ func getStruct(structname, fields string) (string, bool, error) {
if hastimeinner { if hastimeinner {
hastime = true hastime = true
} }
structStr = structStr + camelString(kv[0]) + " " + typ + " " + tag + "\n" structStr = structStr + utils.CamelString(kv[0]) + " " + typ + " " + tag + "\n"
} }
structStr += "}\n" structStr += "}\n"
return structStr, hastime, nil return structStr, hastime, nil

50
generate/g_scaffold.go Normal file
View File

@ -0,0 +1,50 @@
package generate
import (
"strings"
"github.com/beego/bee/cmd/commands/migrate"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
)
func GenerateScaffold(sname, fields, currpath, driver, conn string) {
beeLogger.Log.Infof("Do you want to create a '%s' model? [Yes|No] ", sname)
// Generate the model
if utils.AskForConfirmation() {
GenerateModel(sname, fields, currpath)
}
// Generate the controller
beeLogger.Log.Infof("Do you want to create a '%s' controller? [Yes|No] ", sname)
if utils.AskForConfirmation() {
GenerateController(sname, currpath)
}
// Generate the views
beeLogger.Log.Infof("Do you want to create views for this '%s' resource? [Yes|No] ", sname)
if utils.AskForConfirmation() {
GenerateView(sname, currpath)
}
// Generate a migration
beeLogger.Log.Infof("Do you want to create a '%s' migration and schema for this resource? [Yes|No] ", sname)
if utils.AskForConfirmation() {
upsql := ""
downsql := ""
if fields != "" {
dbMigrator := NewDBDriver()
upsql = dbMigrator.GenerateCreateUp(sname)
downsql = dbMigrator.GenerateCreateDown(sname)
}
GenerateMigration(sname, upsql, downsql, currpath)
}
// Run the migration
beeLogger.Log.Infof("Do you want to migrate the database? [Yes|No] ")
if utils.AskForConfirmation() {
migrate.MigrateUpdate(currpath, driver, conn)
}
beeLogger.Log.Successf("All done! Don't forget to add beego.Router(\"/%s\" ,&controllers.%sController{}) to routers/route.go\n", sname, strings.Title(sname))
}

View File

@ -12,60 +12,64 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package generate
import ( import (
"fmt" "fmt"
"os" "os"
"path" "path"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
) )
// recipe // recipe
// admin/recipe // admin/recipe
func generateView(viewpath, currpath string) { func GenerateView(viewpath, currpath string) {
w := NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
logger.Info("Generating view...") beeLogger.Log.Info("Generating view...")
absViewPath := path.Join(currpath, "views", viewpath) absViewPath := path.Join(currpath, "views", viewpath)
err := os.MkdirAll(absViewPath, os.ModePerm) err := os.MkdirAll(absViewPath, os.ModePerm)
if err != nil { if err != nil {
logger.Fatalf("Could not create '%s' view: %s", viewpath, err) beeLogger.Log.Fatalf("Could not create '%s' view: %s", viewpath, err)
} }
cfile := path.Join(absViewPath, "index.tpl") cfile := path.Join(absViewPath, "index.tpl")
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer CloseFile(f) defer utils.CloseFile(f)
f.WriteString(cfile) f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else { } else {
logger.Fatalf("Could not create view file: %s", err) beeLogger.Log.Fatalf("Could not create view file: %s", err)
} }
cfile = path.Join(absViewPath, "show.tpl") cfile = path.Join(absViewPath, "show.tpl")
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer CloseFile(f) defer utils.CloseFile(f)
f.WriteString(cfile) f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else { } else {
logger.Fatalf("Could not create view file: %s", err) beeLogger.Log.Fatalf("Could not create view file: %s", err)
} }
cfile = path.Join(absViewPath, "create.tpl") cfile = path.Join(absViewPath, "create.tpl")
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer CloseFile(f) defer utils.CloseFile(f)
f.WriteString(cfile) f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else { } else {
logger.Fatalf("Could not create view file: %s", err) beeLogger.Log.Fatalf("Could not create view file: %s", err)
} }
cfile = path.Join(absViewPath, "edit.tpl") cfile = path.Join(absViewPath, "edit.tpl")
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer CloseFile(f) defer utils.CloseFile(f)
f.WriteString(cfile) f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else { } else {
logger.Fatalf("Could not create view file: %s", err) beeLogger.Log.Fatalf("Could not create view file: %s", err)
} }
} }

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package swaggergen
import ( import (
"encoding/json" "encoding/json"
@ -35,6 +35,7 @@ import (
"github.com/astaxie/beego/swagger" "github.com/astaxie/beego/swagger"
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
beeLogger "github.com/beego/bee/logger"
) )
const ( const (
@ -90,7 +91,7 @@ func init() {
astPkgs = map[string]*ast.Package{} astPkgs = map[string]*ast.Package{}
} }
func parsePackagesFromDir(dirpath string) { func ParsePackagesFromDir(dirpath string) {
c := make(chan error) c := make(chan error)
go func() { go func() {
@ -116,7 +117,7 @@ func parsePackagesFromDir(dirpath string) {
}() }()
for err := range c { for err := range c {
logger.Warnf("%s", err) beeLogger.Log.Warnf("%s", err)
} }
} }
@ -137,12 +138,12 @@ func parsePackageFromDir(path string) error {
return nil return nil
} }
func generateDocs(curpath string) { func GenerateDocs(curpath string) {
fset := token.NewFileSet() fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path.Join(curpath, "routers", "router.go"), nil, parser.ParseComments) f, err := parser.ParseFile(fset, path.Join(curpath, "routers", "router.go"), nil, parser.ParseComments)
if err != nil { if err != nil {
logger.Fatalf("Error while parsing router.go: %s", err) // beeLogger.Log.Fatalf("Error while parsing router.go: %s", err)
} }
rootapi.Infos = swagger.Information{} rootapi.Infos = swagger.Information{}
@ -251,6 +252,9 @@ func generateDocs(curpath string) {
} }
os.Mkdir(path.Join(curpath, "swagger"), 0755) os.Mkdir(path.Join(curpath, "swagger"), 0755)
fd, err := os.Create(path.Join(curpath, "swagger", "swagger.json")) fd, err := os.Create(path.Join(curpath, "swagger", "swagger.json"))
if err != nil {
panic(err)
}
fdyml, err := os.Create(path.Join(curpath, "swagger", "swagger.yml")) fdyml, err := os.Create(path.Join(curpath, "swagger", "swagger.yml"))
if err != nil { if err != nil {
panic(err) panic(err)
@ -348,7 +352,7 @@ func analyseControllerPkg(localName, pkgpath string) {
} }
gopath := os.Getenv("GOPATH") gopath := os.Getenv("GOPATH")
if gopath == "" { if gopath == "" {
logger.Fatal("GOPATH environment variable is not set or empty") // beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
} }
pkgRealpath := "" pkgRealpath := ""
@ -366,7 +370,7 @@ func analyseControllerPkg(localName, pkgpath string) {
} }
pkgCache[pkgpath] = struct{}{} pkgCache[pkgpath] = struct{}{}
} else { } else {
logger.Fatalf("Package '%s' does not exist in the GOPATH", pkgpath) // beeLogger.Log.Fatalf("Package '%s' does not exist in the GOPATH", pkgpath)
} }
fileSet := token.NewFileSet() fileSet := token.NewFileSet()
@ -375,7 +379,7 @@ func analyseControllerPkg(localName, pkgpath string) {
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}, parser.ParseComments) }, parser.ParseComments)
if err != nil { if err != nil {
logger.Fatalf("Error while parsing dir at '%s': %s", pkgpath, err) // beeLogger.Log.Fatalf("Error while parsing dir at '%s': %s", pkgpath, err)
} }
for _, pkg := range astPkgs { for _, pkg := range astPkgs {
for _, fl := range pkg.Files { for _, fl := range pkg.Files {
@ -413,7 +417,7 @@ func isSystemPackage(pkgpath string) bool {
goroot = runtime.GOROOT() goroot = runtime.GOROOT()
} }
if goroot == "" { if goroot == "" {
logger.Fatalf("GOROOT environment variable is not set or empty") // beeLogger.Log.Fatalf("GOROOT environment variable is not set or empty")
} }
wg, _ := filepath.EvalSymlinks(filepath.Join(goroot, "src", "pkg", pkgpath)) wg, _ := filepath.EvalSymlinks(filepath.Join(goroot, "src", "pkg", pkgpath))
@ -423,11 +427,7 @@ func isSystemPackage(pkgpath string) bool {
//TODO(zh):support go1.4 //TODO(zh):support go1.4
wg, _ = filepath.EvalSymlinks(filepath.Join(goroot, "src", pkgpath)) wg, _ = filepath.EvalSymlinks(filepath.Join(goroot, "src", pkgpath))
if utils.FileExists(wg) { return utils.FileExists(wg)
return true
}
return false
} }
func peekNextSplitString(ss string) (s string, spacePos int) { func peekNextSplitString(ss string) (s string, spacePos int) {
@ -481,7 +481,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
ss = strings.TrimSpace(ss[pos:]) ss = strings.TrimSpace(ss[pos:])
schemaName, pos := peekNextSplitString(ss) schemaName, pos := peekNextSplitString(ss)
if schemaName == "" { if schemaName == "" {
logger.Fatalf("[%s.%s] Schema must follow {object} or {array}", controllerName, funcName) // beeLogger.Log.Fatalf("[%s.%s] Schema must follow {object} or {array}", controllerName, funcName)
} }
if strings.HasPrefix(schemaName, "[]") { if strings.HasPrefix(schemaName, "[]") {
schemaName = schemaName[2:] schemaName = schemaName[2:]
@ -496,7 +496,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
m, mod, realTypes := getModel(schemaName) m, mod, realTypes := getModel(schemaName)
schema.Ref = "#/definitions/" + m schema.Ref = "#/definitions/" + m
if _, ok := modelsList[pkgpath+controllerName]; !ok { if _, ok := modelsList[pkgpath+controllerName]; !ok {
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0) modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema)
} }
modelsList[pkgpath+controllerName][schemaName] = mod modelsList[pkgpath+controllerName][schemaName] = mod
appendModels(pkgpath, controllerName, realTypes) appendModels(pkgpath, controllerName, realTypes)
@ -518,7 +518,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
para := swagger.Parameter{} para := swagger.Parameter{}
p := getparams(strings.TrimSpace(t[len("@Param "):])) p := getparams(strings.TrimSpace(t[len("@Param "):]))
if len(p) < 4 { if len(p) < 4 {
logger.Fatal(controllerName + "_" + funcName + "'s comments @Param should have at least 4 params") // beeLogger.Log.Fatal(controllerName + "_" + funcName + "'s comments @Param should have at least 4 params")
} }
para.Name = p[0] para.Name = p[0]
switch p[1] { switch p[1] {
@ -533,7 +533,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
case "body": case "body":
break break
default: default:
logger.Warnf("[%s.%s] Unknown param location: %s. Possible values are `query`, `header`, `path`, `formData` or `body`.\n", controllerName, funcName, p[1]) // beeLogger.Log.Warnf("[%s.%s] Unknown param location: %s. Possible values are `query`, `header`, `path`, `formData` or `body`.\n", controllerName, funcName, p[1])
} }
para.In = p[1] para.In = p[1]
pp := strings.Split(p[2], ".") pp := strings.Split(p[2], ".")
@ -544,7 +544,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
Ref: "#/definitions/" + m, Ref: "#/definitions/" + m,
} }
if _, ok := modelsList[pkgpath+controllerName]; !ok { if _, ok := modelsList[pkgpath+controllerName]; !ok {
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0) modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema)
} }
modelsList[pkgpath+controllerName][typ] = mod modelsList[pkgpath+controllerName][typ] = mod
appendModels(pkgpath, controllerName, realTypes) appendModels(pkgpath, controllerName, realTypes)
@ -564,7 +564,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
paraType = typeFormat[0] paraType = typeFormat[0]
paraFormat = typeFormat[1] paraFormat = typeFormat[1]
} else { } else {
logger.Warnf("[%s.%s] Unknown param type: %s\n", controllerName, funcName, typ) // beeLogger.Log.Warnf("[%s.%s] Unknown param type: %s\n", controllerName, funcName, typ)
} }
if isArray { if isArray {
para.Type = "array" para.Type = "array"
@ -717,7 +717,7 @@ func getModel(str string) (objectname string, m swagger.Schema, realTypes []stri
} }
} }
if m.Title == "" { if m.Title == "" {
logger.Warnf("Cannot find the object: %s", str) // beeLogger.Log.Warnf("Cannot find the object: %s", str)
// TODO remove when all type have been supported // TODO remove when all type have been supported
//os.Exit(1) //os.Exit(1)
} }
@ -732,7 +732,7 @@ func getModel(str string) (objectname string, m swagger.Schema, realTypes []stri
func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package, packageName string) { func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package, packageName string) {
ts, ok := d.Decl.(*ast.TypeSpec) ts, ok := d.Decl.(*ast.TypeSpec)
if !ok { if !ok {
logger.Fatalf("Unknown type without TypeSec: %v\n", d) // beeLogger.Log.Fatalf("Unknown type without TypeSec: %v\n", d)
} }
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc... // TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc...
st, ok := ts.Type.(*ast.StructType) st, ok := ts.Type.(*ast.StructType)
@ -808,7 +808,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
mp.Default = str2RealType(res[1], realType) mp.Default = str2RealType(res[1], realType)
} else { } else {
logger.Warnf("Invalid default value: %s", defaultValue) // beeLogger.Log.Warnf("Invalid default value: %s", defaultValue)
} }
} }
@ -899,16 +899,6 @@ func isBasicType(Type string) bool {
return false return false
} }
// regexp get json tag
func grepJSONTag(tag string) string {
r, _ := regexp.Compile(`json:"([^"]*)"`)
matches := r.FindAllStringSubmatch(tag, -1)
if len(matches) > 0 {
return matches[0][1]
}
return ""
}
// append models // append models
func appendModels(pkgpath, controllerName string, realTypes []string) { func appendModels(pkgpath, controllerName string, realTypes []string) {
for _, realType := range realTypes { for _, realType := range realTypes {
@ -956,7 +946,7 @@ func str2RealType(s string, typ string) interface{} {
} }
if err != nil { if err != nil {
logger.Warnf("Invalid default value type '%s': %s", typ, s) // beeLogger.Log.Warnf("Invalid default value type '%s': %s", typ, s)
return s return s
} }

View File

@ -1,369 +0,0 @@
// 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 (
"fmt"
"os"
path "path/filepath"
"strings"
)
var cmdHproseapp = &Command{
// CustomFlags: true,
UsageLine: "hprose [appname]",
Short: "Creates an RPC application based on Hprose and Beego frameworks",
Long: `
The command 'hprose' creates an RPC application based on both Beego and Hprose (http://hprose.com/).
{{"To scaffold out your application, use:"|bold}}
$ bee hprose [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test]
If 'conn' is empty, the command will generate a sample application. Otherwise the command
will connect to your database and generate models based on the existing tables.
The command 'hprose' creates a folder named [appname] with the following structure:
main.go
{{"conf"|foldername}}
app.conf
{{"models"|foldername}}
object.go
user.go
`,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() },
Run: createhprose,
}
var hproseconf = `appname = {{.Appname}}
httpport = 8080
runmode = dev
autorender = false
copyrequestbody = true
EnableDocs = true
`
var hproseMaingo = `package main
import (
"fmt"
"reflect"
"{{.Appname}}/models"
"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() {
// 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()
}
`
var hproseMainconngo = `package main
import (
"fmt"
"reflect"
"{{.Appname}}/models"
"github.com/hprose/hprose-golang/rpc"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
{{.DriverPkg}}
)
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() {
// 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()
}
`
var hproseModels = `package models
import (
"errors"
"strconv"
"time"
)
var (
Objects map[string]*Object
)
type Object struct {
ObjectId string
Score int64
PlayerName string
}
func init() {
Objects = make(map[string]*Object)
Objects["hjkhsbnmn123"] = &Object{"hjkhsbnmn123", 100, "astaxie"}
Objects["mjjkxsxsaa23"] = &Object{"mjjkxsxsaa23", 101, "someone"}
}
func AddOne(object Object) (ObjectId string) {
object.ObjectId = "astaxie" + strconv.FormatInt(time.Now().UnixNano(), 10)
Objects[object.ObjectId] = &object
return object.ObjectId
}
func GetOne(ObjectId string) (object *Object, err error) {
if v, ok := Objects[ObjectId]; ok {
return v, nil
}
return nil, errors.New("ObjectId Not Exist")
}
func GetAll() map[string]*Object {
return Objects
}
func Update(ObjectId string, Score int64) (err error) {
if v, ok := Objects[ObjectId]; ok {
v.Score = Score
return nil
}
return errors.New("ObjectId Not Exist")
}
func Delete(ObjectId string) {
delete(Objects, ObjectId)
}
`
var hproseModels2 = `package models
import (
"errors"
"strconv"
"time"
)
var (
UserList map[string]*User
)
func init() {
UserList = make(map[string]*User)
u := User{"user_11111", "astaxie", "11111", Profile{"male", 20, "Singapore", "astaxie@gmail.com"}}
UserList["user_11111"] = &u
}
type User struct {
Id string
Username string
Password string
Profile Profile
}
type Profile struct {
Gender string
Age int
Address string
Email string
}
func AddUser(u User) string {
u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10)
UserList[u.Id] = &u
return u.Id
}
func GetUser(uid string) (u *User, err error) {
if u, ok := UserList[uid]; ok {
return u, nil
}
return nil, errors.New("User not exists")
}
func GetAllUsers() map[string]*User {
return UserList
}
func UpdateUser(uid string, uu *User) (a *User, err error) {
if u, ok := UserList[uid]; ok {
if uu.Username != "" {
u.Username = uu.Username
}
if uu.Password != "" {
u.Password = uu.Password
}
if uu.Profile.Age != 0 {
u.Profile.Age = uu.Profile.Age
}
if uu.Profile.Address != "" {
u.Profile.Address = uu.Profile.Address
}
if uu.Profile.Gender != "" {
u.Profile.Gender = uu.Profile.Gender
}
if uu.Profile.Email != "" {
u.Profile.Email = uu.Profile.Email
}
return u, nil
}
return nil, errors.New("User Not Exist")
}
func Login(username, password string) bool {
for _, u := range UserList {
if u.Username == username && u.Password == password {
return true
}
}
return false
}
func DeleteUser(uid string) {
delete(UserList, uid)
}
`
var hproseAddFunctions = []string{}
func init() {
cmdHproseapp.Flag.Var(&tables, "tables", "List of table names separated by a comma.")
cmdHproseapp.Flag.Var(&driver, "driver", "Database driver. Either mysql, postgres or sqlite.")
cmdHproseapp.Flag.Var(&conn, "conn", "Connection string used by the driver to connect to a database instance.")
}
func createhprose(cmd *Command, args []string) int {
output := cmd.Out()
if len(args) != 1 {
logger.Fatal("Argument [appname] is missing")
}
curpath, _ := os.Getwd()
if len(args) > 1 {
cmd.Flag.Parse(args[1:])
}
apppath, packpath, err := checkEnv(args[0])
if err != nil {
logger.Fatalf("%s", err)
}
if driver == "" {
driver = "mysql"
}
if conn == "" {
}
logger.Info("Creating Hprose application...")
os.MkdirAll(apppath, 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m")
os.Mkdir(path.Join(apppath, "conf"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m")
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m")
WriteToFile(path.Join(apppath, "conf", "app.conf"),
strings.Replace(hproseconf, "{{.Appname}}", args[0], -1))
if conn != "" {
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(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
maingoContent := strings.Replace(hproseMainconngo, "{{.Appname}}", packpath, -1)
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1)
maingoContent = strings.Replace(maingoContent, "{{HproseFunctionList}}", strings.Join(hproseAddFunctions, ""), -1)
if driver == "mysql" {
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/go-sql-driver/mysql"`, -1)
} else if driver == "postgres" {
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1)
}
WriteToFile(path.Join(apppath, "main.go"),
strings.Replace(
maingoContent,
"{{.conn}}",
conn.String(),
-1,
),
)
} else {
os.Mkdir(path.Join(apppath, "models"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m")
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "models", "object.go"), apiModels)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "models", "user.go"), apiModels2)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m")
WriteToFile(path.Join(apppath, "main.go"),
strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1))
}
logger.Success("New Hprose application successfully created!")
return 0
}

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package colors
import ( import (
"fmt" "fmt"
@ -53,7 +53,7 @@ func NewModeColorWriter(w io.Writer, mode outputMode) io.Writer {
return w return w
} }
func bold(message string) string { func Bold(message string) string {
return fmt.Sprintf("\x1b[1m%s\x1b[21m", message) return fmt.Sprintf("\x1b[1m%s\x1b[21m", message)
} }
@ -102,47 +102,47 @@ func Magenta(message string) string {
return fmt.Sprintf("\x1b[35m%s\x1b[0m", message) return fmt.Sprintf("\x1b[35m%s\x1b[0m", message)
} }
// BlackBold returns a black bold string // BlackBold returns a black Bold string
func BlackBold(message string) string { func BlackBold(message string) string {
return fmt.Sprintf("\x1b[30m%s\x1b[0m", bold(message)) return fmt.Sprintf("\x1b[30m%s\x1b[0m", Bold(message))
} }
// WhiteBold returns a white bold string // WhiteBold returns a white Bold string
func WhiteBold(message string) string { func WhiteBold(message string) string {
return fmt.Sprintf("\x1b[37m%s\x1b[0m", bold(message)) return fmt.Sprintf("\x1b[37m%s\x1b[0m", Bold(message))
} }
// CyanBold returns a cyan bold string // CyanBold returns a cyan Bold string
func CyanBold(message string) string { func CyanBold(message string) string {
return fmt.Sprintf("\x1b[36m%s\x1b[0m", bold(message)) return fmt.Sprintf("\x1b[36m%s\x1b[0m", Bold(message))
} }
// BlueBold returns a blue bold string // BlueBold returns a blue Bold string
func BlueBold(message string) string { func BlueBold(message string) string {
return fmt.Sprintf("\x1b[34m%s\x1b[0m", bold(message)) return fmt.Sprintf("\x1b[34m%s\x1b[0m", Bold(message))
} }
// RedBold returns a red bold string // RedBold returns a red Bold string
func RedBold(message string) string { func RedBold(message string) string {
return fmt.Sprintf("\x1b[31m%s\x1b[0m", bold(message)) return fmt.Sprintf("\x1b[31m%s\x1b[0m", Bold(message))
} }
// GreenBold returns a green bold string // GreenBold returns a green Bold string
func GreenBold(message string) string { func GreenBold(message string) string {
return fmt.Sprintf("\x1b[32m%s\x1b[0m", bold(message)) return fmt.Sprintf("\x1b[32m%s\x1b[0m", Bold(message))
} }
// YellowBold returns a yellow bold string // YellowBold returns a yellow Bold string
func YellowBold(message string) string { func YellowBold(message string) string {
return fmt.Sprintf("\x1b[33m%s\x1b[0m", bold(message)) return fmt.Sprintf("\x1b[33m%s\x1b[0m", Bold(message))
} }
// GrayBold returns a gray bold string // GrayBold returns a gray Bold string
func GrayBold(message string) string { func GrayBold(message string) string {
return fmt.Sprintf("\x1b[37m%s\x1b[0m", bold(message)) return fmt.Sprintf("\x1b[37m%s\x1b[0m", Bold(message))
} }
// MagentaBold returns a magenta bold string // MagentaBold returns a magenta Bold string
func MagentaBold(message string) string { func MagentaBold(message string) string {
return fmt.Sprintf("\x1b[35m%s\x1b[0m", bold(message)) return fmt.Sprintf("\x1b[35m%s\x1b[0m", Bold(message))
} }

View File

@ -14,7 +14,7 @@
// +build !windows // +build !windows
package main package colors
import "io" import "io"

View File

@ -14,7 +14,7 @@
// +build windows // +build windows
package main package colors
import ( import (
"bytes" "bytes"

View File

@ -11,7 +11,7 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package beeLogger
import ( import (
"errors" "errors"
@ -22,6 +22,9 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"text/template" "text/template"
"time"
"github.com/beego/bee/logger/colors"
) )
var errInvalidLogLevel = errors.New("logger: invalid log level") var errInvalidLogLevel = errors.New("logger: invalid log level")
@ -59,6 +62,8 @@ type LogRecord struct {
LineNo int LineNo int
} }
var Log = GetBeeLogger(os.Stdout)
var ( var (
logRecordTemplate *template.Template logRecordTemplate *template.Template
debugLogRecordTemplate *template.Template debugLogRecordTemplate *template.Template
@ -80,11 +85,15 @@ func GetBeeLogger(w io.Writer) *BeeLogger {
"EndLine": EndLine, "EndLine": EndLine,
} }
logRecordTemplate, err = template.New("simpleLogFormat").Funcs(funcs).Parse(simpleLogFormat) logRecordTemplate, err = template.New("simpleLogFormat").Funcs(funcs).Parse(simpleLogFormat)
MustCheck(err) if err != nil {
panic(err)
}
debugLogRecordTemplate, err = template.New("debugLogFormat").Funcs(funcs).Parse(debugLogFormat) debugLogRecordTemplate, err = template.New("debugLogFormat").Funcs(funcs).Parse(debugLogFormat)
MustCheck(err) if err != nil {
panic(err)
}
instance = &BeeLogger{output: NewColorWriter(w)} instance = &BeeLogger{output: colors.NewColorWriter(w)}
}) })
return instance return instance
} }
@ -93,7 +102,17 @@ func GetBeeLogger(w io.Writer) *BeeLogger {
func (l *BeeLogger) SetOutput(w io.Writer) { func (l *BeeLogger) SetOutput(w io.Writer) {
l.mu.Lock() l.mu.Lock()
defer l.mu.Unlock() defer l.mu.Unlock()
l.output = NewColorWriter(w) l.output = colors.NewColorWriter(w)
}
// Now returns the current local time in the specified layout
func Now(layout string) string {
return time.Now().Format(layout)
}
// EndLine returns the a newline escape character
func EndLine() string {
return "\n"
} }
func (l *BeeLogger) getLevelTag(level int) string { func (l *BeeLogger) getLevelTag(level int) string {
@ -122,21 +141,21 @@ func (l *BeeLogger) getLevelTag(level int) string {
func (l *BeeLogger) getColorLevel(level int) string { func (l *BeeLogger) getColorLevel(level int) string {
switch level { switch level {
case levelCritical: case levelCritical:
return RedBold(l.getLevelTag(level)) return colors.RedBold(l.getLevelTag(level))
case levelFatal: case levelFatal:
return RedBold(l.getLevelTag(level)) return colors.RedBold(l.getLevelTag(level))
case levelInfo: case levelInfo:
return BlueBold(l.getLevelTag(level)) return colors.BlueBold(l.getLevelTag(level))
case levelHint: case levelHint:
return CyanBold(l.getLevelTag(level)) return colors.CyanBold(l.getLevelTag(level))
case levelDebug: case levelDebug:
return YellowBold(l.getLevelTag(level)) return colors.YellowBold(l.getLevelTag(level))
case levelError: case levelError:
return RedBold(l.getLevelTag(level)) return colors.RedBold(l.getLevelTag(level))
case levelWarn: case levelWarn:
return YellowBold(l.getLevelTag(level)) return colors.YellowBold(l.getLevelTag(level))
case levelSuccess: case levelSuccess:
return GreenBold(l.getLevelTag(level)) return colors.GreenBold(l.getLevelTag(level))
default: default:
panic(errInvalidLogLevel) panic(errInvalidLogLevel)
} }
@ -157,15 +176,17 @@ func (l *BeeLogger) mustLog(level int, message string, args ...interface{}) {
} }
err := logRecordTemplate.Execute(l.output, record) err := logRecordTemplate.Execute(l.output, record)
MustCheck(err) if err != nil {
panic(err)
}
} }
// mustLogDebug logs a debug message only if debug mode // mustLogDebug logs a debug message only if debug mode
// is enabled. i.e. DEBUG_ENABLED="1" // is enabled. i.e. DEBUG_ENABLED="1"
func (l *BeeLogger) mustLogDebug(message string, file string, line int, args ...interface{}) { func (l *BeeLogger) mustLogDebug(message string, file string, line int, args ...interface{}) {
if !IsDebugEnabled() { //if !IsDebugEnabled() {
return // return
} //}
// Change the output to Stderr // Change the output to Stderr
l.SetOutput(os.Stderr) l.SetOutput(os.Stderr)
@ -179,7 +200,9 @@ func (l *BeeLogger) mustLogDebug(message string, file string, line int, args ...
Filename: filepath.Base(file), Filename: filepath.Base(file),
} }
err := debugLogRecordTemplate.Execute(l.output, record) err := debugLogRecordTemplate.Execute(l.output, record)
MustCheck(err) if err != nil {
panic(err)
}
} }
// Debug outputs a debug log message // Debug outputs a debug log message

59
main.go Normal file
View File

@ -0,0 +1,59 @@
package main
import (
"flag"
"log"
"os"
"strings"
"github.com/beego/bee/cmd"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/generate/swaggergen"
"github.com/beego/bee/utils"
)
func main() {
currentpath, _ := os.Getwd()
flag.Usage = cmd.Usage
flag.Parse()
log.SetFlags(0)
args := flag.Args()
if len(args) < 1 {
cmd.Usage()
}
if args[0] == "help" {
cmd.Help(args[1:])
return
}
for _, c := range commands.AvailableCommands {
if c.Name() == args[0] && c.Run != nil {
c.Flag.Usage = func() { c.Usage() }
if c.CustomFlags {
args = args[1:]
} else {
c.Flag.Parse(args[1:])
args = c.Flag.Args()
}
if c.PreRun != nil {
c.PreRun(c, args)
}
// Check if current directory is inside the GOPATH,
// if so parse the packages inside it.
if strings.Contains(currentpath, utils.GetGOPATHs()[0]+"/src") && cmd.IfGenerateDocs(c.Name(), args) {
swaggergen.ParsePackagesFromDir(currentpath)
}
os.Exit(c.Run(c, args))
return
}
}
utils.PrintErrorAndExit("Unknown subcommand", cmd.ErrorTemplate)
}

View File

@ -1,155 +0,0 @@
// 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 (
"archive/zip"
"fmt"
"io"
"net/http"
"os"
"strings"
)
var cmdRundocs = &Command{
UsageLine: "rundocs [-isDownload=true] [-docport=8888]",
Short: "rundocs will run the docs server,default is 8089",
Long: `
-d meaning will download the docs file from github
-p meaning server the Server on which port, default is 8089
`,
}
var (
swaggerVersion = "2"
swaggerlink = "https://github.com/beego/swagger/archive/v" + swaggerVersion + ".zip"
)
type docValue string
func (d *docValue) String() string {
return fmt.Sprint(*d)
}
func (d *docValue) Set(value string) error {
*d = docValue(value)
return nil
}
var isDownload docValue
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")
}
func runDocs(cmd *Command, args []string) int {
if isDownload == "true" {
downloadFromURL(swaggerlink, "swagger.zip")
err := unzipAndDelete("swagger.zip")
if err != nil {
logger.Errorf("Error while unzipping 'swagger.zip' file: %s", err)
}
}
if docport == "" {
docport = "8089"
}
if _, err := os.Stat("swagger"); err != nil && os.IsNotExist(err) {
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)
}
return 0
}
func downloadFromURL(url, fileName string) {
var down bool
if fd, err := os.Stat(fileName); err != nil && os.IsNotExist(err) {
down = true
} else if fd.Size() == int64(0) {
down = true
} else {
logger.Infof("'%s' already exists", fileName)
return
}
if down {
logger.Infof("Downloading '%s' to '%s'...", url, fileName)
output, err := os.Create(fileName)
if err != nil {
logger.Errorf("Error while creating '%s': %s", fileName, err)
return
}
defer output.Close()
response, err := http.Get(url)
if err != nil {
logger.Errorf("Error while downloading '%s': %s", url, err)
return
}
defer response.Body.Close()
n, err := io.Copy(output, response.Body)
if err != nil {
logger.Errorf("Error while downloading '%s': %s", url, err)
return
}
logger.Successf("%d bytes downloaded!", n)
}
}
func unzipAndDelete(src string) error {
logger.Infof("Unzipping '%s'...", src)
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer r.Close()
rp := strings.NewReplacer("swagger-"+swaggerVersion, "swagger")
for _, f := range r.File {
rc, err := f.Open()
if err != nil {
return err
}
defer rc.Close()
fname := rp.Replace(f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(fname, f.Mode())
} else {
f, err := os.OpenFile(
fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, rc)
if err != nil {
return err
}
}
}
logger.Successf("Done! Deleting '%s'...", src)
return os.RemoveAll(src)
}

102
test.go
View File

@ -1,102 +0,0 @@
// 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 (
"os"
"os/exec"
path "path/filepath"
"time"
_ "github.com/smartystreets/goconvey/convey"
)
var cmdTest = &Command{
UsageLine: "test [appname]",
Short: "test the app",
Long: ``,
}
func init() {
cmdTest.Run = testApp
cmdTest.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
}
func safePathAppend(arr []string, paths ...string) []string {
for _, path := range paths {
if pathExists(path) {
arr = append(arr, path)
}
}
return arr
}
func pathExists(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}
var started = make(chan bool)
func testApp(cmd *Command, args []string) int {
if len(args) != 1 {
logger.Fatalf("Failed to start: %s", "argument 'appname' is missing")
}
currpath, _ := os.Getwd()
logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath)
err := loadConfig()
if err != nil {
logger.Fatalf("Failed to load configuration: %s", err)
}
var paths []string
readAppDirectories(currpath, &paths)
NewWatcher(paths, nil, false)
appname = args[0]
for {
select {
case <-started:
runTest()
}
}
}
func runTest() {
logger.Info("Start testing...")
time.Sleep(time.Second * 1)
crupwd, _ := os.Getwd()
testDir := path.Join(crupwd, "tests")
if pathExists(testDir) {
os.Chdir(testDir)
}
var err error
icmd := exec.Command("go", "test")
icmd.Stdout = os.Stdout
icmd.Stderr = os.Stderr
logger.Info("============== Test Begin ===================")
err = icmd.Run()
logger.Info("============== Test End =====================")
if err != nil {
logger.Error("============== Test failed ===================")
logger.Errorf("Cause: %s", err)
return
}
logger.Success("Test Completed")
}

View File

@ -1,28 +0,0 @@
package router
import (
"github.com/astaxie/beego"
)
type Router struct {
beego.Controller
}
func (r *Router) Get() {
}
func (r *Router) Post() {
}
type Controller struct {
}
func (c *Controller) Put() {
}
func (c *Controller) Delete() {
}

14
utils/doc_value.go Normal file
View File

@ -0,0 +1,14 @@
package utils
import "fmt"
type DocValue string
func (d *DocValue) String() string {
return fmt.Sprint(*d)
}
func (d *DocValue) Set(value string) error {
*d = DocValue(value)
return nil
}

14
utils/list_opts.go Normal file
View File

@ -0,0 +1,14 @@
package utils
import "fmt"
type ListOpts []string
func (opts *ListOpts) String() string {
return fmt.Sprint(*opts)
}
func (opts *ListOpts) Set(value string) error {
*opts = append(*opts, value)
return nil
}

15
utils/str_flag.go Normal file
View File

@ -0,0 +1,15 @@
package utils
import "fmt"
// The string flag list, implemented flag.Value interface
type StrFlags []string
func (s *StrFlags) String() string {
return fmt.Sprintf("%s", *s)
}
func (s *StrFlags) Set(value string) error {
*s = append(*s, value)
return nil
}

View File

@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package main package utils
import ( import (
"bytes" "bytes"
@ -26,7 +26,9 @@ import (
"runtime" "runtime"
"strings" "strings"
"text/template" "text/template"
"time"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
) )
// Go is a basic promise implementation: it wraps calls a function in a goroutine // Go is a basic promise implementation: it wraps calls a function in a goroutine
@ -39,18 +41,8 @@ func Go(f func() error) chan error {
return ch return ch
} }
// Now returns the current local time in the specified layout
func Now(layout string) string {
return time.Now().Format(layout)
}
// EndLine returns the a newline escape character
func EndLine() string {
return "\n"
}
// IsExist returns whether a file or directory exists. // IsExist returns whether a file or directory exists.
func isExist(path string) bool { func IsExist(path string) bool {
_, err := os.Stat(path) _, err := os.Stat(path)
return err == nil || os.IsExist(err) return err == nil || os.IsExist(err)
} }
@ -99,7 +91,7 @@ func IsBeegoProject(thePath string) bool {
}() }()
if err := <-c; err != nil { if err := <-c; err != nil {
logger.Fatalf("Unable to walk '%s' tree: %s", thePath, err) beeLogger.Log.Fatalf("Unable to walk '%s' tree: %s", thePath, err)
} }
if len(mainFiles) > 0 { if len(mainFiles) > 0 {
@ -113,7 +105,7 @@ func IsBeegoProject(thePath string) bool {
func SearchGOPATHs(app string) (bool, string, string) { func SearchGOPATHs(app string) (bool, string, string) {
gps := GetGOPATHs() gps := GetGOPATHs()
if len(gps) == 0 { if len(gps) == 0 {
logger.Fatal("GOPATH environment variable is not set or empty") beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
} }
// Lookup the application inside the user workspace(s) // Lookup the application inside the user workspace(s)
@ -127,7 +119,7 @@ func SearchGOPATHs(app string) (bool, string, string) {
currentPath = app currentPath = app
} }
if isExist(currentPath) { if IsExist(currentPath) {
return true, gopath, currentPath return true, gopath, currentPath
} }
} }
@ -139,11 +131,11 @@ func SearchGOPATHs(app string) (bool, string, string) {
// confirmations. If the input is not recognized, it will ask again. The function does not return // confirmations. If the input is not recognized, it will ask again. The function does not return
// until it gets a valid response from the user. Typically, you should use fmt to print out a question // until it gets a valid response from the user. Typically, you should use fmt to print out a question
// before calling askForConfirmation. E.g. fmt.Println("WARNING: Are you sure? (yes/no)") // before calling askForConfirmation. E.g. fmt.Println("WARNING: Are you sure? (yes/no)")
func askForConfirmation() bool { func AskForConfirmation() bool {
var response string var response string
_, err := fmt.Scanln(&response) _, err := fmt.Scanln(&response)
if err != nil { if err != nil {
logger.Fatalf("%s", err) beeLogger.Log.Fatalf("%s", err)
} }
okayResponses := []string{"y", "Y", "yes", "Yes", "YES"} okayResponses := []string{"y", "Y", "yes", "Yes", "YES"}
nokayResponses := []string{"n", "N", "no", "No", "NO"} nokayResponses := []string{"n", "N", "no", "No", "NO"}
@ -153,7 +145,7 @@ func askForConfirmation() bool {
return false return false
} else { } else {
fmt.Println("Please type yes or no and then press enter:") fmt.Println("Please type yes or no and then press enter:")
return askForConfirmation() return AskForConfirmation()
} }
} }
@ -167,7 +159,7 @@ func containsString(slice []string, element string) bool {
} }
// snake string, XxYy to xx_yy // snake string, XxYy to xx_yy
func snakeString(s string) string { func SnakeString(s string) string {
data := make([]byte, 0, len(s)*2) data := make([]byte, 0, len(s)*2)
j := false j := false
num := len(s) num := len(s)
@ -184,17 +176,17 @@ func snakeString(s string) string {
return strings.ToLower(string(data[:])) return strings.ToLower(string(data[:]))
} }
func camelString(s string) string { func CamelString(s string) string {
data := make([]byte, 0, len(s)) data := make([]byte, 0, len(s))
j := false j := false
k := false k := false
num := len(s) - 1 num := len(s) - 1
for i := 0; i <= num; i++ { for i := 0; i <= num; i++ {
d := s[i] d := s[i]
if k == false && d >= 'A' && d <= 'Z' { if !k && d >= 'A' && d <= 'Z' {
k = true k = true
} }
if d >= 'a' && d <= 'z' && (j || k == false) { if d >= 'a' && d <= 'z' && (j || !k) {
d = d - 32 d = d - 32
j = false j = false
k = true k = true
@ -210,7 +202,7 @@ func camelString(s string) string {
// camelCase converts a _ delimited string to camel case // camelCase converts a _ delimited string to camel case
// e.g. very_important_person => VeryImportantPerson // e.g. very_important_person => VeryImportantPerson
func camelCase(in string) string { func CamelCase(in string) string {
tokens := strings.Split(in, "_") tokens := strings.Split(in, "_")
for i := range tokens { for i := range tokens {
tokens[i] = strings.Title(strings.Trim(tokens[i], " ")) tokens[i] = strings.Title(strings.Trim(tokens[i], " "))
@ -219,25 +211,13 @@ func camelCase(in string) string {
} }
// formatSourceCode formats source files // formatSourceCode formats source files
func formatSourceCode(filename string) { func FormatSourceCode(filename string) {
cmd := exec.Command("gofmt", "-w", filename) cmd := exec.Command("gofmt", "-w", filename)
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
logger.Warnf("Error while running gofmt: %s", err) beeLogger.Log.Warnf("Error while running gofmt: %s", err)
} }
} }
// The string flag list, implemented flag.Value interface
type strFlags []string
func (s *strFlags) String() string {
return fmt.Sprintf("%s", *s)
}
func (s *strFlags) Set(value string) error {
*s = append(*s, value)
return nil
}
// CloseFile attempts to close the passed file // CloseFile attempts to close the passed file
// or panics with the actual error // or panics with the actual error
func CloseFile(f *os.File) { func CloseFile(f *os.File) {
@ -252,11 +232,6 @@ func MustCheck(err error) {
} }
} }
func exitPrint(con string) {
fmt.Fprintln(os.Stderr, con)
os.Exit(2)
}
// WriteToFile creates a file and writes content to it // WriteToFile creates a file and writes content to it
func WriteToFile(filename, content string) { func WriteToFile(filename, content string) {
f, err := os.Create(filename) f, err := os.Create(filename)
@ -273,13 +248,13 @@ func IsDebugEnabled() bool {
} }
// __FILE__ returns the file name in which the function was invoked // __FILE__ returns the file name in which the function was invoked
func __FILE__() string { func FILE() string {
_, file, _, _ := runtime.Caller(1) _, file, _, _ := runtime.Caller(1)
return file return file
} }
// __LINE__ returns the line number at which the function was invoked // __LINE__ returns the line number at which the function was invoked
func __LINE__() int { func LINE() int {
_, _, line, _ := runtime.Caller(1) _, _, line, _ := runtime.Caller(1)
return line return line
} }
@ -288,9 +263,9 @@ func __LINE__() int {
func BeeFuncMap() template.FuncMap { func BeeFuncMap() template.FuncMap {
return template.FuncMap{ return template.FuncMap{
"trim": strings.TrimSpace, "trim": strings.TrimSpace,
"bold": bold, "bold": colors.Bold,
"headline": MagentaBold, "headline": colors.MagentaBold,
"foldername": RedBold, "foldername": colors.RedBold,
"endline": EndLine, "endline": EndLine,
"tmpltostr": TmplToString, "tmpltostr": TmplToString,
} }
@ -307,3 +282,59 @@ func TmplToString(tmpl string, data interface{}) string {
return doc.String() return doc.String()
} }
// EndLine returns the a newline escape character
func EndLine() string {
return "\n"
}
func Tmpl(text string, data interface{}) {
output := colors.NewColorWriter(os.Stderr)
t := template.New("Usage").Funcs(BeeFuncMap())
template.Must(t.Parse(text))
err := t.Execute(output, data)
if err != nil {
beeLogger.Log.Error(err.Error())
}
}
func CheckEnv(appname string) (apppath, packpath string, err error) {
gps := GetGOPATHs()
if len(gps) == 0 {
beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
}
currpath, _ := os.Getwd()
currpath = path.Join(currpath, appname)
for _, gpath := range gps {
gsrcpath := path.Join(gpath, "src")
if strings.HasPrefix(currpath, gsrcpath) {
packpath = strings.Replace(currpath[len(gsrcpath)+1:], string(filepath.Separator), "/", -1)
return currpath, packpath, nil
}
}
// In case of multiple paths in the GOPATH, by default
// we use the first path
gopath := gps[0]
beeLogger.Log.Warn("You current workdir is not inside $GOPATH/src.")
beeLogger.Log.Debugf("GOPATH: %s", FILE(), LINE(), gopath)
gosrcpath := path.Join(gopath, "src")
apppath = path.Join(gosrcpath, appname)
if _, e := os.Stat(apppath); !os.IsNotExist(e) {
err = fmt.Errorf("Cannot create application without removing '%s' first.", apppath)
beeLogger.Log.Errorf("Path '%s' already exists", apppath)
return
}
packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(filepath.Separator)), "/")
return
}
func PrintErrorAndExit(message, errorTemplate string) {
Tmpl(fmt.Sprintf(errorTemplate, message), nil)
os.Exit(2)
}