apigo/main.go

868 lines
24 KiB
Go
Raw Normal View History

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()
}