mirror of
https://github.com/apricote/releaser-pleaser.git
synced 2026-01-13 21:21:03 +00:00
feat: real user as commit author (#187)
Previously all commits were authored and committed by
releaser-pleaser <>
This looked weird when looking at the commit. We now check with the
Forge API for details on the currently authenticated user, and use that
name and email as the commit author. The commit committer stays the same
for now.
In GitHub, the default `$GITHUB_TOKEN` does not allow access to the
required endpoint, so for github the user `github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>` is hardcoded
when the request fails.
This commit is contained in:
parent
f2786c8f39
commit
175d6d0633
6 changed files with 121 additions and 13 deletions
|
|
@ -17,6 +17,9 @@ type Forge interface {
|
|||
|
||||
GitAuth() transport.AuthMethod
|
||||
|
||||
// CommitAuthor returns the git author used for the release commit. It should be the user whose token is used to talk to the API.
|
||||
CommitAuthor(context.Context) (git.Author, error)
|
||||
|
||||
// LatestTags returns the last stable tag created on the main branch. If there is a more recent pre-release tag,
|
||||
// that is also returned. If no tag is found, it returns nil.
|
||||
LatestTags(context.Context) (git.Releases, error)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@ const (
|
|||
EnvRepository = "GITHUB_REPOSITORY"
|
||||
)
|
||||
|
||||
var (
|
||||
gitHubActionsBotAuthor = git.Author{
|
||||
Name: "github-actions[bot]",
|
||||
Email: "41898282+github-actions[bot]@users.noreply.github.com",
|
||||
}
|
||||
)
|
||||
|
||||
var _ forge.Forge = &GitHub{}
|
||||
|
||||
type GitHub struct {
|
||||
|
|
@ -61,6 +68,22 @@ func (g *GitHub) GitAuth() transport.AuthMethod {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *GitHub) CommitAuthor(ctx context.Context) (git.Author, error) {
|
||||
g.log.DebugContext(ctx, "getting commit author from current token user")
|
||||
|
||||
user, _, err := g.client.Users.Get(ctx, "")
|
||||
if err != nil {
|
||||
g.log.WarnContext(ctx, "failed to get commit author from API, using default github-actions[bot] user", "error", err)
|
||||
|
||||
return gitHubActionsBotAuthor, nil
|
||||
}
|
||||
|
||||
return git.Author{
|
||||
Name: user.GetName(),
|
||||
Email: user.GetEmail(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *GitHub) LatestTags(ctx context.Context) (git.Releases, error) {
|
||||
g.log.DebugContext(ctx, "listing all tags in github repository")
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,22 @@ func (g *GitLab) GitAuth() transport.AuthMethod {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *GitLab) CommitAuthor(ctx context.Context) (git.Author, error) {
|
||||
g.log.DebugContext(ctx, "getting commit author from current token user")
|
||||
|
||||
user, _, err := g.client.Users.CurrentUser(gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return git.Author{}, err
|
||||
}
|
||||
|
||||
// TODO: Return bot when nothing is returned?
|
||||
|
||||
return git.Author{
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *GitLab) LatestTags(ctx context.Context) (git.Releases, error) {
|
||||
g.log.DebugContext(ctx, "listing all tags in gitlab repository")
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,27 @@ type Releases struct {
|
|||
Stable *Tag
|
||||
}
|
||||
|
||||
type Author struct {
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
|
||||
func (a Author) signature(when time.Time) *object.Signature {
|
||||
return &object.Signature{
|
||||
Name: a.Name,
|
||||
Email: a.Email,
|
||||
When: when,
|
||||
}
|
||||
}
|
||||
|
||||
func (a Author) String() string {
|
||||
return fmt.Sprintf("%s <%s>", a.Name, a.Email)
|
||||
}
|
||||
|
||||
var (
|
||||
committer = Author{Name: "releaser-pleaser", Email: ""}
|
||||
)
|
||||
|
||||
func CloneRepo(ctx context.Context, logger *slog.Logger, cloneURL, branch string, auth transport.AuthMethod) (*Repository, error) {
|
||||
dir, err := os.MkdirTemp("", "releaser-pleaser.*")
|
||||
if err != nil {
|
||||
|
|
@ -150,15 +171,17 @@ func (r *Repository) UpdateFile(_ context.Context, path string, create bool, upd
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) Commit(_ context.Context, message string) (Commit, error) {
|
||||
func (r *Repository) Commit(_ context.Context, message string, author Author) (Commit, error) {
|
||||
worktree, err := r.r.Worktree()
|
||||
if err != nil {
|
||||
return Commit{}, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
releaseCommitHash, err := worktree.Commit(message, &git.CommitOptions{
|
||||
Author: signature(),
|
||||
Committer: signature(),
|
||||
Author: author.signature(now),
|
||||
Committer: committer.signature(now),
|
||||
})
|
||||
if err != nil {
|
||||
return Commit{}, fmt.Errorf("failed to commit changes: %w", err)
|
||||
|
|
@ -223,11 +246,3 @@ func (r *Repository) ForcePush(ctx context.Context, branch string) error {
|
|||
Auth: r.auth,
|
||||
})
|
||||
}
|
||||
|
||||
func signature() *object.Signature {
|
||||
return &object.Signature{
|
||||
Name: "releaser-pleaser",
|
||||
Email: "",
|
||||
When: time.Now(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
46
internal/git/git_test.go
Normal file
46
internal/git/git_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
|
||||
func TestAuthor_signature(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
tests := []struct {
|
||||
author Author
|
||||
want *object.Signature
|
||||
}{
|
||||
{author: Author{Name: "foo", Email: "bar@example.com"}, want: &object.Signature{Name: "foo", Email: "bar@example.com", When: now}},
|
||||
{author: Author{Name: "bar", Email: "foo@example.com"}, want: &object.Signature{Name: "bar", Email: "foo@example.com", When: now}},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
t.Run(strconv.FormatInt(int64(i), 10), func(t *testing.T) {
|
||||
if got := tt.author.signature(now); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("signature() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthor_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
author Author
|
||||
want string
|
||||
}{
|
||||
{author: Author{Name: "foo", Email: "bar@example.com"}, want: "foo <bar@example.com>"},
|
||||
{author: Author{Name: "bar", Email: "foo@example.com"}, want: "bar <foo@example.com>"},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
t.Run(strconv.FormatInt(int64(i), 10), func(t *testing.T) {
|
||||
if got := tt.author.String(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("String() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue