0
This commit is contained in:
		
						commit
						24282364a2
					
				
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					# ag - tools for apigo.cloud
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```shell
 | 
				
			||||||
 | 
					go install apigo.cc/apigo/ag@latest
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					tools for apigo.cloud
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Usage:
 | 
				
			||||||
 | 
					  ag [command] [...]
 | 
				
			||||||
 | 
					  ag [short command] [...]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Commands:
 | 
				
			||||||
 | 
					  init [i]                            init a new project for empty dir
 | 
				
			||||||
 | 
					  init plugin [i p]                   init a new plugin project for empty dir
 | 
				
			||||||
 | 
					  run [r]                             will exec `go run .`
 | 
				
			||||||
 | 
					  watch run [rr] [...]                run project use gowatch, if project files changed will restart auto, ... args see gowatch help https://github.com/ssgo/tool
 | 
				
			||||||
 | 
					  test [t]                            will exec `go test -v .`, will exec into tests if exists tests dir
 | 
				
			||||||
 | 
					  watch test [tt] [...]               test project use gowatch, if project files changed will restart auto, ... args see gowatch help https://github.com/ssgo/tool
 | 
				
			||||||
 | 
					  tidy [td]                           tidy project, find imported plugins from .js files add import code to jsImports.go, export typescript code for used plugins into "plugins/"
 | 
				
			||||||
 | 
					  tags []                             show git tags
 | 
				
			||||||
 | 
					  commit [co] comment                 commit git repo and push, comment is need
 | 
				
			||||||
 | 
					  tag+ [t+] [version]                 add git tag push, if no new tag specified will use last tag +1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										27
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					module apigo.cc/apigo/ag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.18
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						github.com/ssgo/httpclient v1.7.7
 | 
				
			||||||
 | 
						github.com/ssgo/u v1.7.9
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						github.com/dlclark/regexp2 v1.11.4 // indirect
 | 
				
			||||||
 | 
						github.com/fsnotify/fsnotify v1.7.0 // indirect
 | 
				
			||||||
 | 
						github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
 | 
				
			||||||
 | 
						github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/tool v0.4.27 // indirect
 | 
				
			||||||
 | 
						golang.org/x/sys v0.26.0 // indirect
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						apigo.cc/gojs v0.0.2
 | 
				
			||||||
 | 
						github.com/ssgo/config v1.7.7 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/log v1.7.7 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/standard v1.7.7 // indirect
 | 
				
			||||||
 | 
						golang.org/x/net v0.30.0 // indirect
 | 
				
			||||||
 | 
						golang.org/x/text v0.19.0 // indirect
 | 
				
			||||||
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										32
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					apigo.cc/gojs v0.0.2 h1:MMJfnFuQHNOVFnxiNhfMa4Yrr/CtCcW6sO27DZux/DE=
 | 
				
			||||||
 | 
					apigo.cc/gojs v0.0.2/go.mod h1:iuTCYlxSnz2ARxaYigk5sHpUEjLCSKdtKXcG9xwxKxY=
 | 
				
			||||||
 | 
					github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
 | 
				
			||||||
 | 
					github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 | 
				
			||||||
 | 
					github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
 | 
				
			||||||
 | 
					github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
 | 
				
			||||||
 | 
					github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
 | 
				
			||||||
 | 
					github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
 | 
				
			||||||
 | 
					github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
 | 
				
			||||||
 | 
					github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
 | 
				
			||||||
 | 
					github.com/ssgo/config v1.7.7 h1:gYYDuEEdesH41oNtvKHuwaua9quU655O8TRd3pBcuK8=
 | 
				
			||||||
 | 
					github.com/ssgo/config v1.7.7/go.mod h1:4mnR3YLGkmK+YnVT0NAbfUpvxqGyjyEElag+bH4zb0c=
 | 
				
			||||||
 | 
					github.com/ssgo/httpclient v1.7.7 h1:ex7pEwEpDaNtm334b04F0EQ62rPCNNxjOMi9tosXvGA=
 | 
				
			||||||
 | 
					github.com/ssgo/httpclient v1.7.7/go.mod h1:YA1MaQaThr8F9U1gnbUZhvUZY8ppAeI0A5GksPaVeYE=
 | 
				
			||||||
 | 
					github.com/ssgo/log v1.7.7 h1:EjgPGDTAEz+ApNyluLpof9QlftxBiqh1Jt3e3E0h/m4=
 | 
				
			||||||
 | 
					github.com/ssgo/log v1.7.7/go.mod h1:5E2Mkk+np9SCU84bX+lxBOG8QBD3XVkvFCbFi0RiKyk=
 | 
				
			||||||
 | 
					github.com/ssgo/standard v1.7.7 h1:5tnlcr9Nmftp7JI3jszYCEbW7VgS5HHsGueD+yWxbh0=
 | 
				
			||||||
 | 
					github.com/ssgo/standard v1.7.7/go.mod h1:LZcn56DzHu8OlDXrUPLI6h+RZbZRXhkmiKh6PSE8eDs=
 | 
				
			||||||
 | 
					github.com/ssgo/tool v0.4.27 h1:tB2VvEWt0jSazel2/G+/JmhsYesJWCeRLEdyV2r5QeM=
 | 
				
			||||||
 | 
					github.com/ssgo/tool v0.4.27/go.mod h1:qIQYzXya36WWVypWP5T/AgKwKBwZkEvMRfjACkAeUuw=
 | 
				
			||||||
 | 
					github.com/ssgo/u v1.7.9 h1:m1wcJWQg13+NbqpG+c2z3yWO+PC7qE3PIwKfXhQqdPg=
 | 
				
			||||||
 | 
					github.com/ssgo/u v1.7.9/go.mod h1:dUG/PBG5k9fSM7SOp8RZLsK0KytNxhtenpoLgjhfxpY=
 | 
				
			||||||
 | 
					golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
 | 
				
			||||||
 | 
					golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
				
			||||||
 | 
					golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
 | 
				
			||||||
 | 
					golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
							
								
								
									
										811
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										811
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,811 @@
 | 
				
			|||||||
 | 
					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()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								templates/_gitignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								templates/_gitignore
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					.*
 | 
				
			||||||
 | 
					!.gitignore
 | 
				
			||||||
 | 
					go.sum
 | 
				
			||||||
 | 
					/dist
 | 
				
			||||||
 | 
					/build*
 | 
				
			||||||
 | 
					node_modules
 | 
				
			||||||
 | 
					package.json
 | 
				
			||||||
							
								
								
									
										197
									
								
								templates/_main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								templates/_main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,197 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/gojs"
 | 
				
			||||||
 | 
						{{range $pkg, $ver := .Module}}
 | 
				
			||||||
 | 
						_ "{{$pkg}}"
 | 
				
			||||||
 | 
						{{- end}}
 | 
				
			||||||
 | 
						{{range $pkg, $ver := .ExtraImport}}
 | 
				
			||||||
 | 
						_ "{{$pkg}}"
 | 
				
			||||||
 | 
						{{- end}}
 | 
				
			||||||
 | 
						"github.com/ssgo/log"
 | 
				
			||||||
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var appName = "{{.Name}}"
 | 
				
			||||||
 | 
					var appVersion = "{{.Version}}"
 | 
				
			||||||
 | 
					var idFixMatcher = regexp.MustCompile(`[^a-zA-Z0-9_]`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO embed .CacheFiles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						{{- range $alias, $pkg := .ModuleAlias }}
 | 
				
			||||||
 | 
						gojs.Alias("{{$alias}}", "{{$pkg}}")
 | 
				
			||||||
 | 
						{{- end}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						if appName == "" {
 | 
				
			||||||
 | 
							appName = filepath.Base(os.Args[0])
 | 
				
			||||||
 | 
							appName = strings.TrimSuffix(appName, ".exe")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if appVersion == "" {
 | 
				
			||||||
 | 
							appVersion = "0.0.1"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						args := os.Args[1:]
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							if args[0] == "help" || args[0] == "--help" || args[0] == "-h" {
 | 
				
			||||||
 | 
								printUsage()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if args[0] == "version" || args[0] == "--version" || args[0] == "-v" {
 | 
				
			||||||
 | 
								fmt.Println(appName, appVersion)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if args[0] == "export" || args[0] == "-e" {
 | 
				
			||||||
 | 
								gojs.ExportForDev()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := ""
 | 
				
			||||||
 | 
						if len(args) > 0 && (args[0] == "start" || args[0] == "stop" || args[0] == "restart" || args[0] == "test") {
 | 
				
			||||||
 | 
							cmd = args[0]
 | 
				
			||||||
 | 
							args = args[1:]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id := ""
 | 
				
			||||||
 | 
						mainFile := "{{.Main}}"
 | 
				
			||||||
 | 
						isWatch := false
 | 
				
			||||||
 | 
						mainArgs := make([]any, 0)
 | 
				
			||||||
 | 
						for i := 0; i < len(args); i++ {
 | 
				
			||||||
 | 
							arg := args[i]
 | 
				
			||||||
 | 
							if strings.HasPrefix(arg, "-") {
 | 
				
			||||||
 | 
								switch arg[1:] {
 | 
				
			||||||
 | 
								case "id":
 | 
				
			||||||
 | 
									i++
 | 
				
			||||||
 | 
									strings.TrimSpace(args[i])
 | 
				
			||||||
 | 
								case "main":
 | 
				
			||||||
 | 
									i++
 | 
				
			||||||
 | 
									strings.TrimSpace(args[i])
 | 
				
			||||||
 | 
								case "w":
 | 
				
			||||||
 | 
									isWatch = true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								mainArgs = append(mainArgs, arg)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !u.FileExists(mainFile) {
 | 
				
			||||||
 | 
							log.DefaultLogger.Error("main file not found", "mainFile", mainFile)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if id == "" {
 | 
				
			||||||
 | 
							id = fmt.Sprintf("%s_%s_%s", appName, mainFile, appVersion)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id = idFixMatcher.ReplaceAllString(id, "_")
 | 
				
			||||||
 | 
						homeDir, _ := os.UserHomeDir()
 | 
				
			||||||
 | 
						pidFile := filepath.Join(homeDir, ".pids", id+".pid")
 | 
				
			||||||
 | 
						pid := u.Int(u.ReadFileN(pidFile))
 | 
				
			||||||
 | 
						fmt.Println(appName, appVersion, mainFile, id, pidFile, pid)
 | 
				
			||||||
 | 
						switch cmd {
 | 
				
			||||||
 | 
						case "start":
 | 
				
			||||||
 | 
							if pid > 0 && checkProcess(pid) {
 | 
				
			||||||
 | 
								log.DefaultLogger.Info("process already started", "pid", pid)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								startProcess(pidFile, mainFile)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "stop":
 | 
				
			||||||
 | 
							if pid > 0 {
 | 
				
			||||||
 | 
								killProcess(pid, mainFile)
 | 
				
			||||||
 | 
								_ = os.Remove(pidFile)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "restart":
 | 
				
			||||||
 | 
							if pid > 0 {
 | 
				
			||||||
 | 
								killProcess(pid, mainFile)
 | 
				
			||||||
 | 
								_ = os.Remove(pidFile)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							startProcess(pidFile, mainFile)
 | 
				
			||||||
 | 
						case "test":
 | 
				
			||||||
 | 
							if pid > 0 {
 | 
				
			||||||
 | 
								killProcess(pid, mainFile)
 | 
				
			||||||
 | 
								_ = os.Remove(pidFile)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							startProcess(pidFile, mainFile)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							if isWatch {
 | 
				
			||||||
 | 
								gojs.WatchRun(mainFile, mainArgs...)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								gojs.RunFile(mainFile, mainArgs...)
 | 
				
			||||||
 | 
								gojs.WaitAll()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func startProcess(pidFile string, mainFile string) {
 | 
				
			||||||
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
 | 
						cmd = exec.Command(os.Args[0], "-main", mainFile)
 | 
				
			||||||
 | 
						cmd.Stdout = os.Stdout
 | 
				
			||||||
 | 
						cmd.Stderr = os.Stderr
 | 
				
			||||||
 | 
						if err := cmd.Start(); err == nil {
 | 
				
			||||||
 | 
							u.WriteFile(pidFile, u.String(cmd.Process.Pid))
 | 
				
			||||||
 | 
							log.DefaultLogger.Info("started", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", cmd.Process.Pid)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							log.DefaultLogger.Error("start failed", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "err", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func kill(pid int, sig os.Signal) error {
 | 
				
			||||||
 | 
						if proc, err := os.FindProcess(pid); err == nil {
 | 
				
			||||||
 | 
							return proc.Signal(sig)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func killProcess(pid int, mainFile string) {
 | 
				
			||||||
 | 
						if err := kill(pid, syscall.SIGTERM); err == nil {
 | 
				
			||||||
 | 
							log.DefaultLogger.Info("killing", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", pid)
 | 
				
			||||||
 | 
							t1 := time.Now().UnixMilli()
 | 
				
			||||||
 | 
							for i := 0; i < 100; i++ {
 | 
				
			||||||
 | 
								if !checkProcess(pid) {
 | 
				
			||||||
 | 
									log.DefaultLogger.Info("killed", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", pid, "usedTime", (time.Duration(time.Now().UnixMilli()-t1) * time.Millisecond).String())
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								time.Sleep(100 * time.Millisecond)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err := kill(pid, syscall.SIGKILL)
 | 
				
			||||||
 | 
							if checkProcess(pid) {
 | 
				
			||||||
 | 
								log.DefaultLogger.Error("fource kill failed", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", pid, "err", err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								log.DefaultLogger.Info("fource killed", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", pid)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkProcess(pid int) bool {
 | 
				
			||||||
 | 
						// return kill(pid, 0) == nil
 | 
				
			||||||
 | 
						_, err := os.FindProcess(pid)
 | 
				
			||||||
 | 
						return err == nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func printUsage() {
 | 
				
			||||||
 | 
						fmt.Println(appName, appVersion)
 | 
				
			||||||
 | 
						fmt.Println("Usage:")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "[-main mainFile] ...", "run script")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "version", "- show version")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "export", "- export for development")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "start [-id id] [-main mainFile]", "start server")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "stop [-id id]", "stop server")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "restart [-id id] [-main mainFile]", "restart server")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "help", "- show help")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								tests/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tests/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					.*
 | 
				
			||||||
 | 
					!.gitignore
 | 
				
			||||||
 | 
					go.sum
 | 
				
			||||||
 | 
					/dist
 | 
				
			||||||
 | 
					/build*
 | 
				
			||||||
 | 
					node_modules
 | 
				
			||||||
 | 
					package.json
 | 
				
			||||||
							
								
								
									
										25
									
								
								tests/apigo.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								tests/apigo.yml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					name:
 | 
				
			||||||
 | 
					version: v0.0.1
 | 
				
			||||||
 | 
					main: main.js
 | 
				
			||||||
 | 
					output: dist
 | 
				
			||||||
 | 
					target:
 | 
				
			||||||
 | 
					  darwin: amd64 arm64
 | 
				
			||||||
 | 
					  linux: amd64 arm64
 | 
				
			||||||
 | 
					  windows: amd64
 | 
				
			||||||
 | 
					module:
 | 
				
			||||||
 | 
					  apigo.cc/gojs/console: latest
 | 
				
			||||||
 | 
					  apigo.cc/gojs/util: latest
 | 
				
			||||||
 | 
					  apigo.cc/gojs/log: latest
 | 
				
			||||||
 | 
					  apigo.cc/gojs/file: latest
 | 
				
			||||||
 | 
					  apigo.cc/gojs/http: latest
 | 
				
			||||||
 | 
					  apigo.cc/gojs/db: latest
 | 
				
			||||||
 | 
					  apigo.cc/gojs/client: latest
 | 
				
			||||||
 | 
					  apigo.cc/gojs/service: latest
 | 
				
			||||||
 | 
					  apigo.cc/ai/llm: latest
 | 
				
			||||||
 | 
					modulealias:
 | 
				
			||||||
 | 
					extraimport:
 | 
				
			||||||
 | 
					  apigo.cc/ai/llm/openai: latest
 | 
				
			||||||
 | 
					  apigo.cc/ai/llm/zhipu: latest
 | 
				
			||||||
 | 
					  modernc.org/sqlite: latest
 | 
				
			||||||
 | 
					cachefile:
 | 
				
			||||||
 | 
					  - main.js
 | 
				
			||||||
							
								
								
									
										70
									
								
								tests/go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								tests/go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					module tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.18
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						apigo.cc/ai/llm v0.0.1
 | 
				
			||||||
 | 
						apigo.cc/gojs v0.0.2
 | 
				
			||||||
 | 
						apigo.cc/gojs/client v0.0.0-20241013151255-35c2a0f3cf99
 | 
				
			||||||
 | 
						apigo.cc/gojs/console v0.0.1
 | 
				
			||||||
 | 
						apigo.cc/gojs/db v0.0.1
 | 
				
			||||||
 | 
						apigo.cc/gojs/file v0.0.1
 | 
				
			||||||
 | 
						apigo.cc/gojs/http v0.0.3
 | 
				
			||||||
 | 
						apigo.cc/gojs/log v0.0.1
 | 
				
			||||||
 | 
						apigo.cc/gojs/service v0.0.1
 | 
				
			||||||
 | 
						apigo.cc/gojs/util v0.0.2
 | 
				
			||||||
 | 
						github.com/ssgo/log v1.7.7
 | 
				
			||||||
 | 
						github.com/ssgo/s v1.7.16
 | 
				
			||||||
 | 
						github.com/ssgo/u v1.7.9
 | 
				
			||||||
 | 
						modernc.org/sqlite v1.33.1
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						apigo.cc/apigo/gojs v0.1.1 // indirect
 | 
				
			||||||
 | 
						filippo.io/edwards25519 v1.1.0 // indirect
 | 
				
			||||||
 | 
						github.com/dlclark/regexp2 v1.11.4 // indirect
 | 
				
			||||||
 | 
						github.com/dustin/go-humanize v1.0.1 // indirect
 | 
				
			||||||
 | 
						github.com/fsnotify/fsnotify v1.7.0 // indirect
 | 
				
			||||||
 | 
						github.com/go-ole/go-ole v1.3.0 // indirect
 | 
				
			||||||
 | 
						github.com/go-resty/resty/v2 v2.15.2 // indirect
 | 
				
			||||||
 | 
						github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
 | 
				
			||||||
 | 
						github.com/go-sql-driver/mysql v1.8.1 // indirect
 | 
				
			||||||
 | 
						github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
 | 
				
			||||||
 | 
						github.com/gomodule/redigo v1.9.2 // indirect
 | 
				
			||||||
 | 
						github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
 | 
				
			||||||
 | 
						github.com/google/uuid v1.6.0 // indirect
 | 
				
			||||||
 | 
						github.com/gorilla/websocket v1.5.3 // indirect
 | 
				
			||||||
 | 
						github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
 | 
				
			||||||
 | 
						github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
 | 
				
			||||||
 | 
						github.com/mattn/go-isatty v0.0.20 // indirect
 | 
				
			||||||
 | 
						github.com/mitchellh/mapstructure v1.5.0 // indirect
 | 
				
			||||||
 | 
						github.com/ncruces/go-strftime v0.1.9 // indirect
 | 
				
			||||||
 | 
						github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
 | 
				
			||||||
 | 
						github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
				
			||||||
 | 
						github.com/sashabaranov/go-openai v1.32.0 // indirect
 | 
				
			||||||
 | 
						github.com/shirou/gopsutil/v3 v3.24.5 // indirect
 | 
				
			||||||
 | 
						github.com/shoenig/go-m1cpu v0.1.6 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/config v1.7.7 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/dao v0.1.5 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/db v1.7.9 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/discover v1.7.9 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/httpclient v1.7.8 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/redis v1.7.7 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/standard v1.7.7 // indirect
 | 
				
			||||||
 | 
						github.com/ssgo/tool v0.4.27 // indirect
 | 
				
			||||||
 | 
						github.com/tklauser/go-sysconf v0.3.14 // indirect
 | 
				
			||||||
 | 
						github.com/tklauser/numcpus v0.8.0 // indirect
 | 
				
			||||||
 | 
						github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6 // indirect
 | 
				
			||||||
 | 
						github.com/yankeguo/zhipu v0.1.2 // indirect
 | 
				
			||||||
 | 
						github.com/yusufpapurcu/wmi v1.2.4 // indirect
 | 
				
			||||||
 | 
						golang.org/x/net v0.30.0 // indirect
 | 
				
			||||||
 | 
						golang.org/x/sys v0.26.0 // indirect
 | 
				
			||||||
 | 
						golang.org/x/text v0.19.0 // indirect
 | 
				
			||||||
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
 | 
						modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
 | 
				
			||||||
 | 
						modernc.org/libc v1.55.3 // indirect
 | 
				
			||||||
 | 
						modernc.org/mathutil v1.6.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/memory v1.8.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/strutil v1.2.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/token v1.1.0 // indirect
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										205
									
								
								tests/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								tests/main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,205 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"apigo.cc/gojs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ "apigo.cc/ai/llm"
 | 
				
			||||||
 | 
						_ "apigo.cc/gojs/client"
 | 
				
			||||||
 | 
						_ "apigo.cc/gojs/console"
 | 
				
			||||||
 | 
						_ "apigo.cc/gojs/db"
 | 
				
			||||||
 | 
						_ "apigo.cc/gojs/file"
 | 
				
			||||||
 | 
						_ "apigo.cc/gojs/http"
 | 
				
			||||||
 | 
						_ "apigo.cc/gojs/log"
 | 
				
			||||||
 | 
						_ "apigo.cc/gojs/service"
 | 
				
			||||||
 | 
						_ "apigo.cc/gojs/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ "apigo.cc/ai/llm/openai"
 | 
				
			||||||
 | 
						_ "apigo.cc/ai/llm/zhipu"
 | 
				
			||||||
 | 
						"github.com/ssgo/log"
 | 
				
			||||||
 | 
						_ "github.com/ssgo/s"
 | 
				
			||||||
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
 | 
						_ "modernc.org/sqlite"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var appName = "tests"
 | 
				
			||||||
 | 
					var appVersion = "v0.0.1"
 | 
				
			||||||
 | 
					var idFixMatcher = regexp.MustCompile(`[^a-zA-Z0-9_]`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO embed .CacheFiles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						gojs.Alias("ai/llm", "apigo.cc/ai/llm")
 | 
				
			||||||
 | 
						gojs.Alias("client", "apigo.cc/gojs/client")
 | 
				
			||||||
 | 
						gojs.Alias("console", "apigo.cc/gojs/console")
 | 
				
			||||||
 | 
						gojs.Alias("db", "apigo.cc/gojs/db")
 | 
				
			||||||
 | 
						gojs.Alias("file", "apigo.cc/gojs/file")
 | 
				
			||||||
 | 
						gojs.Alias("http", "apigo.cc/gojs/http")
 | 
				
			||||||
 | 
						gojs.Alias("log", "apigo.cc/gojs/log")
 | 
				
			||||||
 | 
						gojs.Alias("service", "apigo.cc/gojs/service")
 | 
				
			||||||
 | 
						gojs.Alias("util", "apigo.cc/gojs/util")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						if appName == "" {
 | 
				
			||||||
 | 
							appName = filepath.Base(os.Args[0])
 | 
				
			||||||
 | 
							appName = strings.TrimSuffix(appName, ".exe")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if appVersion == "" {
 | 
				
			||||||
 | 
							appVersion = "0.0.1"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						args := os.Args[1:]
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							if args[0] == "help" || args[0] == "--help" || args[0] == "-h" {
 | 
				
			||||||
 | 
								printUsage()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if args[0] == "version" || args[0] == "--version" || args[0] == "-v" {
 | 
				
			||||||
 | 
								fmt.Println(appName, appVersion)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if args[0] == "export" || args[0] == "-e" {
 | 
				
			||||||
 | 
								gojs.ExportForDev()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := ""
 | 
				
			||||||
 | 
						if len(args) > 0 && (args[0] == "start" || args[0] == "stop" || args[0] == "restart") {
 | 
				
			||||||
 | 
							cmd = args[0]
 | 
				
			||||||
 | 
							args = args[1:]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id := ""
 | 
				
			||||||
 | 
						if len(args) > 1 && args[0] == "-id" {
 | 
				
			||||||
 | 
							id = strings.TrimSpace(args[1])
 | 
				
			||||||
 | 
							args = args[2:]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mainFile := "main.js"
 | 
				
			||||||
 | 
						if len(args) > 1 && args[0] == "-main" {
 | 
				
			||||||
 | 
							mainFile = strings.TrimSpace(args[1])
 | 
				
			||||||
 | 
							args = args[2:]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !u.FileExists(mainFile) {
 | 
				
			||||||
 | 
							log.DefaultLogger.Error("main file not found", "mainFile", mainFile)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if id == "" {
 | 
				
			||||||
 | 
							id = fmt.Sprintf("%s_%s_%s", appName, mainFile, appVersion)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id = idFixMatcher.ReplaceAllString(id, "_")
 | 
				
			||||||
 | 
						homeDir, _ := os.UserHomeDir()
 | 
				
			||||||
 | 
						pidFile := filepath.Join(homeDir, ".pids", id+".pid")
 | 
				
			||||||
 | 
						pid := u.Int(u.ReadFileN(pidFile))
 | 
				
			||||||
 | 
						fmt.Println(appName, appVersion, mainFile, id, pidFile, pid)
 | 
				
			||||||
 | 
						switch cmd {
 | 
				
			||||||
 | 
						case "start":
 | 
				
			||||||
 | 
							if pid > 0 && checkProcess(pid) {
 | 
				
			||||||
 | 
								log.DefaultLogger.Info("process already started", "pid", pid)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								startProcess(pidFile, mainFile)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "stop":
 | 
				
			||||||
 | 
							if pid > 0 {
 | 
				
			||||||
 | 
								killProcess(pid, mainFile)
 | 
				
			||||||
 | 
								_ = os.Remove(pidFile)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "restart":
 | 
				
			||||||
 | 
							if pid > 0 {
 | 
				
			||||||
 | 
								killProcess(pid, mainFile)
 | 
				
			||||||
 | 
								_ = os.Remove(pidFile)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							startProcess(pidFile, mainFile)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							gojs.RunFile(mainFile, u.ToInterfaceArray(args)...)
 | 
				
			||||||
 | 
							gojs.WaitAll()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func startProcess(pidFile string, mainFile string) {
 | 
				
			||||||
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
 | 
						cmd = exec.Command(os.Args[0], "-main", mainFile)
 | 
				
			||||||
 | 
						cmd.Stdout = os.Stdout
 | 
				
			||||||
 | 
						cmd.Stderr = os.Stderr
 | 
				
			||||||
 | 
						if err := cmd.Start(); err == nil {
 | 
				
			||||||
 | 
							u.WriteFile(pidFile, u.String(cmd.Process.Pid))
 | 
				
			||||||
 | 
							log.DefaultLogger.Info("started", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", cmd.Process.Pid)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							log.DefaultLogger.Error("start failed", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "err", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// func killProcess1(pid int) error {
 | 
				
			||||||
 | 
					// 	switch runtime.GOOS {
 | 
				
			||||||
 | 
					// 	case "windows":
 | 
				
			||||||
 | 
					// 		// Windows 系统使用 taskkill 命令
 | 
				
			||||||
 | 
					// 		cmd := exec.Command("taskkill", "/F", "/PID", fmt.Sprintf("%d", pid))
 | 
				
			||||||
 | 
					// 		return cmd.Run()
 | 
				
			||||||
 | 
					// 	case "linux", "darwin":
 | 
				
			||||||
 | 
					// 		// Linux 和 macOS 系统使用 syscall.Kill
 | 
				
			||||||
 | 
					// 		return syscall.Kill(pid, syscall.SIGKILL)
 | 
				
			||||||
 | 
					// 	default:
 | 
				
			||||||
 | 
					// 		return fmt.Errorf("unsupported platform")
 | 
				
			||||||
 | 
					// 	}
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func kill(pid int, sig os.Signal) error {
 | 
				
			||||||
 | 
						if proc, err := os.FindProcess(pid); err == nil {
 | 
				
			||||||
 | 
							return proc.Signal(sig)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func killProcess(pid int, mainFile string) {
 | 
				
			||||||
 | 
						if err := kill(pid, syscall.SIGTERM); err == nil {
 | 
				
			||||||
 | 
							log.DefaultLogger.Info("killing", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", pid)
 | 
				
			||||||
 | 
							t1 := time.Now().UnixMilli()
 | 
				
			||||||
 | 
							for i := 0; i < 100; i++ {
 | 
				
			||||||
 | 
								if !checkProcess(pid) {
 | 
				
			||||||
 | 
									log.DefaultLogger.Info("killed", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", pid, "usedTime", (time.Duration(time.Now().UnixMilli()-t1) * time.Millisecond).String())
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								time.Sleep(100 * time.Millisecond)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err := kill(pid, syscall.SIGKILL)
 | 
				
			||||||
 | 
							if checkProcess(pid) {
 | 
				
			||||||
 | 
								log.DefaultLogger.Error("fource kill failed", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", pid, "err", err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								log.DefaultLogger.Info("fource killed", "appName", appName, "appVersion", appVersion, "mainFile", mainFile, "pid", pid)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkProcess(pid int) bool {
 | 
				
			||||||
 | 
						// return kill(pid, 0) == nil
 | 
				
			||||||
 | 
						_, err := os.FindProcess(pid)
 | 
				
			||||||
 | 
						return err == nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func printUsage() {
 | 
				
			||||||
 | 
						fmt.Println(appName, appVersion)
 | 
				
			||||||
 | 
						fmt.Println("Usage:")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "[-main mainFile] ...", "run script")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "version", "- show version")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "export", "- export for development")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "start [-id id] [-main mainFile]", "start server")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "stop [-id id]", "stop server")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "restart [-id id] [-main mainFile]", "restart server")
 | 
				
			||||||
 | 
						fmt.Println("  ", appName, "help", "- show help")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user