Merge pull request #550 from franzwilhelm/develop

Add support for apps outside of GOPATH with go modules, fixes #549
This commit is contained in:
Faissal Elamraoui 2018-10-08 14:00:36 +02:00 committed by GitHub
commit e3e401cadd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 88 additions and 63 deletions

View File

@ -79,37 +79,44 @@ func init() {
commands.AvailableCommands = append(commands.AvailableCommands, CmdRun) commands.AvailableCommands = append(commands.AvailableCommands, CmdRun)
} }
// RunApp locates files to watch, and starts the beego application
func RunApp(cmd *commands.Command, args []string) int { func RunApp(cmd *commands.Command, args []string) int {
if len(args) == 0 || args[0] == "watchall" { // The default app path is the current working directory
currpath, _ = os.Getwd() appPath, _ := os.Getwd()
if found, _gopath, _ := utils.SearchGOPATHs(currpath); found {
appname = path.Base(currpath)
currentGoPath = _gopath
} else {
beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", currpath)
}
} else {
// Check if passed Bee application path/name exists in the GOPATH(s)
if found, _gopath, _path := utils.SearchGOPATHs(args[0]); found {
currpath = _path
currentGoPath = _gopath
appname = path.Base(currpath)
} else {
beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", args[0])
}
if strings.HasSuffix(appname, ".go") && utils.IsExist(currpath) { // If an argument is presented, we use it as the app path
if len(args) != 0 && args[0] != "watchall" {
if path.IsAbs(args[0]) {
appPath = args[0]
} else {
appPath = path.Join(appPath, args[0])
}
}
if utils.IsInGOPATH(appPath) {
if found, _gopath, _path := utils.SearchGOPATHs(appPath); found {
appPath = _path
appname = path.Base(appPath)
currentGoPath = _gopath
} else {
beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", appPath)
}
if strings.HasSuffix(appname, ".go") && utils.IsExist(appPath) {
beeLogger.Log.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)
beeLogger.Log.Info("Do you want to overwrite it? [yes|no] ") beeLogger.Log.Info("Do you want to overwrite it? [yes|no] ")
if !utils.AskForConfirmation() { if !utils.AskForConfirmation() {
return 0 return 0
} }
} }
} else {
beeLogger.Log.Warn("Running application outside of GOPATH")
appname = path.Base(appPath)
currentGoPath = appPath
} }
beeLogger.Log.Infof("Using '%s' as 'appname'", appname) beeLogger.Log.Infof("Using '%s' as 'appname'", appname)
beeLogger.Log.Debugf("Current path: %s", utils.FILE(), utils.LINE(), currpath) beeLogger.Log.Debugf("Current path: %s", utils.FILE(), utils.LINE(), appPath)
if runmode == "prod" || runmode == "dev" { if runmode == "prod" || runmode == "dev" {
os.Setenv("BEEGO_RUNMODE", runmode) os.Setenv("BEEGO_RUNMODE", runmode)
@ -122,7 +129,7 @@ func RunApp(cmd *commands.Command, args []string) int {
} }
var paths []string var paths []string
readAppDirectories(currpath, &paths) readAppDirectories(appPath, &paths)
// 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.
@ -159,7 +166,7 @@ func RunApp(cmd *commands.Command, args []string) int {
} }
} }
if downdoc == "true" { if downdoc == "true" {
if _, err := os.Stat(path.Join(currpath, "swagger", "index.html")); err != nil { if _, err := os.Stat(path.Join(appPath, "swagger", "index.html")); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
downloadFromURL(swaggerlink, "swagger.zip") downloadFromURL(swaggerlink, "swagger.zip")
unzipAndDelete("swagger.zip") unzipAndDelete("swagger.zip")

View File

@ -48,6 +48,12 @@ const (
aform = "multipart/form-data" aform = "multipart/form-data"
) )
const (
astTypeArray = "array"
astTypeObject = "object"
astTypeMap = "map"
)
var pkgCache map[string]struct{} //pkg:controller:function:comments comments: key:value var pkgCache map[string]struct{} //pkg:controller:function:comments comments: key:value
var controllerComments map[string]string var controllerComments map[string]string
var importlist map[string]string var importlist map[string]string
@ -78,11 +84,13 @@ var basicTypes = map[string]string{
"byte": "string:byte", "byte": "string:byte",
"rune": "string:byte", "rune": "string:byte",
// builtin golang objects // builtin golang objects
"time.Time": "string:datetime", "time.Time": "string:datetime",
"json.RawMessage": "object:",
} }
var stdlibObject = map[string]string{ var stdlibObject = map[string]string{
"&{time Time}": "time.Time", "&{time Time}": "time.Time",
"&{json RawMessage}": "json.RawMessage",
} }
func init() { func init() {
@ -586,7 +594,7 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
} }
if isArray { if isArray {
rs.Schema = &swagger.Schema{ rs.Schema = &swagger.Schema{
Type: "array", Type: astTypeArray,
Items: &schema, Items: &schema,
} }
} else { } else {
@ -640,7 +648,7 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
m, mod, realTypes := getModel(p[2]) m, mod, realTypes := getModel(p[2])
if isArray { if isArray {
para.Schema = &swagger.Schema{ para.Schema = &swagger.Schema{
Type: "array", Type: astTypeArray,
Items: &swagger.Schema{ Items: &swagger.Schema{
Ref: "#/definitions/" + m, Ref: "#/definitions/" + m,
}, },
@ -781,7 +789,7 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s
isArray = true isArray = true
} }
if typ == "string" || typ == "number" || typ == "integer" || typ == "boolean" || if typ == "string" || typ == "number" || typ == "integer" || typ == "boolean" ||
typ == "array" || typ == "file" { typ == astTypeArray || typ == "file" {
paraType = typ paraType = typ
} else if sType, ok := basicTypes[typ]; ok { } else if sType, ok := basicTypes[typ]; ok {
typeFormat := strings.Split(sType, ":") typeFormat := strings.Split(sType, ":")
@ -801,14 +809,14 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s
if isArray { if isArray {
if para.In == "body" { if para.In == "body" {
para.Schema = &swagger.Schema{ para.Schema = &swagger.Schema{
Type: "array", Type: astTypeArray,
Items: &swagger.Schema{ Items: &swagger.Schema{
Type: paraType, Type: paraType,
Format: paraFormat, Format: paraFormat,
}, },
} }
} else { } else {
para.Type = "array" para.Type = astTypeArray
para.Items = &swagger.ParameterItems{ para.Items = &swagger.ParameterItems{
Type: paraType, Type: paraType,
Format: paraFormat, Format: paraFormat,
@ -901,49 +909,60 @@ func getparams(str string) []string {
return r return r
} }
func getModel(str string) (objectname string, m swagger.Schema, realTypes []string) { func getModel(str string) (definitionName string, m swagger.Schema, realTypes []string) {
strs := strings.Split(str, ".") strs := strings.Split(str, ".")
objectname = strs[len(strs)-1] // strs = [packageName].[objectName]
packageName := "" packageName := strs[0]
m.Type = "object" objectname := strs[len(strs)-1]
// Default all swagger schemas to object, if no other type is found
m.Type = astTypeObject
L:
for _, pkg := range astPkgs { for _, pkg := range astPkgs {
if strs[0] == pkg.Name { if strs[0] == pkg.Name {
for _, fl := range pkg.Files { for _, fl := range pkg.Files {
for k, d := range fl.Scope.Objects { for k, d := range fl.Scope.Objects {
if d.Kind == ast.Typ { if d.Kind == ast.Typ {
if k != objectname { if k != objectname {
// Still searching for the right object
continue continue
} }
packageName = pkg.Name parseObject(d, k, &m, &realTypes, astPkgs, packageName)
parseObject(d, k, &m, &realTypes, astPkgs, pkg.Name)
// When we've found the correct object, we can stop searching
break L
} }
} }
} }
} }
} }
if m.Title == "" { if m.Title == "" {
beeLogger.Log.Warnf("Cannot find the object: %s", str) // Don't log when error has already been logged
if _, found := rootapi.Definitions[str]; !found {
beeLogger.Log.Warnf("Cannot find the object: %s", str)
}
m.Title = objectname
// TODO remove when all type have been supported // TODO remove when all type have been supported
//os.Exit(1)
} }
if len(rootapi.Definitions) == 0 { if len(rootapi.Definitions) == 0 {
rootapi.Definitions = make(map[string]swagger.Schema) rootapi.Definitions = make(map[string]swagger.Schema)
} }
objectname = packageName + "." + objectname rootapi.Definitions[str] = m
rootapi.Definitions[objectname] = m return str, m, realTypes
return
} }
func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs []*ast.Package, packageName string) { func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs []*ast.Package, packageName string) {
ts, ok := d.Decl.(*ast.TypeSpec) ts, ok := d.Decl.(*ast.TypeSpec)
if !ok { if !ok {
beeLogger.Log.Fatalf("Unknown type without TypeSec: %v\n", d) beeLogger.Log.Fatalf("Unknown type without TypeSec: %v", d)
} }
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc... // TODO support other types, such as `MapType`, `InterfaceType` etc...
switch t := ts.Type.(type) { switch t := ts.Type.(type) {
case *ast.ArrayType: case *ast.ArrayType:
m.Title = k m.Title = k
m.Type = "array" m.Type = astTypeArray
if isBasicType(fmt.Sprint(t.Elt)) { if isBasicType(fmt.Sprint(t.Elt)) {
typeFormat := strings.Split(basicTypes[fmt.Sprint(t.Elt)], ":") typeFormat := strings.Split(basicTypes[fmt.Sprint(t.Elt)], ":")
m.Format = typeFormat[0] m.Format = typeFormat[0]
@ -970,8 +989,8 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject { if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject {
basicType = object basicType = object
} }
if k, ok := basicTypes[basicType]; ok { if t, ok := basicTypes[basicType]; ok {
typeFormat := strings.Split(k, ":") typeFormat := strings.Split(t, ":")
m.Type = typeFormat[0] m.Type = typeFormat[0]
m.Format = typeFormat[1] m.Format = typeFormat[1]
} }
@ -983,7 +1002,7 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa
if obj.Kind == ast.Con { if obj.Kind == ast.Con {
vs, ok := obj.Decl.(*ast.ValueSpec) vs, ok := obj.Decl.(*ast.ValueSpec)
if !ok { if !ok {
beeLogger.Log.Fatalf("Unknown type without ValueSpec: %v\n", vs) beeLogger.Log.Fatalf("Unknown type without ValueSpec: %v", vs)
} }
ti, ok := vs.Type.(*ast.Ident) ti, ok := vs.Type.(*ast.Ident)
@ -1000,7 +1019,7 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa
for i, val := range vs.Values { for i, val := range vs.Values {
v, ok := val.(*ast.BasicLit) v, ok := val.(*ast.BasicLit)
if !ok { if !ok {
beeLogger.Log.Warnf("Unknown type without BasicLit: %v\n", v) beeLogger.Log.Warnf("Unknown type without BasicLit: %v", v)
continue continue
} }
enums[int(val.Pos())] = fmt.Sprintf("%s = %s", vs.Names[i].Name, v.Value) enums[int(val.Pos())] = fmt.Sprintf("%s = %s", vs.Names[i].Name, v.Value)
@ -1008,14 +1027,14 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa
case token.INT: case token.INT:
vv, err := strconv.Atoi(v.Value) vv, err := strconv.Atoi(v.Value)
if err != nil { if err != nil {
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v\n", v.Value) beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v", v.Value)
continue continue
} }
enumValues[int(val.Pos())] = vv enumValues[int(val.Pos())] = vv
case token.FLOAT: case token.FLOAT:
vv, err := strconv.ParseFloat(v.Value, 64) vv, err := strconv.ParseFloat(v.Value, 64)
if err != nil { if err != nil {
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v\n", v.Value) beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v", v.Value)
continue continue
} }
enumValues[int(val.Pos())] = vv enumValues[int(val.Pos())] = vv
@ -1050,7 +1069,7 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
m.Properties = make(map[string]swagger.Propertie) m.Properties = make(map[string]swagger.Propertie)
for _, field := range st.Fields.List { for _, field := range st.Fields.List {
isSlice, realType, sType := typeAnalyser(field) isSlice, realType, sType := typeAnalyser(field)
if (isSlice && isBasicType(realType)) || sType == "object" { if (isSlice && isBasicType(realType)) || sType == astTypeObject {
if len(strings.Split(realType, " ")) > 1 { if len(strings.Split(realType, " ")) > 1 {
realType = strings.Replace(realType, " ", ".", -1) realType = strings.Replace(realType, " ", ".", -1)
realType = strings.Replace(realType, "&", "", -1) realType = strings.Replace(realType, "&", "", -1)
@ -1064,9 +1083,9 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
mp := swagger.Propertie{} mp := swagger.Propertie{}
isObject := false isObject := false
if isSlice { if isSlice {
mp.Type = "array" mp.Type = astTypeArray
if sType, ok := basicTypes[(strings.Replace(realType, "[]", "", -1))]; ok { if t, ok := basicTypes[(strings.Replace(realType, "[]", "", -1))]; ok {
typeFormat := strings.Split(sType, ":") typeFormat := strings.Split(t, ":")
mp.Items = &swagger.Propertie{ mp.Items = &swagger.Propertie{
Type: typeFormat[0], Type: typeFormat[0],
Format: typeFormat[1], Format: typeFormat[1],
@ -1077,14 +1096,14 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
} }
} }
} else { } else {
if sType == "object" { if sType == astTypeObject {
isObject = true isObject = true
mp.Ref = "#/definitions/" + realType mp.Ref = "#/definitions/" + realType
} else if isBasicType(realType) { } else if isBasicType(realType) {
typeFormat := strings.Split(sType, ":") typeFormat := strings.Split(sType, ":")
mp.Type = typeFormat[0] mp.Type = typeFormat[0]
mp.Format = typeFormat[1] mp.Format = typeFormat[1]
} else if realType == "map" { } else if realType == astTypeMap {
typeFormat := strings.Split(sType, ":") typeFormat := strings.Split(sType, ":")
mp.AdditionalProperties = &swagger.Propertie{ mp.AdditionalProperties = &swagger.Propertie{
Type: typeFormat[0], Type: typeFormat[0],
@ -1120,7 +1139,6 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
} }
tag := stag.Get("json") tag := stag.Get("json")
if tag != "" { if tag != "" {
tagValues = strings.Split(tag, ",") tagValues = strings.Split(tag, ",")
} }
@ -1202,12 +1220,12 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
return true, fmt.Sprintf("[]%v", arr.Elt), basicTypes[fmt.Sprint(arr.Elt)] return true, fmt.Sprintf("[]%v", arr.Elt), basicTypes[fmt.Sprint(arr.Elt)]
} }
if mp, ok := arr.Elt.(*ast.MapType); ok { if mp, ok := arr.Elt.(*ast.MapType); ok {
return false, fmt.Sprintf("map[%v][%v]", mp.Key, mp.Value), "object" return false, fmt.Sprintf("map[%v][%v]", mp.Key, mp.Value), astTypeObject
} }
if star, ok := arr.Elt.(*ast.StarExpr); ok { if star, ok := arr.Elt.(*ast.StarExpr); ok {
return true, fmt.Sprint(star.X), "object" return true, fmt.Sprint(star.X), astTypeObject
} }
return true, fmt.Sprint(arr.Elt), "object" return true, fmt.Sprint(arr.Elt), astTypeObject
} }
switch t := f.Type.(type) { switch t := f.Type.(type) {
case *ast.StarExpr: case *ast.StarExpr:
@ -1218,13 +1236,13 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
if k, ok := basicTypes[basicType]; ok { if k, ok := basicTypes[basicType]; ok {
return false, basicType, k return false, basicType, k
} }
return false, basicType, "object" return false, basicType, astTypeObject
case *ast.MapType: case *ast.MapType:
val := fmt.Sprintf("%v", t.Value) val := fmt.Sprintf("%v", t.Value)
if isBasicType(val) { if isBasicType(val) {
return false, "map", basicTypes[val] return false, astTypeMap, basicTypes[val]
} }
return false, val, "object" return false, val, astTypeObject
} }
basicType := fmt.Sprint(f.Type) basicType := fmt.Sprint(f.Type)
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject { if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject {
@ -1233,7 +1251,7 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
if k, ok := basicTypes[basicType]; ok { if k, ok := basicTypes[basicType]; ok {
return false, basicType, k return false, basicType, k
} }
return false, basicType, "object" return false, basicType, astTypeObject
} }
func isBasicType(Type string) bool { func isBasicType(Type string) bool {
@ -1247,7 +1265,7 @@ func isBasicType(Type string) bool {
func appendModels(pkgpath, controllerName string, realTypes []string) { func appendModels(pkgpath, controllerName string, realTypes []string) {
for _, realType := range realTypes { for _, realType := range realTypes {
if realType != "" && !isBasicType(strings.TrimLeft(realType, "[]")) && if realType != "" && !isBasicType(strings.TrimLeft(realType, "[]")) &&
!strings.HasPrefix(realType, "map") && !strings.HasPrefix(realType, "&") { !strings.HasPrefix(realType, astTypeMap) && !strings.HasPrefix(realType, "&") {
if _, ok := modelsList[pkgpath+controllerName][realType]; ok { if _, ok := modelsList[pkgpath+controllerName][realType]; ok {
continue continue
} }