diff --git a/cmd/rp/cmd/run.go b/cmd/rp/cmd/run.go index 945a948..3d6baf7 100644 --- a/cmd/rp/cmd/run.go +++ b/cmd/rp/cmd/run.go @@ -65,11 +65,17 @@ func run(cmd *cobra.Command, args []string) error { return err } - for _, commit := range commits { + analyzedCommits, versionBump, err := rp.AnalyzeCommits(commits) + if err != nil { + return err + } + + for _, commit := range analyzedCommits { title, _, _ := strings.Cut(commit.Message, "\n") fmt.Printf("%s %s\n", commit.Hash, title) } fmt.Printf("Previous Tag: %s\n", previousTag.Name) + fmt.Printf("Recommended Bump: %v\n", versionBump) return nil } diff --git a/commits.go b/commits.go new file mode 100644 index 0000000..99c76a2 --- /dev/null +++ b/commits.go @@ -0,0 +1,55 @@ +package rp + +import ( + "fmt" + + "github.com/leodido/go-conventionalcommits" + "github.com/leodido/go-conventionalcommits/parser" +) + +type AnalyzedCommit struct { + Commit + Type string + Description string + Scope *string +} + +func AnalyzeCommits(commits []Commit) ([]AnalyzedCommit, conventionalcommits.VersionBump, error) { + parserMachine := parser.NewMachine( + parser.WithBestEffort(), + parser.WithTypes(conventionalcommits.TypesConventional), + ) + + analyzedCommits := make([]AnalyzedCommit, 0, len(commits)) + + highestVersionBump := conventionalcommits.UnknownVersion + + for _, commit := range commits { + msg, err := parserMachine.Parse([]byte(commit.Message)) + if err != nil { + return nil, conventionalcommits.UnknownVersion, fmt.Errorf("failed to parse message of commit %q: %w", commit.Hash, err) + } + conventionalCommit, ok := msg.(*conventionalcommits.ConventionalCommit) + if !ok { + return nil, conventionalcommits.UnknownVersion, fmt.Errorf("unable to get ConventionalCommit from parser result: %T", msg) + } + + commitVersionBump := conventionalCommit.VersionBump(conventionalcommits.DefaultStrategy) + 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, + }) + } + + if commitVersionBump > highestVersionBump { + // Get max version bump from all releasable commits + highestVersionBump = commitVersionBump + } + } + + return analyzedCommits, highestVersionBump, nil +} diff --git a/commits_test.go b/commits_test.go new file mode 100644 index 0000000..15c8966 --- /dev/null +++ b/commits_test.go @@ -0,0 +1,133 @@ +package rp + +import ( + "testing" + + "github.com/leodido/go-conventionalcommits" + "github.com/stretchr/testify/assert" +) + +func TestAnalyzeCommits(t *testing.T) { + type args struct { + commits []Commit + } + tests := []struct { + name string + commits []Commit + expectedCommits []AnalyzedCommit + expectedBump conventionalcommits.VersionBump + wantErr assert.ErrorAssertionFunc + }{ + { + name: "empty commits", + commits: []Commit{}, + expectedCommits: []AnalyzedCommit{}, + expectedBump: conventionalcommits.UnknownVersion, + wantErr: assert.NoError, + }, + { + name: "malformed commit message", + commits: []Commit{ + { + Message: "aksdjaklsdjka", + }, + }, + expectedCommits: nil, + expectedBump: conventionalcommits.UnknownVersion, + wantErr: assert.Error, + }, + { + name: "drops unreleasable", + commits: []Commit{ + { + Message: "chore: foobar", + }, + }, + expectedCommits: []AnalyzedCommit{}, + expectedBump: conventionalcommits.UnknownVersion, + wantErr: assert.NoError, + }, + { + name: "highest bump (patch)", + commits: []Commit{ + { + Message: "chore: foobar", + }, + { + Message: "fix: blabla", + }, + }, + expectedCommits: []AnalyzedCommit{ + { + Commit: Commit{Message: "fix: blabla"}, + Type: "fix", + Description: "blabla", + }, + }, + expectedBump: conventionalcommits.PatchVersion, + wantErr: assert.NoError, + }, + { + name: "highest bump (minor)", + commits: []Commit{ + { + Message: "fix: blabla", + }, + { + Message: "feat: foobar", + }, + }, + expectedCommits: []AnalyzedCommit{ + { + Commit: Commit{Message: "fix: blabla"}, + Type: "fix", + Description: "blabla", + }, + { + Commit: Commit{Message: "feat: foobar"}, + Type: "feat", + Description: "foobar", + }, + }, + expectedBump: conventionalcommits.MinorVersion, + wantErr: assert.NoError, + }, + + { + name: "highest bump (major)", + commits: []Commit{ + { + Message: "fix: blabla", + }, + { + Message: "feat!: foobar", + }, + }, + expectedCommits: []AnalyzedCommit{ + { + Commit: Commit{Message: "fix: blabla"}, + Type: "fix", + Description: "blabla", + }, + { + Commit: Commit{Message: "feat!: foobar"}, + Type: "feat", + Description: "foobar", + }, + }, + expectedBump: conventionalcommits.MajorVersion, + wantErr: assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + analyzedCommits, versionBump, err := AnalyzeCommits(tt.commits) + if !tt.wantErr(t, err) { + return + } + + assert.Equal(t, tt.expectedCommits, analyzedCommits) + assert.Equal(t, tt.expectedBump, versionBump) + }) + } +} diff --git a/go.mod b/go.mod index 927a519..2ee14e9 100644 --- a/go.mod +++ b/go.mod @@ -22,9 +22,11 @@ require ( 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/leodido/go-conventionalcommits v0.12.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // 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 github.com/spf13/pflag v1.0.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect diff --git a/go.sum b/go.sum index 83b63c7..731977e 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-conventionalcommits v0.12.0 h1:pG01rl8Ze+mxnSSVB2wPdGASXyyU25EGwLUc0bWrmKc= +github.com/leodido/go-conventionalcommits v0.12.0/go.mod h1:DW+n8pQb5w/c7Vba7iGOMS3rkbPqykVlnrDykGjlsJM= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= @@ -64,6 +66,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD 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= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= @@ -73,6 +77,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 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= @@ -149,5 +154,6 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=