github.com/windmeup/goreleaser@v1.21.95/internal/pipe/release/release.go (about)

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