Skip to content

Commit

Permalink
feat(pipe/release): Mark GitHub releases as non-draft only after all …
Browse files Browse the repository at this point in the history
…artifacts are uploaded. (#4626)

Previously end-users would see missing artifacts if trying to use latest
version while artifacts are being uploaded.

This currently applies only to GitHub releases. GitLab does not support
drafts, and I don't dare to make the change for Gitea since I don't use
it (and can't test easily).

---------

Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
  • Loading branch information
nichtverstehen and caarlos0 committed Feb 19, 2024
1 parent 42d2db2 commit ef90821
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 5 deletions.
2 changes: 2 additions & 0 deletions internal/client/client.go
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions internal/client/gitea.go
Expand Up @@ -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 {
Expand Down
23 changes: 19 additions & 4 deletions internal/client/github.go
Expand Up @@ -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"),
}
Expand Down Expand Up @@ -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(
Expand Down
5 changes: 5 additions & 0 deletions internal/client/gitlab.go
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions internal/client/mock.go
Expand Up @@ -31,6 +31,7 @@ type Mock struct {
FailToUpload bool
CreatedRelease bool
UploadedFile bool
ReleasePublished bool
UploadedFileNames []string
UploadedFilePaths map[string]string
FailFirstUpload bool
Expand Down Expand Up @@ -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
}
Expand Down
6 changes: 5 additions & 1 deletion internal/pipe/release/release.go
Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions internal/pipe/release/release_test.go
Expand Up @@ -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")
Expand Down Expand Up @@ -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")
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions www/docs/customization/release.md
Expand Up @@ -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

Expand Down

0 comments on commit ef90821

Please sign in to comment.