diff --git a/internal/semerrgroup/sem.go b/internal/semerrgroup/sem.go index d4a8150ab..5d92489bf 100644 --- a/internal/semerrgroup/sem.go +++ b/internal/semerrgroup/sem.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/goreleaser/goreleaser/internal/pipe" + "github.com/hashicorp/go-multierror" "golang.org/x/sync/errgroup" ) @@ -30,9 +31,9 @@ func NewSkipAware(g Group) Group { } type skipAwareGroup struct { - g Group - skipErr error - skipOnce sync.Once + g Group + skipErr *multierror.Error + l sync.Mutex } // Go execs runs `fn` and saves the result if no error has been encountered. @@ -42,9 +43,9 @@ func (s *skipAwareGroup) Go(fn func() error) { // if the err is a skip, set it for later, but return nil for now so the // group proceeds. if pipe.IsSkip(err) { - s.skipOnce.Do(func() { - s.skipErr = err - }) + s.l.Lock() + defer s.l.Unlock() + s.skipErr = multierror.Append(s.skipErr, err) return nil } return err @@ -57,5 +58,13 @@ func (s *skipAwareGroup) Wait() error { if err := s.g.Wait(); err != nil { return err } + if s.skipErr == nil { + return nil + } + + if s.skipErr.Len() == 1 { + return s.skipErr.Errors[0] + } + return s.skipErr } diff --git a/internal/semerrgroup/sem_test.go b/internal/semerrgroup/sem_test.go index 68b9ae3eb..4435680f9 100644 --- a/internal/semerrgroup/sem_test.go +++ b/internal/semerrgroup/sem_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/goreleaser/goreleaser/internal/pipe" + "github.com/hashicorp/go-multierror" "github.com/stretchr/testify/require" ) @@ -71,19 +72,49 @@ func TestSemaphoreSkipAware(t *testing.T) { for _, i := range []int{1, 4} { t.Run(fmt.Sprintf("limit-%d", i), func(t *testing.T) { g := NewSkipAware(New(i)) - var lock sync.Mutex - var counter int for i := 0; i < 10; i++ { g.Go(func() error { time.Sleep(10 * time.Millisecond) - lock.Lock() - counter++ - lock.Unlock() return pipe.Skip("fake skip") }) } + merr := &multierror.Error{} + require.ErrorAs(t, g.Wait(), &merr, "must be a multierror") + require.Len(t, merr.Errors, 10) + }) + } +} + +func TestSemaphoreSkipAwareSingleError(t *testing.T) { + for _, i := range []int{1, 4} { + t.Run(fmt.Sprintf("limit-%d", i), func(t *testing.T) { + g := NewSkipAware(New(i)) + for i := 0; i < 10; i++ { + i := i + g.Go(func() error { + time.Sleep(10 * time.Millisecond) + if i == 5 { + return pipe.Skip("fake skip") + } + return nil + }) + } require.EqualError(t, g.Wait(), "fake skip") - require.Equal(t, 10, counter) + }) + } +} + +func TestSemaphoreSkipAwareNoSkips(t *testing.T) { + for _, i := range []int{1, 4} { + t.Run(fmt.Sprintf("limit-%d", i), func(t *testing.T) { + g := NewSkipAware(New(i)) + for i := 0; i < 10; i++ { + g.Go(func() error { + time.Sleep(10 * time.Millisecond) + return nil + }) + } + require.NoError(t, g.Wait()) }) } }