diff --git a/internal/client/client.go b/internal/client/client.go index 395e29805ac..9b957fe5362 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -52,7 +52,9 @@ func (r Repo) String() string { // Client interface. type Client interface { CloseMilestone(ctx *context.Context, repo Repo, title string) (err error) + // Creates a release. It's marked as draft if possible (should call PublishRelease to finish publishing). CreateRelease(ctx *context.Context, body string) (releaseID string, err error) + PublishRelease(ctx *context.Context, releaseID string) (err error) Upload(ctx *context.Context, releaseID string, artifact *artifact.Artifact, file *os.File) (err error) Changelog(ctx *context.Context, repo Repo, prev, current string) (string, error) ReleaseURLTemplater diff --git a/internal/client/gitea.go b/internal/client/gitea.go index 227187cd765..2613cc7de9b 100644 --- a/internal/client/gitea.go +++ b/internal/client/gitea.go @@ -276,6 +276,11 @@ func (c *giteaClient) CreateRelease(ctx *context.Context, body string) (string, return strconv.FormatInt(release.ID, 10), nil } +func (c *giteaClient) PublishRelease(_ *context.Context, _ string /* releaseID */) (err error) { + // TODO: Create release as draft while uploading artifacts and only publish it here. + return nil +} + func (c *giteaClient) ReleaseURLTemplate(ctx *context.Context) (string, error) { downloadURL, err := tmpl.New(ctx).Apply(ctx.Config.GiteaURLs.Download) if err != nil { diff --git a/internal/client/github.go b/internal/client/github.go index 16c60c90d16..382c3a6613e 100644 --- a/internal/client/github.go +++ b/internal/client/github.go @@ -354,10 +354,12 @@ func (c *githubClient) CreateRelease(ctx *context.Context, body string) (string, body = truncateReleaseBody(body) data := &github.RepositoryRelease{ - Name: github.String(title), - TagName: github.String(ctx.Git.CurrentTag), - Body: github.String(body), - Draft: github.Bool(ctx.Config.Release.Draft), + Name: github.String(title), + TagName: github.String(ctx.Git.CurrentTag), + Body: github.String(body), + // Always start with a draft release while uploading artifacts. + // PublishRelease will undraft it. + Draft: github.Bool(true), Prerelease: github.Bool(ctx.PreRelease), MakeLatest: github.String("true"), } @@ -388,6 +390,19 @@ func (c *githubClient) CreateRelease(ctx *context.Context, body string) (string, return strconv.FormatInt(release.GetID(), 10), nil } +func (c *githubClient) PublishRelease(ctx *context.Context, releaseID string) (err error) { + releaseIDInt, err := strconv.ParseInt(releaseID, 10, 64) + if err != nil { + return fmt.Errorf("non-numeric release ID %q: %w", releaseID, err) + } + if _, err := c.updateRelease(ctx, releaseIDInt, &github.RepositoryRelease{ + Draft: github.Bool(ctx.Config.Release.Draft), + }); err != nil { + return fmt.Errorf("could not update existing release: %w", err) + } + return nil +} + func (c *githubClient) createOrUpdateRelease(ctx *context.Context, data *github.RepositoryRelease, body string) (*github.RepositoryRelease, error) { c.checkRateLimit(ctx) release, _, err := c.client.Repositories.GetReleaseByTag( diff --git a/internal/client/gitlab.go b/internal/client/gitlab.go index 145f5dc3761..61103b9492a 100644 --- a/internal/client/gitlab.go +++ b/internal/client/gitlab.go @@ -348,6 +348,11 @@ func (c *gitlabClient) CreateRelease(ctx *context.Context, body string) (release return tagName, err // gitlab references a tag in a repo by its name } +func (c *gitlabClient) PublishRelease(_ *context.Context, _ string /* releaseID */) (err error) { + // GitLab doesn't support draft releases. So a created release is already published. + return nil +} + func (c *gitlabClient) ReleaseURLTemplate(ctx *context.Context) (string, error) { var urlTemplate string gitlabName, err := tmpl.New(ctx).Apply(ctx.Config.Release.GitLab.Name) diff --git a/internal/client/mock.go b/internal/client/mock.go index 60ee68e7669..b27ffb528c7 100644 --- a/internal/client/mock.go +++ b/internal/client/mock.go @@ -31,6 +31,7 @@ type Mock struct { FailToUpload bool CreatedRelease bool UploadedFile bool + ReleasePublished bool UploadedFileNames []string UploadedFilePaths map[string]string FailFirstUpload bool @@ -81,6 +82,11 @@ func (c *Mock) CreateRelease(_ *context.Context, _ string) (string, error) { return "", nil } +func (c *Mock) PublishRelease(_ *context.Context, _ string /* releaseID */) (err error) { + c.ReleasePublished = true + return nil +} + func (c *Mock) ReleaseURLTemplate(_ *context.Context) (string, error) { return "https://dummyhost/download/{{ .Tag }}/{{ .ArtifactName }}", nil } diff --git a/internal/pipe/release/release.go b/internal/pipe/release/release.go index 27956bcc90f..9a4e0c0ac56 100644 --- a/internal/pipe/release/release.go +++ b/internal/pipe/release/release.go @@ -171,7 +171,11 @@ func doPublish(ctx *context.Context, client client.Client) error { return upload(ctx, client, releaseID, artifact) }) } - return g.Wait() + if err := g.Wait(); err != nil { + return err + } + + return client.PublishRelease(ctx, releaseID) } func upload(ctx *context.Context, cli client.Client, releaseID string, artifact *artifact.Artifact) error { diff --git a/internal/pipe/release/release_test.go b/internal/pipe/release/release_test.go index 6aa87babfd7..36264b64b70 100644 --- a/internal/pipe/release/release_test.go +++ b/internal/pipe/release/release_test.go @@ -117,6 +117,7 @@ func TestRunPipeWithoutIDsThenDoesNotFilter(t *testing.T) { require.NoError(t, doPublish(ctx, client)) require.True(t, client.CreatedRelease) require.True(t, client.UploadedFile) + require.True(t, client.ReleasePublished) require.Contains(t, client.UploadedFileNames, "source.tar.gz") require.Contains(t, client.UploadedFileNames, "bin.deb") require.Contains(t, client.UploadedFileNames, "bin.tar.gz") @@ -192,6 +193,7 @@ func TestRunPipeWithIDsThenFilters(t *testing.T) { require.NoError(t, doPublish(ctx, client)) require.True(t, client.CreatedRelease) require.True(t, client.UploadedFile) + require.True(t, client.ReleasePublished) require.Contains(t, client.UploadedFileNames, "bin.deb") require.Contains(t, client.UploadedFileNames, "bin.tar.gz") require.Contains(t, client.UploadedFileNames, "f1") @@ -215,6 +217,7 @@ func TestRunPipeReleaseCreationFailed(t *testing.T) { require.Error(t, doPublish(ctx, client)) require.False(t, client.CreatedRelease) require.False(t, client.UploadedFile) + require.False(t, client.ReleasePublished) } func TestRunPipeWithFileThatDontExist(t *testing.T) { @@ -262,6 +265,7 @@ func TestRunPipeUploadFailure(t *testing.T) { require.EqualError(t, doPublish(ctx, client), "failed to upload bin.tar.gz after 1 tries: upload failed") require.True(t, client.CreatedRelease) require.False(t, client.UploadedFile) + require.False(t, client.ReleasePublished) } func TestRunPipeExtraFileNotFound(t *testing.T) { @@ -330,6 +334,7 @@ func TestRunPipeUploadRetry(t *testing.T) { require.NoError(t, doPublish(ctx, client)) require.True(t, client.CreatedRelease) require.True(t, client.UploadedFile) + require.True(t, client.ReleasePublished) } func TestDefault(t *testing.T) { diff --git a/www/docs/customization/release.md b/www/docs/customization/release.md index 4c5dbd3152d..7e45a251c67 100644 --- a/www/docs/customization/release.md +++ b/www/docs/customization/release.md @@ -26,6 +26,7 @@ release: - bar # If set to true, will not auto-publish the release. + # Note: all GitHub releases start as drafts while artifacts are uploaded. # Available only for GitHub and Gitea. draft: true