Browse Source

Refactor!

create sub packages
delete unused code
delete code from not use command cmdRouter,cmdTest, cmdRundocs
make command plugins
check with gosimple,staticcheck,go vet,unused,unconvert
tags/v1.8.3
Sergey Lanzman 2 years ago
parent
commit
c538bfbc8f
47 changed files with 1838 additions and 2467 deletions
  1. +0
    -280
      autorouter.go
  2. +0
    -9
      autorouter_test.go
  3. +0
    -258
      bee.go
  4. +102
    -0
      cmd/bee.go
  5. +41
    -71
      cmd/commands/api/apiapp.go
  6. +24
    -19
      cmd/commands/bale/bale.go
  7. +16
    -10
      cmd/commands/beefix/fix.go
  8. +94
    -0
      cmd/commands/command.go
  9. +32
    -24
      cmd/commands/dockerize/dockerize.go
  10. +203
    -0
      cmd/commands/generate/generate.go
  11. +119
    -0
      cmd/commands/hprose/hprose.go
  12. +79
    -91
      cmd/commands/migrate/migrate.go
  13. +80
    -70
      cmd/commands/new/new.go
  14. +36
    -59
      cmd/commands/pack/pack.go
  15. +88
    -0
      cmd/commands/run/docs.go
  16. +7
    -6
      cmd/commands/run/reload.go
  17. +49
    -46
      cmd/commands/run/run.go
  18. +32
    -28
      cmd/commands/run/watch.go
  19. +18
    -7
      cmd/commands/version/banner.go
  20. +33
    -23
      cmd/commands/version/version.go
  21. +0
    -261
      code.go
  22. +25
    -26
      config/conf.go
  23. +0
    -200
      g.go
  24. +0
    -48
      g_scaffold.go
  25. +23
    -0
      generate/g.go
  26. +74
    -71
      generate/g_appcode.go
  27. +14
    -10
      generate/g_controllers.go
  28. +266
    -21
      generate/g_hproseappcode.go
  29. +31
    -27
      generate/g_migration.go
  30. +15
    -11
      generate/g_model.go
  31. +50
    -0
      generate/g_scaffold.go
  32. +17
    -13
      generate/g_views.go
  33. +25
    -35
      generate/swaggergen/g_docs.go
  34. +0
    -369
      hproseapp.go
  35. +20
    -20
      logger/colors/color.go
  36. +1
    -1
      logger/colors/colorwriter.go
  37. +1
    -1
      logger/colors/colorwriter_windows.go
  38. +41
    -18
      logger/logger.go
  39. +59
    -0
      main.go
  40. +0
    -155
      rundocs.go
  41. +0
    -102
      test.go
  42. +0
    -28
      testdata/router/router.go
  43. +14
    -0
      utils/doc_value.go
  44. +14
    -0
      utils/list_opts.go
  45. +15
    -0
      utils/str_flag.go
  46. +79
    -48
      utils/utils.go
  47. +1
    -1
      vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go

+ 0
- 280
autorouter.go 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
}

+ 0
- 9
autorouter_test.go View File

@@ -1,9 +0,0 @@
package main

import (
"testing"
)

func TestGetControllerInfo(t *testing.T) {
getControllerInfo("testdata/router/")
}

+ 0
- 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
- 0
cmd/bee.go 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)
}

apiapp.go → cmd/commands/api/apiapp.go View File

@@ -12,16 +12,22 @@
// License for the specific language governing permissions and limitations
// under the License.

package main
package apiapp

