diff --git a/.gitignore b/.gitignore index f1c181e..d63d50d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +.version +.vscode/settings.json diff --git a/README.md b/README.md index a13f991..b124392 100644 --- a/README.md +++ b/README.md @@ -1 +1,15 @@ -# go-semantic-release \ No newline at end of file +# go-semantic-release + +## Build + +`go build ./cmd/go-semantic-release/` + +## Run + +Print the next version + +`./go-semantic-release version next` + +Set a version + +`./go-semantic-release version set v1.1.1` diff --git a/cmd/go-semantic-release/main.go b/cmd/go-semantic-release/main.go new file mode 100644 index 0000000..73241e2 --- /dev/null +++ b/cmd/go-semantic-release/main.go @@ -0,0 +1,112 @@ +package main + +import ( + "os" + + "github.com/Nightapes/go-semantic-release/pkg/semanticrelease" + log "github.com/sirupsen/logrus" + "gopkg.in/urfave/cli.v1" // imports as package "cli" +) + +func main() { + app := cli.NewApp() + + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "loglevel", + Value: "error", + Usage: "Set loglevel 'LEVEL", + }, + } + + app.Commands = []cli.Command{ + { + Name: "version", + Aliases: []string{"v"}, + Usage: "version commands", + Subcommands: []cli.Command{ + { + Name: "set", + Usage: "set version `VERSION`", + Action: func(c *cli.Context) error { + setLoglevel(c.GlobalString("loglevel")) + path := c.String("path") + version := c.Args().First() + log.Infof("Version %s", version) + return semanticrelease.SetVersion(version, path) + }, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "config, c", + Value: "release-config.json", + Usage: "Load release configuration from `FILE`", + }, + cli.StringFlag{ + Name: "path, p", + Usage: "`PATH` to repro ", + }, + }, + }, + { + Name: "next", + Usage: "get next `VERSION` or the set one ", + Action: func(c *cli.Context) error { + setLoglevel(c.GlobalString("loglevel")) + path := c.String("path") + return semanticrelease.GetNextVersion(path) + }, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "config, c", + Value: "release-config.json", + Usage: "Load release configuration from `FILE`", + }, + cli.StringFlag{ + Name: "path, p", + Usage: "`PATH` to repro ", + }, + }, + }, + }, + }, + { + Name: "release", + Aliases: []string{}, + Usage: "make release", + Action: func(c *cli.Context) error { + return nil + }, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "config, c", + Value: "release-config.json", + Usage: "Load release configuration from `FILE`", + }, + }, + }, + { + Name: "init", + Aliases: []string{}, + Usage: "create config", + Action: func(c *cli.Context) error { + return nil + }, + }, + } + + //gitutil.GetCommits(folder) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} + +func setLoglevel(level string) { + parsed, err := log.ParseLevel(level) + if err != nil { + log.Errorf("Invalid loglevel %s", level) + } else { + log.SetLevel(parsed) + } + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..738c147 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module github.com/Nightapes/go-semantic-release + +go 1.12 + +require ( + github.com/Masterminds/semver v1.4.2 + github.com/sirupsen/logrus v1.4.1 + github.com/urfave/cli v1.20.0 // indirect + golang.org/x/tools v0.0.0-20190508150211-cf84161cff3f // indirect + gopkg.in/src-d/go-git.v4 v4.11.0 + gopkg.in/urfave/cli.v1 v1.20.0 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e4a93b0 --- /dev/null +++ b/go.sum @@ -0,0 +1,78 @@ +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo= +github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8= +github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro= +github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9 h1:lkiLiLBHGoH3XnqSLUIaBsilGMUjI+Uy2Xu2JLUtTas= +golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190508150211-cf84161cff3f h1:sjxqKRXfnzgJFg/igBXeLZoBVAKXuAAljgr+PcNr7u8= +golang.org/x/tools v0.0.0-20190508150211-cf84161cff3f/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo= +gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= +gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs= +gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.11.0 h1:cJwWgJ0DXifrNrXM6RGN1Y2yR60Rr1zQ9Q5DX5S9qgU= +gopkg.in/src-d/go-git.v4 v4.11.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk= +gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/analyzer/analyzer.go b/internal/analyzer/analyzer.go new file mode 100644 index 0000000..3e32c64 --- /dev/null +++ b/internal/analyzer/analyzer.go @@ -0,0 +1,63 @@ +package analyzer + +import ( + "github.com/Nightapes/go-semantic-release/internal/gitutil" + log "github.com/sirupsen/logrus" +) + +type Analyzer struct { + CommitFormat string +} + +type Rules struct { + Tag string + Release string +} + +type AnalyzeCommit interface { + Analyze(commit gitutil.Commit, tag string) (AnalyzedCommit, bool) + GetRules() []Rules +} + +type AnalyzedCommit struct { + Commit gitutil.Commit + ParsedMessage string + Scope string + ParsedBreakingChangeMessage string +} + +func New(format string) *Analyzer { + return &Analyzer{ + CommitFormat: format, + } + +} + +func (a *Analyzer) Analyze(commits []gitutil.Commit) map[string][]AnalyzedCommit { + + var commitAnalayzer AnalyzeCommit + switch a.CommitFormat { + case "angular": + log.Infof("analyze angular format") + commitAnalayzer = NewAngular() + } + + analyzedCommits := make(map[string][]AnalyzedCommit) + analyzedCommits["major"] = make([]AnalyzedCommit, 0) + analyzedCommits["minor"] = make([]AnalyzedCommit, 0) + analyzedCommits["patch"] = make([]AnalyzedCommit, 0) + + for _, commit := range commits { + for _, rule := range commitAnalayzer.GetRules() { + analyzedCommit, hasBreakingChange := commitAnalayzer.Analyze(commit, rule.Tag) + if hasBreakingChange { + analyzedCommits["major"] = append(analyzedCommits["major"], analyzedCommit) + } else { + analyzedCommits[rule.Release] = append(analyzedCommits[rule.Release], analyzedCommit) + } + + } + } + + return analyzedCommits +} diff --git a/internal/analyzer/angular.go b/internal/analyzer/angular.go new file mode 100644 index 0000000..936e42e --- /dev/null +++ b/internal/analyzer/angular.go @@ -0,0 +1,65 @@ +package analyzer + +import ( + "regexp" + "strings" + + "github.com/Nightapes/go-semantic-release/internal/gitutil" +) + +type Angular struct { + rules []Rules + regex string +} + +func NewAngular() *Angular { + return &Angular{ + regex: `(TAG)(?:\((.*)\))?: (.*)`, + rules: []Rules{ + { + Tag: "feat", + Release: "minor", + }, + { + Tag: "fix", + Release: "patch", + }, { + Tag: "perf", + Release: "patch", + }, + }, + } +} + +func (a *Angular) GetRules() []Rules { + return a.rules +} + +func (a *Angular) Analyze(commit gitutil.Commit, tag string) (AnalyzedCommit, bool) { + + analyzed := AnalyzedCommit{ + Commit: commit, + } + + re := regexp.MustCompile(strings.Replace(a.regex, "TAG", tag, -1)) + matches := re.FindAllStringSubmatch(commit.Message+" "+commit.Message, -1) + if len(matches) >= 1 { + if len(matches[0]) >= 3 { + analyzed.Scope = matches[0][2] + + message := strings.Join(matches[0][3:], "") + splitted := strings.SplitN(message, "BREAKING CHANGE:", 1) + + if len(splitted) == 1 { + analyzed.ParsedMessage = splitted[0] + return analyzed, false + } + analyzed.ParsedMessage = splitted[0] + analyzed.ParsedBreakingChangeMessage = splitted[1] + return analyzed, true + + } + } + return analyzed, false + +} diff --git a/internal/gitutil/gitutil.go b/internal/gitutil/gitutil.go new file mode 100644 index 0000000..f68f7eb --- /dev/null +++ b/internal/gitutil/gitutil.go @@ -0,0 +1,140 @@ +package gitutil + +import ( + "fmt" + "sort" + + "github.com/Masterminds/semver" + log "github.com/sirupsen/logrus" + "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/plumbing/object" +) + +type Commit struct { + Message string + Author string + Hash string +} + +type GitUtils struct { + Repository *git.Repository +} + +func New(folder string) (*GitUtils, error) { + r, err := git.PlainOpen(folder) + if err != nil { + return nil, err + } + utils := &GitUtils{ + Repository: r, + } + return utils, nil + +} + +func (g *GitUtils) GetHash() (string, error) { + ref, err := g.Repository.Head() + if err != nil { + return "", err + } + return ref.Hash().String(), nil +} + +func (g *GitUtils) GetBranch() (string, error) { + ref, err := g.Repository.Head() + if err != nil { + return "", err + } + + if !ref.Name().IsBranch() { + return "", fmt.Errorf("No branch found, found %s, please checkout a branch (git checkout )", ref.Name().String()) + } + + return ref.Name().Short(), nil +} + +func (g *GitUtils) GetLastVersion() (*semver.Version, string, error) { + + log.Debugf("GetLastVersion") + + tagObjects, err := g.Repository.TagObjects() + if err != nil { + return nil, "", err + } + + var tags []*semver.Version + + err = tagObjects.ForEach(func(t *object.Tag) error { + v, err := semver.NewVersion(t.Name) + + if err != nil { + log.Debugf("Tag %s is not a valid version, skip", t.Name) + } else { + log.Debugf("Add tag %s", t.Name) + tags = append(tags, v) + } + return nil + }) + + if err != nil { + return nil, "", err + } + + sort.Sort(sort.Reverse(semver.Collection(tags))) + + if len(tags) == 0 { + log.Debugf("Found no tags") + return nil, "", nil + } + + log.Debugf("Found old version %s", tags[0].String()) + + tag, err := g.Repository.Tag(tags[0].Original()) + if err != nil { + return nil, "", err + } + + tagObject, err := g.Repository.TagObject(tag.Hash()) + if err != nil { + return nil, "", err + } + + log.Debugf("Found old hash %s", tagObject.Target.String()) + return tags[0], tagObject.Target.String(), nil +} + +func (g *GitUtils) GetCommits(lastTagHash string) ([]Commit, error) { + + log.Printf("Read head") + ref, err := g.Repository.Head() + if err != nil { + return nil, err + } + + cIter, err := g.Repository.Log(&git.LogOptions{From: ref.Hash(), Order: git.LogOrderCommitterTime}) + if err != nil { + return nil, err + } + + var commits []Commit + var foundEnd bool + + err = cIter.ForEach(func(c *object.Commit) error { + if c.Hash.String() == lastTagHash { + log.Infof("%s == %s", c.Hash.String(), lastTagHash) + foundEnd = true + } + + if !foundEnd { + commit := Commit{ + Message: c.Message, + Author: c.Committer.Name, + Hash: c.Hash.String(), + } + commits = append(commits, commit) + } + return nil + }) + + return commits, nil +} diff --git a/internal/sdk/sdk.go b/internal/sdk/sdk.go new file mode 100644 index 0000000..e69de29 diff --git a/internal/storage/storage.go b/internal/storage/storage.go new file mode 100644 index 0000000..76787cc --- /dev/null +++ b/internal/storage/storage.go @@ -0,0 +1,42 @@ +package storage + +import ( + "io/ioutil" + + "gopkg.in/yaml.v2" +) + +// VersionFileContent struct +type VersionFileContent struct { + Version string `yaml:"version"` + NextVersion string `yaml:"nextVersion"` + Commit string `yaml:"commit"` + Branch string `yaml:"branch"` +} + +// Write version into .version +func Write(versionFileContent VersionFileContent) error { + data, err := yaml.Marshal(&versionFileContent) + if err != nil { + return err + } + + return ioutil.WriteFile(".version", data, 0644) +} + +// Read version into .version +func Read() (*VersionFileContent, error) { + + content, err := ioutil.ReadFile(".version") + if err != nil { + return &VersionFileContent{}, err + } + + var versionFileContent VersionFileContent + err = yaml.Unmarshal(content, &versionFileContent) + if err != nil { + return &VersionFileContent{}, err + } + + return &versionFileContent, nil +} diff --git a/pkg/semanticrelease/semantic-release.go b/pkg/semanticrelease/semantic-release.go new file mode 100644 index 0000000..5d97007 --- /dev/null +++ b/pkg/semanticrelease/semantic-release.go @@ -0,0 +1,114 @@ +package semanticrelease + +import ( + "fmt" + + "github.com/Masterminds/semver" + "github.com/Nightapes/go-semantic-release/internal/analyzer" + "github.com/Nightapes/go-semantic-release/internal/gitutil" + "github.com/Nightapes/go-semantic-release/internal/storage" + log "github.com/sirupsen/logrus" +) + +// GetNextVersion from .version or calculate new +func GetNextVersion(repro string) error { + util, err := gitutil.New(repro) + if err != nil { + return err + } + + hash, err := util.GetHash() + if err != nil { + return err + } + + content, err := storage.Read() + + if err == nil && content.Commit == hash { + fmt.Printf(content.NextVersion) + return nil + } + + log.Debugf("Mismatch git and version file %s - %s", content.Commit, hash) + + lastVersion, lastVersionHash, err := util.GetLastVersion() + if err != nil { + return err + } + + if lastVersion == nil { + defaultVersion, _ := semver.NewVersion("1.0.0") + SetVersion(defaultVersion.String(), repro) + fmt.Printf(defaultVersion.String()) + return nil + } + + commits, err := util.GetCommits(lastVersionHash) + if err != nil { + return err + } + + log.Debugf("Found %d commits till last release", len(commits)) + + a := analyzer.New("angular") + result := a.Analyze(commits) + + var newVersion semver.Version + + if len(result["major"]) > 0 { + newVersion = lastVersion.IncMajor() + return nil + } else if len(result["minor"]) > 0 { + newVersion = lastVersion.IncMinor() + } else if len(result["patch"]) > 0 { + newVersion = lastVersion.IncPatch() + } + + SetVersion(newVersion.String(), repro) + fmt.Printf(newVersion.String()) + + return err +} + +func SetVersion(version string, repro string) error { + + util, err := gitutil.New(repro) + if err != nil { + return err + } + + newVersion, err := semver.NewVersion(version) + if err != nil { + return err + } + + hash, err := util.GetHash() + if err != nil { + return err + } + + branch, err := util.GetBranch() + if err != nil { + return err + } + + newVersionContent := storage.VersionFileContent{ + Commit: hash, + NextVersion: newVersion.String(), + Branch: branch, + } + + lastVersion, _, err := util.GetLastVersion() + if err != nil { + return err + } + + if lastVersion != nil { + newVersionContent.Version = lastVersion.String() + } + + return storage.Write(newVersionContent) +} + +func Release() { +} diff --git a/release-config.json b/release-config.json new file mode 100644 index 0000000..cc4605a --- /dev/null +++ b/release-config.json @@ -0,0 +1,20 @@ +{ + "commitFormat": "angular", + "branch": { + "master": "release", + "rc-*": "rc", + "beta-*": "beta", + "alpha-*": "alpha", + "*": "none" + }, + "changelog": { + "print": "all/compact", + "template": "" + }, + "github": { + "url": "" + }, + "gitlab": { + "url": "" + } +} \ No newline at end of file