github.com/goreleaser/goreleaser@v1.25.1/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/goreleaser/goreleaser/internal/artifact"
    12  	"github.com/goreleaser/goreleaser/internal/client"
    13  	"github.com/goreleaser/goreleaser/internal/extrafiles"
    14  	"github.com/goreleaser/goreleaser/internal/git"
    15  	"github.com/goreleaser/goreleaser/internal/pipe"
    16  	"github.com/goreleaser/goreleaser/internal/semerrgroup"
    17  	"github.com/goreleaser/goreleaser/internal/tmpl"
    18  	"github.com/goreleaser/goreleaser/pkg/config"
    19  	"github.com/goreleaser/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/goreleaser/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).WithField("published", !ctx.Config.Release.Draft).Info("release created/updated")
   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  	if err := ctx.Artifacts.Refresh(); err != nil {
   118  		return err
   119  	}
   120  	body, err := describeBody(ctx)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	releaseID, err := client.CreateRelease(ctx, body.String())
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	skipUpload, err := tmpl.New(ctx).Bool(ctx.Config.Release.SkipUpload)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	if skipUpload {
   134  		if err := client.PublishRelease(ctx, releaseID); err != nil {
   135  			return err
   136  		}
   137  		return pipe.Skip("release.skip_upload is set")
   138  	}
   139  
   140  	extraFiles, err := extrafiles.Find(ctx, ctx.Config.Release.ExtraFiles)
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	for name, path := range extraFiles {
   146  		if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
   147  			return fmt.Errorf("failed to upload %s: %w", name, err)
   148  		}
   149  		ctx.Artifacts.Add(&artifact.Artifact{
   150  			Name: name,
   151  			Path: path,
   152  			Type: artifact.UploadableFile,
   153  		})
   154  	}
   155  
   156  	typeFilters := []artifact.Filter{
   157  		artifact.ByType(artifact.UploadableArchive),
   158  		artifact.ByType(artifact.UploadableBinary),
   159  		artifact.ByType(artifact.UploadableSourceArchive),
   160  		artifact.ByType(artifact.UploadableFile),
   161  		artifact.ByType(artifact.Checksum),
   162  		artifact.ByType(artifact.Signature),
   163  		artifact.ByType(artifact.Certificate),
   164  		artifact.ByType(artifact.LinuxPackage),
   165  		artifact.ByType(artifact.SBOM),
   166  	}
   167  	if ctx.Config.Release.IncludeMeta {
   168  		typeFilters = append(typeFilters, artifact.ByType(artifact.Metadata))
   169  	}
   170  	filters := artifact.Or(typeFilters...)
   171  
   172  	if len(ctx.Config.Release.IDs) > 0 {
   173  		filters = artifact.And(filters, artifact.ByIDs(ctx.Config.Release.IDs...))
   174  	}
   175  
   176  	g := semerrgroup.New(ctx.Parallelism)
   177  	for _, artifact := range ctx.Artifacts.Filter(filters).List() {
   178  		artifact := artifact
   179  		g.Go(func() error {
   180  			return upload(ctx, client, releaseID, artifact)
   181  		})
   182  	}
   183  	if err := g.Wait(); err != nil {
   184  		return err
   185  	}
   186  
   187  	return client.PublishRelease(ctx, releaseID)
   188  }
   189  
   190  func upload(ctx *context.Context, cli client.Client, releaseID string, artifact *artifact.Artifact) error {
   191  	var try int
   192  	tryUpload := func() error {
   193  		try++
   194  		file, err := os.Open(artifact.Path)
   195  		if err != nil {
   196  			return err
   197  		}
   198  		defer file.Close()
   199  		log.WithField("file", file.Name()).WithField("name", artifact.Name).Info("uploading to release")
   200  		if err := cli.Upload(ctx, releaseID, artifact, file); err != nil {
   201  			log.WithField("try", try).
   202  				WithField("artifact", artifact.Name).
   203  				WithError(err).
   204  				Warnf("failed to upload artifact, will retry")
   205  			return err
   206  		}
   207  		return nil
   208  	}
   209  
   210  	var err error
   211  	for try < 10 {
   212  		err = tryUpload()
   213  		if err == nil {
   214  			return nil
   215  		}
   216  		if errors.As(err, &client.RetriableError{}) {
   217  			time.Sleep(time.Duration(try*50) * time.Millisecond)
   218  			continue
   219  		}
   220  		break
   221  	}
   222  
   223  	return fmt.Errorf("failed to upload %s after %d tries: %w", artifact.Name, try, err)
   224  }