github.com/amane3/goreleaser@v0.182.0/internal/pipe/release/release.go (about)

     1  package release
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/amane3/goreleaser/internal/artifact"
    10  	"github.com/amane3/goreleaser/internal/client"
    11  	"github.com/amane3/goreleaser/internal/extrafiles"
    12  	"github.com/amane3/goreleaser/internal/git"
    13  	"github.com/amane3/goreleaser/internal/pipe"
    14  	"github.com/amane3/goreleaser/internal/semerrgroup"
    15  	"github.com/amane3/goreleaser/pkg/context"
    16  	"github.com/apex/log"
    17  )
    18  
    19  // ErrMultipleReleases indicates that multiple releases are defined. ATM only one of them is allowed.
    20  // See https://github.com/amane3/goreleaser/pull/809
    21  var ErrMultipleReleases = errors.New("multiple releases are defined. Only one is allowed")
    22  
    23  // Pipe for github release.
    24  type Pipe struct{}
    25  
    26  func (Pipe) String() string {
    27  	return "github/gitlab/gitea releases"
    28  }
    29  
    30  // Default sets the pipe defaults.
    31  func (Pipe) Default(ctx *context.Context) error {
    32  	numOfReleases := 0
    33  	if ctx.Config.Release.GitHub.String() != "" {
    34  		numOfReleases++
    35  	}
    36  	if ctx.Config.Release.GitLab.String() != "" {
    37  		numOfReleases++
    38  	}
    39  	if ctx.Config.Release.Gitea.String() != "" {
    40  		numOfReleases++
    41  	}
    42  	if numOfReleases > 1 {
    43  		return ErrMultipleReleases
    44  	}
    45  
    46  	if ctx.Config.Release.NameTemplate == "" {
    47  		ctx.Config.Release.NameTemplate = "{{.Tag}}"
    48  	}
    49  
    50  	// nolint: exhaustive
    51  	switch ctx.TokenType {
    52  	case context.TokenTypeGitLab:
    53  		{
    54  			if ctx.Config.Release.GitLab.Name == "" {
    55  				repo, err := git.ExtractRepoFromConfig()
    56  				if err != nil {
    57  					return err
    58  				}
    59  				ctx.Config.Release.GitLab = repo
    60  			}
    61  
    62  			return nil
    63  		}
    64  	case context.TokenTypeGitea:
    65  		{
    66  			if ctx.Config.Release.Gitea.Name == "" {
    67  				repo, err := git.ExtractRepoFromConfig()
    68  				if err != nil {
    69  					return err
    70  				}
    71  				ctx.Config.Release.Gitea = repo
    72  			}
    73  
    74  			return nil
    75  		}
    76  	}
    77  
    78  	// We keep github as default for now
    79  	if ctx.Config.Release.GitHub.Name == "" {
    80  		repo, err := git.ExtractRepoFromConfig()
    81  		if err != nil && !ctx.Snapshot {
    82  			return err
    83  		}
    84  		ctx.Config.Release.GitHub = repo
    85  	}
    86  
    87  	// Check if we have to check the git tag for an indicator to mark as pre release
    88  	switch ctx.Config.Release.Prerelease {
    89  	case "auto":
    90  		if ctx.Semver.Prerelease != "" {
    91  			ctx.PreRelease = true
    92  		}
    93  		log.Debugf("pre-release was detected for tag %s: %v", ctx.Git.CurrentTag, ctx.PreRelease)
    94  	case "true":
    95  		ctx.PreRelease = true
    96  	}
    97  	log.Debugf("pre-release for tag %s set to %v", ctx.Git.CurrentTag, ctx.PreRelease)
    98  
    99  	return nil
   100  }
   101  
   102  // Publish the release.
   103  func (Pipe) Publish(ctx *context.Context) error {
   104  	if ctx.SkipPublish {
   105  		return pipe.ErrSkipPublishEnabled
   106  	}
   107  	c, err := client.New(ctx)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	return doPublish(ctx, c)
   112  }
   113  
   114  func doPublish(ctx *context.Context, client client.Client) error {
   115  	if ctx.Config.Release.Disable {
   116  		return pipe.Skip("release pipe is disabled")
   117  	}
   118  	log.WithField("tag", ctx.Git.CurrentTag).
   119  		WithField("repo", ctx.Config.Release.GitHub.String()).
   120  		Info("creating or updating release")
   121  	body, err := describeBody(ctx)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	releaseID, err := client.CreateRelease(ctx, body.String())
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	extraFiles, err := extrafiles.Find(ctx.Config.Release.ExtraFiles)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	for name, path := range extraFiles {
   136  		if _, err := os.Stat(path); os.IsNotExist(err) {
   137  			return fmt.Errorf("failed to upload %s: %w", name, err)
   138  		}
   139  		ctx.Artifacts.Add(&artifact.Artifact{
   140  			Name: name,
   141  			Path: path,
   142  			Type: artifact.UploadableFile,
   143  		})
   144  	}
   145  
   146  	var filters = artifact.Or(
   147  		artifact.ByType(artifact.UploadableArchive),
   148  		artifact.ByType(artifact.UploadableBinary),
   149  		artifact.ByType(artifact.UploadableSourceArchive),
   150  		artifact.ByType(artifact.Checksum),
   151  		artifact.ByType(artifact.Signature),
   152  		artifact.ByType(artifact.LinuxPackage),
   153  	)
   154  
   155  	if len(ctx.Config.Release.IDs) > 0 {
   156  		filters = artifact.And(filters, artifact.ByIDs(ctx.Config.Release.IDs...))
   157  	}
   158  
   159  	filters = artifact.Or(filters, artifact.ByType(artifact.UploadableFile))
   160  
   161  	var g = semerrgroup.New(ctx.Parallelism)
   162  	for _, artifact := range ctx.Artifacts.Filter(filters).List() {
   163  		artifact := artifact
   164  		g.Go(func() error {
   165  			return upload(ctx, client, releaseID, artifact)
   166  		})
   167  	}
   168  	return g.Wait()
   169  }
   170  
   171  func upload(ctx *context.Context, cli client.Client, releaseID string, artifact *artifact.Artifact) error {
   172  	var try int
   173  	tryUpload := func() error {
   174  		try++
   175  		file, err := os.Open(artifact.Path)
   176  		if err != nil {
   177  			return err
   178  		}
   179  		defer file.Close()
   180  		log.WithField("file", file.Name()).WithField("name", artifact.Name).Info("uploading to release")
   181  		if err := cli.Upload(ctx, releaseID, artifact, file); err != nil {
   182  			log.WithField("try", try).
   183  				WithField("artifact", artifact.Name).
   184  				WithError(err).
   185  				Warnf("failed to upload artifact, will retry")
   186  			return err
   187  		}
   188  		return nil
   189  	}
   190  
   191  	var err error
   192  	for try < 10 {
   193  		err = tryUpload()
   194  		if err == nil {
   195  			return nil
   196  		}
   197  		if errors.As(err, &client.RetriableError{}) {
   198  			time.Sleep(time.Duration(try*50) * time.Millisecond)
   199  			continue
   200  		}
   201  		break
   202  	}
   203  
   204  	return fmt.Errorf("failed to upload %s after %d tries: %w", artifact.Name, try, err)
   205  }