mirror of
https://github.com/beego/bee.git
synced 2024-11-01 00:00:53 +00:00
c538bfbc8f
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
341 lines
8.6 KiB
Go
341 lines
8.6 KiB
Go
// 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 utils
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"regexp"
|
|
"runtime"
|
|
"strings"
|
|
"text/template"
|
|
|
|
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
|
|
// and returns a channel which will later return the function's return value.
|
|
func Go(f func() error) chan error {
|
|
ch := make(chan error)
|
|
go func() {
|
|
ch <- f()
|
|
}()
|
|
return ch
|
|
}
|
|
|
|
// IsExist returns whether a file or directory exists.
|
|
func IsExist(path string) bool {
|
|
_, err := os.Stat(path)
|
|
return err == nil || os.IsExist(err)
|
|
}
|
|
|
|
// GetGOPATHs returns all paths in GOPATH variable.
|
|
func GetGOPATHs() []string {
|
|
gopath := os.Getenv("GOPATH")
|
|
var paths []string
|
|
if runtime.GOOS == "windows" {
|
|
gopath = strings.Replace(gopath, "\\", "/", -1)
|
|
paths = strings.Split(gopath, ";")
|
|
} else {
|
|
paths = strings.Split(gopath, ":")
|
|
}
|
|
return paths
|
|
}
|
|
|
|
// IsBeegoProject checks whether the current path is a Beego application or not
|
|
func IsBeegoProject(thePath string) bool {
|
|
mainFiles := []string{}
|
|
hasBeegoRegex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`)
|
|
c := make(chan error)
|
|
// Walk the application path tree to look for main files.
|
|
// Main files must satisfy the 'hasBeegoRegex' regular expression.
|
|
go func() {
|
|
filepath.Walk(thePath, func(fpath string, f os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
// Skip sub-directories
|
|
if !f.IsDir() {
|
|
var data []byte
|
|
data, err = ioutil.ReadFile(fpath)
|
|
if err != nil {
|
|
c <- err
|
|
return nil
|
|
}
|
|
|
|
if len(hasBeegoRegex.Find(data)) > 0 {
|
|
mainFiles = append(mainFiles, fpath)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
close(c)
|
|
}()
|
|
|
|
if err := <-c; err != nil {
|
|
beeLogger.Log.Fatalf("Unable to walk '%s' tree: %s", thePath, err)
|
|
}
|
|
|
|
if len(mainFiles) > 0 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// SearchGOPATHs searchs the user GOPATH(s) for the specified application name.
|
|
// It returns a boolean, the application's GOPATH and its full path.
|
|
func SearchGOPATHs(app string) (bool, string, string) {
|
|
gps := GetGOPATHs()
|
|
if len(gps) == 0 {
|
|
beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
|
|
}
|
|
|
|
// Lookup the application inside the user workspace(s)
|
|
for _, gopath := range gps {
|
|
var currentPath string
|
|
|
|
if !strings.Contains(app, "src") {
|
|
gopathsrc := path.Join(gopath, "src")
|
|
currentPath = path.Join(gopathsrc, app)
|
|
} else {
|
|
currentPath = app
|
|
}
|
|
|
|
if IsExist(currentPath) {
|
|
return true, gopath, currentPath
|
|
}
|
|
}
|
|
return false, "", ""
|
|
}
|
|
|
|
// askForConfirmation uses Scanln to parse user input. A user must type in "yes" or "no" and
|
|
// then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", and "Yes" all count as
|
|
// 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
|
|
// before calling askForConfirmation. E.g. fmt.Println("WARNING: Are you sure? (yes/no)")
|
|
func AskForConfirmation() bool {
|
|
var response string
|
|
_, err := fmt.Scanln(&response)
|
|
if err != nil {
|
|
beeLogger.Log.Fatalf("%s", err)
|
|
}
|
|
okayResponses := []string{"y", "Y", "yes", "Yes", "YES"}
|
|
nokayResponses := []string{"n", "N", "no", "No", "NO"}
|
|
if containsString(okayResponses, response) {
|
|
return true
|
|
} else if containsString(nokayResponses, response) {
|
|
return false
|
|
} else {
|
|
fmt.Println("Please type yes or no and then press enter:")
|
|
return AskForConfirmation()
|
|
}
|
|
}
|
|
|
|
func containsString(slice []string, element string) bool {
|
|
for _, elem := range slice {
|
|
if elem == element {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// snake string, XxYy to xx_yy
|
|
func SnakeString(s string) string {
|
|
data := make([]byte, 0, len(s)*2)
|
|
j := false
|
|
num := len(s)
|
|
for i := 0; i < num; i++ {
|
|
d := s[i]
|
|
if i > 0 && d >= 'A' && d <= 'Z' && j {
|
|
data = append(data, '_')
|
|
}
|
|
if d != '_' {
|
|
j = true
|
|
}
|
|
data = append(data, d)
|
|
}
|
|
return strings.ToLower(string(data[:]))
|
|
}
|
|
|
|
func CamelString(s string) string {
|
|
data := make([]byte, 0, len(s))
|
|
j := false
|
|
k := false
|
|
num := len(s) - 1
|
|
for i := 0; i <= num; i++ {
|
|
d := s[i]
|
|
if !k && d >= 'A' && d <= 'Z' {
|
|
k = true
|
|
}
|
|
if d >= 'a' && d <= 'z' && (j || !k) {
|
|
d = d - 32
|
|
j = false
|
|
k = true
|
|
}
|
|
if k && d == '_' && num > i && s[i+1] >= 'a' && s[i+1] <= 'z' {
|
|
j = true
|
|
continue
|
|
}
|
|
data = append(data, d)
|
|
}
|
|
return string(data[:])
|
|
}
|
|
|
|
// camelCase converts a _ delimited string to camel case
|
|
// e.g. very_important_person => VeryImportantPerson
|
|
func CamelCase(in string) string {
|
|
tokens := strings.Split(in, "_")
|
|
for i := range tokens {
|
|
tokens[i] = strings.Title(strings.Trim(tokens[i], " "))
|
|
}
|
|
return strings.Join(tokens, "")
|
|
}
|
|
|
|
// formatSourceCode formats source files
|
|
func FormatSourceCode(filename string) {
|
|
cmd := exec.Command("gofmt", "-w", filename)
|
|
if err := cmd.Run(); err != nil {
|
|
beeLogger.Log.Warnf("Error while running gofmt: %s", err)
|
|
}
|
|
}
|
|
|
|
// CloseFile attempts to close the passed file
|
|
// or panics with the actual error
|
|
func CloseFile(f *os.File) {
|
|
err := f.Close()
|
|
MustCheck(err)
|
|
}
|
|
|
|
// MustCheck panics when the error is not nil
|
|
func MustCheck(err error) {
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// WriteToFile creates a file and writes content to it
|
|
func WriteToFile(filename, content string) {
|
|
f, err := os.Create(filename)
|
|
MustCheck(err)
|
|
defer CloseFile(f)
|
|
_, err = f.WriteString(content)
|
|
MustCheck(err)
|
|
}
|
|
|
|
// IsDebugEnabled checks if DEBUG_ENABLED is set or not
|
|
func IsDebugEnabled() bool {
|
|
debugMode := os.Getenv("DEBUG_ENABLED")
|
|
return map[string]bool{"1": true, "0": false}[debugMode]
|
|
}
|
|
|
|
// __FILE__ returns the file name in which the function was invoked
|
|
func FILE() string {
|
|
_, file, _, _ := runtime.Caller(1)
|
|
return file
|
|
}
|
|
|
|
// __LINE__ returns the line number at which the function was invoked
|
|
func LINE() int {
|
|
_, _, line, _ := runtime.Caller(1)
|
|
return line
|
|
}
|
|
|
|
// BeeFuncMap returns a FuncMap of functions used in different templates.
|
|
func BeeFuncMap() template.FuncMap {
|
|
return template.FuncMap{
|
|
"trim": strings.TrimSpace,
|
|
"bold": colors.Bold,
|
|
"headline": colors.MagentaBold,
|
|
"foldername": colors.RedBold,
|
|
"endline": EndLine,
|
|
"tmpltostr": TmplToString,
|
|
}
|
|
}
|
|
|
|
// TmplToString parses a text template and return the result as a string.
|
|
func TmplToString(tmpl string, data interface{}) string {
|
|
t := template.New("tmpl").Funcs(BeeFuncMap())
|
|
template.Must(t.Parse(tmpl))
|
|
|
|
var doc bytes.Buffer
|
|
err := t.Execute(&doc, data)
|
|
MustCheck(err)
|
|
|
|
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)
|
|
}
|