1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-30 04:50:45 +02:00

feat: support github release notes (#2638)

* feat: support github release notes

* chore: godoc

* test: cover it with tests

* chore: fmt

* refactor: remove unused code
This commit is contained in:
Carlos Alexandro Becker 2021-11-07 12:53:28 -03:00 committed by GitHub
parent 17a6bb8dfb
commit 8eae8b013a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 137 additions and 20 deletions

View File

@ -46,6 +46,12 @@ type Client interface {
Changelog(ctx *context.Context, repo Repo, prev, current string) (string, error)
}
// GitHubClient is the client with GitHub-only features.
type GitHubClient interface {
Client
GenerateReleaseNotes(ctx *context.Context, repo Repo, prev, current string) (string, error)
}
// New creates a new client depending on the token type.
func New(ctx *context.Context) (Client, error) {
return newWithToken(ctx, ctx.Token)

View File

@ -25,14 +25,8 @@ type githubClient struct {
client *github.Client
}
// NewUnauthenticatedGitHub returns a github client that is not authenticated.
// Used in tests only.
func NewUnauthenticatedGitHub() Client {
return &githubClient{client: github.NewClient(nil)}
}
// NewGitHub returns a github client implementation.
func NewGitHub(ctx *context.Context, token string) (Client, error) {
func NewGitHub(ctx *context.Context, token string) (GitHubClient, error) {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
@ -58,6 +52,17 @@ func NewGitHub(ctx *context.Context, token string) (Client, error) {
return &githubClient{client: client}, nil
}
func (c *githubClient) GenerateReleaseNotes(ctx *context.Context, repo Repo, prev, current string) (string, error) {
notes, _, err := c.client.Repositories.GenerateReleaseNotes(ctx, repo.Owner, repo.Name, &github.GenerateNotesOptions{
TagName: current,
PreviousTagName: github.String(prev),
})
if err != nil {
return "", err
}
return notes.Body, err
}
func (c *githubClient) Changelog(ctx *context.Context, repo Repo, prev, current string) (string, error) {
var log []string

View File

@ -16,17 +16,6 @@ import (
)
func TestNewGitHubClient(t *testing.T) {
t.Run("unauthenticated", func(t *testing.T) {
ctx := context.New(config.Project{})
repo := Repo{
Owner: "goreleaser",
Name: "goreleaser",
}
b, err := NewUnauthenticatedGitHub().GetDefaultBranch(ctx, repo)
require.NoError(t, err)
require.Equal(t, "master", b)
})
t.Run("good urls", func(t *testing.T) {
githubURL := "https://github.mycompany.com"
ctx := context.New(config.Project{
@ -302,3 +291,35 @@ func TestChangelog(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "6dcb09b5b57875f334f61aebed695e2e4193db5e: Fix all the bugs (@octocat)", log)
}
func TestReleaseNotes(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
if r.URL.Path == "/repos/someone/something/releases/generate-notes" {
r, err := os.Open("testdata/github/releasenotes.json")
require.NoError(t, err)
_, err = io.Copy(w, r)
require.NoError(t, err)
return
}
}))
defer srv.Close()
ctx := context.New(config.Project{
GitHubURLs: config.GitHubURLs{
API: srv.URL + "/",
},
})
client, err := NewGitHub(ctx, "test-token")
require.NoError(t, err)
repo := Repo{
Owner: "someone",
Name: "something",
Branch: "somebranch",
}
log, err := client.GenerateReleaseNotes(ctx, repo, "v1.0.0", "v1.1.0")
require.NoError(t, err)
require.Equal(t, "**Full Changelog**: https://github.com/someone/something/compare/v1.0.0...v1.1.0", log)
}

View File

@ -12,6 +12,11 @@ import (
"github.com/goreleaser/goreleaser/pkg/context"
)
var (
_ Client = &Mock{}
_ GitHubClient = &Mock{}
)
func NewMock() *Mock {
return &Mock{}
}
@ -30,9 +35,21 @@ type Mock struct {
Lock sync.Mutex
ClosedMilestone string
FailToCloseMilestone bool
Changes string
ReleaseNotes string
}
func (c *Mock) Changelog(ctx *context.Context, repo Repo, prev, current string) (string, error) {
if c.Changes != "" {
return c.Changes, nil
}
return "", ErrNotImplemented
}
func (c *Mock) GenerateReleaseNotes(ctx *context.Context, repo Repo, prev, current string) (string, error) {
if c.ReleaseNotes != "" {
return c.ReleaseNotes, nil
}
return "", ErrNotImplemented
}

View File

@ -0,0 +1 @@
{"name":"v1.1.0","body":"**Full Changelog**: https://github.com/someone/something/compare/v1.0.0...v1.1.0"}

View File

@ -191,11 +191,31 @@ func getChangeloger(ctx *context.Context) (changeloger, error) {
fallthrough
case "gitlab":
return newSCMChangeloger(ctx)
case "github-native":
return newGithubChangeloger(ctx)
default:
return nil, fmt.Errorf("invalid changelog.use: %q", ctx.Config.Changelog.Use)
}
}
func newGithubChangeloger(ctx *context.Context) (changeloger, error) {
cli, err := client.NewGitHub(ctx, ctx.Token)
if err != nil {
return nil, err
}
repo, err := git.ExtractRepoFromConfig()
if err != nil {
return nil, err
}
return &githubNativeChangeloger{
client: cli,
repo: client.Repo{
Owner: repo.Owner,
Name: repo.Name,
},
}, nil
}
func newSCMChangeloger(ctx *context.Context) (changeloger, error) {
cli, err := client.New(ctx)
if err != nil {
@ -270,3 +290,12 @@ type scmChangeloger struct {
func (c *scmChangeloger) Log(ctx *context.Context, prev, current string) (string, error) {
return c.client.Changelog(ctx, c.repo, prev, current)
}
type githubNativeChangeloger struct {
client client.GitHubClient
repo client.Repo
}
func (c *githubNativeChangeloger) Log(ctx *context.Context, prev, current string) (string, error) {
return c.client.GenerateReleaseNotes(ctx, c.repo, prev, current)
}

View File

@ -437,8 +437,11 @@ func TestGetChangelogGitHub(t *testing.T) {
},
})
expected := "c90f1085f255d0af0b055160bfff5ee40f47af79: fix: do not skip any defaults (#2521) (@caarlos0)"
mock := client.NewMock()
mock.Changes = expected
l := scmChangeloger{
client: client.NewUnauthenticatedGitHub(),
client: mock,
repo: client.Repo{
Owner: "goreleaser",
Name: "goreleaser",
@ -446,7 +449,29 @@ func TestGetChangelogGitHub(t *testing.T) {
}
log, err := l.Log(ctx, "v0.180.1", "v0.180.2")
require.NoError(t, err)
require.Equal(t, "c90f1085f255d0af0b055160bfff5ee40f47af79: fix: do not skip any defaults (#2521) (@caarlos0)", log)
require.Equal(t, expected, log)
}
func TestGetChangelogGitHubNative(t *testing.T) {
ctx := context.New(config.Project{
Changelog: config.Changelog{
Use: "github-native",
},
})
expected := "**Full Changelog**: https://github.com/gorelease/goreleaser/compare/v0.180.1...v0.180.2"
mock := client.NewMock()
mock.ReleaseNotes = expected
l := githubNativeChangeloger{
client: mock,
repo: client.Repo{
Owner: "goreleaser",
Name: "goreleaser",
},
}
log, err := l.Log(ctx, "v0.180.1", "v0.180.2")
require.NoError(t, err)
require.Equal(t, expected, log)
}
func TestGetChangeloger(t *testing.T) {
@ -478,6 +503,18 @@ func TestGetChangeloger(t *testing.T) {
require.IsType(t, c, &scmChangeloger{})
})
t.Run("github-native", func(t *testing.T) {
ctx := context.New(config.Project{
Changelog: config.Changelog{
Use: "github-native",
},
})
ctx.TokenType = context.TokenTypeGitHub
c, err := getChangeloger(ctx)
require.NoError(t, err)
require.IsType(t, c, &githubNativeChangeloger{})
})
t.Run("gitlab", func(t *testing.T) {
ctx := context.New(config.Project{
Changelog: config.Changelog{

View File

@ -15,6 +15,7 @@ changelog:
# - `git`: uses `git log`;
# - `github`: uses the compare GitHub API, appending the author login to the changelog.
# - `gitlab`: uses the compare GitLab API, appending the author name and email to the changelog.
# - `github-native`: uses the GitHub release notes generation API.
#
# Defaults to `git`.
use: github