mirror of
https://github.com/apricote/releaser-pleaser.git
synced 2026-01-13 13:21:00 +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
|
||||
}
|
||||
|
||||
func NewChangelogEntry(commits []AnalyzedCommit, version, link string) (string, error) {
|
||||
func NewChangelogEntry(changesets []Changeset, version, link string) (string, error) {
|
||||
features := make([]AnalyzedCommit, 0)
|
||||
fixes := make([]AnalyzedCommit, 0)
|
||||
|
||||
for _, commit := range commits {
|
||||
switch commit.Type {
|
||||
case "feat":
|
||||
features = append(features, commit)
|
||||
case "fix":
|
||||
fixes = append(fixes, commit)
|
||||
for _, changeset := range changesets {
|
||||
for _, commit := range changeset.ChangelogEntries {
|
||||
switch commit.Type {
|
||||
case "feat":
|
||||
features = append(features, commit)
|
||||
case "fix":
|
||||
fixes = append(fixes, commit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import (
|
|||
"context"
|
||||
"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"
|
||||
|
||||
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 {
|
||||
return fmt.Errorf("failed to get changesets: %w", err)
|
||||
}
|
||||
|
||||
err = reconcileReleasePR(ctx, f, changesets)
|
||||
err = reconcileReleasePR(ctx, f, changesets, tag)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to reconcile release pr: %w", err)
|
||||
}
|
||||
|
|
@ -74,32 +77,38 @@ func run(cmd *cobra.Command, args []string) error {
|
|||
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)
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
logger.InfoContext(ctx, "Found releasable commits", "length", len(releasableCommits))
|
||||
|
||||
changesets, err := forge.Changesets(ctx, releasableCommits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
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
|
||||
// Get any modifications from open PR
|
||||
// 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
16
commits.go
16
commits.go
|
|
@ -9,9 +9,10 @@ import (
|
|||
|
||||
type AnalyzedCommit struct {
|
||||
Commit
|
||||
Type string
|
||||
Description string
|
||||
Scope *string
|
||||
Type string
|
||||
Description string
|
||||
Scope *string
|
||||
BreakingChange bool
|
||||
}
|
||||
|
||||
func AnalyzeCommits(commits []Commit) ([]AnalyzedCommit, conventionalcommits.VersionBump, error) {
|
||||
|
|
@ -38,10 +39,11 @@ func AnalyzeCommits(commits []Commit) ([]AnalyzedCommit, conventionalcommits.Ver
|
|||
if commitVersionBump > conventionalcommits.UnknownVersion {
|
||||
// We only care about releasable commits
|
||||
analyzedCommits = append(analyzedCommits, AnalyzedCommit{
|
||||
Commit: commit,
|
||||
Type: conventionalCommit.Type,
|
||||
Description: conventionalCommit.Description,
|
||||
Scope: conventionalCommit.Scope,
|
||||
Commit: commit,
|
||||
Type: conventionalCommit.Type,
|
||||
Description: conventionalCommit.Description,
|
||||
Scope: conventionalCommit.Scope,
|
||||
BreakingChange: conventionalCommit.IsBreakingChange(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
53
forge.go
53
forge.go
|
|
@ -2,15 +2,21 @@ package rp
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
GITHUB_PER_PAGE_MAX = 100
|
||||
GITHUB_PR_STATE_OPEN = "open"
|
||||
GitHubPerPageMax = 100
|
||||
GitHubPRStateOpen = "open"
|
||||
GitHubEnvAPIToken = "GITHUB_TOKEN"
|
||||
GitHubEnvUsername = "GITHUB_USER"
|
||||
)
|
||||
|
||||
type Changeset struct {
|
||||
|
|
@ -21,6 +27,10 @@ type Changeset struct {
|
|||
|
||||
type Forge interface {
|
||||
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(context.Context) (*Tag, error)
|
||||
|
|
@ -54,7 +64,22 @@ type GitHub struct {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
@ -115,7 +140,7 @@ func (g *GitHub) commitsSinceTag(ctx context.Context, tag *Tag) ([]*github.Repos
|
|||
ctx, g.options.Owner, g.options.Repo,
|
||||
tag.Hash, head, &github.ListOptions{
|
||||
Page: page,
|
||||
PerPage: GITHUB_PER_PAGE_MAX,
|
||||
PerPage: GitHubPerPageMax,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -158,7 +183,7 @@ func (g *GitHub) Changesets(ctx context.Context, commits []Commit) ([]Changeset,
|
|||
ctx, g.options.Owner, g.options.Repo,
|
||||
commit.Hash, &github.ListOptions{
|
||||
Page: page,
|
||||
PerPage: GITHUB_PER_PAGE_MAX,
|
||||
PerPage: GitHubPerPageMax,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -214,14 +239,20 @@ func (g *GitHub) PullRequestForBranch(ctx context.Context, branch string) (*Rele
|
|||
for {
|
||||
prs, resp, err := g.client.PullRequests.ListPullRequestsWithCommit(ctx, g.options.Owner, g.options.Repo, branch, &github.ListOptions{
|
||||
Page: page,
|
||||
PerPage: GITHUB_PER_PAGE_MAX,
|
||||
PerPage: GitHubPerPageMax,
|
||||
})
|
||||
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
|
||||
}
|
||||
|
||||
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))
|
||||
for _, label := range pr.Labels {
|
||||
labels = append(labels, label.GetName())
|
||||
|
|
@ -246,6 +277,13 @@ func (g *GitHub) PullRequestForBranch(ctx context.Context, branch string) (*Rele
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -256,6 +294,7 @@ type GitHubOptions struct {
|
|||
Repo string
|
||||
|
||||
APIToken string
|
||||
Username string
|
||||
}
|
||||
|
||||
func NewGitHub(log *slog.Logger, options *GitHubOptions) *GitHub {
|
||||
|
|
|
|||
27
git.go
27
git.go
|
|
@ -1,17 +1,22 @@
|
|||
package rp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
)
|
||||
|
||||
const (
|
||||
CommitSearchDepth = 50 // TODO: Increase
|
||||
GitRemoteName = "origin"
|
||||
)
|
||||
|
||||
type Commit struct {
|
||||
|
|
@ -24,6 +29,28 @@ type Tag struct {
|
|||
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) {
|
||||
|
||||
ref, err := repo.Head()
|
||||
|
|
|
|||
5
go.mod
5
go.mod
|
|
@ -3,11 +3,14 @@ module github.com/apricote/releaser-pleaser
|
|||
go 1.22.4
|
||||
|
||||
require (
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/go-git/go-billy/v5 v5.5.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/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/yuin/goldmark v1.4.13
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
@ -20,14 +23,12 @@ require (
|
|||
github.com/emirpasic/gods v1.18.1 // 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/google/go-github/v63 v63.0.0 // indirect
|
||||
github.com/google/go-querystring 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/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.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/sirupsen/logrus v1.9.3 // 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/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/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/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
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/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
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/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
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/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/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
|
|
|
|||
17
releasepr.go
17
releasepr.go
|
|
@ -32,6 +32,23 @@ const (
|
|||
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
|
||||
const (
|
||||
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