import (
"fmt"
"os"
path "path/filepath"
"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,
UsageLine: "api [appname]",
Short: "Creates a Beego API application",
@@ -50,10 +56,9 @@ var cmdApiapp = &Command{
└── object.go
└── user.go
`,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() },
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: createapi,
}

var apiconf = `appname = {{.Appname}}
httpport = 8080
runmode = dev
@@ -134,7 +139,7 @@ func init() {
}
`

var apiModels = `package models
var ApiModels = `package models

import (
"errors"
@@ -189,7 +194,7 @@ func Delete(ObjectId string) {

`

var apiModels2 = `package models
var ApiModels2 = `package models

import (
"errors"
@@ -532,33 +537,32 @@ func TestGet(t *testing.T) {
`

func init() {
cmdApiapp.Flag.Var(&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(&conn, "conn", "Connection string used by the driver to connect to a database instance.")
CmdApiapp.Flag.Var(&generate.Tables, "tables", "List of table names separated by a comma.")
CmdApiapp.Flag.Var(&generate.SQLDriver, "driver", "Database driver. Either mysql, postgres or sqlite.")
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()

if len(args) < 1 {
logger.Fatal("Argument [appname] is missing")
beeLogger.Log.Fatal("Argument [appname] is missing")
}

if len(args) > 1 {
cmd.Flag.Parse(args[1:])
}

apppath, packpath, err := checkEnv(args[0])
apppath, packpath, err := utils.CheckEnv(args[0])
if err != nil {
logger.Fatalf("%s", err)
beeLogger.Log.Fatalf("%s", err)
}
if driver == "" {
driver = "mysql"
}
if conn == "" {
if generate.SQLDriver == "" {
generate.SQLDriver = "mysql"
}

logger.Info("Creating API...")
beeLogger.Log.Info("Creating API...")

os.MkdirAll(apppath, 0755)
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)
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")
WriteToFile(path.Join(apppath, "conf", "app.conf"),
utils.WriteToFile(path.Join(apppath, "conf", "app.conf"),
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")
maingoContent := strings.Replace(apiMainconngo, "{{.Appname}}", packpath, -1)
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1)
if driver == "mysql" {
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(generate.SQLDriver), -1)
if generate.SQLDriver == "mysql" {
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)
}
WriteToFile(path.Join(apppath, "main.go"),
utils.WriteToFile(path.Join(apppath, "main.go"),
strings.Replace(
maingoContent,
"{{.conn}}",
conn.String(),
generate.SQLConn.String(),
-1,
),
)
logger.Infof("Using '%s' as 'driver'", driver)
logger.Infof("Using '%s' as 'conn'", conn)
logger.Infof("Using '%s' as 'tables'", tables)
generateAppcode(string(driver), string(conn), "3", string(tables), apppath)
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.GenerateAppcode(string(generate.SQLDriver), string(generate.SQLConn), "3", string(generate.Tables), apppath)
} 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")
@@ -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, "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))

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))

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))

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))

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")
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")
WriteToFile(path.Join(apppath, "main.go"),
utils.WriteToFile(path.Join(apppath, "main.go"),
strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1))
}
logger.Success("New API successfully created!")
beeLogger.Log.Success("New API successfully created!")
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
}

bale.go → cmd/commands/bale/bale.go View File

@@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations
// under the License.

package main
package bale

import (
"bytes"
@@ -24,9 +24,15 @@ import (
"path/filepath"
"runtime"
"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",
Short: "Transforms non-Go files to Go source files",
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.
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,
}

func runBale(cmd *Command, args []string) int {
err := loadConfig()
if err != nil {
logger.Fatalf("Failed to load configuration: %s", err)
}
func init() {
commands.AvailableCommands = append(commands.AvailableCommands, CmdBale)
}

func runBale(cmd *commands.Command, args []string) int {
os.RemoveAll("bale")
os.Mkdir("bale", os.ModePerm)

// Pack and compress data
for _, p := range conf.Bale.Dirs {
if !isExist(p) {
logger.Warnf("Skipped directory: %s", p)
for _, p := range config.Conf.Bale.Dirs {
if !utils.IsExist(p) {
beeLogger.Log.Warnf("Skipped directory: %s", p)
continue
}
logger.Infof("Packaging directory: %s", p)
beeLogger.Log.Infof("Packaging directory: %s", p)
filepath.Walk(p, walkFn)
}

// Generate auto-uncompress function.
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\tbale.R")))

fw, err := os.Create("bale.go")
if err != nil {
logger.Fatalf("Failed to create file: %s", err)
beeLogger.Log.Fatalf("Failed to create file: %s", err)
}
defer fw.Close()

_, err = fw.Write(buf.Bytes())
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
}

@@ -146,7 +151,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
// Open resource files
fr, err := os.Open(resPath)
if err != nil {
logger.Fatalf("Failed to read file: %s", err)
beeLogger.Log.Fatalf("Failed to read file: %s", err)
}

// Convert path
@@ -164,7 +169,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
os.MkdirAll(path.Dir(resPath), os.ModePerm)
fw, err := os.Create("bale/" + resPath + ".go")
if err != nil {
logger.Fatalf("Failed to create file: %s", err)
beeLogger.Log.Fatalf("Failed to create file: %s", err)
}
defer fw.Close()

@@ -184,7 +189,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
}

func filterSuffix(name string) bool {
for _, s := range conf.Bale.IngExt {
for _, s := range config.Conf.Bale.IngExt {
if strings.HasSuffix(name, s) {
return true
}

fix.go → cmd/commands/beefix/fix.go View File

@@ -1,4 +1,4 @@
package main
package beefix

import (
"fmt"
@@ -9,9 +9,14 @@ import (
"path/filepath"
"regexp"
"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",
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.
@@ -22,18 +27,19 @@ var cmdFix = &Command{
}

func init() {
cmdFix.Run = runFix
cmdFix.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() }
CmdFix.Run = runFix
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()

logger.Info("Upgrading the application...")
beeLogger.Log.Info("Upgrading the application...")

dir, err := os.Getwd()
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 {
@@ -50,13 +56,13 @@ func runFix(cmd *Command, args []string) int {
return nil
}
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 {
logger.Errorf("Could not fix file: %s", err)
beeLogger.Log.Errorf("Could not fix file: %s", err)
}
return err
})
logger.Success("Upgrade Done!")
beeLogger.Log.Success("Upgrade Done!")
return 0
}


+ 94
- 0
cmd/commands/command.go 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
}

dockerize.go → cmd/commands/dockerize/dockerize.go View File

@@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations
// under the License.

package main
package dockerize

import (
"flag"
@@ -21,21 +21,12 @@ import (
"path/filepath"
"strings"
"text/template"
)

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,
}
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
)

const dockerBuildTemplate = `FROM {{.BaseImage}}

@@ -67,6 +58,20 @@ type Dockerfile struct {
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 (
expose string
baseImage string
@@ -76,19 +81,22 @@ func init() {
fs := flag.NewFlagSet("dockerize", flag.ContinueOnError)
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.")
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 {
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")
dir, err := filepath.Abs(".")
MustCheck(err)
if err != nil {
beeLogger.Log.Error(err.Error())
}

appdir := strings.Replace(dir, gopath, "", 1)

@@ -112,15 +120,15 @@ func dockerizeApp(cmd *Command, args []string) int {
}

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")
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)

logger.Success("Dockerfile generated.")
beeLogger.Log.Success("Dockerfile generated.")
}

+ 203
- 0
cmd/commands/generate/generate.go 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")
}
}

+ 119
- 0
cmd/commands/hprose/hprose.go 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
}

migrate.go → cmd/commands/migrate/migrate.go View File

@@ -1,18 +1,4 @@
// 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
package migrate

import (
"database/sql"
@@ -23,9 +9,16 @@ import (
"strconv"
"strings"
"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]",
Short: "Runs database migrations",
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"]
`,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() },
Run: runMigration,
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: RunMigration,
}

var mDriver docValue
var mConn docValue
var mDriver utils.DocValue
var mConn utils.DocValue

func init() {
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(&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.")
commands.AvailableCommands = append(commands.AvailableCommands, CmdMigrate)
}

// 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()

gps := GetGOPATHs()
gps := utils.GetGOPATHs()
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]

logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath)

// Load the configuration
err := loadConfig()
if err != nil {
logger.Errorf("Failed to load configuration: %s", err)
}
beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath)

// Getting command line arguments
if len(args) != 0 {
cmd.Flag.Parse(args[1:])
}
if mDriver == "" {
mDriver = docValue(conf.Database.Driver)
mDriver = utils.DocValue(config.Conf.Database.Driver)
if mDriver == "" {
mDriver = "mysql"
}
}
if mConn == "" {
mConn = docValue(conf.Database.Conn)
mConn = utils.DocValue(config.Conf.Database.Conn)
if mConn == "" {
mConn = "root:@tcp(127.0.0.1:3306)/test"
}
}
logger.Infof("Using '%s' as 'driver'", mDriver)
logger.Infof("Using '%s' as 'conn'", mConn)
beeLogger.Log.Infof("Using '%s' as 'driver'", mDriver)
beeLogger.Log.Infof("Using '%s' as 'conn'", mConn)
driverStr, connStr := string(mDriver), string(mConn)
if len(args) == 0 {
// run all outstanding migrations
logger.Info("Running all outstanding migrations")
migrateUpdate(currpath, driverStr, connStr)
beeLogger.Log.Info("Running all outstanding migrations")
MigrateUpdate(currpath, driverStr, connStr)
} else {
mcmd := args[0]
switch mcmd {
case "rollback":
logger.Info("Rolling back the last migration operation")
migrateRollback(currpath, driverStr, connStr)
beeLogger.Log.Info("Rolling back the last migration operation")
MigrateRollback(currpath, driverStr, connStr)
case "reset":
logger.Info("Reseting all migrations")
migrateReset(currpath, driverStr, connStr)
beeLogger.Log.Info("Reseting all migrations")
MigrateReset(currpath, driverStr, connStr)
case "refresh":
logger.Info("Refreshing all migrations")
migrateRefresh(currpath, driverStr, connStr)
beeLogger.Log.Info("Refreshing all migrations")
MigrateRefresh(currpath, driverStr, connStr)
default:
logger.Fatal("Command is missing")
beeLogger.Log.Fatal("Command is missing")
}
}
logger.Success("Migration successful!")
beeLogger.Log.Success("Migration successful!")
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
func migrate(goal, currpath, driver, connStr string) {
dir := path.Join(currpath, "database", "migrations")
@@ -153,7 +121,7 @@ func migrate(goal, currpath, driver, connStr string) {
// Connect to database
db, err := sql.Open(driver, connStr)
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()

@@ -171,44 +139,44 @@ func migrate(goal, currpath, driver, connStr string) {
func checkForSchemaUpdateTable(db *sql.DB, driver string) {
showTableSQL := showMigrationsTableSQL(driver)
if rows, err := db.Query(showTableSQL); err != nil {
logger.Fatalf("Could not show migrations table: %s", err)
beeLogger.Log.Fatalf("Could not show migrations table: %s", err)
} else if !rows.Next() {
// No migrations table, create new ones
createTableSQL := createMigrationsTableSQL(driver)

logger.Infof("Creating 'migrations' table...")
beeLogger.Log.Infof("Creating 'migrations' table...")

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
selectTableSQL := selectMigrationsTableSQL(driver)
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 {
for rows.Next() {
var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte
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 :=
string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes)
if fieldStr == "id_migration" {
if keyStr != "PRI" || extraStr != "auto_increment" {
logger.Hint("Expecting KEY: PRI, EXTRA: auto_increment")
logger.Fatalf("Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s", keyStr, extraStr)
beeLogger.Log.Hint("Expecting KEY: PRI, EXTRA: auto_increment")
beeLogger.Log.Fatalf("Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s", keyStr, extraStr)
}
} else if fieldStr == "name" {
if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" {
logger.Hint("Expecting TYPE: varchar, NULL: YES")
logger.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr)
beeLogger.Log.Hint("Expecting TYPE: varchar, NULL: YES")
beeLogger.Log.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr)
}
} else if fieldStr == "created_at" {
if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" {
logger.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP")
logger.Fatalf("Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s", typeStr, defaultStr)
beeLogger.Log.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP")
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) {
sql := "SELECT name FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1"
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 {
if rows.Next() {
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:]
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 {
createdAt = t.Unix()
}
} else {
// migration table has no 'update' record, no point rolling back
if goal == "rollback" {
logger.Fatal("There is nothing to rollback")
beeLogger.Log.Fatal("There is nothing to rollback")
}
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) {
changeDir(dir)
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 {
content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -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, "{{Task}}", task, -1)
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)
cmd := exec.Command("go", "build", "-o", binary)
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))
removeTempFile(dir, binary)
removeTempFile(dir, binary+".go")
@@ -312,7 +280,7 @@ func runMigrationBinary(dir, binary string) {
cmd := exec.Command("./" + binary)
if out, err := cmd.CombinedOutput(); err != nil {
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+".go")
os.Exit(2)
@@ -325,7 +293,7 @@ func runMigrationBinary(dir, binary string) {
// It exits the system when encouter an error
func changeDir(dir string) {
if err := os.Chdir(dir); err != nil {
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) {
changeDir(dir)
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) {
for _, line := range strings.Split(o, "\n") {
if line != "" {
logger.Errorf("|> %s", line)
beeLogger.Log.Errorf("|> %s", line)
}
}
}
@@ -350,7 +318,7 @@ func formatShellErrOutput(o string) {
func formatShellOutput(o string) {
for _, line := range strings.Split(o, "\n") {
if line != "" {
logger.Infof("|> %s", line)
beeLogger.Log.Infof("|> %s", line)
}
}
}
@@ -421,3 +389,23 @@ CREATE TABLE migrations (
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)
}

new.go → cmd/commands/new/new.go View File

@@ -12,16 +12,22 @@
// License for the specific language governing permissions and limitations
// under the License.

package main
package new

import (
"fmt"
"os"
path "path/filepath"
"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]",
Short: "Creates a Beego application",
Long: `
@@ -47,74 +53,8 @@ Creates a Beego application for the given app name in the current directory.
└── index.tpl

`,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() },
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))