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 }