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 }