chore: initial commit (starter)
This commit is contained in:
commit
0a996ee6e4
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.geminiignore
|
||||
384
.log.meta.json
Normal file
384
.log.meta.json
Normal file
@ -0,0 +1,384 @@
|
||||
{
|
||||
"debug": [
|
||||
{
|
||||
"Index": 0,
|
||||
"Name": "LogName",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "cyan",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 1,
|
||||
"Name": "LogType",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "magenta",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 2,
|
||||
"Name": "LogTime",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "time",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 3,
|
||||
"Name": "TraceId",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "gray",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": true,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 4,
|
||||
"Name": "Image",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 5,
|
||||
"Name": "Server",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 6,
|
||||
"Name": "Debug",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": true,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 7,
|
||||
"Name": "Extra",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
}
|
||||
],
|
||||
"error": [
|
||||
{
|
||||
"Index": 0,
|
||||
"Name": "LogName",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "cyan",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 1,
|
||||
"Name": "LogType",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "magenta",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 2,
|
||||
"Name": "LogTime",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "time",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 3,
|
||||
"Name": "TraceId",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "gray",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": true,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 4,
|
||||
"Name": "Image",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 5,
|
||||
"Name": "Server",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 6,
|
||||
"Name": "Error",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "red",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": true,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 7,
|
||||
"Name": "Extra",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 8,
|
||||
"Name": "CallStacks",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
}
|
||||
],
|
||||
"info": [
|
||||
{
|
||||
"Index": 0,
|
||||
"Name": "LogName",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "cyan",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 1,
|
||||
"Name": "LogType",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "magenta",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 2,
|
||||
"Name": "LogTime",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "time",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 3,
|
||||
"Name": "TraceId",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "gray",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": true,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 4,
|
||||
"Name": "Image",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 5,
|
||||
"Name": "Server",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 6,
|
||||
"Name": "Info",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "cyan",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": true,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 7,
|
||||
"Name": "Extra",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
}
|
||||
],
|
||||
"warning": [
|
||||
{
|
||||
"Index": 0,
|
||||
"Name": "LogName",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "cyan",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 1,
|
||||
"Name": "LogType",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "magenta",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 2,
|
||||
"Name": "LogTime",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "time",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 3,
|
||||
"Name": "TraceId",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "gray",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": true,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 4,
|
||||
"Name": "Image",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 5,
|
||||
"Name": "Server",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": true
|
||||
},
|
||||
{
|
||||
"Index": 6,
|
||||
"Name": "Warning",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "yellow",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": true,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 7,
|
||||
"Name": "Extra",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
},
|
||||
{
|
||||
"Index": 8,
|
||||
"Name": "CallStacks",
|
||||
"KeyName": "",
|
||||
"AttachBefore": false,
|
||||
"Color": "",
|
||||
"Format": "",
|
||||
"Precision": 0,
|
||||
"WithoutKey": false,
|
||||
"Hide": false
|
||||
}
|
||||
]
|
||||
}
|
||||
10
CHANGELOG.md
Normal file
10
CHANGELOG.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Changelog: @go/starter
|
||||
|
||||
## v1.0.0 (2026-05-10)
|
||||
|
||||
### 🚀 Features
|
||||
- **Initial Release**: Migrated from `ssgo/starter` to `apigo.cc/go/starter`.
|
||||
- **Background Mode**: Supports running services in the background with `-log` redirection.
|
||||
- **PID Management**: Automatic PID file creation and cleanup.
|
||||
- **Signal Handling**: Graceful shutdown on `SIGINT`/`SIGTERM`, reload support on `SIGHUP`.
|
||||
- **Infrastructure Alignment**: Integrated with `go/file` and `go/log`.
|
||||
60
README.md
Normal file
60
README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# @go/starter
|
||||
|
||||
Service starter for @go applications, supporting background mode, PID management, and signal handling.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get apigo.cc/go/starter
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"apigo.cc/go/starter"
|
||||
)
|
||||
|
||||
func main() {
|
||||
starter.SetInfo("myapp", "1.0.0")
|
||||
|
||||
starter.OnStart(func(ctx context.Context) {
|
||||
fmt.Println("Service starting...")
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
fmt.Println("Service stopping...")
|
||||
return
|
||||
case <-time.After(1 * time.Second):
|
||||
fmt.Println("Tick")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
starter.OnStop(func() {
|
||||
fmt.Println("Service stopped.")
|
||||
})
|
||||
|
||||
starter.Run()
|
||||
}
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
- `start`: Start the service in background.
|
||||
- `stop`: Stop the service.
|
||||
- `restart`: Restart the service.
|
||||
- `status`: Show service status.
|
||||
- `-v`, `--version`: Show version.
|
||||
- `-h`, `--help`: Show help.
|
||||
|
||||
## Options
|
||||
|
||||
- `-pid`: PID file path (default `.pid`).
|
||||
- `-log`: Log file path for background mode.
|
||||
277
starter.go
Normal file
277
starter.go
Normal file
@ -0,0 +1,277 @@
|
||||
package starter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"apigo.cc/go/file"
|
||||
"apigo.cc/go/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// Default configuration
|
||||
appName = filepath.Base(os.Args[0])
|
||||
appVersion = "1.0.0"
|
||||
pidPath = ".pid"
|
||||
logPath = ""
|
||||
stopTimeout = 5 * time.Second
|
||||
|
||||
// Internal state
|
||||
commands = make(map[string]*command)
|
||||
onStart func(ctx context.Context)
|
||||
onStop func()
|
||||
onReload func()
|
||||
|
||||
// Flags
|
||||
flagSet = flag.NewFlagSet(appName, flag.ContinueOnError)
|
||||
)
|
||||
|
||||
type command struct {
|
||||
name string
|
||||
desc string
|
||||
fn func()
|
||||
}
|
||||
|
||||
func init() {
|
||||
flagSet.StringVar(&pidPath, "pid", ".pid", "PID file path")
|
||||
flagSet.StringVar(&logPath, "log", "", "Log file path (for background mode)")
|
||||
|
||||
AddCmd("start", "Start the service in background", startCmd)
|
||||
AddCmd("stop", "Stop the service", stopCmd)
|
||||
AddCmd("restart", "Restart the service", restartCmd)
|
||||
AddCmd("status", "Show service status", statusCmd)
|
||||
}
|
||||
|
||||
// SetInfo sets the application name and version.
|
||||
func SetInfo(name, version string) {
|
||||
appName = name
|
||||
appVersion = version
|
||||
}
|
||||
|
||||
// SetPidFile sets the default PID file path.
|
||||
func SetPidFile(path string) {
|
||||
pidPath = path
|
||||
}
|
||||
|
||||
// SetLogFile sets the default log file path.
|
||||
func SetLogFile(path string) {
|
||||
logPath = path
|
||||
}
|
||||
|
||||
// AddCmd adds a custom command.
|
||||
func AddCmd(name, desc string, fn func()) {
|
||||
commands[name] = &command{name: name, desc: desc, fn: fn}
|
||||
}
|
||||
|
||||
// OnStart sets the function to be called when the service starts.
|
||||
// The context will be canceled when a stop signal is received.
|
||||
func OnStart(fn func(ctx context.Context)) {
|
||||
onStart = fn
|
||||
}
|
||||
|
||||
// OnStop sets the function to be called when the service is stopping.
|
||||
func OnStop(fn func()) {
|
||||
onStop = fn
|
||||
}
|
||||
|
||||
// OnReload sets the function to be called when SIGHUP is received.
|
||||
func OnReload(fn func()) {
|
||||
onReload = fn
|
||||
}
|
||||
|
||||
// Run parses arguments and executes the service.
|
||||
func Run() {
|
||||
flagSet.Usage = showHelp
|
||||
if len(os.Args) > 1 {
|
||||
arg := os.Args[1]
|
||||
if cmd, ok := commands[arg]; ok {
|
||||
// Subcommand detected, parse flags after the command
|
||||
_ = flagSet.Parse(os.Args[2:])
|
||||
cmd.fn()
|
||||
return
|
||||
}
|
||||
|
||||
// Check for help/version
|
||||
switch arg {
|
||||
case "-h", "--help", "help":
|
||||
showHelp()
|
||||
return
|
||||
case "-v", "--version", "version":
|
||||
fmt.Printf("%s version %s\n", appName, appVersion)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// No starter command, treat all as app flags
|
||||
_ = flagSet.Parse(os.Args[1:])
|
||||
runForeground()
|
||||
}
|
||||
|
||||
func showHelp() {
|
||||
fmt.Printf("%s (%s)\n\nUsage:\n %s [command] [options]\n\nCommands:\n",
|
||||
appName, appVersion, filepath.Base(os.Args[0]))
|
||||
|
||||
var names []string
|
||||
for cmdName := range commands {
|
||||
names = append(names, cmdName)
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
for _, cmdName := range names {
|
||||
fmt.Printf(" %-10s %s\n", cmdName, commands[cmdName].desc)
|
||||
}
|
||||
|
||||
fmt.Println("\nOptions:")
|
||||
flagSet.PrintDefaults()
|
||||
fmt.Println("\nIf no command is provided, the service runs in the foreground.")
|
||||
}
|
||||
|
||||
func runForeground() {
|
||||
pid := os.Getpid()
|
||||
savePid(pid)
|
||||
defer removePid()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Setup signal handling
|
||||
sigChan := make(chan os.Signal, 2)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
|
||||
|
||||
if onStart != nil {
|
||||
go onStart(ctx)
|
||||
}
|
||||
|
||||
for sig := range sigChan {
|
||||
if sig == syscall.SIGHUP {
|
||||
if onReload != nil {
|
||||
log.DefaultLogger.Info("Received SIGHUP. Reloading...")
|
||||
onReload()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
log.DefaultLogger.Info(fmt.Sprintf("Received signal: %v. Shutting down...", sig))
|
||||
break
|
||||
}
|
||||
|
||||
cancel() // Trigger context cancellation for onStart
|
||||
|
||||
if onStop != nil {
|
||||
onStop()
|
||||
}
|
||||
log.DefaultLogger.Info("Shutdown complete.")
|
||||
}
|
||||
|
||||
func startCmd() {
|
||||
pid := loadPid()
|
||||
if pid > 0 && isProcessRunning(pid) {
|
||||
log.DefaultLogger.Info(fmt.Sprintf("%s is already running (PID %d)", appName, pid))
|
||||
return
|
||||
}
|
||||
|
||||
// Build arguments for background process
|
||||
args := []string{}
|
||||
for i := 1; i < len(os.Args); i++ {
|
||||
if os.Args[i] != "start" {
|
||||
args = append(args, os.Args[i])
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command(os.Args[0], args...)
|
||||
|
||||
if logPath != "" {
|
||||
file.EnsureParentDir(logPath)
|
||||
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err == nil {
|
||||
cmd.Stdout = f
|
||||
cmd.Stderr = f
|
||||
}
|
||||
}
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
log.DefaultLogger.Error(fmt.Sprintf("Failed to start %s: %v", appName, err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
log.DefaultLogger.Info(fmt.Sprintf("%s started (PID %d)", appName, cmd.Process.Pid))
|
||||
}
|
||||
|
||||
func stopCmd() {
|
||||
pid := loadPid()
|
||||
if pid <= 0 || !isProcessRunning(pid) {
|
||||
log.DefaultLogger.Info(fmt.Sprintf("%s is not running", appName))
|
||||
return
|
||||
}
|
||||
|
||||
process, _ := os.FindProcess(pid)
|
||||
log.DefaultLogger.Info(fmt.Sprintf("Stopping %s (PID %d)...", appName, pid))
|
||||
_ = process.Signal(syscall.SIGTERM)
|
||||
|
||||
deadline := time.Now().Add(stopTimeout)
|
||||
for time.Now().Before(deadline) {
|
||||
if !isProcessRunning(pid) {
|
||||
log.DefaultLogger.Info("Stopped OK")
|
||||
removePid()
|
||||
return
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
|
||||
log.DefaultLogger.Info("Stop timeout, killing...")
|
||||
_ = process.Kill()
|
||||
removePid()
|
||||
}
|
||||
|
||||
func restartCmd() {
|
||||
stopCmd()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
startCmd()
|
||||
}
|
||||
|
||||
func statusCmd() {
|
||||
pid := loadPid()
|
||||
if pid > 0 && isProcessRunning(pid) {
|
||||
log.DefaultLogger.Info(fmt.Sprintf("%s is running (PID %d)", appName, pid))
|
||||
} else {
|
||||
log.DefaultLogger.Info(fmt.Sprintf("%s is not running", appName))
|
||||
}
|
||||
}
|
||||
|
||||
func savePid(p int) {
|
||||
file.EnsureParentDir(pidPath)
|
||||
_ = os.WriteFile(pidPath, []byte(strconv.Itoa(p)), 0644)
|
||||
}
|
||||
|
||||
func loadPid() int {
|
||||
data, err := os.ReadFile(pidPath)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
p, _ := strconv.Atoi(strings.TrimSpace(string(data)))
|
||||
return p
|
||||
}
|
||||
|
||||
func removePid() {
|
||||
_ = os.Remove(pidPath)
|
||||
}
|
||||
|
||||
func isProcessRunning(p int) bool {
|
||||
process, err := os.FindProcess(p)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
err = process.Signal(syscall.Signal(0))
|
||||
return err == nil
|
||||
}
|
||||
27
starter_test.go
Normal file
27
starter_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
package starter_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"apigo.cc/go/starter"
|
||||
)
|
||||
|
||||
func TestStarterBasics(t *testing.T) {
|
||||
starter.SetInfo("TestApp", "1.0.0")
|
||||
|
||||
// Since we are using a singleton, we just check if the methods can be called
|
||||
starter.OnStart(func(ctx context.Context) {
|
||||
// Mock start
|
||||
})
|
||||
|
||||
starter.OnStop(func() {
|
||||
// Mock stop
|
||||
})
|
||||
|
||||
starter.OnReload(func() {
|
||||
// Mock reload
|
||||
})
|
||||
|
||||
// Run() cannot be easily tested here as it calls os.Exit and parses os.Args
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user