mirror of
https://github.com/apricote/releaser-pleaser.git
synced 2026-01-13 21:21:03 +00:00
feat: push branch with changelog
This commit is contained in:
parent
8199918903
commit
c7743e0a80
10 changed files with 313 additions and 36 deletions
16
changelog.go
16
changelog.go
|
|
@ -90,16 +90,18 @@ func UpdateChangelogFile(wt *git.Worktree, newEntry string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChangelogEntry(commits []AnalyzedCommit, version, link string) (string, error) {
|
func NewChangelogEntry(changesets []Changeset, version, link string) (string, error) {
|
||||||
features := make([]AnalyzedCommit, 0)
|
features := make([]AnalyzedCommit, 0)
|
||||||
fixes := make([]AnalyzedCommit, 0)
|
fixes := make([]AnalyzedCommit, 0)
|
||||||
|
|
||||||
for _, commit := range commits {
|
for _, changeset := range changesets {
|
||||||
switch commit.Type {
|
for _, commit := range changeset.ChangelogEntries {
|
||||||
case "feat":
|
switch commit.Type {
|
||||||
features = append(features, commit)
|
case "feat":
|
||||||
case "fix":
|
features = append(features, commit)
|
||||||
fixes = append(fixes, commit)
|
case "fix":
|
||||||
|
fixes = append(fixes, commit)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
"github.com/go-git/go-git/v5/config"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
rp "github.com/apricote/releaser-pleaser"
|
rp "github.com/apricote/releaser-pleaser"
|
||||||
|
|
@ -61,12 +64,12 @@ func run(cmd *cobra.Command, args []string) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
changesets, err := getChangesetsFromForge(ctx, f)
|
changesets, tag, err := getChangesetsFromForge(ctx, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get changesets: %w", err)
|
return fmt.Errorf("failed to get changesets: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = reconcileReleasePR(ctx, f, changesets)
|
err = reconcileReleasePR(ctx, f, changesets, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to reconcile release pr: %w", err)
|
return fmt.Errorf("failed to reconcile release pr: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -74,32 +77,38 @@ func run(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChangesetsFromForge(ctx context.Context, forge rp.Forge) ([]rp.Changeset, error) {
|
func getChangesetsFromForge(ctx context.Context, forge rp.Forge) ([]rp.Changeset, *rp.Tag, error) {
|
||||||
tag, err := forge.LatestTag(ctx)
|
tag, err := forge.LatestTag(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.InfoContext(ctx, "Latest Tag", "tag.hash", tag.Hash, "tag.name", tag.Name)
|
if tag != nil {
|
||||||
|
logger.InfoContext(ctx, "found previous tag", "tag.hash", tag.Hash, "tag.name", tag.Name)
|
||||||
|
} else {
|
||||||
|
logger.InfoContext(ctx, "no previous tag found")
|
||||||
|
}
|
||||||
|
|
||||||
releasableCommits, err := forge.CommitsSince(ctx, tag)
|
releasableCommits, err := forge.CommitsSince(ctx, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.InfoContext(ctx, "Found releasable commits", "length", len(releasableCommits))
|
logger.InfoContext(ctx, "Found releasable commits", "length", len(releasableCommits))
|
||||||
|
|
||||||
changesets, err := forge.Changesets(ctx, releasableCommits)
|
changesets, err := forge.Changesets(ctx, releasableCommits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.InfoContext(ctx, "Found changesets", "length", len(changesets))
|
logger.InfoContext(ctx, "Found changesets", "length", len(changesets))
|
||||||
|
|
||||||
return changesets, nil
|
return changesets, tag, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconcileReleasePR(ctx context.Context, forge rp.Forge, changesets []rp.Changeset) error {
|
func reconcileReleasePR(ctx context.Context, forge rp.Forge, changesets []rp.Changeset, tag *rp.Tag) error {
|
||||||
|
rpBranch := fmt.Sprintf(RELEASER_PLEASER_BRANCH, flagBranch)
|
||||||
|
rpBranchRef := plumbing.NewBranchReferenceName(rpBranch)
|
||||||
// Check Forge for open PR
|
// Check Forge for open PR
|
||||||
// Get any modifications from open PR
|
// Get any modifications from open PR
|
||||||
// Clone Repo
|
// Clone Repo
|
||||||
|
|
@ -114,12 +123,90 @@ func reconcileReleasePR(ctx context.Context, forge rp.Forge, changesets []rp.Cha
|
||||||
logger.InfoContext(ctx, "found existing release pull request: %d: %s", pr.ID, pr.Title)
|
logger.InfoContext(ctx, "found existing release pull request: %d: %s", pr.ID, pr.Title)
|
||||||
}
|
}
|
||||||
|
|
||||||
releaseOverrides, err := pr.GetOverrides()
|
var releaseOverrides rp.ReleaseOverrides
|
||||||
|
if pr != nil {
|
||||||
|
releaseOverrides, err = pr.GetOverrides()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextVersion, err := rp.NextVersion(tag, changesets, releaseOverrides.NextVersionType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.InfoContext(ctx, "next version", "version", nextVersion)
|
||||||
|
|
||||||
|
logger.DebugContext(ctx, "cloning repository", "clone.url", forge.CloneURL())
|
||||||
|
repo, err := rp.CloneRepo(ctx, forge.CloneURL(), flagBranch, forge.GitAuth())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
worktree, err := repo.Worktree()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
if branch, _ := repo.Branch(rpBranch); branch != nil {
|
||||||
|
logger.DebugContext(ctx, "deleting previous releaser-pleaser branch locally", "branch.name", rpBranch)
|
||||||
|
if err = repo.DeleteBranch(rpBranch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = worktree.Checkout(&git.CheckoutOptions{
|
||||||
|
Branch: rpBranchRef,
|
||||||
|
Create: true,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rp.RunUpdater(ctx, nextVersion, worktree)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
changelogEntry, err := rp.NewChangelogEntry(changesets, nextVersion, forge.ReleaseURL(nextVersion))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rp.UpdateChangelogFile(worktree, changelogEntry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseCommitMessage := fmt.Sprintf("chore(%s): release %s", flagBranch, nextVersion)
|
||||||
|
releaseCommit, err := worktree.Commit(releaseCommitMessage, &git.CommitOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.InfoContext(ctx, "created release commit", "commit.hash", releaseCommit.String(), "commit.message", releaseCommitMessage)
|
||||||
|
|
||||||
|
// TODO: Check if there is a diff between forge/rpBranch..rpBranch..forge/rpBranch and only push if there are changes
|
||||||
|
// To reduce wasted CI cycles
|
||||||
|
|
||||||
|
pushRefSpec := config.RefSpec(fmt.Sprintf(
|
||||||
|
"+%s:%s",
|
||||||
|
rpBranchRef,
|
||||||
|
// This needs to be the local branch name, not the remotes/origin ref
|
||||||
|
// See https://stackoverflow.com/a/75727620
|
||||||
|
rpBranchRef,
|
||||||
|
))
|
||||||
|
logger.DebugContext(ctx, "pushing branch", "commit.hash", releaseCommit.String(), "branch.name", rpBranch, "refspec", pushRefSpec.String())
|
||||||
|
if err = repo.PushContext(ctx, &git.PushOptions{
|
||||||
|
RemoteName: rp.GitRemoteName,
|
||||||
|
RefSpecs: []config.RefSpec{pushRefSpec},
|
||||||
|
Force: true,
|
||||||
|
Auth: forge.GitAuth(),
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.InfoContext(ctx, "pushed branch", "commit.hash", releaseCommit.String(), "branch.name", rpBranch, "refspec", pushRefSpec.String())
|
||||||
|
|
||||||
|
// TODO Open PR
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
commits.go
16
commits.go
|
|
@ -9,9 +9,10 @@ import (
|
||||||
|
|
||||||
type AnalyzedCommit struct {
|
type AnalyzedCommit struct {
|
||||||
Commit
|
Commit
|
||||||
Type string
|
Type string
|
||||||
Description string
|
Description string
|
||||||
Scope *string
|
Scope *string
|
||||||
|
BreakingChange bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyzeCommits(commits []Commit) ([]AnalyzedCommit, conventionalcommits.VersionBump, error) {
|
func AnalyzeCommits(commits []Commit) ([]AnalyzedCommit, conventionalcommits.VersionBump, error) {
|
||||||
|
|
@ -38,10 +39,11 @@ func AnalyzeCommits(commits []Commit) ([]AnalyzedCommit, conventionalcommits.Ver
|
||||||
if commitVersionBump > conventionalcommits.UnknownVersion {
|
if commitVersionBump > conventionalcommits.UnknownVersion {
|
||||||
// We only care about releasable commits
|
// We only care about releasable commits
|
||||||
analyzedCommits = append(analyzedCommits, AnalyzedCommit{
|
analyzedCommits = append(analyzedCommits, AnalyzedCommit{
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
Type: conventionalCommit.Type,
|
Type: conventionalCommit.Type,
|
||||||
Description: conventionalCommit.Description,
|
Description: conventionalCommit.Description,
|
||||||
Scope: conventionalCommit.Scope,
|
Scope: conventionalCommit.Scope,
|
||||||
|
BreakingChange: conventionalCommit.IsBreakingChange(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
53
forge.go
53
forge.go
|
|
@ -2,15 +2,21 @@ package rp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||||
"github.com/google/go-github/v63/github"
|
"github.com/google/go-github/v63/github"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GITHUB_PER_PAGE_MAX = 100
|
GitHubPerPageMax = 100
|
||||||
GITHUB_PR_STATE_OPEN = "open"
|
GitHubPRStateOpen = "open"
|
||||||
|
GitHubEnvAPIToken = "GITHUB_TOKEN"
|
||||||
|
GitHubEnvUsername = "GITHUB_USER"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Changeset struct {
|
type Changeset struct {
|
||||||
|
|
@ -21,6 +27,10 @@ type Changeset struct {
|
||||||
|
|
||||||
type Forge interface {
|
type Forge interface {
|
||||||
RepoURL() string
|
RepoURL() string
|
||||||
|
CloneURL() string
|
||||||
|
ReleaseURL(version string) string
|
||||||
|
|
||||||
|
GitAuth() transport.AuthMethod
|
||||||
|
|
||||||
// LatestTag returns the last tag created on the main branch. If no tag is found, it returns nil.
|
// LatestTag returns the last tag created on the main branch. If no tag is found, it returns nil.
|
||||||
LatestTag(context.Context) (*Tag, error)
|
LatestTag(context.Context) (*Tag, error)
|
||||||
|
|
@ -54,7 +64,22 @@ type GitHub struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHub) RepoURL() string {
|
func (g *GitHub) RepoURL() string {
|
||||||
return fmt.Sprintf("https://github.com/%s", g.options.Repository)
|
return fmt.Sprintf("https://github.com/%s/%s", g.options.Owner, g.options.Repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GitHub) CloneURL() string {
|
||||||
|
return fmt.Sprintf("https://github.com/%s/%s.git", g.options.Owner, g.options.Repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GitHub) ReleaseURL(version string) string {
|
||||||
|
return fmt.Sprintf("https://github.com/%s/%s/releases/tag/%s", g.options.Owner, g.options.Repo, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GitHub) GitAuth() transport.AuthMethod {
|
||||||
|
return &http.BasicAuth{
|
||||||
|
Username: g.options.Username,
|
||||||
|
Password: g.options.APIToken,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHub) LatestTag(ctx context.Context) (*Tag, error) {
|
func (g *GitHub) LatestTag(ctx context.Context) (*Tag, error) {
|
||||||
|
|
@ -115,7 +140,7 @@ func (g *GitHub) commitsSinceTag(ctx context.Context, tag *Tag) ([]*github.Repos
|
||||||
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: GITHUB_PER_PAGE_MAX,
|
PerPage: GitHubPerPageMax,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -158,7 +183,7 @@ func (g *GitHub) Changesets(ctx context.Context, commits []Commit) ([]Changeset,
|
||||||
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: GITHUB_PER_PAGE_MAX,
|
PerPage: GitHubPerPageMax,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -214,14 +239,20 @@ func (g *GitHub) PullRequestForBranch(ctx context.Context, branch string) (*Rele
|
||||||
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: GITHUB_PER_PAGE_MAX,
|
PerPage: GitHubPerPageMax,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
var ghErr *github.ErrorResponse
|
||||||
|
if errors.As(err, &ghErr) {
|
||||||
|
if ghErr.Message == fmt.Sprintf("No commit found for SHA: %s", branch) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pr := range prs {
|
for _, pr := range prs {
|
||||||
if pr.Base.GetLabel() == g.options.BaseBranch && pr.Head.GetLabel() == branch && pr.GetState() == GITHUB_PR_STATE_OPEN {
|
if pr.Base.GetLabel() == g.options.BaseBranch && pr.Head.GetLabel() == branch && pr.GetState() == GitHubPRStateOpen {
|
||||||
labels := make([]string, 0, len(pr.Labels))
|
labels := make([]string, 0, len(pr.Labels))
|
||||||
for _, label := range pr.Labels {
|
for _, label := range pr.Labels {
|
||||||
labels = append(labels, label.GetName())
|
labels = append(labels, label.GetName())
|
||||||
|
|
@ -246,6 +277,13 @@ func (g *GitHub) PullRequestForBranch(ctx context.Context, branch string) (*Rele
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHubOptions) autodiscover() {
|
func (g *GitHubOptions) autodiscover() {
|
||||||
|
if apiToken := os.Getenv(GitHubEnvAPIToken); apiToken != "" {
|
||||||
|
g.APIToken = apiToken
|
||||||
|
}
|
||||||
|
// TODO: Check if there is a better solution for cloning/pushing locally
|
||||||
|
if username := os.Getenv(GitHubEnvUsername); username != "" {
|
||||||
|
g.Username = username
|
||||||
|
}
|
||||||
// TODO: Read settings from GitHub Actions env vars
|
// TODO: Read settings from GitHub Actions env vars
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,6 +294,7 @@ type GitHubOptions struct {
|
||||||
Repo string
|
Repo string
|
||||||
|
|
||||||
APIToken string
|
APIToken string
|
||||||
|
Username string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGitHub(log *slog.Logger, options *GitHubOptions) *GitHub {
|
func NewGitHub(log *slog.Logger, options *GitHubOptions) *GitHub {
|
||||||
|
|
|
||||||
27
git.go
27
git.go
|
|
@ -1,17 +1,22 @@
|
||||||
package rp
|
package rp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CommitSearchDepth = 50 // TODO: Increase
|
CommitSearchDepth = 50 // TODO: Increase
|
||||||
|
GitRemoteName = "origin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Commit struct {
|
type Commit struct {
|
||||||
|
|
@ -24,6 +29,28 @@ type Tag struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CloneRepo(ctx context.Context, cloneURL, branch string, auth transport.AuthMethod) (*git.Repository, error) {
|
||||||
|
dir, err := os.MkdirTemp("", "releaser-pleaser.*")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create temporary directory for repo clone: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Log tmpdir
|
||||||
|
fmt.Printf("Clone tmpdir: %s\n", dir)
|
||||||
|
repo, err := git.PlainCloneContext(ctx, dir, false, &git.CloneOptions{
|
||||||
|
URL: cloneURL,
|
||||||
|
RemoteName: GitRemoteName,
|
||||||
|
ReferenceName: plumbing.NewBranchReferenceName(branch),
|
||||||
|
SingleBranch: false,
|
||||||
|
Auth: auth,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to clone repository: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ReleasableCommits(repo *git.Repository) ([]Commit, *Tag, error) {
|
func ReleasableCommits(repo *git.Repository) ([]Commit, *Tag, error) {
|
||||||
|
|
||||||
ref, err := repo.Head()
|
ref, err := repo.Head()
|
||||||
|
|
|
||||||
5
go.mod
5
go.mod
|
|
@ -3,11 +3,14 @@ module github.com/apricote/releaser-pleaser
|
||||||
go 1.22.4
|
go 1.22.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/blang/semver/v4 v4.0.0
|
||||||
github.com/go-git/go-billy/v5 v5.5.0
|
github.com/go-git/go-billy/v5 v5.5.0
|
||||||
github.com/go-git/go-git/v5 v5.12.0
|
github.com/go-git/go-git/v5 v5.12.0
|
||||||
|
github.com/google/go-github/v63 v63.0.0
|
||||||
github.com/leodido/go-conventionalcommits v0.12.0
|
github.com/leodido/go-conventionalcommits v0.12.0
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
|
github.com/yuin/goldmark v1.4.13
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
@ -20,14 +23,12 @@ require (
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/google/go-github/v63 v63.0.0 // indirect
|
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rwtodd/Go.Sed v0.0.0-20230610052213-ba3e9c186f0a // indirect
|
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||||
|
|
|
||||||
5
go.sum
5
go.sum
|
|
@ -9,6 +9,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
|
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||||
|
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
|
|
@ -68,8 +70,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/rwtodd/Go.Sed v0.0.0-20230610052213-ba3e9c186f0a h1:URwYffGNuBQkfwkcn+1CZhb8IE/mKSXxPXp/zzQsn80=
|
|
||||||
github.com/rwtodd/Go.Sed v0.0.0-20230610052213-ba3e9c186f0a/go.mod h1:c6qgHcSUeSISur4+Kcf3WYTvpL07S8eAsoP40hDiQ1I=
|
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
|
@ -89,6 +89,7 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||||
|
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
|
|
||||||
17
releasepr.go
17
releasepr.go
|
|
@ -32,6 +32,23 @@ const (
|
||||||
NextVersionTypeAlpha
|
NextVersionTypeAlpha
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (n NextVersionType) String() string {
|
||||||
|
switch n {
|
||||||
|
case NextVersionTypeUndefined:
|
||||||
|
return "undefined"
|
||||||
|
case NextVersionTypeNormal:
|
||||||
|
return "normal"
|
||||||
|
case NextVersionTypeRC:
|
||||||
|
return "rc"
|
||||||
|
case NextVersionTypeBeta:
|
||||||
|
return "beta"
|
||||||
|
case NextVersionTypeAlpha:
|
||||||
|
return "alpha"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PR Labels
|
// PR Labels
|
||||||
const (
|
const (
|
||||||
LabelNextVersionTypeNormal = "rp-next-version::normal"
|
LabelNextVersionTypeNormal = "rp-next-version::normal"
|
||||||
|
|
|
||||||
12
updater.go
Normal file
12
updater.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package rp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunUpdater(ctx context.Context, version string, worktree *git.Worktree) error {
|
||||||
|
// TODO: Implement updater for Go,Python,ExtraFilesMarkers
|
||||||
|
return nil
|
||||||
|
}
|
||||||
89
versioning.go
Normal file
89
versioning.go
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package rp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/blang/semver/v4"
|
||||||
|
"github.com/leodido/go-conventionalcommits"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NextVersion(currentTag *Tag, changesets []Changeset, nextVersionType NextVersionType) (string, error) {
|
||||||
|
// TODO: Validate for versioning after pre-releases
|
||||||
|
currentVersion := "v0.0.0"
|
||||||
|
if currentTag != nil {
|
||||||
|
currentVersion = currentTag.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// The lib can not handle v prefixes
|
||||||
|
currentVersion = strings.TrimPrefix(currentVersion, "v")
|
||||||
|
|
||||||
|
version, err := semver.Parse(currentVersion)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
versionBump := maxVersionBump(changesets)
|
||||||
|
switch versionBump {
|
||||||
|
case conventionalcommits.UnknownVersion:
|
||||||
|
// No new version, TODO: Throw error?
|
||||||
|
case conventionalcommits.PatchVersion:
|
||||||
|
err = version.IncrementPatch()
|
||||||
|
case conventionalcommits.MinorVersion:
|
||||||
|
err = version.IncrementMinor()
|
||||||
|
case conventionalcommits.MajorVersion:
|
||||||
|
err = version.IncrementMajor()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch nextVersionType {
|
||||||
|
case NextVersionTypeAlpha, NextVersionTypeBeta, NextVersionTypeRC:
|
||||||
|
id := uint64(0)
|
||||||
|
|
||||||
|
if version.Pre[0].String() == nextVersionType.String() {
|
||||||
|
if version.Pre[1].String() == "" || !version.Pre[1].IsNumeric() {
|
||||||
|
return "", fmt.Errorf("invalid format of previous tag")
|
||||||
|
}
|
||||||
|
id = version.Pre[1].VersionNum + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
setPRVersion(&version, nextVersionType.String(), id)
|
||||||
|
case NextVersionTypeUndefined, NextVersionTypeNormal:
|
||||||
|
version.Pre = make([]semver.PRVersion, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "v" + version.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxVersionBump(changesets []Changeset) conventionalcommits.VersionBump {
|
||||||
|
bump := conventionalcommits.UnknownVersion
|
||||||
|
|
||||||
|
for _, changeset := range changesets {
|
||||||
|
for _, entry := range changeset.ChangelogEntries {
|
||||||
|
entryBump := conventionalcommits.UnknownVersion
|
||||||
|
switch {
|
||||||
|
case entry.BreakingChange:
|
||||||
|
entryBump = conventionalcommits.MajorVersion
|
||||||
|
case entry.Type == "feat":
|
||||||
|
entryBump = conventionalcommits.MinorVersion
|
||||||
|
case entry.Type == "fix":
|
||||||
|
entryBump = conventionalcommits.PatchVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
if entryBump > bump {
|
||||||
|
bump = entryBump
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bump
|
||||||
|
}
|
||||||
|
|
||||||
|
func setPRVersion(version *semver.Version, prType string, count uint64) {
|
||||||
|
version.Pre = []semver.PRVersion{
|
||||||
|
{VersionStr: prType},
|
||||||
|
{VersionNum: count, IsNum: true},
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue