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