github.com/goreleaser/goreleaser@v1.25.1/internal/semerrgroup/sem.go (about) 1 // Package semerrgroup wraps an error group with a semaphore with configurable 2 // size, so you can control the number of tasks being executed simultaneously. 3 package semerrgroup 4 5 import ( 6 "sync" 7 8 "github.com/goreleaser/goreleaser/internal/pipe" 9 "github.com/hashicorp/go-multierror" 10 "golang.org/x/sync/errgroup" 11 ) 12 13 // Group is the Semaphore ErrorGroup itself. 14 type Group interface { 15 Go(func() error) 16 Wait() error 17 } 18 19 // New returns a new Group of a given size. 20 func New(size int) Group { 21 var g errgroup.Group 22 g.SetLimit(size) 23 return &g 24 } 25 26 var _ Group = &skipAwareGroup{} 27 28 // NewSkipAware returns a new Group of a given size and aware of pipe skips. 29 func NewSkipAware(g Group) Group { 30 return &skipAwareGroup{g: g} 31 } 32 33 type skipAwareGroup struct { 34 g Group 35 skipErr *multierror.Error 36 l sync.Mutex 37 } 38 39 // Go execs runs `fn` and saves the result if no error has been encountered. 40 func (s *skipAwareGroup) Go(fn func() error) { 41 s.g.Go(func() error { 42 err := fn() 43 // if the err is a skip, set it for later, but return nil for now so the 44 // group proceeds. 45 if pipe.IsSkip(err) { 46 s.l.Lock() 47 defer s.l.Unlock() 48 s.skipErr = multierror.Append(s.skipErr, err) 49 return nil 50 } 51 return err 52 }) 53 } 54 55 // Wait waits for Go to complete and returns the first error encountered. 56 func (s *skipAwareGroup) Wait() error { 57 // if we got a "real error", return it, otherwise return skipErr or nil. 58 if err := s.g.Wait(); err != nil { 59 return err 60 } 61 if s.skipErr == nil { 62 return nil 63 } 64 65 if s.skipErr.Len() == 1 { 66 return s.skipErr.Errors[0] 67 } 68 69 return s.skipErr 70 }