diff --git a/bootstrap/error.go b/bootstrap/error.go index aaf7b803..056fa6fa 100644 --- a/bootstrap/error.go +++ b/bootstrap/error.go @@ -10,9 +10,16 @@ import ( "go.uber.org/multierr" "io" "os" + "strconv" "sync" ) +var skipPathLogFile bool + +func init() { + skipPathLogFile, _ = strconv.ParseBool(os.Getenv("HEPH_SKIP_PATH_LOG")) +} + func PrintHumanError(err error) { errs := worker2.CollectRootErrors(err) skippedCount := 0 @@ -55,7 +62,9 @@ func PrintHumanError(err error) { f.Close() fmt.Fprintln(log.Writer()) } - log.Errorf("The log file can be found at %v", logFile) + if !skipPathLogFile { + log.Errorf("The log file can be found at %v", logFile) + } } } diff --git a/test/BUILD b/test/BUILD index 259d3722..af013567 100644 --- a/test/BUILD +++ b/test/BUILD @@ -12,7 +12,7 @@ def e2e_test( cmd = "(" + cmd + ")" if expected_failure: - test_cmd = '%s | tee output && RC=$? || RC=$?; if [ $RC -eq 0 ]; then exit 1; fi; echo "Command exited with code $RC, as expected"; ' % cmd + test_cmd = '%s 2>&1 | tee output && RC=$? || RC=$?; if [ $RC -eq 0 ]; then exit 1; fi; echo "Command exited with code $RC, as expected"; ' % cmd else: test_cmd = "%s | tee output && RC=$? " % cmd diff --git a/test/features/errors.BUILD b/test/features/errors.BUILD new file mode 100644 index 00000000..54b16384 --- /dev/null +++ b/test/features/errors.BUILD @@ -0,0 +1,34 @@ +load("//test", "e2e_test") + +will_err = target( + name = "_will_err", + run = "echo failed; exit 1", + cache = False, +) + +target( + name = "err1", + deps = will_err, + cache = False, + labels = 'will-err', +) + +target( + name = "err2", + deps = will_err, + cache = False, + labels = 'will-err', +) + +e2e_test( + name = "e2e_errors", + cmd = "HEPH_SKIP_PATH_LOG=1 heph run '//test/features && will-err'", + expected_failure = True, + expected_output = """ +ERR| //test/features:_will_err failed: exec: exit status 1 + +failed + +ERR| exec: exit status 1 +""".strip(), +) diff --git a/worker2/engine.go b/worker2/engine.go index 34b1553f..a7dedbcf 100644 --- a/worker2/engine.go +++ b/worker2/engine.go @@ -157,23 +157,21 @@ func (e *Engine) tryFreeze(depObj *Node[Dep]) (bool, error) { } switch state { - case ExecStateSkipped: - errs.Add(depExec.Err) - case ExecStateFailed: + case ExecStateSkipped, ExecStateFailed: for _, err := range multierr.Errors(depExec.Err) { if serr, ok := xerrors.As[xcontext.SignalCause](err); ok { errs.Add(serr) } else { if jerr, ok := xerrors.As[Error](err); ok { - err = jerr.Root() + errs.Add(jerr.Root()) + } else { + errs.Add(Error{ + ID: depExec.ID, + State: depExec.State, + Name: depExec.Dep.GetName(), + Err: err, + }) } - - errs.Add(Error{ - ID: depExec.ID, - State: depExec.State, - Name: depExec.Dep.GetName(), - Err: err, - }) } } } diff --git a/worker2/engine_test.go b/worker2/engine_test.go index 10f7411e..9abff83d 100644 --- a/worker2/engine_test.go +++ b/worker2/engine_test.go @@ -248,24 +248,19 @@ func TestExecErrorSkip(t *testing.T) { err2 := <-err2Ch err3 := <-err3Ch - assert.Equal(t, err1, errors.New("beep bop")) - assert.Equal(t, err2, Error{ + assert.Equal(t, errors.New("beep bop"), err1) + assert.Equal(t, Error{ ID: 1, Name: "a1", State: ExecStateFailed, Err: errors.New("beep bop"), - }) - assert.Equal(t, err3, Error{ - ID: 2, - Name: "a2", - State: ExecStateSkipped, - Err: Error{ - ID: 1, - Name: "a1", - State: ExecStateFailed, - Err: errors.New("beep bop"), - }, - }) + }, err2) + assert.Equal(t, Error{ + ID: 1, + Name: "a1", + State: ExecStateFailed, + Err: errors.New("beep bop"), + }, err3) } func TestExecErrorSkipStress(t *testing.T) { @@ -331,28 +326,23 @@ func TestExecErrorSkipStress(t *testing.T) { for _, errCh := range errChs2 { err := <-errCh - assert.Equal(t, err, Error{ + assert.Equal(t, Error{ ID: 1, Name: "a1", State: ExecStateFailed, Err: errors.New("beep bop"), - }) + }, err) } for _, c := range errChs3 { err := <-c.ch - assert.Equal(t, err, Error{ - ID: c.d.getExecution().ID, - Name: c.d.GetName(), - State: ExecStateSkipped, - Err: Error{ - ID: 1, - Name: "a1", - State: ExecStateFailed, - Err: errors.New("beep bop"), - }, - }) + require.Equal(t, Error{ + ID: 1, + Name: "a1", + State: ExecStateFailed, + Err: errors.New("beep bop"), + }, err) } } @@ -385,7 +375,7 @@ func TestExecCancel(t *testing.T) { err := <-errCh - assert.ErrorIs(t, err, context.Canceled) + assert.ErrorIs(t, context.Canceled, err) } func TestExecDeps(t *testing.T) { diff --git a/worker2/error.go b/worker2/error.go index 1ee872ef..a965a153 100644 --- a/worker2/error.go +++ b/worker2/error.go @@ -1,7 +1,6 @@ package worker2 import ( - "context" "errors" "fmt" "go.uber.org/multierr" @@ -27,11 +26,7 @@ func CollectUniqueErrors(inErrs []error) []error { for _, err := range inErrs { var jerr Error if errors.As(err, &jerr) { - if errors.Is(err, context.Canceled) { - merrs[err] = struct{}{} - } else { - jerrs[jerr.ID] = jerr - } + jerrs[jerr.ID] = jerr } else { merrs[err] = struct{}{} } @@ -54,7 +49,7 @@ func CollectRootErrors(err error) []error { for _, err := range multierr.Errors(err) { var jerr Error if errors.As(err, &jerr) { - errs = append(errs, jerr.Err) + errs = append(errs, jerr.Root()) } else { errs = append(errs, err) } @@ -69,7 +64,7 @@ func (e *Error) Root() error { } if !e.Skipped() { - return e + return *e } var roots []error diff --git a/worker2/poolwait/wait.go b/worker2/poolwait/wait.go index 81f9c67c..57310296 100644 --- a/worker2/poolwait/wait.go +++ b/worker2/poolwait/wait.go @@ -6,6 +6,7 @@ import ( "github.com/hephbuild/heph/log/log" "github.com/hephbuild/heph/utils/xtea" "github.com/hephbuild/heph/worker2" + "go.uber.org/multierr" "os" "strconv" "time" @@ -47,5 +48,7 @@ func Wait(ctx context.Context, name string, pool *worker2.Engine, deps worker2.D } } - return deps.GetErr() + err := deps.GetErr() + + return multierr.Combine(worker2.CollectRootErrors(err)...) }