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 }