diff --git a/daemon.go b/daemon.go index ffa71bf..f0565ac 100644 --- a/daemon.go +++ b/daemon.go @@ -3,10 +3,11 @@ package process import ( "context" "errors" - "fmt" "os" "sync" "time" + + coreerr "forge.lthn.ai/core/go-log" ) // DaemonOptions configures daemon mode execution. @@ -72,7 +73,7 @@ func (d *Daemon) Start() error { defer d.mu.Unlock() if d.running { - return errors.New("daemon already running") + return coreerr.E("Daemon.Start", "daemon already running", nil) } if d.pid != nil { @@ -100,7 +101,7 @@ func (d *Daemon) Start() error { entry.Health = d.health.Addr() } if err := d.opts.Registry.Register(entry); err != nil { - return fmt.Errorf("registry: %w", err) + return coreerr.E("Daemon.Start", "registry", err) } } @@ -112,7 +113,7 @@ func (d *Daemon) Run(ctx context.Context) error { d.mu.Lock() if !d.running { d.mu.Unlock() - return errors.New("daemon not started - call Start() first") + return coreerr.E("Daemon.Run", "daemon not started - call Start() first", nil) } d.mu.Unlock() @@ -138,13 +139,13 @@ func (d *Daemon) Stop() error { if d.health != nil { d.health.SetReady(false) if err := d.health.Stop(shutdownCtx); err != nil { - errs = append(errs, fmt.Errorf("health server: %w", err)) + errs = append(errs, coreerr.E("Daemon.Stop", "health server", err)) } } if d.pid != nil { if err := d.pid.Release(); err != nil && !os.IsNotExist(err) { - errs = append(errs, fmt.Errorf("pid file: %w", err)) + errs = append(errs, coreerr.E("Daemon.Stop", "pid file", err)) } } diff --git a/exec/exec.go b/exec/exec.go index 21978a9..ae875a0 100644 --- a/exec/exec.go +++ b/exec/exec.go @@ -8,6 +8,8 @@ import ( "os" "os/exec" "strings" + + coreerr "forge.lthn.ai/core/go-log" ) // Options configuration for command execution @@ -147,7 +149,7 @@ func RunQuiet(ctx context.Context, name string, args ...string) error { cmd := Command(ctx, name, args...).WithStderr(&stderr) if err := cmd.Run(); err != nil { // Include stderr in error message - return fmt.Errorf("%w: %s", err, strings.TrimSpace(stderr.String())) + return coreerr.E("RunQuiet", strings.TrimSpace(stderr.String()), err) } return nil } @@ -155,9 +157,9 @@ func RunQuiet(ctx context.Context, name string, args ...string) error { func wrapError(err error, name string, args []string) error { cmdStr := name + " " + strings.Join(args, " ") if exitErr, ok := err.(*exec.ExitError); ok { - return fmt.Errorf("command %q failed with exit code %d: %w", cmdStr, exitErr.ExitCode(), err) + return coreerr.E("wrapError", fmt.Sprintf("command %q failed with exit code %d", cmdStr, exitErr.ExitCode()), err) } - return fmt.Errorf("failed to execute %q: %w", cmdStr, err) + return coreerr.E("wrapError", fmt.Sprintf("failed to execute %q", cmdStr), err) } func (c *Cmd) getLogger() Logger { diff --git a/go.mod b/go.mod index 3a0b343..4e5072c 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,13 @@ require ( forge.lthn.ai/core/api v0.1.2 forge.lthn.ai/core/go v0.3.1 forge.lthn.ai/core/go-io v0.1.2 + forge.lthn.ai/core/go-log v0.0.2 forge.lthn.ai/core/go-ws v0.2.0 github.com/gin-gonic/gin v1.12.0 github.com/stretchr/testify v1.11.1 ) require ( - forge.lthn.ai/core/go-log v0.0.2 // indirect github.com/99designs/gqlgen v0.17.88 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/agnivade/levenshtein v1.2.1 // indirect diff --git a/health.go b/health.go index 0f8563a..98ecdd3 100644 --- a/health.go +++ b/health.go @@ -7,6 +7,8 @@ import ( "net/http" "sync" "time" + + coreerr "forge.lthn.ai/core/go-log" ) // HealthCheck is a function that returns nil if healthy. @@ -82,7 +84,7 @@ func (h *HealthServer) Start() error { listener, err := net.Listen("tcp", h.addr) if err != nil { - return fmt.Errorf("failed to listen on %s: %w", h.addr, err) + return coreerr.E("HealthServer.Start", fmt.Sprintf("failed to listen on %s", h.addr), err) } h.listener = listener diff --git a/pidfile.go b/pidfile.go index 96e7d5b..240c44c 100644 --- a/pidfile.go +++ b/pidfile.go @@ -10,6 +10,7 @@ import ( "syscall" coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // PIDFile manages a process ID file for single-instance enforcement. @@ -34,7 +35,7 @@ func (p *PIDFile) Acquire() error { if err == nil && pid > 0 { if proc, err := os.FindProcess(pid); err == nil { if err := proc.Signal(syscall.Signal(0)); err == nil { - return fmt.Errorf("another instance is running (PID %d)", pid) + return coreerr.E("PIDFile.Acquire", fmt.Sprintf("another instance is running (PID %d)", pid), nil) } } } @@ -43,13 +44,13 @@ func (p *PIDFile) Acquire() error { if dir := filepath.Dir(p.path); dir != "." { if err := coreio.Local.EnsureDir(dir); err != nil { - return fmt.Errorf("failed to create PID directory: %w", err) + return coreerr.E("PIDFile.Acquire", "failed to create PID directory", err) } } pid := os.Getpid() if err := coreio.Local.Write(p.path, strconv.Itoa(pid)); err != nil { - return fmt.Errorf("failed to write PID file: %w", err) + return coreerr.E("PIDFile.Acquire", "failed to write PID file", err) } return nil diff --git a/process.go b/process.go index e69c5ee..f9cc222 100644 --- a/process.go +++ b/process.go @@ -8,6 +8,8 @@ import ( "os/exec" "sync" "time" + + coreerr "forge.lthn.ai/core/go-log" ) // Process represents a managed external process. @@ -87,13 +89,13 @@ func (p *Process) Wait() error { p.mu.RLock() defer p.mu.RUnlock() if p.Status == StatusFailed { - return fmt.Errorf("process failed to start: %s", p.ID) + return coreerr.E("Process.Wait", fmt.Sprintf("process failed to start: %s", p.ID), nil) } if p.Status == StatusKilled { - return fmt.Errorf("process was killed: %s", p.ID) + return coreerr.E("Process.Wait", fmt.Sprintf("process was killed: %s", p.ID), nil) } if p.ExitCode != 0 { - return fmt.Errorf("process exited with code %d", p.ExitCode) + return coreerr.E("Process.Wait", fmt.Sprintf("process exited with code %d", p.ExitCode), nil) } return nil } diff --git a/runner.go b/runner.go index 8eb1fd5..61eed29 100644 --- a/runner.go +++ b/runner.go @@ -2,9 +2,10 @@ package process import ( "context" - "errors" "sync" "time" + + coreerr "forge.lthn.ai/core/go-log" ) // Runner orchestrates multiple processes with dependencies. @@ -104,7 +105,7 @@ func (r *Runner) RunAll(ctx context.Context, specs []RunSpec) (*RunAllResult, er Name: name, Spec: remaining[name], Skipped: true, - Error: errors.New("circular dependency or missing dependency"), + Error: coreerr.E("Runner.RunAll", "circular dependency or missing dependency", nil), }) } break @@ -136,7 +137,7 @@ func (r *Runner) RunAll(ctx context.Context, specs []RunSpec) (*RunAllResult, er Name: spec.Name, Spec: spec, Skipped: true, - Error: errors.New("skipped due to dependency failure"), + Error: coreerr.E("Runner.RunAll", "skipped due to dependency failure", nil), } } else { result = r.runSpec(ctx, spec) diff --git a/service.go b/service.go index 661dac9..461d0e1 100644 --- a/service.go +++ b/service.go @@ -13,6 +13,7 @@ import ( "time" "forge.lthn.ai/core/go/pkg/core" + coreerr "forge.lthn.ai/core/go-log" ) // Default buffer size for process output (1MB). @@ -20,9 +21,9 @@ const DefaultBufferSize = 1024 * 1024 // Errors var ( - ErrProcessNotFound = errors.New("process not found") - ErrProcessNotRunning = errors.New("process is not running") - ErrStdinNotAvailable = errors.New("stdin not available") + ErrProcessNotFound = coreerr.E("", "process not found", nil) + ErrProcessNotRunning = coreerr.E("", "process is not running", nil) + ErrStdinNotAvailable = coreerr.E("", "stdin not available", nil) ) // Service manages process execution with Core IPC integration. @@ -121,19 +122,19 @@ func (s *Service) StartWithOptions(ctx context.Context, opts RunOptions) (*Proce stdout, err := cmd.StdoutPipe() if err != nil { cancel() - return nil, fmt.Errorf("failed to create stdout pipe: %w", err) + return nil, coreerr.E("Service.StartWithOptions", "failed to create stdout pipe", err) } stderr, err := cmd.StderrPipe() if err != nil { cancel() - return nil, fmt.Errorf("failed to create stderr pipe: %w", err) + return nil, coreerr.E("Service.StartWithOptions", "failed to create stderr pipe", err) } stdin, err := cmd.StdinPipe() if err != nil { cancel() - return nil, fmt.Errorf("failed to create stdin pipe: %w", err) + return nil, coreerr.E("Service.StartWithOptions", "failed to create stdin pipe", err) } // Create output buffer (enabled by default) @@ -161,7 +162,7 @@ func (s *Service) StartWithOptions(ctx context.Context, opts RunOptions) (*Proce // Start the process if err := cmd.Start(); err != nil { cancel() - return nil, fmt.Errorf("failed to start process: %w", err) + return nil, coreerr.E("Service.StartWithOptions", "failed to start process", err) } // Store process @@ -327,7 +328,7 @@ func (s *Service) Remove(id string) error { } if proc.IsRunning() { - return errors.New("cannot remove running process") + return coreerr.E("Service.Remove", "cannot remove running process", nil) } delete(s.processes, id) @@ -367,7 +368,7 @@ func (s *Service) Run(ctx context.Context, command string, args ...string) (stri output := proc.Output() if proc.ExitCode != 0 { - return output, fmt.Errorf("process exited with code %d", proc.ExitCode) + return output, coreerr.E("Service.Run", fmt.Sprintf("process exited with code %d", proc.ExitCode), nil) } return output, nil } @@ -383,7 +384,7 @@ func (s *Service) RunWithOptions(ctx context.Context, opts RunOptions) (string, output := proc.Output() if proc.ExitCode != 0 { - return output, fmt.Errorf("process exited with code %d", proc.ExitCode) + return output, coreerr.E("Service.RunWithOptions", fmt.Sprintf("process exited with code %d", proc.ExitCode), nil) } return output, nil }