fix(ax): align action handlers and exec errors
This commit is contained in:
parent
8a85c3cd86
commit
8a6c253ea2
5 changed files with 134 additions and 128 deletions
124
actions.go
124
actions.go
|
|
@ -1,6 +1,12 @@
|
|||
package process
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/core"
|
||||
)
|
||||
|
||||
// --- ACTION messages (broadcast via Core.ACTION) ---
|
||||
|
||||
|
|
@ -35,3 +41,119 @@ type ActionProcessKilled struct {
|
|||
ID string
|
||||
Signal string
|
||||
}
|
||||
|
||||
// --- Core Action Handlers ---------------------------------------------------
|
||||
|
||||
func (s *Service) handleRun(ctx context.Context, opts core.Options) core.Result {
|
||||
command := opts.String("command")
|
||||
if command == "" {
|
||||
return core.Result{Value: core.E("process.run", "command is required", nil), OK: false}
|
||||
}
|
||||
|
||||
runOpts := RunOptions{
|
||||
Command: command,
|
||||
Dir: opts.String("dir"),
|
||||
}
|
||||
if r := opts.Get("args"); r.OK {
|
||||
runOpts.Args = optionStrings(r.Value)
|
||||
}
|
||||
if r := opts.Get("env"); r.OK {
|
||||
runOpts.Env = optionStrings(r.Value)
|
||||
}
|
||||
|
||||
return s.runCommand(ctx, runOpts)
|
||||
}
|
||||
|
||||
func (s *Service) handleStart(ctx context.Context, opts core.Options) core.Result {
|
||||
command := opts.String("command")
|
||||
if command == "" {
|
||||
return core.Result{Value: core.E("process.start", "command is required", nil), OK: false}
|
||||
}
|
||||
|
||||
detach := true
|
||||
if opts.Has("detach") {
|
||||
detach = opts.Bool("detach")
|
||||
}
|
||||
|
||||
runOpts := RunOptions{
|
||||
Command: command,
|
||||
Dir: opts.String("dir"),
|
||||
Detach: detach,
|
||||
}
|
||||
if r := opts.Get("args"); r.OK {
|
||||
runOpts.Args = optionStrings(r.Value)
|
||||
}
|
||||
if r := opts.Get("env"); r.OK {
|
||||
runOpts.Env = optionStrings(r.Value)
|
||||
}
|
||||
|
||||
r := s.StartWithOptions(ctx, runOpts)
|
||||
if !r.OK {
|
||||
return r
|
||||
}
|
||||
return core.Result{Value: r.Value.(*ManagedProcess).ID, OK: true}
|
||||
}
|
||||
|
||||
func (s *Service) handleKill(_ context.Context, opts core.Options) core.Result {
|
||||
id := opts.String("id")
|
||||
if id != "" {
|
||||
if err := s.Kill(id); err != nil {
|
||||
if core.Is(err, ErrProcessNotFound) {
|
||||
return core.Result{Value: core.E("process.kill", core.Concat("not found: ", id), nil), OK: false}
|
||||
}
|
||||
return core.Result{Value: core.E("process.kill", core.Concat("kill failed: ", id), err), OK: false}
|
||||
}
|
||||
return core.Result{OK: true}
|
||||
}
|
||||
|
||||
pid := opts.Int("pid")
|
||||
if pid > 0 {
|
||||
proc, err := processHandle(pid)
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("process.kill", core.Concat("find pid failed: ", core.Sprintf("%d", pid)), err), OK: false}
|
||||
}
|
||||
if err := proc.Signal(syscall.SIGTERM); err != nil {
|
||||
return core.Result{Value: core.E("process.kill", core.Concat("signal failed: ", core.Sprintf("%d", pid)), err), OK: false}
|
||||
}
|
||||
return core.Result{OK: true}
|
||||
}
|
||||
|
||||
return core.Result{Value: core.E("process.kill", "need id or pid", nil), OK: false}
|
||||
}
|
||||
|
||||
func (s *Service) handleList(_ context.Context, _ core.Options) core.Result {
|
||||
return core.Result{Value: s.managed.Names(), OK: true}
|
||||
}
|
||||
|
||||
func (s *Service) handleGet(_ context.Context, opts core.Options) core.Result {
|
||||
id := opts.String("id")
|
||||
if id == "" {
|
||||
return core.Result{Value: core.E("process.get", "id is required", nil), OK: false}
|
||||
}
|
||||
proc, err := s.Get(id)
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("process.get", core.Concat("not found: ", id), err), OK: false}
|
||||
}
|
||||
return core.Result{Value: proc.Info(), OK: true}
|
||||
}
|
||||
|
||||
func optionStrings(value any) []string {
|
||||
switch typed := value.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case []string:
|
||||
return append([]string(nil), typed...)
|
||||
case []any:
|
||||
result := make([]string, 0, len(typed))
|
||||
for _, item := range typed {
|
||||
text, ok := item.(string)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
result = append(result, text)
|
||||
}
|
||||
return result
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ go-process provides: c.Action("process.run", s.handleRun)
|
|||
|
||||
Without go-process registered, `c.Process().Run()` returns `Result{OK: false}`. Permission-by-registration.
|
||||
|
||||
### Current State (2026-03-25)
|
||||
### Current State (2026-03-30)
|
||||
|
||||
The codebase is PRE-migration. The RFC describes the v0.8.0 target. What exists today:
|
||||
The codebase now matches the v0.8.0 target. The bullets below are the historical migration delta that was closed out:
|
||||
|
||||
- `service.go` — `NewService(opts) func(*Core) (any, error)` — **old factory signature**. Change to `Register(c *Core) core.Result`
|
||||
- `OnStartup() error` / `OnShutdown() error` — **Change** to return `core.Result`
|
||||
|
|
@ -44,7 +44,7 @@ daemon.go — DaemonEntry, managed daemon lifecycle
|
|||
health.go — health check endpoints
|
||||
pidfile.go — PID file management
|
||||
buffer.go — output buffering
|
||||
actions.go — WILL CONTAIN Action handlers after migration
|
||||
actions.go — Action payloads and Core action handlers
|
||||
global.go — global Default() singleton — DELETE after migration
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func (c *Cmd) Run() error {
|
|||
c.logDebug("executing command")
|
||||
|
||||
if err := c.cmd.Run(); err != nil {
|
||||
wrapped := wrapError("Cmd.Run", err, c.name, c.args)
|
||||
wrapped := wrapError("exec.cmd.run", err, c.name, c.args)
|
||||
c.logError("command failed", wrapped)
|
||||
return wrapped
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ func (c *Cmd) Output() ([]byte, error) {
|
|||
|
||||
out, err := c.cmd.Output()
|
||||
if err != nil {
|
||||
wrapped := wrapError("Cmd.Output", err, c.name, c.args)
|
||||
wrapped := wrapError("exec.cmd.output", err, c.name, c.args)
|
||||
c.logError("command failed", wrapped)
|
||||
return nil, wrapped
|
||||
}
|
||||
|
|
@ -114,7 +114,7 @@ func (c *Cmd) CombinedOutput() ([]byte, error) {
|
|||
|
||||
out, err := c.cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
wrapped := wrapError("Cmd.CombinedOutput", err, c.name, c.args)
|
||||
wrapped := wrapError("exec.cmd.combined_output", err, c.name, c.args)
|
||||
c.logError("command failed", wrapped)
|
||||
return out, wrapped
|
||||
}
|
||||
|
|
@ -147,7 +147,7 @@ func RunQuiet(ctx context.Context, name string, args ...string) error {
|
|||
var stderr bytes.Buffer
|
||||
cmd := Command(ctx, name, args...).WithStderr(&stderr)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return core.E("RunQuiet", core.Trim(stderr.String()), err)
|
||||
return core.E("exec.run_quiet", core.Trim(stderr.String()), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
116
service.go
116
service.go
|
|
@ -370,89 +370,6 @@ func (s *Service) RunWithOptions(ctx context.Context, opts RunOptions) core.Resu
|
|||
return s.runCommand(ctx, opts)
|
||||
}
|
||||
|
||||
// --- Internal Request Helpers ---
|
||||
|
||||
func (s *Service) handleRun(ctx context.Context, opts core.Options) core.Result {
|
||||
command := opts.String("command")
|
||||
if command == "" {
|
||||
return core.Result{Value: core.E("process.run", "command is required", nil), OK: false}
|
||||
}
|
||||
|
||||
runOpts := RunOptions{
|
||||
Command: command,
|
||||
Dir: opts.String("dir"),
|
||||
}
|
||||
if r := opts.Get("args"); r.OK {
|
||||
runOpts.Args = optionStrings(r.Value)
|
||||
}
|
||||
if r := opts.Get("env"); r.OK {
|
||||
runOpts.Env = optionStrings(r.Value)
|
||||
}
|
||||
|
||||
return s.runCommand(ctx, runOpts)
|
||||
}
|
||||
|
||||
func (s *Service) handleStart(ctx context.Context, opts core.Options) core.Result {
|
||||
command := opts.String("command")
|
||||
if command == "" {
|
||||
return core.Result{Value: core.E("process.start", "command is required", nil), OK: false}
|
||||
}
|
||||
|
||||
detach := true
|
||||
if opts.Has("detach") {
|
||||
detach = opts.Bool("detach")
|
||||
}
|
||||
|
||||
runOpts := RunOptions{
|
||||
Command: command,
|
||||
Dir: opts.String("dir"),
|
||||
Detach: detach,
|
||||
}
|
||||
if r := opts.Get("args"); r.OK {
|
||||
runOpts.Args = optionStrings(r.Value)
|
||||
}
|
||||
if r := opts.Get("env"); r.OK {
|
||||
runOpts.Env = optionStrings(r.Value)
|
||||
}
|
||||
|
||||
r := s.StartWithOptions(ctx, runOpts)
|
||||
if !r.OK {
|
||||
return r
|
||||
}
|
||||
return core.Result{Value: r.Value.(*ManagedProcess).ID, OK: true}
|
||||
}
|
||||
|
||||
func (s *Service) handleKill(ctx context.Context, opts core.Options) core.Result {
|
||||
id := opts.String("id")
|
||||
if id != "" {
|
||||
if err := s.Kill(id); err != nil {
|
||||
if core.Is(err, ErrProcessNotFound) {
|
||||
return core.Result{Value: core.E("process.kill", core.Concat("not found: ", id), nil), OK: false}
|
||||
}
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
return core.Result{OK: true}
|
||||
}
|
||||
|
||||
pid := opts.Int("pid")
|
||||
if pid > 0 {
|
||||
proc, err := processHandle(pid)
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("process.kill", core.Concat("find pid failed: ", core.Sprintf("%d", pid)), err), OK: false}
|
||||
}
|
||||
if err := proc.Signal(syscall.SIGTERM); err != nil {
|
||||
return core.Result{Value: core.E("process.kill", core.Concat("signal failed: ", core.Sprintf("%d", pid)), err), OK: false}
|
||||
}
|
||||
return core.Result{OK: true}
|
||||
}
|
||||
|
||||
return core.Result{Value: core.E("process.kill", "need id or pid", nil), OK: false}
|
||||
}
|
||||
|
||||
func (s *Service) handleList(ctx context.Context, opts core.Options) core.Result {
|
||||
return core.Result{Value: s.managed.Names(), OK: true}
|
||||
}
|
||||
|
||||
func (s *Service) runCommand(ctx context.Context, opts RunOptions) core.Result {
|
||||
if opts.Command == "" {
|
||||
return core.Result{Value: core.E("process.run", "command is required", nil), OK: false}
|
||||
|
|
@ -523,39 +440,6 @@ func isNotExist(err error) bool {
|
|||
return os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func (s *Service) handleGet(ctx context.Context, opts core.Options) core.Result {
|
||||
id := opts.String("id")
|
||||
if id == "" {
|
||||
return core.Result{Value: core.E("process.get", "id is required", nil), OK: false}
|
||||
}
|
||||
proc, err := s.Get(id)
|
||||
if err != nil {
|
||||
return core.Result{Value: core.E("process.get", core.Concat("not found: ", id), err), OK: false}
|
||||
}
|
||||
return core.Result{Value: proc.Info(), OK: true}
|
||||
}
|
||||
|
||||
func optionStrings(value any) []string {
|
||||
switch typed := value.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case []string:
|
||||
return append([]string(nil), typed...)
|
||||
case []any:
|
||||
result := make([]string, 0, len(typed))
|
||||
for _, item := range typed {
|
||||
text, ok := item.(string)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
result = append(result, text)
|
||||
}
|
||||
return result
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func classifyProcessExit(proc *ManagedProcess, err error) (Status, int, error, string) {
|
||||
if err == nil {
|
||||
return StatusExited, 0, nil, ""
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ Exported fields:
|
|||
### Package Functions
|
||||
|
||||
- `func Command(ctx context.Context, name string, args ...string) *Cmd`: Returns a `Cmd` for the supplied context, executable name, and arguments.
|
||||
- `func RunQuiet(ctx context.Context, name string, args ...string) error`: Runs a command with stderr captured into a buffer and returns `core.E("RunQuiet", core.Trim(stderr.String()), err)` on failure.
|
||||
- `func RunQuiet(ctx context.Context, name string, args ...string) error`: Runs a command with stderr captured into a buffer and returns `core.E("exec.run_quiet", core.Trim(stderr.String()), err)` on failure.
|
||||
- `func SetDefaultLogger(l Logger)`: Sets the package-level default logger. Passing `nil` replaces it with `NopLogger`.
|
||||
- `func DefaultLogger() Logger`: Returns the package-level default logger.
|
||||
|
||||
|
|
@ -58,9 +58,9 @@ Exported fields:
|
|||
- `func (c *Cmd) WithStdout(w io.Writer) *Cmd`: Sets `Options.Stdout` and returns the same command.
|
||||
- `func (c *Cmd) WithStderr(w io.Writer) *Cmd`: Sets `Options.Stderr` and returns the same command.
|
||||
- `func (c *Cmd) WithLogger(l Logger) *Cmd`: Sets a command-specific logger and returns the same command.
|
||||
- `func (c *Cmd) Run() error`: Prepares the underlying `exec.Cmd`, logs `"executing command"`, runs it, and wraps failures with `wrapError("Cmd.Run", ...)`.
|
||||
- `func (c *Cmd) Output() ([]byte, error)`: Prepares the underlying `exec.Cmd`, logs `"executing command"`, returns stdout bytes, and wraps failures with `wrapError("Cmd.Output", ...)`.
|
||||
- `func (c *Cmd) CombinedOutput() ([]byte, error)`: Prepares the underlying `exec.Cmd`, logs `"executing command"`, returns combined stdout and stderr, and wraps failures with `wrapError("Cmd.CombinedOutput", ...)`.
|
||||
- `func (c *Cmd) Run() error`: Prepares the underlying `exec.Cmd`, logs `"executing command"`, runs it, and wraps failures with `wrapError("exec.cmd.run", ...)`.
|
||||
- `func (c *Cmd) Output() ([]byte, error)`: Prepares the underlying `exec.Cmd`, logs `"executing command"`, returns stdout bytes, and wraps failures with `wrapError("exec.cmd.output", ...)`.
|
||||
- `func (c *Cmd) CombinedOutput() ([]byte, error)`: Prepares the underlying `exec.Cmd`, logs `"executing command"`, returns combined stdout and stderr, and wraps failures with `wrapError("exec.cmd.combined_output", ...)`.
|
||||
|
||||
### `NopLogger` Methods
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue