You've already forked go-semantic-release
Compare commits
8 Commits
1.0.0-alph
...
1.0.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d68d5835f | ||
|
|
7b4db67cb7 | ||
|
|
58f2ebb6d0 | ||
|
|
409b260eb7 | ||
|
|
791983faae | ||
|
|
69db52e5b1 | ||
|
|
ab14ab397c | ||
|
|
c86ad684c6 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@ go-semantic-release
|
||||
.version
|
||||
.vscode/settings.json
|
||||
CHANGELOG.md
|
||||
cover.html
|
||||
|
||||
3
go.mod
3
go.mod
@@ -10,12 +10,13 @@ require (
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/stretchr/testify v1.3.0
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
|
||||
google.golang.org/appengine v1.6.1 // indirect
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.1 // indirect
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.1
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
)
|
||||
|
||||
@@ -15,11 +15,17 @@ type Analyzer struct {
|
||||
Config config.ChangelogConfig
|
||||
}
|
||||
|
||||
//Release types, like major
|
||||
type Release string
|
||||
|
||||
//Scope of the commit, like feat, fix,..
|
||||
type Scope string
|
||||
|
||||
//Rule for commits
|
||||
type Rule struct {
|
||||
Tag string
|
||||
TagString string
|
||||
Release string
|
||||
Release Release
|
||||
Changelog bool
|
||||
}
|
||||
|
||||
@@ -32,7 +38,7 @@ type analyzeCommit interface {
|
||||
type AnalyzedCommit struct {
|
||||
Commit gitutil.Commit
|
||||
ParsedMessage string
|
||||
Scope string
|
||||
Scope Scope
|
||||
ParsedBreakingChangeMessage string
|
||||
Tag string
|
||||
TagString string
|
||||
@@ -62,9 +68,9 @@ func (a *Analyzer) GetRules() []Rule {
|
||||
}
|
||||
|
||||
// Analyze commits and return commits splitted by major,minor,patch
|
||||
func (a *Analyzer) Analyze(commits []gitutil.Commit) map[string][]AnalyzedCommit {
|
||||
func (a *Analyzer) Analyze(commits []gitutil.Commit) map[Release][]AnalyzedCommit {
|
||||
|
||||
analyzedCommits := make(map[string][]AnalyzedCommit)
|
||||
analyzedCommits := make(map[Release][]AnalyzedCommit)
|
||||
analyzedCommits["major"] = make([]AnalyzedCommit, 0)
|
||||
analyzedCommits["minor"] = make([]AnalyzedCommit, 0)
|
||||
analyzedCommits["patch"] = make([]AnalyzedCommit, 0)
|
||||
|
||||
16
internal/analyzer/analyzer_test.go
Normal file
16
internal/analyzer/analyzer_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package analyzer_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAnalyzer(t *testing.T) {
|
||||
|
||||
_, err := analyzer.New("unknown", config.ChangelogConfig{})
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
@@ -89,19 +89,19 @@ func (a *angular) analyze(commit gitutil.Commit, rule Rule) (AnalyzedCommit, boo
|
||||
if len(matches) >= 1 {
|
||||
if len(matches[0]) >= 3 {
|
||||
|
||||
analyzed.Scope = matches[0][2]
|
||||
analyzed.Scope = Scope(matches[0][2])
|
||||
|
||||
message := strings.Join(matches[0][3:], "")
|
||||
if !strings.Contains(message, "BREAKING CHANGE:") {
|
||||
analyzed.ParsedMessage = message
|
||||
analyzed.ParsedMessage = strings.Trim(message, " ")
|
||||
|
||||
log.Tracef("%s: found %s", commit.Message, rule.Tag)
|
||||
return analyzed, false, nil
|
||||
}
|
||||
breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2)
|
||||
|
||||
analyzed.ParsedMessage = breakingChange[0]
|
||||
analyzed.ParsedBreakingChangeMessage = breakingChange[1]
|
||||
analyzed.ParsedMessage = strings.Trim(breakingChange[0], " ")
|
||||
analyzed.ParsedBreakingChangeMessage = strings.Trim(breakingChange[1], " ")
|
||||
|
||||
log.Tracef(" %s, BREAKING CHANGE found", commit.Message)
|
||||
return analyzed, true, nil
|
||||
|
||||
173
internal/analyzer/angular_test.go
Normal file
173
internal/analyzer/angular_test.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package analyzer_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAngular(t *testing.T) {
|
||||
|
||||
testConfigs := []struct {
|
||||
testCase string
|
||||
commits []gitutil.Commit
|
||||
analyzedCommits map[analyzer.Release][]analyzer.AnalyzedCommit
|
||||
}{
|
||||
{
|
||||
testCase: "feat",
|
||||
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||
"minor": []analyzer.AnalyzedCommit{
|
||||
analyzer.AnalyzedCommit{
|
||||
Commit: gitutil.Commit{
|
||||
Message: "feat(internal/changelog): my first commit",
|
||||
Author: "me",
|
||||
Hash: "12345667",
|
||||
},
|
||||
Scope: "internal/changelog",
|
||||
ParsedMessage: "my first commit",
|
||||
Tag: "feat",
|
||||
TagString: "Features",
|
||||
Print: true,
|
||||
},
|
||||
},
|
||||
"major": []analyzer.AnalyzedCommit{},
|
||||
"patch": []analyzer.AnalyzedCommit{},
|
||||
"none": []analyzer.AnalyzedCommit{},
|
||||
},
|
||||
commits: []gitutil.Commit{
|
||||
gitutil.Commit{
|
||||
Message: "feat(internal/changelog): my first commit",
|
||||
Author: "me",
|
||||
Hash: "12345667",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testCase: "feat breaking change",
|
||||
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||
"minor": []analyzer.AnalyzedCommit{
|
||||
analyzer.AnalyzedCommit{
|
||||
Commit: gitutil.Commit{
|
||||
Message: "feat(internal/changelog): my first commit",
|
||||
Author: "me",
|
||||
Hash: "12345667",
|
||||
},
|
||||
Scope: "internal/changelog",
|
||||
ParsedMessage: "my first commit",
|
||||
Tag: "feat",
|
||||
TagString: "Features",
|
||||
Print: true,
|
||||
},
|
||||
},
|
||||
"major": []analyzer.AnalyzedCommit{
|
||||
analyzer.AnalyzedCommit{
|
||||
Commit: gitutil.Commit{
|
||||
Message: "feat(internal/changelog): my first break BREAKING CHANGE: change api to v2",
|
||||
Author: "me",
|
||||
Hash: "12345668",
|
||||
},
|
||||
Scope: "internal/changelog",
|
||||
ParsedMessage: "my first break",
|
||||
Tag: "feat",
|
||||
TagString: "Features",
|
||||
Print: true,
|
||||
ParsedBreakingChangeMessage: "change api to v2",
|
||||
},
|
||||
},
|
||||
"patch": []analyzer.AnalyzedCommit{},
|
||||
"none": []analyzer.AnalyzedCommit{},
|
||||
},
|
||||
commits: []gitutil.Commit{
|
||||
gitutil.Commit{
|
||||
Message: "feat(internal/changelog): my first commit",
|
||||
Author: "me",
|
||||
Hash: "12345667",
|
||||
},
|
||||
gitutil.Commit{
|
||||
Message: "feat(internal/changelog): my first break BREAKING CHANGE: change api to v2",
|
||||
Author: "me",
|
||||
Hash: "12345668",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testCase: "invalid",
|
||||
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||
"minor": []analyzer.AnalyzedCommit{},
|
||||
"major": []analyzer.AnalyzedCommit{},
|
||||
"patch": []analyzer.AnalyzedCommit{},
|
||||
"none": []analyzer.AnalyzedCommit{},
|
||||
},
|
||||
commits: []gitutil.Commit{
|
||||
gitutil.Commit{
|
||||
Message: "internal/changelog: my first commit",
|
||||
Author: "me",
|
||||
Hash: "12345667",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testCase: "feat and build",
|
||||
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||
"minor": []analyzer.AnalyzedCommit{
|
||||
analyzer.AnalyzedCommit{
|
||||
Commit: gitutil.Commit{
|
||||
Message: "feat(internal/changelog): my first commit",
|
||||
Author: "me",
|
||||
Hash: "12345667",
|
||||
},
|
||||
Scope: "internal/changelog",
|
||||
ParsedMessage: "my first commit",
|
||||
Tag: "feat",
|
||||
TagString: "Features",
|
||||
Print: true,
|
||||
},
|
||||
},
|
||||
"none": []analyzer.AnalyzedCommit{
|
||||
analyzer.AnalyzedCommit{
|
||||
Commit: gitutil.Commit{
|
||||
Message: "build(internal/changelog): my first build",
|
||||
Author: "me",
|
||||
Hash: "12345668",
|
||||
},
|
||||
Scope: "internal/changelog",
|
||||
ParsedMessage: "my first build",
|
||||
Tag: "build",
|
||||
TagString: "Changes to CI/CD",
|
||||
Print: false,
|
||||
ParsedBreakingChangeMessage: "",
|
||||
},
|
||||
},
|
||||
"patch": []analyzer.AnalyzedCommit{},
|
||||
"major": []analyzer.AnalyzedCommit{},
|
||||
},
|
||||
commits: []gitutil.Commit{
|
||||
gitutil.Commit{
|
||||
Message: "feat(internal/changelog): my first commit",
|
||||
Author: "me",
|
||||
Hash: "12345667",
|
||||
},
|
||||
gitutil.Commit{
|
||||
Message: "build(internal/changelog): my first build",
|
||||
Author: "me",
|
||||
Hash: "12345668",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
angular, err := analyzer.New("angular", config.ChangelogConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, test := range testConfigs {
|
||||
analyzedCommits := angular.Analyze(test.commits)
|
||||
assert.Equalf(t, test.analyzedCommits["major"], analyzedCommits["major"], "Testcase %s should have major commits", test.testCase)
|
||||
assert.Equalf(t, test.analyzedCommits["minor"], analyzedCommits["minor"], "Testcase %s should have minor commits", test.testCase)
|
||||
assert.Equalf(t, test.analyzedCommits["patch"], analyzedCommits["patch"], "Testcase %s should have patch commits", test.testCase)
|
||||
assert.Equalf(t, test.analyzedCommits["none"], analyzedCommits["none"], "Testcase %s should have none commits", test.testCase)
|
||||
}
|
||||
|
||||
}
|
||||
71
internal/cache/cache_test.go
vendored
Normal file
71
internal/cache/cache_test.go
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package cache_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/internal/cache"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
func TestReadCacheNotFound(t *testing.T) {
|
||||
|
||||
_, err := cache.Read("notfound/dir")
|
||||
assert.Errorf(t, err, "Read non exsiting file")
|
||||
|
||||
}
|
||||
|
||||
func TestReadCacheInvalidContent(t *testing.T) {
|
||||
|
||||
dir, err := ioutil.TempDir("", "prefix")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
completePath := path.Join(path.Dir(dir), ".version")
|
||||
brokenContent := []byte("hello broken\ngo: lang\n")
|
||||
err = ioutil.WriteFile(completePath, brokenContent, 0644)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, readError := cache.Read(dir)
|
||||
assert.Errorf(t, readError, "Should give error, when broken content")
|
||||
|
||||
}
|
||||
|
||||
func TestWriteAndReadCache(t *testing.T) {
|
||||
|
||||
dir, err := ioutil.TempDir("", "prefix")
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
content := cache.ReleaseVersion{
|
||||
Last: cache.ReleaseVersionEntry{
|
||||
Commit: "12345",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
Next: cache.ReleaseVersionEntry{
|
||||
Commit: "12346",
|
||||
Version: "1.1.0",
|
||||
},
|
||||
Branch: "master",
|
||||
Draft: true,
|
||||
}
|
||||
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
writeError := cache.Write(dir, content)
|
||||
assert.NoErrorf(t, writeError, "Should write file")
|
||||
result, readError := cache.Read(dir)
|
||||
assert.NoErrorf(t, readError, "Should read file")
|
||||
|
||||
assert.Equal(t, &content, result)
|
||||
|
||||
}
|
||||
|
||||
func TestWriteNotFound(t *testing.T) {
|
||||
|
||||
err := cache.Write("notfound/dir", cache.ReleaseVersion{})
|
||||
assert.Errorf(t, err, "Write non exsiting file")
|
||||
|
||||
}
|
||||
@@ -15,6 +15,14 @@ import (
|
||||
|
||||
const defaultChangelogTitle string = `v{{.Version}} ({{.Now.Format "2006-01-02"}})`
|
||||
const defaultChangelog string = `# v{{$.Version}} ({{.Now.Format "2006-01-02"}})
|
||||
{{ range $index,$commit := .BreakingChanges -}}
|
||||
{{ if eq $index 0 }}
|
||||
## BREAKING CHANGES
|
||||
{{ end}}
|
||||
* **{{$.Backtick}}{{$commit.Scope}}{{$.Backtick}}** {{$commit.ParsedBreakingChangeMessage}}
|
||||
introduced by commit:
|
||||
{{$commit.ParsedMessage}} {{if $.HasURL}} ([{{ printf "%.7s" $commit.Commit.Hash}}]({{ replace $.URL "{{hash}}" $commit.Commit.Hash}})) {{end}}
|
||||
{{ end -}}
|
||||
{{ range $key := .Order }}
|
||||
{{ $commits := index $.Commits $key}} {{if $commits -}}
|
||||
### {{ $key }}
|
||||
@@ -26,33 +34,37 @@ const defaultChangelog string = `# v{{$.Version}} ({{.Now.Format "2006-01-02"}})
|
||||
`
|
||||
|
||||
type changelogContent struct {
|
||||
Commits map[string][]analyzer.AnalyzedCommit
|
||||
Order []string
|
||||
Version string
|
||||
Now time.Time
|
||||
Backtick string
|
||||
HasURL bool
|
||||
URL string
|
||||
Commits map[string][]analyzer.AnalyzedCommit
|
||||
BreakingChanges []analyzer.AnalyzedCommit
|
||||
Order []string
|
||||
Version string
|
||||
Now time.Time
|
||||
Backtick string
|
||||
HasURL bool
|
||||
URL string
|
||||
}
|
||||
|
||||
//Changelog struct
|
||||
type Changelog struct {
|
||||
config *config.ReleaseConfig
|
||||
rules []analyzer.Rule
|
||||
config *config.ReleaseConfig
|
||||
rules []analyzer.Rule
|
||||
releaseTime time.Time
|
||||
}
|
||||
|
||||
//New Changelog struct for generating changelog from commits
|
||||
func New(config *config.ReleaseConfig, rules []analyzer.Rule) *Changelog {
|
||||
func New(config *config.ReleaseConfig, rules []analyzer.Rule, releaseTime time.Time) *Changelog {
|
||||
return &Changelog{
|
||||
config: config,
|
||||
rules: rules,
|
||||
config: config,
|
||||
rules: rules,
|
||||
releaseTime: releaseTime,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateChanglog from given commits
|
||||
func (c *Changelog) GenerateChanglog(templateConfig shared.ChangelogTemplateConfig, analyzedCommits map[string][]analyzer.AnalyzedCommit) (*shared.GeneratedChangelog, error) {
|
||||
func (c *Changelog) GenerateChanglog(templateConfig shared.ChangelogTemplateConfig, analyzedCommits map[analyzer.Release][]analyzer.AnalyzedCommit) (*shared.GeneratedChangelog, error) {
|
||||
|
||||
commitsPerScope := map[string][]analyzer.AnalyzedCommit{}
|
||||
commitsBreakingChange := []analyzer.AnalyzedCommit{}
|
||||
order := make([]string, 0)
|
||||
|
||||
for _, rule := range c.rules {
|
||||
@@ -65,6 +77,10 @@ func (c *Changelog) GenerateChanglog(templateConfig shared.ChangelogTemplateConf
|
||||
for _, commits := range analyzedCommits {
|
||||
for _, commit := range commits {
|
||||
if commit.Print {
|
||||
if commit.ParsedBreakingChangeMessage != "" {
|
||||
commitsBreakingChange = append(commitsBreakingChange, commit)
|
||||
continue
|
||||
}
|
||||
if _, ok := commitsPerScope[commit.TagString]; !ok {
|
||||
commitsPerScope[commit.TagString] = make([]analyzer.AnalyzedCommit, 0)
|
||||
}
|
||||
@@ -74,13 +90,14 @@ func (c *Changelog) GenerateChanglog(templateConfig shared.ChangelogTemplateConf
|
||||
}
|
||||
|
||||
changelogContent := changelogContent{
|
||||
Version: templateConfig.Version,
|
||||
Commits: commitsPerScope,
|
||||
Now: time.Now(),
|
||||
Backtick: "`",
|
||||
Order: order,
|
||||
HasURL: templateConfig.CommitURL != "",
|
||||
URL: templateConfig.CommitURL,
|
||||
Version: templateConfig.Version,
|
||||
Commits: commitsPerScope,
|
||||
Now: c.releaseTime,
|
||||
BreakingChanges: commitsBreakingChange,
|
||||
Backtick: "`",
|
||||
Order: order,
|
||||
HasURL: templateConfig.CommitURL != "",
|
||||
URL: templateConfig.CommitURL,
|
||||
}
|
||||
|
||||
title, err := generateTemplate(defaultChangelogTitle, changelogContent)
|
||||
|
||||
120
internal/changelog/changelog_test.go
Normal file
120
internal/changelog/changelog_test.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package changelog_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||
"github.com/Nightapes/go-semantic-release/internal/changelog"
|
||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestChangelog(t *testing.T) {
|
||||
|
||||
templateConfig := shared.ChangelogTemplateConfig{
|
||||
CommitURL: "https://commit.url",
|
||||
CompareURL: "https://compare.url",
|
||||
Hash: "hash",
|
||||
Version: "1.0.0",
|
||||
}
|
||||
|
||||
testConfigs := []struct {
|
||||
testCase string
|
||||
analyzedCommits map[analyzer.Release][]analyzer.AnalyzedCommit
|
||||
result *shared.GeneratedChangelog
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
testCase: "feat",
|
||||
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||
"minor": []analyzer.AnalyzedCommit{
|
||||
analyzer.AnalyzedCommit{
|
||||
Commit: gitutil.Commit{
|
||||
Message: "feat(test): my first commit",
|
||||
Author: "me",
|
||||
Hash: "12345667",
|
||||
},
|
||||
Scope: "internal/changelog",
|
||||
ParsedMessage: "my first commit",
|
||||
Tag: "feat",
|
||||
TagString: "Features",
|
||||
Print: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &shared.GeneratedChangelog{
|
||||
Title: "v1.0.0 (2019-07-19)",
|
||||
Content: "# v1.0.0 (2019-07-19)\n\n ### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url)) \n\n ",
|
||||
},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
testCase: "feat breaking change",
|
||||
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||
"minor": []analyzer.AnalyzedCommit{
|
||||
analyzer.AnalyzedCommit{
|
||||
Commit: gitutil.Commit{
|
||||
Message: "feat(test): my first commit",
|
||||
Author: "me",
|
||||
Hash: "12345667",
|
||||
},
|
||||
Scope: "internal/changelog",
|
||||
ParsedMessage: "my first commit",
|
||||
Tag: "feat",
|
||||
TagString: "Features",
|
||||
Print: true,
|
||||
},
|
||||
analyzer.AnalyzedCommit{
|
||||
Commit: gitutil.Commit{
|
||||
Message: "feat(test): my first break: BREAKING CHANGE: change api to v2",
|
||||
Author: "me",
|
||||
Hash: "12345668",
|
||||
},
|
||||
Scope: "internal/changelog",
|
||||
ParsedMessage: "my first break",
|
||||
Tag: "feat",
|
||||
TagString: "Features",
|
||||
Print: true,
|
||||
ParsedBreakingChangeMessage: "change api to v2",
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &shared.GeneratedChangelog{
|
||||
Title: "v1.0.0 (2019-07-19)",
|
||||
Content: "# v1.0.0 (2019-07-19)\n\n## BREAKING CHANGES\n\n* **`internal/changelog`** change api to v2 \nintroduced by commit: \nmy first break ([1234566](https://commit.url)) \n\n ### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url)) \n\n ",
|
||||
},
|
||||
hasError: false,
|
||||
},
|
||||
}
|
||||
|
||||
cl := changelog.New(&config.ReleaseConfig{}, []analyzer.Rule{
|
||||
{
|
||||
Tag: "feat",
|
||||
TagString: "Features",
|
||||
Release: "minor",
|
||||
Changelog: true,
|
||||
},
|
||||
{
|
||||
Tag: "fix",
|
||||
TagString: "Bug fixes",
|
||||
Release: "patch",
|
||||
Changelog: true,
|
||||
},
|
||||
{
|
||||
Tag: "build",
|
||||
TagString: "Build",
|
||||
Release: "none",
|
||||
Changelog: false,
|
||||
},
|
||||
}, time.Date(2019, 7, 19, 0, 0, 0, 0, time.UTC))
|
||||
|
||||
for _, config := range testConfigs {
|
||||
generatedChangelog, err := cl.GenerateChanglog(templateConfig, config.analyzedCommits)
|
||||
assert.Equalf(t, config.hasError, err != nil, "Testcase %s should have error: %t -> %s", config.testCase, config.hasError, err)
|
||||
assert.Equalf(t, config.result, generatedChangelog, "Testcase %s should have generated changelog", config.testCase)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,8 +2,11 @@ package ci
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//ProviderConfig struct
|
||||
@@ -21,11 +24,21 @@ type ProviderConfig struct {
|
||||
|
||||
//Service interface
|
||||
type Service interface {
|
||||
Detect() (*ProviderConfig, error)
|
||||
detect(envs map[string]string) (*ProviderConfig, error)
|
||||
}
|
||||
|
||||
//ReadAllEnvs as a map
|
||||
func ReadAllEnvs() map[string]string {
|
||||
envs := map[string]string{}
|
||||
for _, pair := range os.Environ() {
|
||||
splitted := strings.SplitN(pair, "=", 2)
|
||||
envs[splitted[0]] = splitted[1]
|
||||
}
|
||||
return envs
|
||||
}
|
||||
|
||||
//GetCIProvider get provider
|
||||
func GetCIProvider(gitUtil *gitutil.GitUtil) (*ProviderConfig, error) {
|
||||
func GetCIProvider(gitUtil *gitutil.GitUtil, envs map[string]string) (*ProviderConfig, error) {
|
||||
|
||||
services := []Service{
|
||||
Travis{},
|
||||
@@ -33,12 +46,12 @@ func GetCIProvider(gitUtil *gitutil.GitUtil) (*ProviderConfig, error) {
|
||||
}
|
||||
|
||||
for _, service := range services {
|
||||
config, err := service.Detect()
|
||||
config, err := service.detect(envs)
|
||||
if err == nil {
|
||||
log.Infof("Found CI: %s", config.Name)
|
||||
return config, nil
|
||||
}
|
||||
log.Debugf("%s", err.Error())
|
||||
log.Infof("%s", err.Error())
|
||||
}
|
||||
return nil, fmt.Errorf("could not find any CI, if running locally set env CI=true")
|
||||
}
|
||||
|
||||
100
internal/ci/ci_test.go
Normal file
100
internal/ci/ci_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package ci_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/internal/ci"
|
||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/src-d/go-billy.v4/memfs"
|
||||
"gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
"gopkg.in/src-d/go-git.v4/storage/memory"
|
||||
)
|
||||
|
||||
func TestCi(t *testing.T) {
|
||||
|
||||
fs := memfs.New()
|
||||
|
||||
repository, err := git.Init(memory.NewStorage(), fs)
|
||||
assert.NoError(t, err, "should open git repository")
|
||||
|
||||
file, err := fs.Create("README.md")
|
||||
assert.NoError(t, err, "should create file")
|
||||
|
||||
w, err := repository.Worktree()
|
||||
assert.NoError(t, err, "should get worktree")
|
||||
|
||||
_, err = w.Add(file.Name())
|
||||
assert.NoError(t, err, "should add file")
|
||||
|
||||
gitUtilInMemory := &gitutil.GitUtil{
|
||||
Repository: repository,
|
||||
}
|
||||
|
||||
newCommit, err := w.Commit("fix(test): add a commit", &git.CommitOptions{
|
||||
Author: &object.Signature{
|
||||
Name: "John Doe",
|
||||
Email: "john@doe.org",
|
||||
When: time.Now(),
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err, "should commit")
|
||||
|
||||
testConfigs := []struct {
|
||||
service string
|
||||
envs map[string]string
|
||||
result *ci.ProviderConfig
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
service: "none",
|
||||
envs: map[string]string{},
|
||||
result: nil,
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
service: "Git",
|
||||
envs: map[string]string{
|
||||
"CI": "true",
|
||||
},
|
||||
result: &ci.ProviderConfig{IsPR: false, PR: "", PRBranch: "", Branch: "master", Tag: "", Commit: newCommit.String(), BuildURL: "", Service: "git", Name: "Git only"},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
service: "Travis PR",
|
||||
envs: map[string]string{
|
||||
"TRAVIS": "true",
|
||||
"TRAVIS_PULL_REQUEST": "10",
|
||||
"TRAVIS_COMMIT": "190bfd6aa60022afd0ef830342cfb07e33c45f37",
|
||||
"TRAVIS_TAG": "TAG",
|
||||
"TRAVIS_BUILD_WEB_URL": "https://travis-ci.com/owner/repo/builds/1234",
|
||||
"TRAVIS_BRANCH": "master",
|
||||
"TRAVIS_PULL_REQUEST_BRANCH": "pr",
|
||||
},
|
||||
result: &ci.ProviderConfig{IsPR: true, PR: "10", PRBranch: "pr", Branch: "master", Tag: "TAG", Commit: "190bfd6aa60022afd0ef830342cfb07e33c45f37", BuildURL: "https://travis-ci.com/owner/repo/builds/1234", Service: "travis", Name: "Travis CI"},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
service: "Travis Push",
|
||||
envs: map[string]string{
|
||||
"TRAVIS": "true",
|
||||
"TRAVIS_PULL_REQUEST": "false",
|
||||
"TRAVIS_COMMIT": "190bfd6aa60022afd0ef830342cfb07e33c45f37",
|
||||
"TRAVIS_TAG": "TAG",
|
||||
"TRAVIS_BUILD_WEB_URL": "https://travis-ci.com/owner/repo/builds/1234",
|
||||
"TRAVIS_BRANCH": "master",
|
||||
},
|
||||
result: &ci.ProviderConfig{IsPR: false, PR: "", PRBranch: "", Branch: "master", Tag: "TAG", Commit: "190bfd6aa60022afd0ef830342cfb07e33c45f37", BuildURL: "https://travis-ci.com/owner/repo/builds/1234", Service: "travis", Name: "Travis CI"},
|
||||
hasError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, config := range testConfigs {
|
||||
provider, err := ci.GetCIProvider(gitUtilInMemory, config.envs)
|
||||
assert.Equalf(t, config.hasError, err != nil, "Service %s should have error: %t -> %s", config.service, config.hasError, err)
|
||||
assert.Equalf(t, config.result, provider, "Service %s should have provider", config.service)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package ci
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
//Git struct
|
||||
@@ -12,9 +11,9 @@ type Git struct {
|
||||
}
|
||||
|
||||
//Detect if on Git
|
||||
func (t Git) Detect() (*ProviderConfig, error) {
|
||||
func (t Git) detect(envs map[string]string) (*ProviderConfig, error) {
|
||||
|
||||
if _, exists := os.LookupEnv("CI"); !exists {
|
||||
if _, exists := envs["CI"]; !exists {
|
||||
return nil, fmt.Errorf("running not git only")
|
||||
}
|
||||
|
||||
@@ -29,7 +28,7 @@ func (t Git) Detect() (*ProviderConfig, error) {
|
||||
}
|
||||
|
||||
return &ProviderConfig{
|
||||
Service: "Git",
|
||||
Service: "git",
|
||||
Name: "Git only",
|
||||
Commit: hash,
|
||||
Branch: currentBranch,
|
||||
|
||||
@@ -3,37 +3,39 @@ package ci
|
||||
import (
|
||||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"os"
|
||||
)
|
||||
|
||||
//Travis struct
|
||||
type Travis struct{}
|
||||
|
||||
//Detect if on travis
|
||||
func (t Travis) Detect() (*ProviderConfig, error) {
|
||||
func (t Travis) detect(envs map[string]string) (*ProviderConfig, error) {
|
||||
|
||||
if _, exists := os.LookupEnv("TRAVIS"); !exists {
|
||||
if _, exists := envs["TRAVIS"]; !exists {
|
||||
return nil, fmt.Errorf("not running on travis")
|
||||
}
|
||||
|
||||
isPR := false
|
||||
|
||||
value := os.Getenv("TRAVIS_PULL_REQUEST")
|
||||
value := envs["TRAVIS_PULL_REQUEST"]
|
||||
pr := ""
|
||||
|
||||
if value == "false" {
|
||||
log.Debugf("TRAVIS_PULL_REQUEST=%s, not running on pr", value)
|
||||
} else {
|
||||
isPR = true
|
||||
pr = value
|
||||
}
|
||||
|
||||
return &ProviderConfig{
|
||||
Service: "travis",
|
||||
Name: "Travis CI",
|
||||
Commit: os.Getenv("TRAVIS_COMMIT"),
|
||||
Tag: os.Getenv("TRAVIS_TAG"),
|
||||
BuildURL: os.Getenv("TRAVIS_BUILD_WEB_URL"),
|
||||
Branch: os.Getenv("TRAVIS_BRANCH"),
|
||||
Commit: envs["TRAVIS_COMMIT"],
|
||||
Tag: envs["TRAVIS_TAG"],
|
||||
BuildURL: envs["TRAVIS_BUILD_WEB_URL"],
|
||||
Branch: envs["TRAVIS_BRANCH"],
|
||||
IsPR: isPR,
|
||||
PRBranch: os.Getenv("TRAVIS_PULL_REQUEST_BRANCH"),
|
||||
PR: pr,
|
||||
PRBranch: envs["TRAVIS_PULL_REQUEST_BRANCH"],
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -93,15 +93,10 @@ func (g *GitUtil) GetLastVersion() (*semver.Version, string, error) {
|
||||
|
||||
err = gitTags.ForEach(func(p *plumbing.Reference) error {
|
||||
v, err := semver.NewVersion(p.Name().Short())
|
||||
log.Tracef("%+v", p.Name().Short())
|
||||
log.Tracef("%+v with hash: %s", p.Target(), p.Hash())
|
||||
|
||||
if err == nil {
|
||||
_, err := g.Repository.TagObject(p.Hash())
|
||||
if err == nil {
|
||||
log.Debugf("Add tag %s", p.Name().Short())
|
||||
tags = append(tags, v)
|
||||
} else {
|
||||
log.Debugf("Found tag, but is not annotated, skip")
|
||||
}
|
||||
tags = append(tags, v)
|
||||
} else {
|
||||
log.Debugf("Tag %s is not a valid version, skip", p.Name().Short())
|
||||
}
|
||||
@@ -126,13 +121,8 @@ func (g *GitUtil) GetLastVersion() (*semver.Version, string, error) {
|
||||
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
|
||||
log.Debugf("Found old hash %s", tag.Hash().String())
|
||||
return tags[0], tag.Hash().String(), nil
|
||||
}
|
||||
|
||||
// GetCommits from git hash to HEAD
|
||||
@@ -155,9 +145,10 @@ func (g *GitUtil) GetCommits(lastTagHash string) ([]Commit, error) {
|
||||
if c.Hash.String() == lastTagHash {
|
||||
log.Debugf("Found commit with hash %s, will stop here", c.Hash.String())
|
||||
foundEnd = true
|
||||
|
||||
}
|
||||
log.Tracef("Found commit with hash %s", c.Hash.String())
|
||||
if !foundEnd {
|
||||
log.Tracef("Found commit with hash %s", c.Hash.String())
|
||||
commit := Commit{
|
||||
Message: c.Message,
|
||||
Author: c.Committer.Name,
|
||||
|
||||
@@ -35,6 +35,8 @@ func GetAccessToken(providerName string) (string, error) {
|
||||
|
||||
if token, exists = os.LookupEnv(envName); !exists {
|
||||
return "", fmt.Errorf("could not find %s in the enviroment variables. Please check if it is set", envName)
|
||||
} else if token == "" {
|
||||
return "", fmt.Errorf("token %s is set in environment variables but is empty", envName)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
43
internal/releaser/util/util_test.go
Normal file
43
internal/releaser/util/util_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package util_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/internal/releaser/util"
|
||||
)
|
||||
|
||||
func TestCreateBearerHTTPClient(t *testing.T) {
|
||||
client := util.CreateBearerHTTPClient(context.Background(), "")
|
||||
|
||||
assert.True(t, client != nil, "Client is empty")
|
||||
}
|
||||
|
||||
type testDoubleToken struct {
|
||||
providerName, token string
|
||||
valid bool
|
||||
}
|
||||
|
||||
var testDoubles = []testDoubleToken{
|
||||
testDoubleToken{providerName: "test0", token: "foo", valid: true},
|
||||
testDoubleToken{providerName: "test1", token: "", valid: false},
|
||||
}
|
||||
|
||||
func TestGetAccessToken(t *testing.T) {
|
||||
for _, testObject := range testDoubles {
|
||||
envName := fmt.Sprintf("%s_ACCESS_TOKEN", strings.ToUpper(testObject.providerName))
|
||||
if err := os.Setenv(envName, testObject.token); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
_, err := util.GetAccessToken(testObject.providerName)
|
||||
|
||||
assert.Equal(t, testObject.valid, err == nil)
|
||||
os.Unsetenv(envName)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||
@@ -54,7 +55,7 @@ func New(c *config.ReleaseConfig, repository string) (*SemanticRelease, error) {
|
||||
|
||||
// GetNextVersion from .version or calculate new from commits
|
||||
func (s *SemanticRelease) GetNextVersion(force bool) (*shared.ReleaseVersion, error) {
|
||||
provider, err := ci.GetCIProvider(s.gitutil)
|
||||
provider, err := ci.GetCIProvider(s.gitutil, ci.ReadAllEnvs())
|
||||
|
||||
if err != nil {
|
||||
fakeVersion, _ := semver.NewVersion("0.0.0-fake.0")
|
||||
@@ -161,7 +162,7 @@ func (s *SemanticRelease) SetVersion(version string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
provider, err := ci.GetCIProvider(s.gitutil)
|
||||
provider, err := ci.GetCIProvider(s.gitutil, ci.ReadAllEnvs())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("will not set version. Could not find CI Provider, if running locally, set env CI=true")
|
||||
@@ -199,7 +200,7 @@ func (s *SemanticRelease) GetChangelog(releaseVersion *shared.ReleaseVersion) (*
|
||||
|
||||
log.Debugf("Found %d commits till last release", len(commits))
|
||||
|
||||
c := changelog.New(s.config, s.analyzer.GetRules())
|
||||
c := changelog.New(s.config, s.analyzer.GetRules(), time.Now())
|
||||
return c.GenerateChanglog(shared.ChangelogTemplateConfig{
|
||||
Version: releaseVersion.Next.Version.String(),
|
||||
Hash: releaseVersion.Last.Commit,
|
||||
@@ -217,7 +218,7 @@ func (s *SemanticRelease) WriteChangeLog(changelogContent, file string) error {
|
||||
// Release pusblish release to provider
|
||||
func (s *SemanticRelease) Release(force bool) error {
|
||||
|
||||
provider, err := ci.GetCIProvider(s.gitutil)
|
||||
provider, err := ci.GetCIProvider(s.gitutil, ci.ReadAllEnvs())
|
||||
|
||||
if err != nil {
|
||||
log.Debugf("Will not perform a new release. Could not find CI Provider")
|
||||
|
||||
Reference in New Issue
Block a user