refactor: move forge to package

This commit is contained in:
Julian Tölle 2024-08-30 23:12:05 +02:00
parent 5765b48703
commit 8b3bd3ca27
6 changed files with 160 additions and 136 deletions

View file

@ -6,6 +6,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
rp "github.com/apricote/releaser-pleaser" rp "github.com/apricote/releaser-pleaser"
"github.com/apricote/releaser-pleaser/internal/forge"
"github.com/apricote/releaser-pleaser/internal/forge/github"
) )
var runCmd = &cobra.Command{ var runCmd = &cobra.Command{
@ -41,9 +43,9 @@ func run(cmd *cobra.Command, _ []string) error {
"repo", flagRepo, "repo", flagRepo,
) )
var forge rp.Forge var f forge.Forge
forgeOptions := rp.ForgeOptions{ forgeOptions := forge.Options{
Repository: flagRepo, Repository: flagRepo,
BaseBranch: flagBranch, BaseBranch: flagBranch,
} }
@ -53,17 +55,17 @@ func run(cmd *cobra.Command, _ []string) error {
// f = rp.NewGitLab(forgeOptions) // f = rp.NewGitLab(forgeOptions)
case "github": case "github":
logger.DebugContext(ctx, "using forge GitHub") logger.DebugContext(ctx, "using forge GitHub")
forge = rp.NewGitHub(logger, &rp.GitHubOptions{ f = github.New(logger, &github.Options{
ForgeOptions: forgeOptions, Options: forgeOptions,
Owner: flagOwner, Owner: flagOwner,
Repo: flagRepo, Repo: flagRepo,
}) })
} }
extraFiles := parseExtraFiles(flagExtraFiles) extraFiles := parseExtraFiles(flagExtraFiles)
releaserPleaser := rp.New( releaserPleaser := rp.New(
forge, f,
logger, logger,
flagBranch, flagBranch,
rp.NewConventionalCommitsParser(), rp.NewConventionalCommitsParser(),

61
internal/forge/forge.go Normal file
View file

@ -0,0 +1,61 @@
package forge
import (
"context"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/apricote/releaser-pleaser"
"github.com/apricote/releaser-pleaser/internal/git"
)
type Forge interface {
RepoURL() string
CloneURL() string
ReleaseURL(version string) string
GitAuth() transport.AuthMethod
// 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) (rp.Releases, error)
// CommitsSince returns all commits to main branch after the Tag. The tag can be `nil`, in which case this
// function should return all commits.
CommitsSince(context.Context, *git.Tag) ([]git.Commit, error)
// EnsureLabelsExist verifies that all desired labels are available on the repository. If labels are missing, they
// are created them.
EnsureLabelsExist(context.Context, []rp.Label) error
// PullRequestForBranch returns the open pull request between the branch and Options.BaseBranch. If no open PR
// exists, it returns nil.
PullRequestForBranch(context.Context, string) (*rp.ReleasePullRequest, error)
// CreatePullRequest opens a new pull/merge request for the ReleasePullRequest.
CreatePullRequest(context.Context, *rp.ReleasePullRequest) error
// UpdatePullRequest updates the pull/merge request identified through the ID of
// the ReleasePullRequest to the current description and title.
UpdatePullRequest(context.Context, *rp.ReleasePullRequest) error
// SetPullRequestLabels updates the pull/merge request identified through the ID of
// the ReleasePullRequest to the current labels.
SetPullRequestLabels(ctx context.Context, pr *rp.ReleasePullRequest, remove, add []rp.Label) error
// ClosePullRequest closes the pull/merge request identified through the ID of
// the ReleasePullRequest, as it is no longer required.
ClosePullRequest(context.Context, *rp.ReleasePullRequest) error
// PendingReleases returns a list of ReleasePullRequest. The list should contain all pull/merge requests that are
// merged and have the matching label.
PendingReleases(context.Context, rp.Label) ([]*rp.ReleasePullRequest, error)
// CreateRelease creates a release on the Forge, pointing at the commit with the passed in details.
CreateRelease(ctx context.Context, commit git.Commit, title, changelog string, prerelease, latest bool) error
}
type Options struct {
Repository string
BaseBranch string
}

View file

@ -1,4 +1,4 @@
package rp package github
import ( import (
"context" "context"
@ -14,76 +14,26 @@ import (
"github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/google/go-github/v63/github" "github.com/google/go-github/v63/github"
"github.com/apricote/releaser-pleaser"
"github.com/apricote/releaser-pleaser/internal/forge"
"github.com/apricote/releaser-pleaser/internal/git" "github.com/apricote/releaser-pleaser/internal/git"
"github.com/apricote/releaser-pleaser/internal/pointer"
) )
const ( const (
GitHubPerPageMax = 100 PerPageMax = 100
GitHubPRStateOpen = "open" PRStateOpen = "open"
GitHubPRStateClosed = "closed" PRStateClosed = "closed"
GitHubEnvAPIToken = "GITHUB_TOKEN" // nolint:gosec // Not actually a hardcoded credential EnvAPIToken = "GITHUB_TOKEN" // nolint:gosec // Not actually a hardcoded credential
GitHubEnvUsername = "GITHUB_USER" EnvUsername = "GITHUB_USER"
GitHubEnvRepository = "GITHUB_REPOSITORY" EnvRepository = "GITHUB_REPOSITORY"
GitHubLabelColor = "dedede" LabelColor = "dedede"
) )
type Forge interface { var _ forge.Forge = &GitHub{}
RepoURL() string
CloneURL() string
ReleaseURL(version string) string
GitAuth() transport.AuthMethod
// 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) (Releases, error)
// CommitsSince returns all commits to main branch after the Tag. The tag can be `nil`, in which case this
// function should return all commits.
CommitsSince(context.Context, *git.Tag) ([]git.Commit, error)
// EnsureLabelsExist verifies that all desired labels are available on the repository. If labels are missing, they
// are created them.
EnsureLabelsExist(context.Context, []Label) error
// PullRequestForBranch returns the open pull request between the branch and ForgeOptions.BaseBranch. If no open PR
// exists, it returns nil.
PullRequestForBranch(context.Context, string) (*ReleasePullRequest, error)
// CreatePullRequest opens a new pull/merge request for the ReleasePullRequest.
CreatePullRequest(context.Context, *ReleasePullRequest) error
// UpdatePullRequest updates the pull/merge request identified through the ID of
// the ReleasePullRequest to the current description and title.
UpdatePullRequest(context.Context, *ReleasePullRequest) error
// SetPullRequestLabels updates the pull/merge request identified through the ID of
// the ReleasePullRequest to the current labels.
SetPullRequestLabels(ctx context.Context, pr *ReleasePullRequest, remove, add []Label) error
// ClosePullRequest closes the pull/merge request identified through the ID of
// the ReleasePullRequest, as it is no longer required.
ClosePullRequest(context.Context, *ReleasePullRequest) error
// PendingReleases returns a list of ReleasePullRequest. The list should contain all pull/merge requests that are
// merged and have the matching label.
PendingReleases(context.Context, Label) ([]*ReleasePullRequest, error)
// CreateRelease creates a release on the Forge, pointing at the commit with the passed in details.
CreateRelease(ctx context.Context, commit git.Commit, title, changelog string, prerelease, latest bool) error
}
type ForgeOptions struct {
Repository string
BaseBranch string
}
var _ Forge = &GitHub{}
// var _ Forge = &GitLab{}
type GitHub struct { type GitHub struct {
options *GitHubOptions options *Options
client *github.Client client *github.Client
log *slog.Logger log *slog.Logger
@ -108,20 +58,20 @@ func (g *GitHub) GitAuth() transport.AuthMethod {
} }
} }
func (g *GitHub) LatestTags(ctx context.Context) (Releases, error) { func (g *GitHub) LatestTags(ctx context.Context) (rp.Releases, error) {
g.log.DebugContext(ctx, "listing all tags in github repository") g.log.DebugContext(ctx, "listing all tags in github repository")
page := 1 page := 1
var releases Releases var releases rp.Releases
for { for {
tags, resp, err := g.client.Repositories.ListTags( tags, resp, err := g.client.Repositories.ListTags(
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
&github.ListOptions{Page: page, PerPage: GitHubPerPageMax}, &github.ListOptions{Page: page, PerPage: PerPageMax},
) )
if err != nil { if err != nil {
return Releases{}, err return rp.Releases{}, err
} }
for _, ghTag := range tags { for _, ghTag := range tags {
@ -206,7 +156,7 @@ func (g *GitHub) commitsSinceTag(ctx context.Context, tag *git.Tag) ([]*github.R
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
tag.Hash, head, &github.ListOptions{ tag.Hash, head, &github.ListOptions{
Page: page, Page: page,
PerPage: GitHubPerPageMax, PerPage: PerPageMax,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -246,7 +196,7 @@ func (g *GitHub) commitsSinceInit(ctx context.Context) ([]*github.RepositoryComm
SHA: head, SHA: head,
ListOptions: github.ListOptions{ ListOptions: github.ListOptions{
Page: page, Page: page,
PerPage: GitHubPerPageMax, PerPage: PerPageMax,
}, },
}) })
if err != nil { if err != nil {
@ -256,7 +206,7 @@ func (g *GitHub) commitsSinceInit(ctx context.Context) ([]*github.RepositoryComm
if repositoryCommits == nil && resp.LastPage > 0 { if repositoryCommits == nil && resp.LastPage > 0 {
// Pre-initialize slice on first request // Pre-initialize slice on first request
log.Debug("found commits", "pages", resp.LastPage) log.Debug("found commits", "pages", resp.LastPage)
repositoryCommits = make([]*github.RepositoryCommit, 0, resp.LastPage*GitHubPerPageMax) repositoryCommits = make([]*github.RepositoryCommit, 0, resp.LastPage*PerPageMax)
} }
repositoryCommits = append(repositoryCommits, commits...) repositoryCommits = append(repositoryCommits, commits...)
@ -287,7 +237,7 @@ func (g *GitHub) prForCommit(ctx context.Context, commit git.Commit) (*git.PullR
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
commit.Hash, &github.ListOptions{ commit.Hash, &github.ListOptions{
Page: page, Page: page,
PerPage: GitHubPerPageMax, PerPage: PerPageMax,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -316,7 +266,7 @@ func (g *GitHub) prForCommit(ctx context.Context, commit git.Commit) (*git.PullR
return gitHubPRToPullRequest(pullrequest), nil return gitHubPRToPullRequest(pullrequest), nil
} }
func (g *GitHub) EnsureLabelsExist(ctx context.Context, labels []Label) error { func (g *GitHub) EnsureLabelsExist(ctx context.Context, labels []rp.Label) error {
existingLabels := make([]string, 0, len(labels)) existingLabels := make([]string, 0, len(labels))
page := 1 page := 1
@ -327,7 +277,7 @@ func (g *GitHub) EnsureLabelsExist(ctx context.Context, labels []Label) error {
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
&github.ListOptions{ &github.ListOptions{
Page: page, Page: page,
PerPage: GitHubPerPageMax, PerPage: PerPageMax,
}) })
if err != nil { if err != nil {
return err return err
@ -349,8 +299,8 @@ func (g *GitHub) EnsureLabelsExist(ctx context.Context, labels []Label) error {
_, _, err := g.client.Issues.CreateLabel( _, _, err := g.client.Issues.CreateLabel(
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
&github.Label{ &github.Label{
Name: Pointer(string(label)), Name: pointer.Pointer(string(label)),
Color: Pointer(GitHubLabelColor), Color: pointer.Pointer(LabelColor),
}, },
) )
if err != nil { if err != nil {
@ -362,13 +312,13 @@ func (g *GitHub) EnsureLabelsExist(ctx context.Context, labels []Label) error {
return nil return nil
} }
func (g *GitHub) PullRequestForBranch(ctx context.Context, branch string) (*ReleasePullRequest, error) { func (g *GitHub) PullRequestForBranch(ctx context.Context, branch string) (*rp.ReleasePullRequest, error) {
page := 1 page := 1
for { for {
prs, resp, err := g.client.PullRequests.ListPullRequestsWithCommit(ctx, g.options.Owner, g.options.Repo, branch, &github.ListOptions{ prs, resp, err := g.client.PullRequests.ListPullRequestsWithCommit(ctx, g.options.Owner, g.options.Repo, branch, &github.ListOptions{
Page: page, Page: page,
PerPage: GitHubPerPageMax, PerPage: PerPageMax,
}) })
if err != nil { if err != nil {
var ghErr *github.ErrorResponse var ghErr *github.ErrorResponse
@ -381,7 +331,7 @@ func (g *GitHub) PullRequestForBranch(ctx context.Context, branch string) (*Rele
} }
for _, pr := range prs { for _, pr := range prs {
if pr.GetBase().GetRef() == g.options.BaseBranch && pr.GetHead().GetRef() == branch && pr.GetState() == GitHubPRStateOpen { if pr.GetBase().GetRef() == g.options.BaseBranch && pr.GetHead().GetRef() == branch && pr.GetState() == PRStateOpen {
return gitHubPRToReleasePullRequest(pr), nil return gitHubPRToReleasePullRequest(pr), nil
} }
} }
@ -395,7 +345,7 @@ func (g *GitHub) PullRequestForBranch(ctx context.Context, branch string) (*Rele
return nil, nil return nil, nil
} }
func (g *GitHub) CreatePullRequest(ctx context.Context, pr *ReleasePullRequest) error { func (g *GitHub) CreatePullRequest(ctx context.Context, pr *rp.ReleasePullRequest) error {
ghPR, _, err := g.client.PullRequests.Create( ghPR, _, err := g.client.PullRequests.Create(
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
&github.NewPullRequest{ &github.NewPullRequest{
@ -412,7 +362,7 @@ func (g *GitHub) CreatePullRequest(ctx context.Context, pr *ReleasePullRequest)
// TODO: String ID? // TODO: String ID?
pr.ID = ghPR.GetNumber() pr.ID = ghPR.GetNumber()
err = g.SetPullRequestLabels(ctx, pr, []Label{}, pr.Labels) err = g.SetPullRequestLabels(ctx, pr, []rp.Label{}, pr.Labels)
if err != nil { if err != nil {
return err return err
} }
@ -420,7 +370,7 @@ func (g *GitHub) CreatePullRequest(ctx context.Context, pr *ReleasePullRequest)
return nil return nil
} }
func (g *GitHub) UpdatePullRequest(ctx context.Context, pr *ReleasePullRequest) error { func (g *GitHub) UpdatePullRequest(ctx context.Context, pr *rp.ReleasePullRequest) error {
_, _, err := g.client.PullRequests.Edit( _, _, err := g.client.PullRequests.Edit(
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
pr.ID, &github.PullRequest{ pr.ID, &github.PullRequest{
@ -435,7 +385,7 @@ func (g *GitHub) UpdatePullRequest(ctx context.Context, pr *ReleasePullRequest)
return nil return nil
} }
func (g *GitHub) SetPullRequestLabels(ctx context.Context, pr *ReleasePullRequest, remove, add []Label) error { func (g *GitHub) SetPullRequestLabels(ctx context.Context, pr *rp.ReleasePullRequest, remove, add []rp.Label) error {
for _, label := range remove { for _, label := range remove {
_, err := g.client.Issues.RemoveLabelForIssue( _, err := g.client.Issues.RemoveLabelForIssue(
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
@ -462,11 +412,11 @@ func (g *GitHub) SetPullRequestLabels(ctx context.Context, pr *ReleasePullReques
return nil return nil
} }
func (g *GitHub) ClosePullRequest(ctx context.Context, pr *ReleasePullRequest) error { func (g *GitHub) ClosePullRequest(ctx context.Context, pr *rp.ReleasePullRequest) error {
_, _, err := g.client.PullRequests.Edit( _, _, err := g.client.PullRequests.Edit(
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
pr.ID, &github.PullRequest{ pr.ID, &github.PullRequest{
State: Pointer(GitHubPRStateClosed), State: pointer.Pointer(PRStateClosed),
}, },
) )
if err != nil { if err != nil {
@ -476,20 +426,20 @@ func (g *GitHub) ClosePullRequest(ctx context.Context, pr *ReleasePullRequest) e
return nil return nil
} }
func (g *GitHub) PendingReleases(ctx context.Context, pendingLabel Label) ([]*ReleasePullRequest, error) { func (g *GitHub) PendingReleases(ctx context.Context, pendingLabel rp.Label) ([]*rp.ReleasePullRequest, error) {
page := 1 page := 1
var prs []*ReleasePullRequest var prs []*rp.ReleasePullRequest
for { for {
ghPRs, resp, err := g.client.PullRequests.List( ghPRs, resp, err := g.client.PullRequests.List(
ctx, g.options.Owner, g.options.Repo, ctx, g.options.Owner, g.options.Repo,
&github.PullRequestListOptions{ &github.PullRequestListOptions{
State: GitHubPRStateClosed, State: PRStateClosed,
Base: g.options.BaseBranch, Base: g.options.BaseBranch,
ListOptions: github.ListOptions{ ListOptions: github.ListOptions{
Page: page, Page: page,
PerPage: GitHubPerPageMax, PerPage: PerPageMax,
}, },
}) })
if err != nil { if err != nil {
@ -499,7 +449,7 @@ func (g *GitHub) PendingReleases(ctx context.Context, pendingLabel Label) ([]*Re
if prs == nil && resp.LastPage > 0 { if prs == nil && resp.LastPage > 0 {
// Pre-initialize slice on first request // Pre-initialize slice on first request
g.log.Debug("found pending releases", "pages", resp.LastPage) g.log.Debug("found pending releases", "pages", resp.LastPage)
prs = make([]*ReleasePullRequest, 0, (resp.LastPage-1)*GitHubPerPageMax) prs = make([]*rp.ReleasePullRequest, 0, (resp.LastPage-1)*PerPageMax)
} }
for _, pr := range ghPRs { for _, pr := range ghPRs {
@ -561,11 +511,11 @@ func gitHubPRToPullRequest(pr *github.PullRequest) *git.PullRequest {
} }
} }
func gitHubPRToReleasePullRequest(pr *github.PullRequest) *ReleasePullRequest { func gitHubPRToReleasePullRequest(pr *github.PullRequest) *rp.ReleasePullRequest {
labels := make([]Label, 0, len(pr.Labels)) labels := make([]rp.Label, 0, len(pr.Labels))
for _, label := range pr.Labels { for _, label := range pr.Labels {
labelName := Label(label.GetName()) labelName := rp.Label(label.GetName())
if slices.Contains(KnownLabels, Label(label.GetName())) { if slices.Contains(rp.KnownLabels, rp.Label(label.GetName())) {
labels = append(labels, labelName) labels = append(labels, labelName)
} }
} }
@ -575,7 +525,7 @@ func gitHubPRToReleasePullRequest(pr *github.PullRequest) *ReleasePullRequest {
releaseCommit = &git.Commit{Hash: pr.GetMergeCommitSHA()} releaseCommit = &git.Commit{Hash: pr.GetMergeCommitSHA()}
} }
return &ReleasePullRequest{ return &rp.ReleasePullRequest{
ID: pr.GetNumber(), ID: pr.GetNumber(),
Title: pr.GetTitle(), Title: pr.GetTitle(),
Description: pr.GetBody(), Description: pr.GetBody(),
@ -586,16 +536,16 @@ func gitHubPRToReleasePullRequest(pr *github.PullRequest) *ReleasePullRequest {
} }
} }
func (g *GitHubOptions) autodiscover() { func (g *Options) autodiscover() {
if apiToken := os.Getenv(GitHubEnvAPIToken); apiToken != "" { if apiToken := os.Getenv(EnvAPIToken); apiToken != "" {
g.APIToken = apiToken g.APIToken = apiToken
} }
// TODO: Check if there is a better solution for cloning/pushing locally // TODO: Check if there is a better solution for cloning/pushing locally
if username := os.Getenv(GitHubEnvUsername); username != "" { if username := os.Getenv(EnvUsername); username != "" {
g.Username = username g.Username = username
} }
if envRepository := os.Getenv(GitHubEnvRepository); envRepository != "" { if envRepository := os.Getenv(EnvRepository); envRepository != "" {
// GITHUB_REPOSITORY=apricote/releaser-pleaser // GITHUB_REPOSITORY=apricote/releaser-pleaser
parts := strings.Split(envRepository, "/") parts := strings.Split(envRepository, "/")
if len(parts) == 2 { if len(parts) == 2 {
@ -606,8 +556,8 @@ func (g *GitHubOptions) autodiscover() {
} }
} }
type GitHubOptions struct { type Options struct {
ForgeOptions forge.Options
Owner string Owner string
Repo string Repo string
@ -616,7 +566,7 @@ type GitHubOptions struct {
Username string Username string
} }
func NewGitHub(log *slog.Logger, options *GitHubOptions) *GitHub { func New(log *slog.Logger, options *Options) *GitHub {
options.autodiscover() options.autodiscover()
client := github.NewClient(nil) client := github.NewClient(nil)
@ -633,29 +583,3 @@ func NewGitHub(log *slog.Logger, options *GitHubOptions) *GitHub {
return gh return gh
} }
type GitLab struct {
options ForgeOptions
}
func (g *GitLab) autodiscover() {
// Read settings from GitLab-CI env vars
}
func NewGitLab(options ForgeOptions) *GitLab {
gl := &GitLab{
options: options,
}
gl.autodiscover()
return gl
}
func (g *GitLab) RepoURL() string {
return fmt.Sprintf("https://gitlab.com/%s", g.options.Repository)
}
func Pointer[T any](value T) *T {
return &value
}

View file

@ -0,0 +1,31 @@
package gitlab
import (
"fmt"
"github.com/apricote/releaser-pleaser/internal/forge"
)
// var _ forge.Forge = &GitLab{}
type GitLab struct {
options forge.Options
}
func (g *GitLab) autodiscover() {
// Read settings from GitLab-CI env vars
}
func New(options forge.Options) *GitLab {
gl := &GitLab{
options: options,
}
gl.autodiscover()
return gl
}
func (g *GitLab) RepoURL() string {
return fmt.Sprintf("https://gitlab.com/%s", g.options.Repository)
}

View file

@ -0,0 +1,5 @@
package pointer
func Pointer[T any](value T) *T {
return &value
}

View file

@ -11,6 +11,7 @@ import (
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/apricote/releaser-pleaser/internal/forge"
git2 "github.com/apricote/releaser-pleaser/internal/git" git2 "github.com/apricote/releaser-pleaser/internal/git"
) )
@ -19,7 +20,7 @@ const (
) )
type ReleaserPleaser struct { type ReleaserPleaser struct {
forge Forge forge forge.Forge
logger *slog.Logger logger *slog.Logger
targetBranch string targetBranch string
commitParser CommitParser commitParser CommitParser
@ -28,7 +29,7 @@ type ReleaserPleaser struct {
updaters []Updater updaters []Updater
} }
func New(forge Forge, logger *slog.Logger, targetBranch string, commitParser CommitParser, versioningStrategy VersioningStrategy, extraFiles []string, updaters []Updater) *ReleaserPleaser { func New(forge forge.Forge, logger *slog.Logger, targetBranch string, commitParser CommitParser, versioningStrategy VersioningStrategy, extraFiles []string, updaters []Updater) *ReleaserPleaser {
return &ReleaserPleaser{ return &ReleaserPleaser{
forge: forge, forge: forge,
logger: logger, logger: logger,