feat: 🍻 完善 start 命令,完成 stop 命令

main
熊二 2 years ago
parent a0062b93b2
commit 4dce3d03cd
  1. 6
      db/db.go
  2. 3
      go.mod
  3. 2
      go.sum
  4. 302
      main.go

@ -63,7 +63,7 @@ func WithAppName(name string) func(*gorm.DB) *gorm.DB {
} }
} }
func WithAppStatus(statuses ...uint32) func(*gorm.DB) *gorm.DB { func WithAppStatus(statuses ...uint8) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB {
switch len(statuses) { switch len(statuses) {
case 0: case 0:
@ -87,8 +87,8 @@ func FindApp(opts ...func(*gorm.DB) *gorm.DB) (*App, error) {
} }
// FindApps 查找多个 App // FindApps 查找多个 App
func FindApps(opts ...func(*gorm.DB) *gorm.DB) ([]App, error) { func FindApps(opts ...func(*gorm.DB) *gorm.DB) ([]*App, error) {
var apps []App var apps []*App
err := DB.Model(&App{}).Scopes(opts...).Find(&apps).Error err := DB.Model(&App{}).Scopes(opts...).Find(&apps).Error
if err != nil { if err != nil {
return nil, err return nil, err

@ -5,7 +5,7 @@ go 1.19
require ( require (
github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-isatty v0.0.17
github.com/pelletier/go-toml/v2 v2.0.6 github.com/pelletier/go-toml/v2 v2.0.6
github.com/tint/env v1.0.2 github.com/rs/xid v1.4.0
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/sqlite v1.4.4 gorm.io/driver/sqlite v1.4.4
@ -15,7 +15,6 @@ require (
require ( require (
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/joho/godotenv v1.4.0 // indirect
github.com/mattn/go-sqlite3 v1.14.15 // indirect github.com/mattn/go-sqlite3 v1.14.15 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
) )

@ -16,6 +16,8 @@ github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvI
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

@ -1,8 +1,9 @@
package main package main
import ( import (
"flag"
"fmt" "fmt"
"github.com/tint/env" "github.com/rs/xid"
"hupeh.vip/pm/cmd" "hupeh.vip/pm/cmd"
"hupeh.vip/pm/db" "hupeh.vip/pm/db"
"os" "os"
@ -10,86 +11,100 @@ import (
"strconv" "strconv"
) )
//import ( var allowPMTs = map[string]int{
// "flag" "start": 1,
// "fmt" "restart": 1,
// "log" //"list": 1,
// "os" //"shell": 1,
// "os/exec" }
// "time"
//)
//
//// 移除不需要的参数
//func strip(slice []string, remove string) []string {
// for i := 0; i < len(slice); {
// if slice[i] == remove {
// if i != len(slice)-1 {
// slice = append(slice[:i], slice[i+1:]...)
// } else {
// slice = slice[:i]
// }
// } else {
// i++
// }
// }
// return slice
//}
//
//func subProcess(args []string) *exec.Cmd {
// cmd := exec.Command(args[0], args[1:]...)
// cmd.Stdin = os.Stdin
// cmd.Stdout = os.Stdout
// cmd.Stderr = os.Stderr
// err := cmd.Start()
// if err != nil {
// fmt.Fprintf(os.Stderr, "[-] Error: %s\n", err)
// }
// return cmd
//}
//
//func main() {
// daemon := flag.Bool("daemon", false, "run in daemon")
// forever := flag.Bool("forever", false, "run forever")
// flag.Parse()
//
// // 启用守护模式
// if *daemon {
// subProcess(strip(os.Arguments, "-daemon"))
// fmt.Printf("[*] Daemon running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid())
// os.Exit(0)
// }
//
// if *forever {
// for {
// cmd := subProcess(strip(os.Arguments, "-forever"))
// fmt.Printf("[*] Forever running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid())
// if err := cmd.Wait(); err != nil {
// fmt.Println(err)
// os.Exit(1)
// }
// }
// }
//
// fp, _ := os.OpenFile("./dosomething.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
// log.SetOutput(fp)
// log.Printf("[*] Service running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid())
// defer log.Println("exit ...")
// for {
// log.Printf("DoSomething running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid())
// time.Sleep(time.Second * 5)
// }
//}
func main() { func main() {
env.Setup() var sign string // all
var pmt string // all
var id int // start,restart
var daemon bool // start,restart
switch env.String("PMT_CURRENT_FUNCTION") { flag.StringVar(&sign, "sign", "", "sign token")
case "start": flag.StringVar(&pmt, "pmt", "", "task name")
case "stop": flag.IntVar(&id, "id", 0, "app id")
case "shell": flag.BoolVar(&daemon, "daemon", false, "daemon mode")
case "list": flag.Parse()
default:
// 命令行模式
if v, ok := allowPMTs[pmt]; !ok || v != 1 {
runCli() runCli()
return
}
// 验证签名
if _, err := xid.FromString(sign); err != nil {
fmt.Println(cmd.Red("bad sign token"))
return
}
switch pmt {
// 启动应用
case "start":
// 开启守护模式
if !daemon {
subProcess(append(os.Args, "-daemon")...)
fmt.Printf("[*] Daemon running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid())
os.Exit(0)
}
if id <= 0 {
fmt.Println(cmd.Red("missing app id"))
return
}
// 查询 app
app, err := db.FindApp(db.WithAppID(uint(id)))
if err != nil {
fmt.Println(cmd.Red("failed to find app"))
fmt.Println(" " + cmd.Red(err.Error()))
return
}
// 创建进程处理器
proc := db.NewProc(app)
// 忽略运行中的
if proc.IsAlive() {
return
}
// 启动失败
if err = proc.Start(); err != nil {
fmt.Println(cmd.Yellow(app.Name) + " - " + cmd.Red("start fail"))
fmt.Println(" " + cmd.Red(err.Error()))
return
}
// 启动成功
fmt.Println(cmd.Yellow(app.Name) + " - " + cmd.Green("start success"))
// 重启脚本
case "restart":
// 开启守护模式
if !daemon {
subProcess(append(os.Args, "-daemon")...)
fmt.Printf("[*] Daemon running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid())
os.Exit(0)
}
if id <= 0 {
fmt.Println(cmd.Red("missing app id"))
return
}
// 查询 app
app, err := db.FindApp(db.WithAppID(uint(id)))
if err != nil {
fmt.Println(cmd.Red("failed to find app"))
fmt.Println(" " + cmd.Red(err.Error()))
return
}
// 创建进程处理器
proc := db.NewProc(app)
// 重启失败
if err = proc.Restart(); err != nil {
fmt.Println(cmd.Yellow(app.Name) + " - " + cmd.Red("restart fail"))
fmt.Println(" " + cmd.Red(err.Error()))
return
}
// 重启成功
fmt.Println(cmd.Yellow(app.Name) + " - " + cmd.Green("restart success"))
} }
} }
@ -99,7 +114,7 @@ func runCli() {
Description("description"). Description("description").
// start 命令 // start 命令
Command("start", "Start a program in daemon"). Command("start", "Start program(s) in daemon").
Argument("[names:...string]", "file/name/id"). Argument("[names:...string]", "file/name/id").
Option("-w, --watch <watch:boolean>", "Watches folder changes"). Option("-w, --watch <watch:boolean>", "Watches folder changes").
Option("-c, --cwd <cwd:string>", "Sets working directory "+cmd.Yellow("Sets working directory")+" Sets working directory "+cmd.Yellow("Sets working directory")+" Sets working directory "+cmd.Yellow(" Sets working directory")+" Sets working directory "+cmd.Yellow("Sets working directory")+" Sets working directory "+cmd.Yellow("Sets working directory")+" End ..."). Option("-c, --cwd <cwd:string>", "Sets working directory "+cmd.Yellow("Sets working directory")+" Sets working directory "+cmd.Yellow("Sets working directory")+" Sets working directory "+cmd.Yellow(" Sets working directory")+" Sets working directory "+cmd.Yellow("Sets working directory")+" Sets working directory "+cmd.Yellow("Sets working directory")+" End ...").
@ -107,42 +122,106 @@ func runCli() {
Option("-e, --env", "Sets current environment name"). Option("-e, --env", "Sets current environment name").
Option("-i, --interpreter", "Sets interpreter name"). Option("-i, --interpreter", "Sets interpreter name").
Action(func(c *cmd.Command, m map[string]any, a ...any) error { Action(func(c *cmd.Command, m map[string]any, a ...any) error {
var apps []*db.App
var err error
if len(a) != 1 { if len(a) != 1 {
return c.ShowHelp() apps, err = db.FindApps(db.WithAppStatus(db.StatusStopped))
if err != nil {
fmt.Println(cmd.Red("load all apps fail"))
fmt.Println(" " + cmd.Red(err.Error()))
return err
}
} else {
scripts := a[0].([]string)
for _, script := range scripts {
loads, err := db.ParseInput(script)
if err != nil {
fmt.Println(cmd.Red("load all apps fail"))
fmt.Println(" " + cmd.Red(err.Error()))
return err
}
apps = append(apps, loads...)
}
}
if len(apps) == 0 {
fmt.Println("No app found")
return nil
}
for _, app := range apps {
if app.Status == db.StatusRunning {
fmt.Println(cmd.Yellow(app.Name), cmd.Green("running"))
continue
}
if app.ID == 0 {
if err := db.SaveApp(app); err != nil {
fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed to saving"))
fmt.Println(" " + cmd.Red(err.Error()))
continue
}
}
// todo options
proc := subProcess(
"-id="+strconv.Itoa(int(app.ID)),
"-pmt=start",
"-sign="+xid.New().String(),
)
if err := proc.Wait(); err != nil {
fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed"))
fmt.Println(" ", err.Error())
} else {
fmt.Println(cmd.Yellow(app.Name), cmd.Green("success"))
}
} }
fmt.Println("over")
return nil
}).
// restart 命令
Command("restart", "Restarts program(s)").
Argument("<names:...string>", "file/name/id").
Action(func(c *cmd.Command, m map[string]any, a ...any) error {
var apps []*db.App var apps []*db.App
if len(a) != 0 {
c.ShowHelp()
return nil
}
scripts := a[0].([]string) scripts := a[0].([]string)
for _, script := range scripts { for _, script := range scripts {
loads, err := db.ParseInput(script) loads, err := db.ParseInput(script)
if err != nil { if err != nil {
fmt.Println(cmd.Red("load all apps fail"))
fmt.Println(" " + cmd.Red(err.Error()))
return err return err
} }
apps = append(apps, loads...) apps = append(apps, loads...)
} }
if len(apps) == 0 { if len(apps) == 0 {
fmt.Println("No app found") fmt.Println("No app found")
return nil return nil
} }
for _, app := range apps { for _, app := range apps {
if app.Status == db.StatusRunning {
fmt.Println(cmd.Yellow(app.Name), cmd.Green("running"))
continue
}
if app.ID == 0 { if app.ID == 0 {
if err := db.SaveApp(app); err != nil { if err := db.SaveApp(app); err != nil {
fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed for save")) fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed to saving"))
return err fmt.Println(" " + cmd.Red(err.Error()))
continue
} }
} }
if err := runStart(app.ID); err != nil { // todo options
proc := subProcess(
"-id="+strconv.Itoa(int(app.ID)),
"-pmt=restart",
"-sign="+xid.New().String(),
)
if err := proc.Wait(); err != nil {
fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed")) fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed"))
fmt.Println(" ", err.Error()) fmt.Println(" ", err.Error())
} else {
fmt.Println(cmd.Yellow(app.Name), cmd.Green("success"))
} }
} }
fmt.Println("over") fmt.Println("over")
return nil return nil
}). }).
@ -150,8 +229,38 @@ func runCli() {
Command("stop", "Stops a program"). Command("stop", "Stops a program").
Argument("<names:...string>", "file/name/id"). Argument("<names:...string>", "file/name/id").
Action(func(c *cmd.Command, m map[string]any, a ...any) error { Action(func(c *cmd.Command, m map[string]any, a ...any) error {
fmt.Println("停止程序") if len(a) != 1 {
fmt.Println(a...) return c.ShowHelp()
}
var apps []*db.App
scripts := a[0].([]string)
for _, script := range scripts {
loads, err := db.ParseInput(script)
if err != nil {
return err
}
apps = append(apps, loads...)
}
if len(apps) == 0 {
fmt.Println("No app found")
return nil
}
for _, app := range apps {
if app.PID <= 0 {
fmt.Println(cmd.Yellow(app.Name), "not running")
return nil
}
proc := db.NewProc(app)
err := proc.Stop()
if err != nil {
fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed to stop"))
fmt.Println(" " + cmd.Red(err.Error()))
return err
}
fmt.Println(cmd.Yellow(app.Name), cmd.Green("stopped"))
return nil
}
fmt.Println("over")
return nil return nil
}). }).
@ -159,17 +268,8 @@ func runCli() {
Run() Run()
} }
func runStart(id uint) error { func subProcess(args ...string) *exec.Cmd {
proc := subProcess( cmd := exec.Command(os.Args[0], args...)
[]string{os.Args[0], "-id", strconv.Itoa(int(id))},
[]string{"PMT_CURRENT_FUNCTION=start"},
)
return proc.Wait()
}
func subProcess(args []string, env []string) *exec.Cmd {
cmd := exec.Command(args[0], args[1:]...)
cmd.Env = append(os.Environ(), env...)
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr

Loading…
Cancel
Save