ag/main.go
2024-09-09 00:00:29 +08:00

806 lines
25 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"archive/zip"
_ "embed"
"fmt"
"github.com/ssgo/httpclient"
"github.com/ssgo/u"
"io"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"text/template"
"time"
)
var version = "v0.0.8"
//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
var goPath = "go"
var goRunEnv = []string{"CGO_ENABLED=1"}
type Command struct {
Name string
ShortName string
Args string
Comment string
Func func([]string) bool
}
var commands = []Command{
{"init", "i", "", "init a new project for empty dir", initProject},
{"init plugin", "i p", "", "init a new plugin project for empty dir", initPluginProject},
//{"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},
//{"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},
//{"download", "dl", "[plugin name]", "will fetch plugin into project", downloadPlugin},
{"run", "r", "", "will exec `go run .`", runProject},
{"watch run", "rr", "[...]", "run project use gowatch, if project files changed will restart auto, ... args see gowatch 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 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},
{"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},
//{"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"},
//{"publish", "p", "", "publish project to target server", },
// TODO 从 plugins 中读取信息增加命令例如dao
// 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 = filepath.Join(homePath, ".ag", "cache")
}
if runtime.GOOS == "windows" {
if u.FileExists("D:\\TDM-GCC-64\\bin\\gcc.exe") {
goRunEnv = append(goRunEnv, "CC=D:\\TDM-GCC-64\\bin\\gcc.exe")
goRunEnv = append(goRunEnv, "CXX=D:\\TDM-GCC-64\\bin\\g++.exe")
} else if u.FileExists("C:\\TDM-GCC-64\\bin\\gcc.exe") {
goRunEnv = append(goRunEnv, "CC=C:\\TDM-GCC-64\\bin\\gcc.exe")
goRunEnv = append(goRunEnv, "CXX=C:\\TDM-GCC-64\\bin\\g++.exe")
} else if binPath, err := exec.LookPath("gcc"); err != nil || binPath == "" {
fmt.Println("gcc is not found, please install TMD-GCC by https://jmeubank.github.io/tdm-gcc/")
fmt.Println("run \"pacman -S mingw-w64-ucrt-x86_64-gcc\" on msys2")
fmt.Println("if not installed at D:\\msys64 or D:\\msys64, please add \"***\\TDM-GCC-64\\bin\" to Environment Variables")
}
envs, _ := u.RunCommand("go", "env")
for _, line := range envs {
if strings.HasPrefix(line, "set GO") && !strings.HasPrefix(line, "set GOTMPDIR") && !strings.HasPrefix(line, "set GOWORK") {
goRunEnv = append(goRunEnv, line[4:])
}
}
}
}
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() (goWatchPath string, logVPath string) {
goWatchPath = "gowatch"
logVPath = "logv"
if binPath, err := exec.LookPath("gowatch"); err == nil && binPath != "" {
goWatchPath = binPath
}
if binPath, err := exec.LookPath("logv"); err == nil && binPath != "" {
logVPath = binPath
}
if goWatchPath == "gowatch" || logVPath == "logv" {
_ = runCommand(goPath, "get", "-u", "github.com/ssgo/tool")
if goWatchPath == "gowatch" {
_ = runCommand(goPath, "install", "github.com/ssgo/tool/gowatch")
}
if logVPath == "logv" {
_ = runCommand(goPath, "install", "github.com/ssgo/tool/logv")
}
}
return goWatchPath, logVPath
}
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 u.FileExists("go.mod") {
fmt.Println(u.BRed("go.mod exists"))
return ""
}
if pathname, err := os.Getwd(); err != nil {
fmt.Println(u.BRed("getwd error: " + err.Error()))
return ""
} else {
return filepath.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:]
} 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 initProject(args []string) bool {
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})
}
_ = runCommand(goPath, "mod", "init", name)
_ = runCommand(goPath, "mod", "edit", "-go=1.18")
_ = runCommand(goPath, "get", "-u", "apigo.cc/apigo/gojs")
_ = runCommand(goPath, "get", "-u", "apigo.cc/apigo/plugins")
writeFile(".gitignore", gitignoreTPL, map[string]any{"name": name})
_ = runCommand(goPath, "mod", "tidy")
findTool()
fmt.Println(u.BGreen("new project " + name + " created"))
return true
}
return false
}
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) bool {
if name := checkProjectPath(); name != "" {
_ = runCommand(goPath, "mod", "init", name)
_ = runCommand(goPath, "mod", "edit", "-go=1.18")
_ = runCommand(goPath, "get", "-u", "apigo.cc/apigo/plugin")
writeFile("plugin.go", pluginCodeTPL, map[string]any{"name": name})
writeFile(".gitignore", gitignoreTPL, map[string]any{"name": name})
_ = runCommand(goPath, "mod", "tidy")
_ = os.Mkdir("tests", 0755)
_ = os.Chdir("tests")
_ = runCommand(goPath, "mod", "init", "tests")
_ = runCommand(goPath, "mod", "edit", "-go=1.18")
_ = runCommand(goPath, "mod", "edit", "-require=current-plugin@v0.0.0")
_ = runCommand(goPath, "mod", "edit", "-replace=current-plugin@v0.0.0=../")
_ = runCommand(goPath, "get", "-u", "apigo.cc/apigo/plugin")
_ = runCommand(goPath, "get", "-u", "apigo.cc/apigo/gojs")
writeFile("plugin_test.go", pluginTestCodeTPL, map[string]any{"name": name})
writeFile("plugin_test.js", pluginTestJSCodeTPL, map[string]any{"name": name})
_ = runCommand(goPath, "mod", "tidy")
findTool()
fmt.Println(u.BGreen("new plugin " + name + " created"))
return true
}
return false
}
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", ".")
}
return nil == runCommandPipeWithEnv(logVPath, goBinPath, goRunEnv, args...)
}
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")
goWatchPath, logVPath := findTool()
if isWatch {
args2 := append([]string{"-pt", ".go,.js,.yml"}, args...)
_ = runCommandPipeWithEnv(logVPath, goWatchPath, 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 {
pathname, _ := os.Getwd()
fmt.Println(u.BMagenta(pathname), u.BCyan(name), u.Cyan(strings.Join(args, " ")))
cmd := exec.Command(name, args...)
if env != nil {
if runtime.GOOS == "windows" {
env = append(env, "GOTMPDIR="+pathname, "GOWORK="+pathname)
}
cmd.Env = append(cmd.Env, env...)
}
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)
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...)
}
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]
//fmt.Println(">>>>>>", cmd1, cmdInfo.Name, "|", cmd2, cmdInfo.ShortName)
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()
}