2024-08-17 15:28:25 +02:00
|
|
|
package rp
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2025-06-14 15:23:05 +02:00
|
|
|
"errors"
|
2024-08-17 15:28:25 +02:00
|
|
|
"fmt"
|
|
|
|
|
"log/slog"
|
|
|
|
|
|
2024-08-31 15:23:21 +02:00
|
|
|
"github.com/apricote/releaser-pleaser/internal/changelog"
|
|
|
|
|
"github.com/apricote/releaser-pleaser/internal/commitparser"
|
|
|
|
|
"github.com/apricote/releaser-pleaser/internal/forge"
|
|
|
|
|
"github.com/apricote/releaser-pleaser/internal/git"
|
|
|
|
|
"github.com/apricote/releaser-pleaser/internal/releasepr"
|
|
|
|
|
"github.com/apricote/releaser-pleaser/internal/updater"
|
|
|
|
|
"github.com/apricote/releaser-pleaser/internal/versioning"
|
2024-08-17 15:28:25 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
PullRequestBranchFormat = "releaser-pleaser--branches--%s"
|
|
|
|
|
)
|
|
|
|
|
|
2025-06-14 15:23:05 +02:00
|
|
|
const (
|
|
|
|
|
PullRequestConflictAttempts = 3
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
ErrorPullRequestConflict = errors.New("conflict: pull request description was changed while releaser-pleaser was running")
|
|
|
|
|
)
|
|
|
|
|
|
2024-08-17 15:28:25 +02:00
|
|
|
type ReleaserPleaser struct {
|
2024-08-31 15:23:21 +02:00
|
|
|
forge forge.Forge
|
2024-08-17 15:28:25 +02:00
|
|
|
logger *slog.Logger
|
|
|
|
|
targetBranch string
|
2024-08-31 15:23:21 +02:00
|
|
|
commitParser commitparser.CommitParser
|
2024-11-15 18:43:40 +01:00
|
|
|
versioning versioning.Strategy
|
2024-08-23 22:02:58 +02:00
|
|
|
extraFiles []string
|
2025-08-23 22:14:34 +02:00
|
|
|
updaters []updater.Updater
|
2024-08-17 15:28:25 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-23 22:14:34 +02:00
|
|
|
func New(forge forge.Forge, logger *slog.Logger, targetBranch string, commitParser commitparser.CommitParser, versioningStrategy versioning.Strategy, extraFiles []string, updaters []updater.Updater) *ReleaserPleaser {
|
2024-08-17 15:28:25 +02:00
|
|
|
return &ReleaserPleaser{
|
|
|
|
|
forge: forge,
|
|
|
|
|
logger: logger,
|
|
|
|
|
targetBranch: targetBranch,
|
2024-08-17 16:26:40 +02:00
|
|
|
commitParser: commitParser,
|
2024-11-15 18:43:40 +01:00
|
|
|
versioning: versioningStrategy,
|
2024-08-23 22:02:58 +02:00
|
|
|
extraFiles: extraFiles,
|
|
|
|
|
updaters: updaters,
|
2024-08-17 15:28:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (rp *ReleaserPleaser) EnsureLabels(ctx context.Context) error {
|
|
|
|
|
// TODO: Wrap Error
|
2024-08-31 15:23:21 +02:00
|
|
|
|
|
|
|
|
return rp.forge.EnsureLabelsExist(ctx, releasepr.KnownLabels)
|
2024-08-17 15:28:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (rp *ReleaserPleaser) Run(ctx context.Context) error {
|
|
|
|
|
err := rp.runOnboarding(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to onboard repository: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = rp.runCreatePendingReleases(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create pending releases: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-14 15:23:05 +02:00
|
|
|
err = rp.runReconcileReleasePRWithRetries(ctx)
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to reconcile release pull request: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (rp *ReleaserPleaser) runOnboarding(ctx context.Context) error {
|
|
|
|
|
err := rp.EnsureLabels(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to ensure all labels exist: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (rp *ReleaserPleaser) runCreatePendingReleases(ctx context.Context) error {
|
|
|
|
|
logger := rp.logger.With("method", "runCreatePendingReleases")
|
|
|
|
|
|
|
|
|
|
logger.InfoContext(ctx, "checking for pending releases")
|
2024-08-31 15:23:21 +02:00
|
|
|
prs, err := rp.forge.PendingReleases(ctx, releasepr.LabelReleasePending)
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(prs) == 0 {
|
|
|
|
|
logger.InfoContext(ctx, "No pending releases found")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.InfoContext(ctx, "Found pending releases", "length", len(prs))
|
|
|
|
|
|
|
|
|
|
for _, pr := range prs {
|
|
|
|
|
err = rp.createPendingRelease(ctx, pr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-31 15:23:21 +02:00
|
|
|
func (rp *ReleaserPleaser) createPendingRelease(ctx context.Context, pr *releasepr.ReleasePullRequest) error {
|
2024-08-17 15:28:25 +02:00
|
|
|
logger := rp.logger.With(
|
|
|
|
|
"method", "createPendingRelease",
|
|
|
|
|
"pr.id", pr.ID,
|
|
|
|
|
"pr.title", pr.Title)
|
|
|
|
|
|
|
|
|
|
if pr.ReleaseCommit == nil {
|
|
|
|
|
return fmt.Errorf("pull request is missing the merge commit")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.Info("Creating release", "commit.hash", pr.ReleaseCommit.Hash)
|
|
|
|
|
|
|
|
|
|
version, err := pr.Version()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-15 18:43:40 +01:00
|
|
|
changelogText, err := pr.ChangelogText()
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-15 18:49:50 +01:00
|
|
|
// TODO: Check if version should be marked latest
|
2024-08-17 15:28:25 +02:00
|
|
|
|
|
|
|
|
logger.DebugContext(ctx, "Creating release on forge")
|
2024-11-15 18:49:50 +01:00
|
|
|
err = rp.forge.CreateRelease(ctx, *pr.ReleaseCommit, version, changelogText, rp.versioning.IsPrerelease(version), true)
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create release on forge: %w", err)
|
|
|
|
|
}
|
|
|
|
|
logger.DebugContext(ctx, "created release", "release.title", version, "release.url", rp.forge.ReleaseURL(version))
|
|
|
|
|
|
|
|
|
|
logger.DebugContext(ctx, "updating pr labels")
|
2024-08-31 15:23:21 +02:00
|
|
|
err = rp.forge.SetPullRequestLabels(ctx, pr, []releasepr.Label{releasepr.LabelReleasePending}, []releasepr.Label{releasepr.LabelReleaseTagged})
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
logger.DebugContext(ctx, "updated pr labels")
|
|
|
|
|
|
|
|
|
|
logger.InfoContext(ctx, "Created release", "release.title", version, "release.url", rp.forge.ReleaseURL(version))
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-14 15:23:05 +02:00
|
|
|
// runReconcileReleasePRWithRetries retries runReconcileReleasePR up to PullRequestConflictAttempts times, but only
|
|
|
|
|
// when a ErrorPullRequestConflict was encountered.
|
|
|
|
|
func (rp *ReleaserPleaser) runReconcileReleasePRWithRetries(ctx context.Context) error {
|
|
|
|
|
logger := rp.logger.With("method", "runReconcileReleasePRWithRetries", "totalAttempts", PullRequestConflictAttempts)
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
for i := range PullRequestConflictAttempts {
|
|
|
|
|
logger := logger.With("attempt", i+1)
|
|
|
|
|
logger.DebugContext(ctx, "attempting runReconcileReleasePR")
|
|
|
|
|
|
|
|
|
|
err = rp.runReconcileReleasePR(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, ErrorPullRequestConflict) {
|
|
|
|
|
logger.WarnContext(ctx, "detected conflict while updating pull request description, retrying")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-17 15:28:25 +02:00
|
|
|
func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error {
|
|
|
|
|
logger := rp.logger.With("method", "runReconcileReleasePR")
|
|
|
|
|
|
2024-08-30 19:27:42 +02:00
|
|
|
rpBranch := fmt.Sprintf(PullRequestBranchFormat, rp.targetBranch)
|
|
|
|
|
|
|
|
|
|
pr, err := rp.forge.PullRequestForBranch(ctx, rpBranch)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-31 15:23:21 +02:00
|
|
|
var releaseOverrides releasepr.ReleaseOverrides
|
2024-08-30 19:27:42 +02:00
|
|
|
|
|
|
|
|
if pr != nil {
|
|
|
|
|
logger = logger.With("pr.id", pr.ID, "pr.title", pr.Title)
|
|
|
|
|
logger.InfoContext(ctx, "found existing release pull request")
|
|
|
|
|
|
|
|
|
|
releaseOverrides, err = pr.GetOverrides()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-17 15:28:25 +02:00
|
|
|
releases, err := rp.forge.LatestTags(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if releases.Latest != nil {
|
|
|
|
|
logger.InfoContext(ctx, "found latest tag", "tag.hash", releases.Latest.Hash, "tag.name", releases.Latest.Name)
|
|
|
|
|
if releases.Stable != nil && releases.Latest.Hash != releases.Stable.Hash {
|
|
|
|
|
logger.InfoContext(ctx, "found stable tag", "tag.hash", releases.Stable.Hash, "tag.name", releases.Stable.Name)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
logger.InfoContext(ctx, "no latest tag found")
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 15:27:49 +02:00
|
|
|
// For stable releases, we want to consider all changes since the last stable release for version and changelog.
|
|
|
|
|
// For prereleases, we want to consider all changes...
|
|
|
|
|
// - since the last stable release for the version
|
|
|
|
|
// - since the latest release (stable or prerelease) for the changelog
|
|
|
|
|
analyzedCommitsForVersioning, err := rp.analyzedCommitsSince(ctx, releases.Stable)
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 15:27:49 +02:00
|
|
|
if len(analyzedCommitsForVersioning) == 0 {
|
2024-08-17 15:28:25 +02:00
|
|
|
if pr != nil {
|
2024-08-17 16:26:40 +02:00
|
|
|
logger.InfoContext(ctx, "closing existing pull requests, no commits available", "pr.id", pr.ID, "pr.title", pr.Title)
|
2024-08-17 15:28:25 +02:00
|
|
|
err = rp.forge.ClosePullRequest(ctx, pr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-08-17 16:26:40 +02:00
|
|
|
logger.InfoContext(ctx, "No commits available for release")
|
2024-08-17 15:28:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 15:27:49 +02:00
|
|
|
versionBump := versioning.BumpFromCommits(analyzedCommitsForVersioning)
|
2024-08-17 16:26:40 +02:00
|
|
|
// TODO: Set version in release pr
|
2024-11-15 18:43:40 +01:00
|
|
|
nextVersion, err := rp.versioning.NextVersion(releases, versionBump, releaseOverrides.NextVersionType)
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
logger.InfoContext(ctx, "next version", "version", nextVersion)
|
|
|
|
|
|
2025-05-22 15:27:49 +02:00
|
|
|
analyzedCommitsForChangelog := analyzedCommitsForVersioning
|
|
|
|
|
if releaseOverrides.NextVersionType.IsPrerelease() && releases.Latest != releases.Stable {
|
|
|
|
|
analyzedCommitsForChangelog, err = rp.analyzedCommitsSince(ctx, releases.Latest)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-17 15:28:25 +02:00
|
|
|
logger.DebugContext(ctx, "cloning repository", "clone.url", rp.forge.CloneURL())
|
2024-08-31 15:23:21 +02:00
|
|
|
repo, err := git.CloneRepo(ctx, logger, rp.forge.CloneURL(), rp.targetBranch, rp.forge.GitAuth())
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to clone repository: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-31 15:23:21 +02:00
|
|
|
if err = repo.DeleteBranch(ctx, rpBranch); err != nil {
|
|
|
|
|
return err
|
2024-08-17 15:28:25 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-31 15:23:21 +02:00
|
|
|
if err = repo.Checkout(ctx, rpBranch); err != nil {
|
|
|
|
|
return err
|
2024-08-17 15:28:25 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-22 15:27:49 +02:00
|
|
|
changelogData := changelog.New(commitparser.ByType(analyzedCommitsForChangelog), nextVersion, rp.forge.ReleaseURL(nextVersion), releaseOverrides.Prefix, releaseOverrides.Suffix)
|
2024-09-22 14:00:30 +02:00
|
|
|
|
|
|
|
|
changelogEntry, err := changelog.Entry(logger, changelog.DefaultTemplate(), changelogData, changelog.Formatting{})
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to build changelog entry: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 22:02:58 +02:00
|
|
|
// Info for updaters
|
2024-08-31 15:23:21 +02:00
|
|
|
info := updater.ReleaseInfo{Version: nextVersion, ChangelogEntry: changelogEntry}
|
2024-08-23 22:02:58 +02:00
|
|
|
|
2025-08-23 22:14:34 +02:00
|
|
|
for _, u := range rp.updaters {
|
|
|
|
|
for _, file := range u.Files() {
|
|
|
|
|
err = repo.UpdateFile(ctx, file, u.CreateNewFiles(), u.Update(info))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to run updater %T: %w", u, err)
|
|
|
|
|
}
|
2024-08-23 22:02:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-09 10:06:56 +02:00
|
|
|
releaseCommitAuthor, err := rp.forge.CommitAuthor(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get commit author: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-17 15:28:25 +02:00
|
|
|
releaseCommitMessage := fmt.Sprintf("chore(%s): release %s", rp.targetBranch, nextVersion)
|
2025-06-09 10:06:56 +02:00
|
|
|
releaseCommit, err := repo.Commit(ctx, releaseCommitMessage, releaseCommitAuthor)
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to commit changes: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-09 10:06:56 +02:00
|
|
|
logger.InfoContext(ctx, "created release commit", "commit.hash", releaseCommit.Hash, "commit.message", releaseCommit.Message, "commit.author", releaseCommitAuthor)
|
2024-08-17 15:28:25 +02:00
|
|
|
|
|
|
|
|
// Check if anything changed in comparison to the remote branch (if exists)
|
2025-06-09 10:52:09 +02:00
|
|
|
newReleasePRChanges, err := repo.HasChangesWithRemote(ctx, rp.targetBranch, rpBranch)
|
2024-08-31 15:23:21 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2024-08-17 15:28:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if newReleasePRChanges {
|
2024-08-31 15:23:21 +02:00
|
|
|
err = repo.ForcePush(ctx, rpBranch)
|
|
|
|
|
if err != nil {
|
2024-08-17 15:28:25 +02:00
|
|
|
return fmt.Errorf("failed to push branch: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-31 15:23:21 +02:00
|
|
|
logger.InfoContext(ctx, "pushed branch", "commit.hash", releaseCommit.Hash, "branch.name", rpBranch)
|
2024-08-17 15:28:25 +02:00
|
|
|
} else {
|
|
|
|
|
logger.InfoContext(ctx, "file content is already up-to-date in remote branch, skipping push")
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-22 14:00:30 +02:00
|
|
|
// We do not need the version title here. In the pull request the version is available from the title, and in the
|
|
|
|
|
// release on the Forge its usually in a heading somewhere above the text.
|
|
|
|
|
changelogEntryPullRequest, err := changelog.Entry(logger, changelog.DefaultTemplate(), changelogData, changelog.Formatting{HideVersionTitle: true})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to build pull request changelog entry: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-17 15:28:25 +02:00
|
|
|
// Open/Update PR
|
|
|
|
|
if pr == nil {
|
2024-09-22 14:00:30 +02:00
|
|
|
pr, err = releasepr.NewReleasePullRequest(rpBranch, rp.targetBranch, nextVersion, changelogEntryPullRequest)
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = rp.forge.CreatePullRequest(ctx, pr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-09-06 23:27:48 +02:00
|
|
|
logger.InfoContext(ctx, "opened pull request", "pr.title", pr.Title, "pr.id", pr.ID, "pr.url", rp.forge.PullRequestURL(pr.ID))
|
2024-08-17 15:28:25 +02:00
|
|
|
} else {
|
2025-06-14 15:23:05 +02:00
|
|
|
// Check if the pull request was updated while releaser-pleaser was running.
|
|
|
|
|
// This avoids a conflict where the user updated the PR while releaser-pleaser already pulled the info, and
|
|
|
|
|
// releaser-pleaser subsequently reverts the users changes. There is still a minimal time window for this to
|
|
|
|
|
// happen between us checking the PR again and submitting our changes.
|
|
|
|
|
|
|
|
|
|
logger.DebugContext(ctx, "checking for conflict in pr description", "pr.id", pr.ID)
|
|
|
|
|
recheckPR, err := rp.forge.PullRequestForBranch(ctx, rpBranch)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if recheckPR == nil {
|
|
|
|
|
return fmt.Errorf("PR was deleted while releaser-pleaser was running")
|
|
|
|
|
}
|
|
|
|
|
if recheckPR.Description != pr.Description {
|
|
|
|
|
return ErrorPullRequestConflict
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-17 15:28:25 +02:00
|
|
|
pr.SetTitle(rp.targetBranch, nextVersion)
|
|
|
|
|
|
|
|
|
|
overrides, err := pr.GetOverrides()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-09-22 14:00:30 +02:00
|
|
|
err = pr.SetDescription(changelogEntryPullRequest, overrides)
|
2024-08-17 15:28:25 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = rp.forge.UpdatePullRequest(ctx, pr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-09-06 23:27:48 +02:00
|
|
|
logger.InfoContext(ctx, "updated pull request", "pr.title", pr.Title, "pr.id", pr.ID, "pr.url", rp.forge.PullRequestURL(pr.ID))
|
2024-08-17 15:28:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-05-22 15:27:49 +02:00
|
|
|
|
|
|
|
|
func (rp *ReleaserPleaser) analyzedCommitsSince(ctx context.Context, since *git.Tag) ([]commitparser.AnalyzedCommit, error) {
|
2025-06-09 11:22:27 +02:00
|
|
|
logger := rp.logger.With("method", "analyzedCommitsSince")
|
|
|
|
|
|
|
|
|
|
if since != nil {
|
|
|
|
|
logger = rp.logger.With("tag.hash", since.Hash, "tag.name", since.Name)
|
|
|
|
|
}
|
2025-05-22 15:27:49 +02:00
|
|
|
|
|
|
|
|
commits, err := rp.forge.CommitsSince(ctx, since)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commits, err = parsePRBodyForCommitOverrides(commits)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.InfoContext(ctx, "Found releasable commits", "length", len(commits))
|
|
|
|
|
|
|
|
|
|
analyzedCommits, err := rp.commitParser.Analyze(commits)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.InfoContext(ctx, "Analyzed commits", "length", len(analyzedCommits))
|
|
|
|
|
|
|
|
|
|
return analyzedCommits, nil
|
|
|
|
|
}
|