2024-10-20 09:03:45 +08:00
package main
import (
"archive/zip"
_ "embed"
"fmt"
"io"
"os"
"os/exec"
2024-10-21 18:33:55 +08:00
"path"
2024-10-20 09:03:45 +08:00
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"text/template"
"time"
"github.com/ssgo/httpclient"
"github.com/ssgo/u"
)
var version = "v0.0.1"
//go:embed templates/_main.go
var mainCodeTPL string
//go:embed templates/_gitignore
var gitignoreTPL string
var goPath = "go"
type Command struct {
Name string
ShortName string
Args string
Comment string
Func func ( [ ] string ) bool
}
type Config struct {
Name string
Version string
Main string
Target map [ string ] string
Module map [ string ] string
ModuleAlias map [ string ] string
ExtraImport map [ string ] string
CacheFile [ ] string
}
var commands = [ ] Command {
{ "init" , "i" , "" , "init a new project for empty dir" , initProject } ,
{ "build" , "b" , "" , "build project for current os" , buildProject } ,
// {"init plugin", "i p", "", "init a new plugin project for empty dir", initPluginProject},
// {"run", "r", "", "will exec `go run .`", runProject},
// {"watch run", "rr", "[...]", "run project use sskey, if project files changed will restart auto, ... args see sskey help https://github.com/ssgo/tool", devProject},
// {"test", "t", "", "will exec `go test -v .`, will exec into tests if exists tests dir", testProject},
// {"watch test", "tt", "[...]", "test project use sskey, if project files changed will restart auto, ... args see sskey help https://github.com/ssgo/tool", devTestProject},
// {"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},
// {"tags", "", "", "show git tags", showGitTags},
// {"commit", "co", "comment", "commit git repo and push, comment is need", commitGitRepo},
// {"commit and +tag", "co+", "comment", "commit git repo and push, than update tag, comment is need", commitAndTagGitRepo},
// {"update tag", "t+", "[version]", "add git tag push, if no new tag specified will use last tag +1", addGitTag},
}
var hc = httpclient . GetClient ( 30 * time . Second )
var cachePath = ".ag"
func init ( ) {
hc . SetGlobalHeader ( "Content-Type" , "application/json" )
if homePath , err := os . UserHomeDir ( ) ; err == nil {
cachePath = filepath . Join ( homePath , ".ag" , "cache" )
}
}
func showGitTags ( args [ ] string ) bool {
return nil == runCommand ( "git" , "tag" , "-l" , "v*" , "--sort=-taggerdate" , "--format=%(refname:short) %(taggerdate:short) %(*objectname:short)" )
}
func commitAndTagGitRepo ( args [ ] string ) bool {
if commitGitRepo ( args ) {
return addGitTag ( [ ] string { } )
}
return false
}
func commitGitRepo ( args [ ] string ) bool {
comment := strings . Join ( args , " " )
if comment != "" {
if err := runCommand ( "git" , "commit" , "-a" , "-m" , comment ) ; err == nil {
if err := runCommand ( "git" , "push" ) ; err == nil {
return true
} else {
fmt . Println ( "git push failed:" , err . Error ( ) )
}
} else {
fmt . Println ( "git commit failed:" , err . Error ( ) )
}
} else {
fmt . Println ( "commit message is empty" )
}
return false
}
func addGitTag ( args [ ] string ) bool {
newVer := ""
if len ( args ) > 0 {
newVer = args [ 0 ]
}
if newVer == "" {
if outs , err := u . RunCommand ( "git" , "tag" , "-l" , "v*" , "--sort=taggerdate" ) ; err == nil {
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 ( args ) > 0 {
newVer = args [ 0 ]
} 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 ( ) )
}
}
return false
}
func findTool ( ) ( sskeyPath string , logVPath string ) {
sskeyPath = "sskey"
logVPath = "logv"
if binPath , err := exec . LookPath ( "sskey" ) ; err == nil && binPath != "" {
sskeyPath = binPath
}
if binPath , err := exec . LookPath ( "logv" ) ; err == nil && binPath != "" {
logVPath = binPath
}
if sskeyPath == "sskey" || logVPath == "logv" {
_ = runCommand ( goPath , "get" , "-u" , "github.com/ssgo/tool" )
if sskeyPath == "sskey" {
_ = runCommand ( goPath , "install" , "github.com/ssgo/tool/sskey" )
}
if logVPath == "logv" {
_ = runCommand ( goPath , "install" , "github.com/ssgo/tool/logv" )
}
}
return sskeyPath , logVPath
}
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 : ]
} else if strings . HasPrefix ( url , "apigo.cc/" ) {
apiUrl = "https://apigo.cc/api/v1/"
name = url [ 16 : ]
} else {
apiUrl = "https://apigo.cc/api/v1/"
if strings . ContainsRune ( url , '/' ) {
name = url
} else {
name = "apigo/" + url
}
}
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
}
func fetchRepo ( name string ) ( repoPath string ) {
apiUrl , owner , repo := parseRepo ( name )
tagsUrl := apiUrl + filepath . 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/" + filepath . 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 getConfig ( ) * Config {
conf := & Config {
Name : "" ,
Version : "v0.0.1" ,
Main : "main.js" ,
Module : map [ string ] string { } ,
ModuleAlias : map [ string ] string { } ,
ExtraImport : map [ string ] string { } ,
CacheFile : [ ] string { } ,
}
u . LoadYaml ( "apigo.yml" , conf )
if conf . Name == "" {
if pathname , err := os . Getwd ( ) ; err != nil {
conf . Name = "apigo"
} else {
conf . Name = filepath . Base ( pathname )
}
}
if conf . Target == nil {
conf . Target = map [ string ] string {
"darwin" : "amd64 arm64" ,
"linux" : "amd64 arm64" ,
"windows" : "amd64" ,
}
}
if conf . Module == nil {
conf . Module = map [ string ] string { }
}
if conf . ModuleAlias == nil {
conf . ModuleAlias = map [ string ] string { }
}
if conf . ExtraImport == nil {
conf . ExtraImport = map [ string ] string { }
}
if conf . CacheFile == nil {
conf . CacheFile = [ ] string { }
}
for pkgName , pkgVersion := range conf . Module {
if pkgVersion == "" {
conf . Module [ pkgName ] = "latest"
}
aliasName := pkgName
if strings . HasPrefix ( aliasName , "apigo.cc/gojs/" ) {
aliasName = aliasName [ 14 : ]
} else if strings . HasPrefix ( aliasName , "apigo.cc/" ) {
aliasName = aliasName [ 9 : ]
}
if aliasName != pkgName && conf . ModuleAlias [ aliasName ] == "" {
conf . ModuleAlias [ aliasName ] = pkgName
}
}
for pkgName , pkgVersion := range conf . ExtraImport {
if pkgVersion == "" {
conf . ExtraImport [ pkgName ] = "latest"
}
}
return conf
}
func initProject ( args [ ] string ) bool {
// if u.FileExists("go.mod") {
// fmt.Println(u.Red("go.mod exists, are you sure to init this project?"))
// sure := 'n'
// _, _ = fmt.Scan(&sure)
// if sure != 'y' && sure != 'Y' {
// return false
// }
// }
if u . FileExists ( "main.go" ) || u . FileExists ( "go.mod" ) {
fmt . Println ( u . Red ( "main.go or go.mod is exists, are you sure to overwrite it? (y/n)" ) )
sure := 'n'
_ , _ = fmt . Scan ( & sure )
if sure != 'y' && sure != 'Y' {
return false
}
}
if ! u . FileExists ( "apigo.yml" ) {
refProjName := "default"
if len ( args ) > 0 {
refProjName = args [ 0 ]
}
// TODO check name is path
repoPath := fetchRepo ( refProjName )
if u . FileExists ( repoPath ) {
if err := CopyFile ( repoPath , "." ) ; err == nil {
fmt . Println ( u . Green ( "copy project files success" ) , repoPath )
} else {
fmt . Println ( u . BRed ( "copy project files failed" ) , repoPath , err . Error ( ) )
return false
}
} else {
fmt . Println ( u . BRed ( "fetch repo failed" ) , repoPath )
return false
}
}
conf := getConfig ( )
writeFile ( "main.go" , mainCodeTPL , conf )
_ = runCommand ( goPath , "mod" , "init" , conf . Name )
_ = runCommand ( goPath , "mod" , "edit" , "-go=1.18" )
for pkgName , pkgVersion := range conf . Module {
_ = runCommand ( goPath , "get" , pkgName + "@" + pkgVersion )
}
for pkgName , pkgVersion := range conf . ExtraImport {
_ = runCommand ( goPath , "get" , pkgName + "@" + pkgVersion )
}
if ! u . FileExists ( ".gitignore" ) {
writeFile ( ".gitignore" , gitignoreTPL , nil )
}
_ = runCommand ( goPath , "mod" , "tidy" )
findTool ( )
fmt . Println ( u . BGreen ( "new project " + conf . Name + " created" ) )
fmt . Println ( u . Cyan ( "run \"ag build\" to use it" ) )
fmt . Println ( u . Cyan ( "run \"ag test\" to test" ) )
fmt . Println ( u . Cyan ( "run \"ag build all\" to publish" ) )
return true
}
2024-10-21 18:33:55 +08:00
func packageProject ( args [ ] string ) bool {
return buildProjectWithOption ( args , true )
}
2024-10-20 09:03:45 +08:00
func buildProject ( args [ ] string ) bool {
2024-10-21 18:33:55 +08:00
return buildProjectWithOption ( args , false )
}
func buildProjectWithOption ( args [ ] string , isPack bool ) bool {
2024-10-20 09:03:45 +08:00
conf := getConfig ( )
2024-10-21 18:33:55 +08:00
targets := make ( [ ] [ ] string , 0 )
if len ( args ) > 0 {
if args [ 0 ] == "all" {
for k , v := range conf . Target {
for _ , arch := range strings . Split ( v , " " ) {
targets = append ( targets , [ ] string { k , arch } )
}
}
} else {
if len ( args ) > 1 {
targets = append ( targets , [ ] string { args [ 0 ] , args [ 1 ] } )
} else {
targets = append ( targets , [ ] string { args [ 0 ] , runtime . GOARCH } )
}
}
} else {
targets = append ( targets , [ ] string { runtime . GOOS , runtime . GOARCH } )
}
2024-10-20 09:03:45 +08:00
2024-10-21 18:33:55 +08:00
output := "build"
if isPack {
output = "release"
// TODO 生成静态文件和入口文件嵌入代码
// TODO 生成SSKey嵌入代码
defer func ( ) {
os . Remove ( "cacheFiles.go" )
os . Remove ( "setSSKey.go" )
} ( )
}
_ = runCommand ( goPath , "mod" , "tidy" )
for _ , target := range targets {
buildOS := target [ 0 ]
buildArch := target [ 1 ]
name := path . Base ( conf . Name )
ext := ""
if buildOS == "windows" {
ext = ".exe"
}
buildPath := filepath . Join ( output , fmt . Sprintf ( "%s_%s_%s%s" , name , buildOS , buildArch , ext ) )
env := append ( make ( [ ] string , 0 ) , "GOOS=" + buildOS , "GOARCH=" + buildArch )
err := runCommandWithEnv ( "go" , env , "build" , "-o" , buildPath , "-ldflags" , "-s -w" , "." )
if err != nil {
fmt . Println ( u . BRed ( "build failed" ) , buildOS , buildArch , err . Error ( ) )
} else {
fmt . Println ( u . Green ( "build for" ) , buildOS , buildArch , "to" , buildPath )
}
}
return true
2024-10-20 09:03:45 +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 _runProject(args []string, isWatch bool) bool {
// _ = runCommand(goPath, "mod", "tidy")
// goBinPath, logVPath := findTool()
// if isWatch {
// args = append(args, "-pt", ".go,.js,.yml", "run", ".")
// } else {
// goBinPath = goPath
// args = append(args, "run", ".")
// }
2024-10-21 18:33:55 +08:00
// return nil == runCommandPipe (logVPath, goBinPath, goRunEnv, args...)
2024-10-20 09:03:45 +08:00
// }
// func runProject(args []string) bool {
// return _runProject(args, false)
// }
// func devProject(args []string) bool {
// return _runProject(args, true)
// }
// func testProject(args []string) bool {
// return _testProject(args, false)
// }
// func devTestProject(args []string) bool {
// return _testProject(args, true)
// }
// func _testProject(args []string, isWatch bool) bool {
// if u.FileExists("tests") {
// if u.FileExists(filepath.Join("tests", "go.mod")) {
// _ = os.Chdir("tests")
// if isWatch {
// args = append(args, "-p", "..")
// }
// 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", ".")
// }
// } else {
// args = append(args, "test", "-v", "tests")
// }
// } else {
// args = append(args, "test", "-v", ".")
// }
// _ = runCommand(goPath, "mod", "tidy")
// sskeyPath, logVPath := findTool()
// if isWatch {
// args2 := append([]string{"-pt", ".go,.js,.yml"}, args...)
// _ = runCommandPipeWithEnv(logVPath, sskeyPath, goRunEnv, args2...)
// } else {
// _ = runCommandPipeWithEnv(logVPath, goPath, goRunEnv, args...)
// }
// return true
// }
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" {
checkFile := filepath . Join ( path , m [ 2 ] )
checkFileInfo := u . GetFileInfo ( checkFile )
if checkFileInfo != nil && checkFileInfo . IsDir {
checkFile = filepath . Join ( checkFile , "index.js" )
} else if ! strings . HasSuffix ( checkFile , ".js" ) {
checkFile += ".js"
}
//fmt.Println(u.BMagenta(checkFile), u.FileExists(checkFile))
if ! u . FileExists ( checkFile ) {
* jsImports = u . AppendUniqueString ( * jsImports , m [ 2 ] )
}
}
}
}
}
}
}
}
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
}
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 {
if currentPkgName + "_test" != m [ 1 ] {
currentPkgName = m [ 1 ]
}
}
for _ , m := range pkgMatcher . FindAllStringSubmatch ( code , 1024 ) {
if m [ 2 ] != "current-plugin" && ! strings . HasPrefix ( f . Name , "jsImports" ) {
* imports = u . AppendUniqueString ( * imports , m [ 2 ] )
}
}
}
}
}
}
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 _ \"" ) + ` "
` )
}
}
}
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 ( ) ) )
}
}
var replaceMatcher = regexp . MustCompile ( ` ([a-zA-Z0-9._\-/]+)\s+(v[0-9.]+)\s+=>\s+([a-zA-Z0-9._\-/\\]+) ` )
var modNameMatcher = regexp . MustCompile ( ` (?m)^module\s+([a-zA-Z0-9._\-/]+)$ ` )
var pkgNameMatcher = regexp . MustCompile ( ` (?m)^package\s+([a-zA-Z0-9._\-/]+)$ ` )
// func tidy(args []string) bool {
// // 判断是否可执行项目(不创建指向项目本身的 import _)
// isMainProject := false
// isEmptyProject := true
// if files, err := os.ReadDir("."); err == nil {
// for _, f := range files {
// if !f.IsDir() && strings.HasSuffix(f.Name(), ".go") {
// isEmptyProject = false
// code, _ := u.ReadFile(f.Name())
// if strings.Contains(code, "package main") || strings.Contains(code, "func main(") {
// isMainProject = true
// break
// }
// }
// }
// }
// // 扫描用到的插件( import _)
// imports := make([]string, 0)
// makePluginCodeImports(".", &imports, "")
// findGoModCode, _ := u.ReadFile("go.mod")
// goModCode := "module main\ngo 1.18\n"
// currentModuleName := "current-project"
// if !isMainProject {
// if m := modNameMatcher.FindStringSubmatch(findGoModCode); m != nil {
// currentModuleName = m[1]
// }
// goModCode += "require " + currentModuleName + " v0.0.0 // indirect\nreplace " + currentModuleName + " v0.0.0 => ../\n"
// }
// // 扫描 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
// }
// goModCode += fmt.Sprintln("replace", m[1], m[2], "=>", replacePath)
// }
// if !isMainProject && !isEmptyProject {
// imports = append(imports, currentModuleName)
// }
// _ = u.WriteFile(filepath.Join("_makePluginCode", "go.mod"), goModCode)
// writeFile(filepath.Join("_makePluginCode", "main.go"), makePluginCodeTPL, map[string]any{"imports": imports})
// _ = os.Chdir("_makePluginCode")
// defer func() {
// _ = os.Chdir("..")
// //_ = os.RemoveAll("_makePluginCode")
// }()
// _ = runCommand(goPath, "mod", "tidy")
// if err := runCommandWithEnv(goPath, goRunEnv, "run", "."); err != nil {
// fmt.Println(u.Red(err.Error()))
// }
// return true
// }
func runCommand ( name string , args ... string ) error {
return runCommandWithEnv ( name , nil , args ... )
}
func runCommandWithEnv ( name string , env [ ] string , args ... string ) error {
2024-10-21 18:33:55 +08:00
// pathname, _ := os.Getwd()
// fmt.Println(u.BMagenta(pathname), u.BCyan(name), u.Cyan(strings.Join(args, " ")))
2024-10-20 09:03:45 +08:00
cmd := exec . Command ( name , args ... )
if env != nil {
2024-10-21 18:33:55 +08:00
// if runtime.GOOS == "windows" {
// env = append(env, "GOTMPDIR="+pathname, "GOWORK="+pathname)
// }
cmd . Env = append ( os . Environ ( ) , env ... )
2024-10-20 09:03:45 +08:00
}
cmd . Stdin = os . Stdin
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
err := cmd . Run ( )
if err != nil {
fmt . Println ( u . Red ( err . Error ( ) ) )
}
return err
}
func runCommandPipe ( pipeCommandName , commandName string , args ... string ) error {
return runCommandPipeWithEnv ( pipeCommandName , commandName , nil , args ... )
}
func runCommandPipeWithEnv ( pipeCommandName , commandName string , env [ ] string , args ... string ) error {
pathname , _ := os . Getwd ( )
fmt . Println ( u . BMagenta ( pathname ) , u . BCyan ( commandName ) , u . Cyan ( strings . Join ( args , " " ) ) , u . BMagenta ( pipeCommandName ) )
cmd1 := exec . Command ( commandName , args ... )
cmd2 := exec . Command ( pipeCommandName )
2024-10-21 18:33:55 +08:00
// if env != nil {
// if runtime.GOOS == "windows" {
// env = append(env, "GOTMPDIR="+pathname, "GOWORK="+pathname)
// }
// cmd1.Env = append(cmd1.Env, env...)
// cmd2.Env = append(cmd2.Env, env...)
// }
2024-10-20 09:03:45 +08:00
r , w := io . Pipe ( )
wClosed := false
defer func ( ) {
if ! wClosed {
w . Close ( )
}
} ( )
cmd1 . Stdin = os . Stdin
cmd1 . Stdout = w
cmd1 . Stderr = w
cmd2 . Stdin = r
cmd2 . Stdout = os . Stdout
cmd2 . Stderr = os . Stderr
var err error
if err = cmd2 . Start ( ) ; err == nil {
if err = cmd1 . Start ( ) ; err == nil {
if err = cmd1 . Wait ( ) ; err == nil {
w . Close ( )
wClosed = true
// 等待第二个命令完成
if err = cmd2 . Wait ( ) ; err == nil {
return nil
}
}
}
}
fmt . Println ( u . Red ( err . Error ( ) ) )
return err
}
func main ( ) {
var err error
if goPath , err = exec . LookPath ( "go" ) ; err != nil || goPath == "" {
fmt . Println ( u . Red ( "Please install Go SDK first" ) )
fmt . Println ( u . Cyan ( "https://go.dev/" ) )
return
}
if len ( os . Args ) > 1 {
cmd1 := os . Args [ 1 ]
cmd2 := cmd1
if len ( os . Args ) > 2 {
cmd2 += " " + os . Args [ 2 ]
}
for i := len ( commands ) - 1 ; i >= 0 ; i -- {
cmdInfo := commands [ i ]
if len ( os . Args ) > 2 && ( cmd2 == cmdInfo . Name || cmd2 == cmdInfo . ShortName ) {
cmdInfo . Func ( os . Args [ 3 : ] )
return
} else if cmd1 == cmdInfo . Name || cmd1 == cmdInfo . ShortName {
cmdInfo . Func ( os . Args [ 2 : ] )
return
}
}
}
fmt . Println ( "tools for apigo.cloud" , version )
fmt . Println ( "go sdk" , goPath )
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 {
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 )
}
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 ( )
}