apigo/templates/_main.go

225 lines
5.9 KiB
Go
Raw Permalink Normal View History

2024-10-20 09:03:45 +08:00
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"
)
2024-10-24 17:56:45 +08:00
var _mainFile = "{{.Main}}"
2024-10-20 09:03:45 +08:00
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}}
}
2024-10-24 17:56:45 +08:00
func setSSKey(key, iv []byte) {
gojs.SetSSKey(key, iv)
}
2024-10-20 09:03:45 +08:00
func main() {
if appName == "" {
appName = filepath.Base(os.Args[0])
appName = strings.TrimSuffix(appName, ".exe")
}
if appVersion == "" {
appVersion = "0.0.1"
}
2024-10-24 17:56:45 +08:00
cmd := ""
id := ""
mainFile := _mainFile
isWatch := false
startArgs := make([]string, 0)
mainArgs := make([]any, 0)
for i := 1; i < len(os.Args); i++ {
arg := os.Args[i]
switch arg {
case "--help", "-help", "-h":
2024-10-20 09:03:45 +08:00
printUsage()
return
2024-10-24 17:56:45 +08:00
case "--version", "-version", "-v":
2024-10-20 09:03:45 +08:00
fmt.Println(appName, appVersion)
return
2024-10-24 17:56:45 +08:00
case "--export", "-export":
fmt.Println(u.Cyan(gojs.ExportForDev()))
2024-10-20 09:03:45 +08:00
return
2024-10-24 17:56:45 +08:00
case "start", "stop", "restart", "reload", "test":
if len(mainArgs) == 0 && cmd == "" {
cmd = arg
} else {
mainArgs = append(mainArgs, arg)
2024-10-20 09:03:45 +08:00
}
2024-10-24 17:56:45 +08:00
case "--env", "-env", "-e":
i++
a := strings.SplitN(os.Args[i], "=", 2)
os.Setenv(a[0], a[1])
case "-id":
i++
id = strings.TrimSpace(os.Args[i])
case "-main":
i++
mainFile = strings.TrimSpace(os.Args[i])
startArgs = append(startArgs, "-main", mainFile)
case "-watch", "-w":
isWatch = true
startArgs = append(startArgs, arg)
default:
2024-10-20 09:03:45 +08:00
mainArgs = append(mainArgs, arg)
2024-10-24 17:56:45 +08:00
startArgs = append(startArgs, arg)
2024-10-20 09:03:45 +08:00
}
}
2024-10-24 17:56:45 +08:00
if cmd != "test" {
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, "_")
2024-10-20 09:03:45 +08:00
}
homeDir, _ := os.UserHomeDir()
pidFile := filepath.Join(homeDir, ".pids", id+".pid")
pid := u.Int(u.ReadFileN(pidFile))
switch cmd {
case "start":
2024-10-24 17:56:45 +08:00
if pid > 0 && processIsExists(pid) {
2024-10-20 09:03:45 +08:00
log.DefaultLogger.Info("process already started", "pid", pid)
} else {
2024-10-24 17:56:45 +08:00
startProcess(pidFile, startArgs)
2024-10-20 09:03:45 +08:00
}
case "stop":
if pid > 0 {
killProcess(pid, mainFile)
_ = os.Remove(pidFile)
}
2024-10-24 17:56:45 +08:00
case "reload":
if pid > 0 {
kill(pid, syscall.Signal(0x1e))
}
2024-10-20 09:03:45 +08:00
case "restart":
if pid > 0 {
killProcess(pid, mainFile)
_ = os.Remove(pidFile)
}
2024-10-24 17:56:45 +08:00
startProcess(pidFile, startArgs)
2024-10-20 09:03:45 +08:00
case "test":
2024-10-24 17:56:45 +08:00
testPath := "."
if u.FileExists("tests") {
testPath = "tests"
}
for _, f := range u.ReadDirN(testPath) {
if !f.IsDir && strings.HasSuffix(f.Name, "_test.js") {
if r, err := gojs.RunFile(f.FullName, mainArgs...); err != nil {
fmt.Println(u.BRed("test failed for "+f.FullName), err.Error())
} else {
fmt.Println(u.Green("test passed for "+f.FullName), r)
}
gojs.WaitAll()
}
2024-10-20 09:03:45 +08:00
}
default:
2024-10-24 17:56:45 +08:00
defer os.Remove(pidFile)
2024-10-20 09:03:45 +08:00
if isWatch {
2024-10-24 17:56:45 +08:00
gojs.WatchRun(mainFile, nil, nil, mainArgs...)
2024-10-20 09:03:45 +08:00
} else {
2024-10-24 17:56:45 +08:00
if r, err := gojs.RunFile(mainFile, mainArgs...); err != nil {
log.DefaultLogger.Error("run failed for "+mainFile, "err", err.Error())
} else if r != nil {
log.DefaultLogger.Error("run "+mainFile, "result", r)
}
2024-10-20 09:03:45 +08:00
gojs.WaitAll()
}
}
}
2024-10-24 17:56:45 +08:00
func startProcess(pidFile string, startArgs []string) {
cmd := exec.Command(os.Args[0], startArgs...)
2024-10-20 09:03:45 +08:00
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err == nil {
u.WriteFile(pidFile, u.String(cmd.Process.Pid))
2024-10-24 17:56:45 +08:00
log.DefaultLogger.Info("started", "appName", appName, "appVersion", appVersion, "startArgs", startArgs, "pid", cmd.Process.Pid)
2024-10-20 09:03:45 +08:00
} else {
2024-10-24 17:56:45 +08:00
log.DefaultLogger.Error("start failed", "appName", appName, "appVersion", appVersion, "startArgs", startArgs, "err", err)
2024-10-20 09:03:45 +08:00
}
2024-10-24 17:56:45 +08:00
os.Exit(0)
2024-10-20 09:03:45 +08:00
}
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++ {
2024-10-24 17:56:45 +08:00
if !processIsExists(pid) {
2024-10-20 09:03:45 +08:00
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)
2024-10-24 17:56:45 +08:00
if processIsExists(pid) {
2024-10-20 09:03:45 +08:00
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)
}
}
}
2024-10-24 17:56:45 +08:00
func processIsExists(pid int) bool {
if proc, err := os.FindProcess(pid); err == nil {
if err2 := proc.Signal(syscall.Signal(0)); err2 == nil {
return true
}
}
return false
2024-10-20 09:03:45 +08:00
}
func printUsage() {
fmt.Println(appName, appVersion)
fmt.Println("Usage:")
fmt.Println(" ", appName, "[-main mainFile] ...", "run script")
2024-10-24 17:56:45 +08:00
fmt.Println(" ", appName, "-version|-v", "show version")
fmt.Println(" ", appName, "-export", "export for development")
fmt.Println(" ", appName, "-env|-e", "set env")
2024-10-20 09:03:45 +08:00
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")
2024-10-24 17:56:45 +08:00
fmt.Println(" ", appName, "reload [-id id] [-main mainFile]", "reload config")
fmt.Println(" ", appName, "test", "test *_test.js")
2024-10-20 09:03:45 +08:00
fmt.Println(" ", appName, "help", "- show help")
}