diff --git a/actions.go b/actions.go index 8723afc..ae238e7 100644 --- a/actions.go +++ b/actions.go @@ -91,6 +91,8 @@ type TaskProcessGet struct { } // TaskProcessWait waits for a managed process to finish through Core.PERFORM. +// Successful exits return an Info snapshot. Unsuccessful exits return a +// TaskProcessWaitError value that preserves the final snapshot. // // Example: // @@ -100,6 +102,30 @@ type TaskProcessWait struct { ID string } +// TaskProcessWaitError is returned as the task value when TaskProcessWait +// completes with a non-successful process outcome. It preserves the final +// process snapshot while still behaving like the underlying wait error. +type TaskProcessWaitError struct { + Info Info + Err error +} + +// Error implements error. +func (e *TaskProcessWaitError) Error() string { + if e == nil || e.Err == nil { + return "" + } + return e.Err.Error() +} + +// Unwrap returns the underlying wait error. +func (e *TaskProcessWaitError) Unwrap() error { + if e == nil { + return nil + } + return e.Err +} + // TaskProcessOutput requests the captured output of a managed process through Core.PERFORM. // // Example: diff --git a/service.go b/service.go index b322200..edb30c5 100644 --- a/service.go +++ b/service.go @@ -727,7 +727,13 @@ func (s *Service) handleTask(c *core.Core, task core.Task) core.Result { info, err := s.Wait(m.ID) if err != nil { - return core.Result{Value: err, OK: false} + return core.Result{ + Value: &TaskProcessWaitError{ + Info: info, + Err: err, + }, + OK: true, + } } return core.Result{Value: info, OK: true} diff --git a/service_test.go b/service_test.go index 52fff4f..9663435 100644 --- a/service_test.go +++ b/service_test.go @@ -946,6 +946,28 @@ func TestService_OnStartup(t *testing.T) { assert.Equal(t, 0, info.ExitCode) }) + t.Run("preserves final snapshot when process.wait task fails", func(t *testing.T) { + svc, c := newTestService(t) + + err := svc.OnStartup(context.Background()) + require.NoError(t, err) + + proc, err := svc.Start(context.Background(), "sh", "-c", "exit 7") + require.NoError(t, err) + + result := c.PERFORM(TaskProcessWait{ID: proc.ID}) + require.True(t, result.OK) + + errValue, ok := result.Value.(error) + require.True(t, ok) + var waitErr *TaskProcessWaitError + require.ErrorAs(t, errValue, &waitErr) + assert.Contains(t, waitErr.Error(), "process exited with code 7") + assert.Equal(t, proc.ID, waitErr.Info.ID) + assert.Equal(t, StatusExited, waitErr.Info.Status) + assert.Equal(t, 7, waitErr.Info.ExitCode) + }) + t.Run("registers process.list task", func(t *testing.T) { svc, c := newTestService(t)