apigo/main.go
2024-10-20 09:03:45 +08:00

812 lines
23 KiB
Go
Raw 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"
"io"
"os"
"os/exec"
"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
Output 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",
Output: "dist",
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
}
func buildProject(args []string) bool {
conf := getConfig()
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 _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")
// 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 {
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]
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()
}