diff --git a/cmd/git-chglog/config.go b/cmd/git-chglog/config.go index 83f1a9ed..a81340ee 100644 --- a/cmd/git-chglog/config.go +++ b/cmd/git-chglog/config.go @@ -108,6 +108,10 @@ func (config *Config) normalizeStyle() { switch config.Style { case "github": config.normalizeStyleOfGitHub() + case "gitlab": + config.normalizeStyleOfGitLab() + case "bitbucket": + config.normalizeStyleOfBitbucket() } } @@ -184,6 +188,61 @@ func (config *Config) normalizeStyleOfGitLab() { config.Options = opts } +// For Bitbucket +func (config *Config) normalizeStyleOfBitbucket() { + opts := config.Options + + if len(opts.Issues.Prefix) == 0 { + opts.Issues.Prefix = []string{ + "#", + } + } + + if len(opts.Refs.Actions) == 0 { + opts.Refs.Actions = []string{ + "close", + "closes", + "closed", + "closing", + "fix", + "fixed", + "fixes", + "fixing", + "resolve", + "resolves", + "resolved", + "resolving", + "eopen", + "reopens", + "reopening", + "hold", + "holds", + "holding", + "wontfix", + "invalidate", + "invalidates", + "invalidated", + "invalidating", + "addresses", + "re", + "references", + "ref", + "refs", + "see", + } + } + + if opts.Merges.Pattern == "" && len(opts.Merges.PatternMaps) == 0 { + opts.Merges.Pattern = "^Merged in (.*) \\(pull request #(\\d+)\\)$" + opts.Merges.PatternMaps = []string{ + "Source", + "Ref", + } + } + + config.Options = opts +} + // Convert ... func (config *Config) Convert(ctx *CLIContext) *chglog.Config { info := config.Info diff --git a/cmd/git-chglog/custom_template_builder.go b/cmd/git-chglog/custom_template_builder.go index 48487cff..eba8aa11 100644 --- a/cmd/git-chglog/custom_template_builder.go +++ b/cmd/git-chglog/custom_template_builder.go @@ -51,6 +51,9 @@ func (*customTemplateBuilderImpl) versionHeader(style, template string) string { case styleGitHub, styleGitLab: tpl = "\n" tagName = "{{if .Tag.Previous}}[{{.Tag.Name}}]({{$.Info.RepositoryURL}}/compare/{{.Tag.Previous.Name}}...{{.Tag.Name}}){{else}}{{.Tag.Name}}{{end}}" + case styleBitbucket: + tpl = "\n" + tagName = "{{if .Tag.Previous}}[{{.Tag.Name}}]({{$.Info.RepositoryURL}}/compare/{{.Tag.Name}}..{{.Tag.Previous.Name}}){{else}}{{.Tag.Name}}{{end}}" } // format @@ -113,7 +116,7 @@ func (t *customTemplateBuilderImpl) merges(style string) string { var title string switch style { - case styleGitHub: + case styleGitHub, styleBitbucket: title = "Pull Requests" case styleGitLab: title = "Merge Requests" diff --git a/cmd/git-chglog/kac_template_builder.go b/cmd/git-chglog/kac_template_builder.go index d929063a..5c0e816e 100644 --- a/cmd/git-chglog/kac_template_builder.go +++ b/cmd/git-chglog/kac_template_builder.go @@ -47,7 +47,7 @@ func (t *kacTemplateBuilderImpl) versionHeader(style string) string { ) switch style { - case styleGitHub, styleGitLab: + case styleGitHub, styleGitLab, styleBitbucket: tagName = "{{if .Tag.Previous}}[{{.Tag.Name}}]{{else}}{{.Tag.Name}}{{end}}" } @@ -85,7 +85,7 @@ func (t *kacTemplateBuilderImpl) merges(style string) string { var title string switch style { - case styleGitHub: + case styleGitHub, styleBitbucket: title = "Pull Requests" case styleGitLab: title = "Merge Requests" @@ -114,6 +114,11 @@ func (*kacTemplateBuilderImpl) footer(style string) string { return `{{if .Versions}} [Unreleased]: {{.Info.RepositoryURL}}/compare/{{$latest := index .Versions 0}}{{$latest.Tag.Name}}...HEAD{{range .Versions}}{{if .Tag.Previous}} [{{.Tag.Name}}]: {{$.Info.RepositoryURL}}/compare/{{.Tag.Previous.Name}}...{{.Tag.Name}}{{end}}{{end}} +{{end}}` + case styleBitbucket: + return `{{if .Versions}} +[Unreleased]: {{.Info.RepositoryURL}}/compare/HEAD..{{$latest := index .Versions 0}}{{$latest.Tag.Name}}{{range .Versions}}{{if .Tag.Previous}} +[{{.Tag.Name}}]: {{$.Info.RepositoryURL}}/compare/{{.Tag.Name}}..{{.Tag.Previous.Name}}{{end}}{{end}} {{end}}` default: return "" diff --git a/cmd/git-chglog/processor_factory.go b/cmd/git-chglog/processor_factory.go index 7122c4a3..2d8fb32e 100644 --- a/cmd/git-chglog/processor_factory.go +++ b/cmd/git-chglog/processor_factory.go @@ -16,8 +16,9 @@ type ProcessorFactory struct { func NewProcessorFactory() *ProcessorFactory { return &ProcessorFactory{ hostRegistry: map[string]string{ - "github": "github.com", - "gitlab": "gitlab.com", + "github": "github.com", + "gitlab": "gitlab.com", + "bitbucket": "bitbucket.org", }, } } @@ -46,6 +47,10 @@ func (factory *ProcessorFactory) Create(config *Config) (chglog.Processor, error return &chglog.GitLabProcessor{ Host: fmt.Sprintf("%s://%s", obj.Scheme, obj.Host), }, nil + case "bitbucket.org": + return &chglog.BitbucketProcessor{ + Host: fmt.Sprintf("%s://%s", obj.Scheme, obj.Host), + }, nil default: return nil, nil } diff --git a/cmd/git-chglog/processor_factory_test.go b/cmd/git-chglog/processor_factory_test.go index 32fd1234..f9d5c5bc 100644 --- a/cmd/git-chglog/processor_factory_test.go +++ b/cmd/git-chglog/processor_factory_test.go @@ -92,3 +92,39 @@ func TestProcessorFactoryForGitLab(t *testing.T) { processor, ) } + +func TestProcessorFactoryForBitbucket(t *testing.T) { + assert := assert.New(t) + factory := NewProcessorFactory() + + // bitbucket.org + processor, err := factory.Create(&Config{ + Info: Info{ + RepositoryURL: "https://bitbucket.org/owner/repo", + }, + }) + + assert.Nil(err) + assert.Equal( + &chglog.BitbucketProcessor{ + Host: "https://bitbucket.org", + }, + processor, + ) + + // self-hosted + processor, err = factory.Create(&Config{ + Style: "bitbucket", + Info: Info{ + RepositoryURL: "https://original-gitserver.com/owner/repo", + }, + }) + + assert.Nil(err) + assert.Equal( + &chglog.BitbucketProcessor{ + Host: "https://original-gitserver.com", + }, + processor, + ) +} diff --git a/cmd/git-chglog/variables.go b/cmd/git-chglog/variables.go index 92132c3f..cfb90c79 100644 --- a/cmd/git-chglog/variables.go +++ b/cmd/git-chglog/variables.go @@ -14,12 +14,14 @@ var ( // Styles var ( - styleGitHub = "github" - styleGitLab = "gitlab" - styleNone = "none" - styles = []string{ + styleGitHub = "github" + styleGitLab = "gitlab" + styleBitbucket = "bitbucket" + styleNone = "none" + styles = []string{ styleGitHub, styleGitLab, + styleBitbucket, styleNone, } ) diff --git a/processor.go b/processor.go index d5da974f..9b46d387 100644 --- a/processor.go +++ b/processor.go @@ -120,3 +120,58 @@ func (p *GitLabProcessor) addLinks(input string) string { return input } + +// BitbucketProcessor is optimized for CHANGELOG used in Bitbucket +// +// The following processing is performed +// - Mentions automatic link (@tsuyoshiwada -> [@tsuyoshiwada](https://bitbucket.org/tsuyoshiwada/)) +// - Automatic link to references (#123 -> [#123](https://bitbucket.org/owner/repo/issues/123/)) +type BitbucketProcessor struct { + Host string // Host name used for link destination. Note: You must include the protocol (e.g. "https://bitbucket.org") + config *Config + reMention *regexp.Regexp + reIssue *regexp.Regexp +} + +// Bootstrap ... +func (p *BitbucketProcessor) Bootstrap(config *Config) { + p.config = config + + if p.Host == "" { + p.Host = "https://bitbucket.org" + } else { + p.Host = strings.TrimRight(p.Host, "/") + } + + p.reMention = regexp.MustCompile("@(\\w+)") + p.reIssue = regexp.MustCompile("(?i)#(\\d+)") +} + +// ProcessCommit ... +func (p *BitbucketProcessor) ProcessCommit(commit *Commit) *Commit { + commit.Header = p.addLinks(commit.Header) + commit.Subject = p.addLinks(commit.Subject) + commit.Body = p.addLinks(commit.Body) + + for _, note := range commit.Notes { + note.Body = p.addLinks(note.Body) + } + + if commit.Revert != nil { + commit.Revert.Header = p.addLinks(commit.Revert.Header) + } + + return commit +} + +func (p *BitbucketProcessor) addLinks(input string) string { + repoURL := strings.TrimRight(p.config.Info.RepositoryURL, "/") + + // mentions + input = p.reMention.ReplaceAllString(input, "[@$1]("+p.Host+"/$1/)") + + // issues + input = p.reIssue.ReplaceAllString(input, "[#$1]("+repoURL+"/issues/$1/)") + + return input +} diff --git a/processor_test.go b/processor_test.go index 4a80b4b6..177f1506 100644 --- a/processor_test.go +++ b/processor_test.go @@ -129,3 +129,65 @@ gh-56 hoge fuga`, ), ) } + +func TestBitbucketProcessor(t *testing.T) { + assert := assert.New(t) + + config := &Config{ + Info: &Info{ + RepositoryURL: "https://example.com", + }, + } + + processor := &BitbucketProcessor{} + + processor.Bootstrap(config) + + assert.Equal( + &Commit{ + Header: "message [@foo](https://bitbucket.org/foo/) [#123](https://example.com/issues/123/)", + Subject: "message [@foo](https://bitbucket.org/foo/) [#123](https://example.com/issues/123/)", + Body: `issue [#456](https://example.com/issues/456/) +multiline [#789](https://example.com/issues/789/) +[@foo](https://bitbucket.org/foo/), [@bar](https://bitbucket.org/bar/)`, + Notes: []*Note{ + &Note{ + Body: `issue1 [#11](https://example.com/issues/11/) +issue2 [#22](https://example.com/issues/22/) +gh-56 hoge fuga`, + }, + }, + }, + processor.ProcessCommit( + &Commit{ + Header: "message @foo #123", + Subject: "message @foo #123", + Body: `issue #456 +multiline #789 +@foo, @bar`, + Notes: []*Note{ + &Note{ + Body: `issue1 #11 +issue2 #22 +gh-56 hoge fuga`, + }, + }, + }, + ), + ) + + assert.Equal( + &Commit{ + Revert: &Revert{ + Header: "revert header [@mention](https://bitbucket.org/mention/) [#123](https://example.com/issues/123/)", + }, + }, + processor.ProcessCommit( + &Commit{ + Revert: &Revert{ + Header: "revert header @mention #123", + }, + }, + ), + ) +}