2024-03-15 22:32:19 +08:00
package main
import (
2024-06-07 18:33:31 +08:00
"archive/zip"
2024-03-15 22:32:19 +08:00
_ "embed"
"fmt"
2024-06-07 18:33:31 +08:00
"github.com/ssgo/httpclient"
2024-03-15 22:32:19 +08:00
"github.com/ssgo/u"
"io"
"os"
"os/exec"
"path"
2024-03-16 22:54:11 +08:00
"path/filepath"
2024-03-15 22:32:19 +08:00
"regexp"
2024-06-07 18:33:31 +08:00
"strconv"
2024-03-15 22:32:19 +08:00
"strings"
"text/template"
2024-06-07 18:33:31 +08:00
"time"
2024-03-15 22:32:19 +08:00
)
2024-06-26 12:16:21 +08:00
var version = "v0.0.8"
2024-06-06 19:00:58 +08:00
2024-03-15 22:32:19 +08:00
//go:embed templates/_makePluginCode.go
var makePluginCodeTPL string
//go:embed templates/_main.go
var mainCodeTPL string
//go:embed templates/_main_test.go
var mainTestCodeTPL string
//go:embed templates/_main.js
var mainJSCodeTPL string
//go:embed templates/_plugin.go
var pluginCodeTPL string
//go:embed templates/_plugin_test.go
var pluginTestCodeTPL string
//go:embed templates/_plugin_test.js
var pluginTestJSCodeTPL string
//go:embed templates/_gitignore
var gitignoreTPL string
//go:embed templates/_gitignore_server
var gitignoreServerTPL string
type Command struct {
Name string
ShortName string
Args string
Comment string
Func func ( [ ] string )
}
var commands = [ ] Command {
2024-06-07 18:33:31 +08:00
{ "init" , "i" , "" , "init a new project for empty dir" , initProject } ,
{ "init plugin" , "i p" , "" , "init a new plugin project for empty dir" , initPluginProject } ,
2024-03-15 22:32:19 +08:00
//{"new server", "+s", "[name]", "create a new server project, will create in the current directory if no name is specified", newServerProject},
//{"new api", "+a", "[path] [method]", "create a new api for server project, will use restful api if specified http method", newServerAPI},
2024-03-16 22:54:11 +08:00
//{"new game", "+g", "[name]", "create a new game project, will create in the current directory if no name is specified", newServerProject},
//{"new webkit", "+w", "[name]", "create a new webkit project, will create in the current directory if no name is specified", newServerProject},
2024-03-24 12:40:01 +08:00
//{"download", "dl", "[plugin name]", "will fetch plugin into project", downloadPlugin},
{ "run" , "r" , "" , "will exec `go run .`" , runProject } ,
2024-05-27 16:57:37 +08:00
{ "watch run" , "rr" , "[...]" , "run project use gowatch, if project files changed will restart auto, ... args see gowatch help https://github.com/ssgo/tool" , devProject } ,
2024-03-24 12:40:01 +08:00
{ "test" , "t" , "" , "will exec `go test -v .`, will exec into tests if exists tests dir" , testProject } ,
2024-05-27 16:57:37 +08:00
{ "watch test" , "tt" , "[...]" , "test project use gowatch, if project files changed will restart auto, ... args see gowatch help https://github.com/ssgo/tool" , devTestProject } ,
//{"export plugins", "ep", "", "export typescript code for used plugins into \"plugins/\"", makePluginCode},
{ "tidy" , "td" , "" , "tidy project, find imported plugins from .js files add import code to jsImports.go, export typescript code for used plugins into \"plugins/\"" , tidy } ,
2024-06-07 18:33:31 +08:00
{ "tags" , "" , "" , "show git tags" , showGitTags } ,
{ "commit" , "co" , "comment" , "commit git repo and push, comment is need" , commitGitRepo } ,
{ "tag+" , "t+" , "[version]" , "add git tag push, if no new tag specified will use last tag +1" , addGitTag } ,
//{"build", "b", "[-m]", "build for current os, output to build/, -m will mix js files into exec file"},
//{"build mac", "bm", "", "build"},
//{"build macarm", "bma", "", "build"},
//{"build win", "bw", "", "build"},
//{"build win32", "bw32", "", "build"},
//{"build linux", "bl", "", "build"},
//{"build linuxarm", "bla", "", "build"},
//{"build all", "ba", "", "build"},
2024-03-24 12:40:01 +08:00
//{"publish", "p", "", "publish project to target server", },
// TODO 从 plugins 中读取信息增加命令, 例如: dao
2024-06-07 18:33:31 +08:00
// TODO 支持 build
// TODO 支持 publish 到 apigo.cloud
}
var hc = httpclient . GetClient ( 30 * time . Second )
var cachePath = "c:\\.ag\\cache"
func init ( ) {
hc . SetGlobalHeader ( "Content-Type" , "application/json" )
if homePath , err := os . UserHomeDir ( ) ; err == nil {
cachePath = path . Join ( homePath , ".ag" , "cache" )
}
}
func showGitTags ( args [ ] string ) {
_ = runCommand ( "git" , "tag" , "-l" , "v*" , "--sort=-taggerdate" , "--format=%(refname:short) %(taggerdate:short) %(*objectname:short)" )
}
func commitGitRepo ( args [ ] string ) {
comment := strings . Join ( args , " " )
if comment != "" {
if err := runCommand ( "git" , "commit" , "-a" , "-m" , comment ) ; err == nil {
if err := runCommand ( "git" , "push" ) ; err != nil {
fmt . Println ( "git push failed:" , err . Error ( ) )
}
} else {
fmt . Println ( "git commit failed:" , err . Error ( ) )
}
} else {
fmt . Println ( "commit message is empty" )
}
}
func addGitTag ( args [ ] string ) {
newVer := ""
if len ( args ) > 0 {
newVer = args [ 0 ]
}
if newVer == "" {
2024-06-07 18:35:15 +08:00
if outs , err := u . RunCommand ( "git" , "tag" , "-l" , "v*" , "--sort=taggerdate" ) ; err == nil {
2024-06-07 18:33:31 +08:00
oldVer := "v0.0.0"
for i := len ( outs ) - 1 ; i >= 0 ; i -- {
if outs [ i ] [ 0 ] == 'v' && strings . IndexByte ( outs [ i ] , '.' ) != - 1 {
oldVer = outs [ len ( outs ) - 1 ]
break
}
}
if len ( os . Args ) > 2 {
newVer = os . Args [ 2 ]
} else {
versionParts := strings . Split ( oldVer , "." )
v , err := strconv . Atoi ( versionParts [ len ( versionParts ) - 1 ] )
if err != nil {
v = 0
}
versionParts [ len ( versionParts ) - 1 ] = strconv . Itoa ( v + 1 )
newVer = strings . Join ( versionParts , "." )
}
} else {
fmt . Println ( "get last tag failed:" , err . Error ( ) )
}
}
if newVer != "" {
if err := runCommand ( "git" , "tag" , "-a" , newVer , "-m" , "update tag by 'ag tag+'" ) ; err == nil {
if err := runCommand ( "git" , "push" , "origin" , newVer ) ; err != nil {
fmt . Println ( "git push failed:" , err . Error ( ) )
}
} else {
fmt . Println ( "git add tag failed:" , err . Error ( ) )
}
}
2024-03-15 22:32:19 +08:00
}
2024-05-27 16:57:37 +08:00
func findTool ( ) ( goWatchPath string , logVPath string ) {
goWatchPath = "gowatch"
logVPath = "logv"
2024-03-15 22:32:19 +08:00
if binPath , err := exec . LookPath ( "gowatch" ) ; err == nil && binPath != "" {
2024-05-27 16:57:37 +08:00
goWatchPath = binPath
2024-03-15 22:32:19 +08:00
}
if binPath , err := exec . LookPath ( "logv" ) ; err == nil && binPath != "" {
2024-05-27 16:57:37 +08:00
logVPath = binPath
2024-03-15 22:32:19 +08:00
}
2024-05-27 16:57:37 +08:00
return goWatchPath , logVPath
2024-03-15 22:32:19 +08:00
}
func checkSSGOTool ( ) {
2024-05-27 16:57:37 +08:00
goWatchPath , logVPath := findTool ( )
if goWatchPath == "gowatch" || logVPath == "logv" {
2024-03-15 22:32:19 +08:00
_ = runCommand ( "go" , "get" , "-u" , "github.com/ssgo/tool" )
2024-05-27 16:57:37 +08:00
if goWatchPath == "gowatch" {
2024-03-15 22:32:19 +08:00
_ = runCommand ( "go" , "install" , "github.com/ssgo/tool/gowatch" )
}
2024-05-27 16:57:37 +08:00
if logVPath == "logv" {
2024-03-15 22:32:19 +08:00
_ = runCommand ( "go" , "install" , "github.com/ssgo/tool/logv" )
}
}
}
2024-06-07 18:33:31 +08:00
func checkProjectPath ( ) string {
//if len(args) > 0 {
// if err := os.Mkdir(args[0], 0755); err != nil {
// fmt.Println(u.BRed("mkdir error: " + err.Error()))
// return ""
// }
// if err := os.Chdir(args[0]); err != nil {
// fmt.Println(u.BRed("chdir error: " + err.Error()))
// return ""
// }
// return args[0]
//} else {
if len ( u . ReadDirN ( "." ) ) != 0 {
fmt . Println ( u . BRed ( "current path is not empty" ) )
return ""
}
if pathname , err := os . Getwd ( ) ; err != nil {
fmt . Println ( u . BRed ( "getwd error: " + err . Error ( ) ) )
return ""
} else {
return path . Base ( pathname )
}
//}
}
func parseRepo ( url string ) ( apiUrl , owner , repo string ) {
name := ""
if strings . HasPrefix ( url , "github.com/" ) {
apiUrl = "https://api.github.com/"
name = url [ 11 : ]
} else if strings . HasPrefix ( url , "gitee.com/" ) {
apiUrl = "https://gitee.com/api/v5/"
name = url [ 10 : ]
2024-06-26 12:16:21 +08:00
} else if strings . HasPrefix ( url , "apigo.cc/" ) {
apiUrl = "https://apigo.cc/api/v1/"
2024-06-07 18:33:31 +08:00
name = url [ 16 : ]
2024-03-15 22:32:19 +08:00
} else {
2024-06-26 12:16:21 +08:00
apiUrl = "https://apigo.cc/api/v1/"
2024-06-07 18:33:31 +08:00
if strings . ContainsRune ( url , '/' ) {
name = url
2024-03-15 22:32:19 +08:00
} else {
2024-06-07 18:33:31 +08:00
name = "apigo/" + url
2024-03-15 22:32:19 +08:00
}
}
2024-06-07 18:33:31 +08:00
if ! strings . ContainsRune ( name , '/' ) {
name = name + " / " + name
}
a := strings . Split ( name , "/" )
owner = a [ 0 ]
repo = a [ 1 ]
return
}
type Tag struct {
Name string
Commit struct {
Sha string
Url string
}
Zipball_url string
Tarball_url string
2024-03-15 22:32:19 +08:00
}
2024-06-07 18:33:31 +08:00
func fetchRepo ( name string ) ( repoPath string ) {
apiUrl , owner , repo := parseRepo ( name )
tagsUrl := apiUrl + path . Join ( "repos" , owner , repo , "tags" )
tags := make ( [ ] Tag , 0 )
fmt . Println ( "try to fetch tags" , tagsUrl )
r := hc . Get ( tagsUrl )
if r . Error == nil {
r . To ( & tags )
} else if apiUrl != "https://api.github.com/" {
tagsUrl = "https://api.github.com/" + path . Join ( "repos" , owner , repo , "tags" )
fmt . Println ( "try to fetch tags" , tagsUrl )
r = hc . Get ( tagsUrl )
if r . Error == nil {
r . To ( & tags )
} else {
fmt . Println ( "fetch tags error" , r . Error )
}
}
if len ( tags ) > 0 {
lastVersion := tags [ 0 ] . Name
zipUrl := tags [ 0 ] . Zipball_url
tagPath := filepath . Join ( cachePath , owner , repo , lastVersion )
if ! u . FileExists ( tagPath ) {
zipFile := filepath . Join ( cachePath , owner , repo , lastVersion + ".zip" )
fmt . Println ( "Download file " , zipUrl )
_ , err := hc . Download ( zipFile , zipUrl , func ( start , end int64 , ok bool , finished , total int64 ) {
fmt . Printf ( " %.2f%%" , float32 ( finished ) / float32 ( total ) * 100 )
} )
fmt . Println ( )
if err != nil {
fmt . Println ( u . BRed ( "download file failed" ) )
return
}
if u . FileExists ( zipFile ) {
if reader , err := zip . OpenReader ( zipFile ) ; err == nil {
for _ , f := range reader . File {
toFile := filepath . Join ( tagPath , f . Name )
if f . FileInfo ( ) . IsDir ( ) {
//fmt.Println("extract dir", f.Name, toFile)
os . MkdirAll ( toFile , 0755 )
} else {
//fmt.Println("extract file", f.Name, toFile)
u . CheckPath ( toFile )
if fp , err := os . OpenFile ( toFile , os . O_RDWR | os . O_CREATE | os . O_TRUNC , 0644 ) ; err == nil {
if rd , err := f . Open ( ) ; err == nil {
if _ , err := io . Copy ( fp , rd ) ; err != nil {
fmt . Println ( u . BRed ( "write file failed" ) , toFile , err . Error ( ) )
} else {
//fmt.Println(u.BGreen("write file success"), toFile)
}
_ = rd . Close ( )
} else {
fmt . Println ( u . BRed ( "open file in zip failed" ) , f . Name , err . Error ( ) )
}
_ = fp . Close ( )
} else {
fmt . Println ( u . BRed ( "open dst file failed" ) , toFile , err . Error ( ) )
}
}
}
_ = reader . Close ( )
} else {
fmt . Println ( u . BRed ( "open zip file failed" ) , zipFile , err . Error ( ) )
}
_ = os . Remove ( zipFile )
}
}
return filepath . Join ( tagPath , repo )
} else {
fmt . Println ( u . BRed ( "no repo found for" ) , name )
return ""
}
}
func initProject ( args [ ] string ) {
if name := checkProjectPath ( ) ; name != "" {
projectOK := false
if len ( args ) > 0 {
repoPath := fetchRepo ( args [ 0 ] )
projectPath := filepath . Join ( repoPath , "_project" )
if u . FileExists ( projectPath ) {
if err := CopyFile ( projectPath , "." ) ; err == nil {
projectOK = true
fmt . Println ( u . Green ( "make project files success" ) )
} else {
fmt . Println ( u . BRed ( "make project files failed" ) , projectPath , err . Error ( ) )
}
}
}
if len ( args ) == 0 || ! projectOK {
writeFile ( "main.go" , mainCodeTPL , map [ string ] any { "name" : name } )
writeFile ( "main_test.go" , mainTestCodeTPL , map [ string ] any { "name" : name } )
writeFile ( "main.js" , mainJSCodeTPL , map [ string ] any { "name" : name } )
}
2024-03-15 22:32:19 +08:00
_ = runCommand ( "go" , "mod" , "init" , name )
_ = runCommand ( "go" , "mod" , "edit" , "-go=1.18" )
2024-06-26 12:16:21 +08:00
_ = runCommand ( "go" , "get" , "-u" , "apigo.cc/apigo/gojs" )
_ = runCommand ( "go" , "get" , "-u" , "apigo.cc/apigo/plugins" )
2024-06-07 18:33:31 +08:00
2024-03-15 22:32:19 +08:00
writeFile ( ".gitignore" , gitignoreTPL , map [ string ] any { "name" : name } )
_ = runCommand ( "go" , "mod" , "tidy" )
checkSSGOTool ( )
fmt . Println ( u . BGreen ( "new project " + name + " created" ) )
}
}
2024-06-07 18:33:31 +08:00
func CopyFile ( from , to string ) error {
fromStat , _ := os . Stat ( from )
if fromStat . IsDir ( ) {
// copy dir
for _ , f := range u . ReadDirN ( from ) {
err := CopyFile ( filepath . Join ( from , f . Name ) , filepath . Join ( to , f . Name ) )
if err != nil {
return err
}
}
return nil
} else {
// copy file
toStat , err := os . Stat ( to )
if err == nil && toStat . IsDir ( ) {
to = filepath . Join ( to , filepath . Base ( from ) )
}
u . CheckPath ( to )
if writer , err := os . OpenFile ( to , os . O_CREATE | os . O_WRONLY | os . O_TRUNC , 0644 ) ; err == nil {
defer writer . Close ( )
if reader , err := os . OpenFile ( from , os . O_RDONLY , 0644 ) ; err == nil {
defer reader . Close ( )
_ , err = io . Copy ( writer , reader )
return err
} else {
return err
}
} else {
return err
}
}
}
func initPluginProject ( args [ ] string ) {
if name := checkProjectPath ( ) ; name != "" {
2024-03-15 22:32:19 +08:00
_ = runCommand ( "go" , "mod" , "init" , name )
_ = runCommand ( "go" , "mod" , "edit" , "-go=1.18" )
2024-06-26 12:16:21 +08:00
_ = runCommand ( "go" , "get" , "-u" , "apigo.cc/apigo/plugin" )
2024-03-15 22:32:19 +08:00
writeFile ( "plugin.go" , pluginCodeTPL , map [ string ] any { "name" : name } )
writeFile ( ".gitignore" , gitignoreTPL , map [ string ] any { "name" : name } )
_ = runCommand ( "go" , "mod" , "tidy" )
_ = os . Mkdir ( "tests" , 0755 )
_ = os . Chdir ( "tests" )
_ = runCommand ( "go" , "mod" , "init" , "tests" )
_ = runCommand ( "go" , "mod" , "edit" , "-go=1.18" )
_ = runCommand ( "go" , "mod" , "edit" , "-require=current-plugin@v0.0.0" )
_ = runCommand ( "go" , "mod" , "edit" , "-replace=current-plugin@v0.0.0=../" )
2024-06-26 12:16:21 +08:00
_ = runCommand ( "go" , "get" , "-u" , "apigo.cc/apigo/plugin" )
_ = runCommand ( "go" , "get" , "-u" , "apigo.cc/apigo/gojs" )
2024-03-15 22:32:19 +08:00
writeFile ( "plugin_test.go" , pluginTestCodeTPL , map [ string ] any { "name" : name } )
writeFile ( "plugin_test.js" , pluginTestJSCodeTPL , map [ string ] any { "name" : name } )
_ = runCommand ( "go" , "mod" , "tidy" )
checkSSGOTool ( )
fmt . Println ( u . BGreen ( "new plugin " + name + " created" ) )
}
}
2024-05-27 16:57:37 +08:00
func _runProject ( args [ ] string , isWatch bool ) {
2024-03-16 22:54:11 +08:00
_ = runCommand ( "go" , "mod" , "tidy" )
2024-05-27 16:57:37 +08:00
goBinPath , logVPath := findTool ( )
if isWatch {
args = append ( args , "-pt" , ".go,.js,.yml" , "run" , "." )
} else {
goBinPath = "go"
args = append ( args , "run" , "." )
}
_ = runCommandPipe ( logVPath , goBinPath , args ... )
}
func runProject ( args [ ] string ) {
_runProject ( args , false )
}
func devProject ( args [ ] string ) {
_runProject ( args , true )
2024-03-15 22:32:19 +08:00
}
func testProject ( args [ ] string ) {
2024-05-27 16:57:37 +08:00
_testProject ( args , false )
}
func devTestProject ( args [ ] string ) {
_testProject ( args , true )
}
func _testProject ( args [ ] string , isWatch bool ) {
2024-03-15 22:32:19 +08:00
if u . FileExists ( "tests" ) {
2024-03-24 12:40:01 +08:00
if u . FileExists ( filepath . Join ( "tests" , "go.mod" ) ) {
_ = os . Chdir ( "tests" )
2024-05-27 16:57:37 +08:00
if isWatch {
args = append ( args , "-p" , ".." )
}
2024-06-06 19:00:58 +08:00
isRun := false
for _ , f := range u . ReadDirN ( "." ) {
if strings . HasSuffix ( f . Name , ".go" ) {
goStr := u . ReadFileN ( f . FullName )
if strings . Contains ( goStr , "package main" ) && ! strings . Contains ( goStr , "package main_test" ) {
isRun = true
}
break
}
}
if ! isRun {
args = append ( args , "test" , "-v" , "." )
} else {
args = append ( args , "run" , "." )
}
2024-03-24 12:40:01 +08:00
} else {
2024-05-27 16:57:37 +08:00
args = append ( args , "test" , "-v" , "tests" )
2024-03-24 12:40:01 +08:00
}
2024-03-15 22:32:19 +08:00
} else {
2024-05-27 16:57:37 +08:00
args = append ( args , "test" , "-v" , "." )
2024-03-15 22:32:19 +08:00
}
2024-03-24 12:40:01 +08:00
_ = runCommand ( "go" , "mod" , "tidy" )
2024-05-27 16:57:37 +08:00
goWatchPath , logVPath := findTool ( )
if isWatch {
args2 := append ( [ ] string { "-pt" , ".go,.js,.yml" } , args ... )
_ = runCommandPipe ( logVPath , goWatchPath , args2 ... )
} else {
_ = runCommandPipe ( logVPath , "go" , args ... )
}
2024-03-24 12:40:01 +08:00
}
2024-05-27 16:57:37 +08:00
var pkgMatcher = regexp . MustCompile ( ` (?im)^\s*(import)?\s*_\s+"([\w-_/.]+)" ` )
var importMatcher = regexp . MustCompile ( ` (?im)^\s*import\s+([\w { }, ]+)\s+from\s+['"]([\w./\\\- ]+)['"] ` )
func makeJsImports ( path string , jsImports * [ ] string , pkgName string ) {
if u . FileExists ( filepath . Join ( path , "go.mod" ) ) {
if m := modNameMatcher . FindStringSubmatch ( u . ReadFileN ( filepath . Join ( path , "go.mod" ) ) ) ; m != nil {
if pkgName != m [ 1 ] {
return
}
}
}
for _ , f := range u . ReadDirN ( path ) {
filename := f . Name
fullName := filepath . Join ( path , filename )
if ! strings . HasPrefix ( filename , "." ) && ! strings . HasPrefix ( filename , "_" ) && filename != "_makePluginCode" && filename != "node_modules" && filename != "www" && filename != "src" {
if f . IsDir {
makeJsImports ( fullName , jsImports , pkgName )
} else if strings . HasSuffix ( filename , ".js" ) {
if code , err := u . ReadFile ( fullName ) ; err == nil {
for _ , m := range importMatcher . FindAllStringSubmatch ( code , 1024 ) {
if ! strings . HasPrefix ( m [ 2 ] , "." ) && ! strings . HasPrefix ( m [ 2 ] , "_" ) && m [ 2 ] != "current-plugin" && m [ 2 ] != "console" && m [ 2 ] != "logger" {
if ! u . FileExists ( filepath . Join ( path , m [ 2 ] ) ) {
* jsImports = u . AppendUniqueString ( * jsImports , m [ 2 ] )
}
}
}
}
}
}
}
2024-03-15 22:32:19 +08:00
}
2024-05-27 16:57:37 +08:00
func makePluginCodeImports ( from string , imports * [ ] string , parentModuleName string ) {
jsImports := make ( [ ] string , 0 )
currentParentModuleName := parentModuleName
if u . FileExists ( filepath . Join ( from , "go.mod" ) ) {
pkgName := ""
if m := modNameMatcher . FindStringSubmatch ( u . ReadFileN ( filepath . Join ( from , "go.mod" ) ) ) ; m != nil {
pkgName = m [ 1 ]
}
makeJsImports ( from , & jsImports , pkgName )
parentModuleName = pkgName
}
2024-03-15 22:32:19 +08:00
2024-05-27 16:57:37 +08:00
currentPkgName := ""
for _ , f := range u . ReadDirN ( from ) {
if f . IsDir {
if ! strings . HasPrefix ( f . Name , "." ) {
makePluginCodeImports ( filepath . Join ( from , f . Name ) , imports , parentModuleName )
}
} else {
if strings . HasSuffix ( f . Name , ".go" ) && ! strings . HasPrefix ( f . Name , "." ) && f . Name != "makePluginCode.go" {
if code , err := u . ReadFile ( filepath . Join ( from , f . Name ) ) ; err == nil {
if m := pkgNameMatcher . FindStringSubmatch ( code ) ; m != nil {
2024-06-07 18:33:31 +08:00
if currentPkgName + "_test" != m [ 1 ] {
currentPkgName = m [ 1 ]
}
2024-05-27 16:57:37 +08:00
}
for _ , m := range pkgMatcher . FindAllStringSubmatch ( code , 1024 ) {
if m [ 2 ] != "current-plugin" && ! strings . HasPrefix ( f . Name , "jsImports" ) {
* imports = u . AppendUniqueString ( * imports , m [ 2 ] )
2024-03-15 22:32:19 +08:00
}
}
}
}
}
}
2024-05-27 16:57:37 +08:00
if currentPkgName != "" && u . FileExists ( filepath . Join ( from , "go.mod" ) ) {
pkgList := make ( [ ] string , 0 )
for _ , plgPkg := range jsImports {
if plgPkg != currentParentModuleName && ! u . StringIn ( * imports , plgPkg ) {
pkgList = append ( pkgList , plgPkg )
* imports = u . AppendUniqueString ( * imports , plgPkg )
}
}
if len ( pkgList ) > 0 {
_ = u . WriteFile ( filepath . Join ( from , u . StringIf ( strings . HasSuffix ( currentPkgName , "_test" ) , "jsImports_test.go" , "jsImports.go" ) ) , ` package ` + currentPkgName + `
import _ "`+strings.Join(pkgList, " \ "\nimport _ \"" ) + ` "
` )
}
}
2024-03-15 22:32:19 +08:00
}
func writeFile ( filename string , fileContent string , data any ) {
if fp , err := os . OpenFile ( filename , os . O_CREATE | os . O_WRONLY | os . O_TRUNC , 0600 ) ; err == nil {
tpl , _ := template . New ( filename ) . Parse ( fileContent )
if err = tpl . Execute ( fp , data ) ; err != nil {
fmt . Println ( u . Red ( err . Error ( ) ) )
}
_ = fp . Close ( )
} else {
fmt . Println ( u . Red ( err . Error ( ) ) )
}
}
2024-03-16 22:54:11 +08:00
var replaceMatcher = regexp . MustCompile ( ` ([a-zA-Z0-9._\-/]+)\s+(v[0-9.]+)\s+=>\s+([a-zA-Z0-9._\-/\\]+) ` )
2024-03-24 12:40:01 +08:00
var modNameMatcher = regexp . MustCompile ( ` (?m)^module\s+([a-zA-Z0-9._\-/]+)$ ` )
2024-05-27 16:57:37 +08:00
var pkgNameMatcher = regexp . MustCompile ( ` (?m)^package\s+([a-zA-Z0-9._\-/]+)$ ` )
2024-03-16 22:54:11 +08:00
2024-05-27 16:57:37 +08:00
func tidy ( args [ ] string ) {
2024-03-16 22:54:11 +08:00
// 判断是否可执行项目(不创建指向项目本身的 import _)
isMainProject := false
2024-03-24 12:40:01 +08:00
isEmptyProject := true
2024-03-16 22:54:11 +08:00
if files , err := os . ReadDir ( "." ) ; err == nil {
for _ , f := range files {
if ! f . IsDir ( ) && strings . HasSuffix ( f . Name ( ) , ".go" ) {
2024-03-24 12:40:01 +08:00
isEmptyProject = false
2024-03-16 22:54:11 +08:00
code , _ := u . ReadFile ( f . Name ( ) )
if strings . Contains ( code , "package main" ) || strings . Contains ( code , "func main(" ) {
isMainProject = true
break
}
}
}
}
// 扫描用到的插件( import _)
2024-03-15 22:32:19 +08:00
imports := make ( [ ] string , 0 )
2024-05-27 16:57:37 +08:00
makePluginCodeImports ( "." , & imports , "" )
2024-03-16 22:54:11 +08:00
2024-03-24 12:40:01 +08:00
findGoModCode , _ := u . ReadFile ( "go.mod" )
2024-03-16 22:54:11 +08:00
goModCode := "module main\ngo 1.18\n"
2024-03-24 12:40:01 +08:00
currentModuleName := "current-project"
2024-03-16 22:54:11 +08:00
if ! isMainProject {
2024-03-24 12:40:01 +08:00
if m := modNameMatcher . FindStringSubmatch ( findGoModCode ) ; m != nil {
currentModuleName = m [ 1 ]
}
goModCode += "require " + currentModuleName + " v0.0.0 // indirect\nreplace " + currentModuleName + " v0.0.0 => ../\n"
2024-03-16 22:54:11 +08:00
}
// 扫描 replace, 处理路径后加入到 _makePluginCode/go.mod
for _ , m := range replaceMatcher . FindAllStringSubmatch ( findGoModCode , 100 ) {
replacePath := m [ 3 ]
if absPath , err := filepath . Abs ( m [ 3 ] ) ; err == nil {
replacePath = absPath
2024-03-15 22:32:19 +08:00
}
2024-03-16 22:54:11 +08:00
goModCode += fmt . Sprintln ( "replace" , m [ 1 ] , m [ 2 ] , "=>" , replacePath )
}
2024-03-24 12:40:01 +08:00
if ! isMainProject && ! isEmptyProject {
imports = append ( imports , currentModuleName )
}
2024-03-16 22:54:11 +08:00
_ = u . WriteFile ( "_makePluginCode/go.mod" , goModCode )
writeFile ( "_makePluginCode/main.go" , makePluginCodeTPL , map [ string ] any { "imports" : imports } )
_ = os . Chdir ( "_makePluginCode" )
2024-03-24 12:40:01 +08:00
defer func ( ) {
_ = os . Chdir ( ".." )
_ = os . RemoveAll ( "_makePluginCode" )
} ( )
2024-03-16 22:54:11 +08:00
_ = runCommand ( "go" , "mod" , "tidy" )
if err := runCommand ( "go" , "run" , "." ) ; err != nil {
fmt . Println ( u . Red ( err . Error ( ) ) )
2024-03-15 22:32:19 +08:00
}
}
func runCommand ( name string , args ... string ) error {
cmd := exec . Command ( name , args ... )
2024-03-24 12:40:01 +08:00
cmd . Stdin = os . Stdin
2024-03-15 22:32:19 +08:00
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
return cmd . Run ( )
}
func runCommandPipe ( pipeCommandName , commandName string , args ... string ) error {
cmd1 := exec . Command ( commandName , args ... )
cmd2 := exec . Command ( pipeCommandName )
r , w := io . Pipe ( )
2024-03-24 12:40:01 +08:00
wClosed := false
defer func ( ) {
if ! wClosed {
w . Close ( )
}
} ( )
cmd1 . Stdin = os . Stdin
2024-03-15 22:32:19 +08:00
cmd1 . Stdout = w
cmd1 . Stderr = w
cmd2 . Stdin = r
cmd2 . Stdout = os . Stdout
cmd2 . Stderr = os . Stderr
if err := cmd2 . Start ( ) ; err != nil {
return err
}
if err := cmd1 . Start ( ) ; err != nil {
return err
}
if err := cmd1 . Wait ( ) ; err != nil {
return err
}
2024-03-24 12:40:01 +08:00
w . Close ( )
wClosed = true
2024-03-15 22:32:19 +08:00
// 等待第二个命令完成
if err := cmd2 . Wait ( ) ; err != nil {
return err
}
return nil
}
func main ( ) {
if len ( os . Args ) > 1 {
cmd1 := os . Args [ 1 ]
cmd2 := cmd1
if len ( os . Args ) > 2 {
cmd2 += " " + os . Args [ 2 ]
}
for _ , cmdInfo := range commands {
if cmd1 == cmdInfo . Name || cmd1 == cmdInfo . ShortName {
cmdInfo . Func ( os . Args [ 2 : ] )
return
} else if cmd2 == cmdInfo . Name {
cmdInfo . Func ( os . Args [ 3 : ] )
return
}
}
}
fmt . Println ( "tools for apigo.cloud" )
2024-06-06 19:00:58 +08:00
fmt . Println ( "version " , version )
2024-03-15 22:32:19 +08:00
fmt . Println ( )
fmt . Println ( "Usage:" )
fmt . Println ( " " , u . Cyan ( "ag [command] [...]" ) )
fmt . Println ( " " , u . Magenta ( "ag [short command] [...]" ) )
fmt . Println ( )
fmt . Println ( "Commands:" )
for _ , cmdInfo := range commands {
2024-05-27 16:57:37 +08:00
padStr := ""
padN := 30 - len ( cmdInfo . Name ) - len ( cmdInfo . ShortName ) - len ( cmdInfo . Args )
if padN > 0 {
padStr = strings . Repeat ( " " , padN )
}
fmt . Println ( " " , u . Cyan ( cmdInfo . Name ) , u . Dim ( "[" ) + u . Magenta ( cmdInfo . ShortName ) + u . Dim ( "]" ) , cmdInfo . Args , padStr , cmdInfo . Comment )
2024-03-15 22:32:19 +08:00
}
fmt . Println ( )
fmt . Println ( "Examples:" )
fmt . Println ( " " , u . Magenta ( "ag +" ) , " create a new simple project with Hello World" )
fmt . Println ( " " , u . Magenta ( "ag +p ali" ) , " create a new plugin project named ali for use aliyun services" )
fmt . Println ( )
}