feat: add Jira integration (#52)

This commit is contained in:
Chao Li 2021-03-11 15:40:32 +08:00 committed by GitHub
parent 8713d96856
commit a1c84d7a0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 742 additions and 331 deletions

View file

@ -7,7 +7,7 @@ clean:
.PHONY: build
build:
go build -i -o git-chglog ./cmd/git-chglog
go build -o git-chglog ./cmd/git-chglog
.PHONY: test
test:

View file

@ -44,6 +44,7 @@
- [`options.notes`](#optionsnotes)
- [Templates](#templates)
- [Supported Styles](#supported-styles)
- [Jira Integration](#jira-integration)
- [FAQ](#faq)
- [TODO](#todo)
- [Thanks](#thanks)
@ -486,6 +487,88 @@ See the godoc [RenderData][doc-render-data] documentation for available variable
> :memo: Even with styles that are not yet supported, it is possible to make ordinary CHANGELOG.
## Jira Integration
Jira is a popular project management tool. When a project uses Jira to track feature development and bug fixes,
it may also want to generate change log based information stored in Jira. With embedding a Jira story id in git
commit header, the git-chglog tool may automatically fetch data of the story from Jira, those data then can be
used to render the template.
Take the following steps to add Jira integration:
#### 1. Change the header parse pattern to recognize Jira issue id in the configure file.
__Where Jira issue is identical Jira story.__
The following is a sample pattern:
```yaml
header:
pattern: "^(?:(\\w*)|(?:\\[(.*)\\])?)\\:\\s(.*)$"
pattern_maps:
- Type
- JiraIssueId
- Subject
```
This sample pattern can match both forms of commit headers:
* `feat: new feature of something`
* `[JIRA-ID]: something`
#### 2. Add Jira configuration to the configure file.
The following is a sample:
```yaml
jira:
info:
username: u
token: p
url: https://jira.com
issue:
type_maps:
Task: fix
Story: feat
description_pattern: "<changelog>(.*)</changelog>"
```
Here you need to define Jira URL, access username and token (password). If you don't want to
write your Jira access credential in configure file, you may define them with environment variables:
`JIRA_URL`, `JIRA_USERNAME` and `JIRA_TOKEN`.
You also needs to define a issue type map. In above sample, Jira issue type `Task` will be
mapped to `fix` and `Story` will be mapped to `feat`.
As a Jira story's description could be very long, you might not want to include the entire
description into change log. In that case, you may define `description_pattern` like above,
so that only content embraced with `<changelog> ... </changelog>` will be included.
#### 3. Update the template to show Jira data.
In the template, if a commit contains a Jira issue id, then you may show Jira data. For example:
```markdown
{{ range .CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ if .JiraIssue }} {{ .JiraIssue.Description }}
{{ end }}
{{ end }}
{{ end -}}
```
Within a `Commit`, the following Jira data can be used in template:
* `.JiraIssue.Summary` - Summary of the Jira story
* `.JiraIssue.Description` - Description of the Jira story
* `.JiraIssue.Type` - Original type of the Jira story, and `.Type` will be mapped type.
* `.JiraIssue.Labels` - A list of strings, each is a Jira label.
## FAQ
<details>

View file

@ -16,25 +16,30 @@ import (
// Options is an option used to process commits
type Options struct {
Processor Processor
NextTag string // Treat unreleased commits as specified tags (EXPERIMENTAL)
TagFilterPattern string // Filter tag by regexp
NoCaseSensitive bool // Filter commits in a case insensitive way
CommitFilters map[string][]string // Filter by using `Commit` properties and values. Filtering is not done by specifying an empty value
CommitSortBy string // Property name to use for sorting `Commit` (e.g. `Scope`)
CommitGroupBy string // Property name of `Commit` to be grouped into `CommitGroup` (e.g. `Type`)
CommitGroupSortBy string // Property name to use for sorting `CommitGroup` (e.g. `Title`)
CommitGroupTitleOrder []string // Predefined sorted list of titles to use for sorting `CommitGroup`. Only if `CommitGroupSortBy` is `Custom`
CommitGroupTitleMaps map[string]string // Map for `CommitGroup` title conversion
HeaderPattern string // A regular expression to use for parsing the commit header
HeaderPatternMaps []string // A rule for mapping the result of `HeaderPattern` to the property of `Commit`
IssuePrefix []string // Prefix used for issues (e.g. `#`, `gh-`)
RefActions []string // Word list of `Ref.Action`
MergePattern string // A regular expression to use for parsing the merge commit
MergePatternMaps []string // Similar to `HeaderPatternMaps`
RevertPattern string // A regular expression to use for parsing the revert commit
RevertPatternMaps []string // Similar to `HeaderPatternMaps`
NoteKeywords []string // Keyword list to find `Note`. A semicolon is a separator, like `<keyword>:` (e.g. `BREAKING CHANGE`)
Processor Processor
NextTag string // Treat unreleased commits as specified tags (EXPERIMENTAL)
TagFilterPattern string // Filter tag by regexp
NoCaseSensitive bool // Filter commits in a case insensitive way
CommitFilters map[string][]string // Filter by using `Commit` properties and values. Filtering is not done by specifying an empty value
CommitSortBy string // Property name to use for sorting `Commit` (e.g. `Scope`)
CommitGroupBy string // Property name of `Commit` to be grouped into `CommitGroup` (e.g. `Type`)
CommitGroupSortBy string // Property name to use for sorting `CommitGroup` (e.g. `Title`)
CommitGroupTitleOrder []string // Predefined sorted list of titles to use for sorting `CommitGroup`. Only if `CommitGroupSortBy` is `Custom`
CommitGroupTitleMaps map[string]string // Map for `CommitGroup` title conversion
HeaderPattern string // A regular expression to use for parsing the commit header
HeaderPatternMaps []string // A rule for mapping the result of `HeaderPattern` to the property of `Commit`
IssuePrefix []string // Prefix used for issues (e.g. `#`, `gh-`)
RefActions []string // Word list of `Ref.Action`
MergePattern string // A regular expression to use for parsing the merge commit
MergePatternMaps []string // Similar to `HeaderPatternMaps`
RevertPattern string // A regular expression to use for parsing the revert commit
RevertPatternMaps []string // Similar to `HeaderPatternMaps`
NoteKeywords []string // Keyword list to find `Note`. A semicolon is a separator, like `<keyword>:` (e.g. `BREAKING CHANGE`)
JiraUsername string
JiraToken string
JiraUrl string
JiraTypeMaps map[string]string
JiraIssueDescriptionPattern string
}
// Info is metadata related to CHANGELOG
@ -97,11 +102,13 @@ type Generator struct {
}
// NewGenerator receives `Config` and create an new `Generator`
func NewGenerator(config *Config) *Generator {
func NewGenerator(logger *Logger, config *Config) *Generator {
client := gitcmd.New(&gitcmd.Config{
Bin: config.Bin,
})
jiraClient := NewJiraClient(config)
if config.Options.Processor != nil {
config.Options.Processor.Bootstrap(config)
}
@ -113,7 +120,7 @@ func NewGenerator(config *Config) *Generator {
config: config,
tagReader: newTagReader(client, config.Options.TagFilterPattern),
tagSelector: newTagSelector(),
commitParser: newCommitParser(client, config),
commitParser: newCommitParser(logger, client, jiraClient, config),
commitExtractor: newCommitExtractor(config.Options),
}
}

View file

@ -76,15 +76,16 @@ func TestGeneratorNotFoundTags(t *testing.T) {
commit("2018-01-01 00:00:00", "feat(*): New feature", "")
})
gen := NewGenerator(&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{},
})
gen := NewGenerator(NewLogger(os.Stdout, os.Stderr, false, true),
&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{},
})
buf := &bytes.Buffer{}
err := gen.Generate(buf, "")
@ -102,15 +103,16 @@ func TestGeneratorNotFoundCommits(t *testing.T) {
tag("1.0.0")
})
gen := NewGenerator(&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{},
})
gen := NewGenerator(NewLogger(os.Stdout, os.Stderr, false, true),
&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{},
})
buf := &bytes.Buffer{}
err := gen.Generate(buf, "foo")
@ -127,44 +129,45 @@ func TestGeneratorNotFoundCommitsOne(t *testing.T) {
tag("1.0.0")
})
gen := NewGenerator(&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{
CommitFilters: map[string][]string{},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
gen := NewGenerator(NewLogger(os.Stdout, os.Stderr, false, true),
&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
IssuePrefix: []string{
"#",
"gh-",
Options: &Options{
CommitFilters: map[string][]string{},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
IssuePrefix: []string{
"#",
"gh-",
},
RefActions: []string{},
MergePattern: "^Merge pull request #(\\d+) from (.*)$",
MergePatternMaps: []string{
"Ref",
"Source",
},
RevertPattern: "^Revert \"([\\s\\S]*)\"$",
RevertPatternMaps: []string{
"Header",
},
NoteKeywords: []string{
"BREAKING CHANGE",
},
},
RefActions: []string{},
MergePattern: "^Merge pull request #(\\d+) from (.*)$",
MergePatternMaps: []string{
"Ref",
"Source",
},
RevertPattern: "^Revert \"([\\s\\S]*)\"$",
RevertPatternMaps: []string{
"Header",
},
NoteKeywords: []string{
"BREAKING CHANGE",
},
},
})
})
buf := &bytes.Buffer{}
err := gen.Generate(buf, "foo")
@ -202,53 +205,54 @@ change message.`)
commit("2018-01-04 00:01:00", "fix(core): Fix commit\n\nThis is body message.", "")
})
gen := NewGenerator(&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
Title: "CHANGELOG Example",
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{
CommitFilters: map[string][]string{
"Type": []string{
"feat",
"fix",
gen := NewGenerator(NewLogger(os.Stdout, os.Stderr, false, true),
&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
Title: "CHANGELOG Example",
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{
CommitFilters: map[string][]string{
"Type": []string{
"feat",
"fix",
},
},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{
"feat": "Features",
"fix": "Bug Fixes",
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
IssuePrefix: []string{
"#",
"gh-",
},
RefActions: []string{},
MergePattern: "^Merge pull request #(\\d+) from (.*)$",
MergePatternMaps: []string{
"Ref",
"Source",
},
RevertPattern: "^Revert \"([\\s\\S]*)\"$",
RevertPatternMaps: []string{
"Header",
},
NoteKeywords: []string{
"BREAKING CHANGE",
},
},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{
"feat": "Features",
"fix": "Bug Fixes",
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
IssuePrefix: []string{
"#",
"gh-",
},
RefActions: []string{},
MergePattern: "^Merge pull request #(\\d+) from (.*)$",
MergePatternMaps: []string{
"Ref",
"Source",
},
RevertPattern: "^Revert \"([\\s\\S]*)\"$",
RevertPatternMaps: []string{
"Header",
},
NoteKeywords: []string{
"BREAKING CHANGE",
},
},
})
})
buf := &bytes.Buffer{}
err := gen.Generate(buf, "")
@ -315,35 +319,36 @@ func TestGeneratorWithNextTag(t *testing.T) {
commit("2018-03-01 00:00:00", "feat(core): version 3.0.0", "")
})
gen := NewGenerator(&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
Title: "CHANGELOG Example",
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{
NextTag: "3.0.0",
CommitFilters: map[string][]string{
"Type": []string{
"feat",
gen := NewGenerator(NewLogger(os.Stdout, os.Stderr, false, true),
&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
Title: "CHANGELOG Example",
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{
NextTag: "3.0.0",
CommitFilters: map[string][]string{
"Type": []string{
"feat",
},
},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{
"feat": "Features",
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{
"feat": "Features",
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
},
})
})
buf := &bytes.Buffer{}
err := gen.Generate(buf, "")
@ -407,35 +412,36 @@ func TestGeneratorWithTagFiler(t *testing.T) {
tag("v1.0.0")
})
gen := NewGenerator(&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
Title: "CHANGELOG Example",
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{
TagFilterPattern: "^v",
CommitFilters: map[string][]string{
"Type": []string{
"feat",
gen := NewGenerator(NewLogger(os.Stdout, os.Stderr, false, true),
&Config{
Bin: "git",
WorkingDir: filepath.Join(testRepoRoot, testName),
Template: filepath.Join(cwd, "testdata", testName+".md"),
Info: &Info{
Title: "CHANGELOG Example",
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{
TagFilterPattern: "^v",
CommitFilters: map[string][]string{
"Type": []string{
"feat",
},
},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{
"feat": "Features",
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{
"feat": "Features",
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
},
})
})
buf := &bytes.Buffer{}
err := gen.Generate(buf, "")

View file

@ -14,7 +14,7 @@ import (
type CLI struct {
ctx *CLIContext
fs FileSystem
logger *Logger
logger *chglog.Logger
configLoader ConfigLoader
generator Generator
processorFactory *ProcessorFactory
@ -34,7 +34,7 @@ func NewCLI(
return &CLI{
ctx: ctx,
fs: fs,
logger: NewLogger(ctx.Stdout, ctx.Stderr, silent, ctx.NoEmoji),
logger: chglog.NewLogger(ctx.Stdout, ctx.Stderr, silent, ctx.NoEmoji),
configLoader: configLoader,
generator: generator,
processorFactory: NewProcessorFactory(),
@ -69,7 +69,7 @@ func (c *CLI) Run() int {
return ExitCodeError
}
err = c.generator.Generate(w, c.ctx.Query, changelogConfig)
err = c.generator.Generate(c.logger, w, c.ctx.Query, changelogConfig)
if err != nil {
c.logger.Error(err.Error())
return ExitCodeError

View file

@ -49,6 +49,24 @@ type NoteOptions struct {
Keywords []string `yaml:"keywords"`
}
type JiraClientInfoOptions struct {
Username string `yaml:"username"`
Token string `yaml:"token"`
URL string `yaml:"url"`
}
// JiraIssueOptions ...
type JiraIssueOptions struct {
TypeMaps map[string]string `yaml:"type_maps"`
DescriptionPattern string `yaml:"description_pattern"`
}
// JiraOptions ...
type JiraOptions struct {
ClintInfo JiraClientInfoOptions `yaml:"info"`
Issue JiraIssueOptions `yaml:"issue"`
}
// Options ...
type Options struct {
TagFilterPattern string `yaml:"tag_filter_pattern"`
@ -60,6 +78,7 @@ type Options struct {
Merges PatternOptions `yaml:"merges"`
Reverts PatternOptions `yaml:"reverts"`
Notes NoteOptions `yaml:"notes"`
Jira JiraOptions `yaml:"jira"`
}
// Config ...
@ -245,6 +264,13 @@ func (config *Config) normalizeStyleOfBitbucket() {
config.Options = opts
}
func orValue(str1 string, str2 string) string {
if str1 != "" {
return str1
}
return str2
}
// Convert ...
func (config *Config) Convert(ctx *CLIContext) *chglog.Config {
info := config.Info
@ -257,30 +283,34 @@ func (config *Config) Convert(ctx *CLIContext) *chglog.Config {
return &chglog.Config{
Bin: config.Bin,
WorkingDir: ctx.WorkingDir,
Template: config.Template,
Template: orValue(ctx.Template, config.Template),
Info: &chglog.Info{
Title: info.Title,
RepositoryURL: info.RepositoryURL,
RepositoryURL: orValue(ctx.RepositoryUrl, info.RepositoryURL),
},
Options: &chglog.Options{
NextTag: ctx.NextTag,
TagFilterPattern: ctx.TagFilterPattern,
NoCaseSensitive: ctx.NoCaseSensitive,
CommitFilters: opts.Commits.Filters,
CommitSortBy: opts.Commits.SortBy,
CommitGroupBy: opts.CommitGroups.GroupBy,
CommitGroupSortBy: opts.CommitGroups.SortBy,
CommitGroupTitleMaps: opts.CommitGroups.TitleMaps,
CommitGroupTitleOrder: opts.CommitGroups.TitleOrder,
HeaderPattern: opts.Header.Pattern,
HeaderPatternMaps: opts.Header.PatternMaps,
IssuePrefix: opts.Issues.Prefix,
RefActions: opts.Refs.Actions,
MergePattern: opts.Merges.Pattern,
MergePatternMaps: opts.Merges.PatternMaps,
RevertPattern: opts.Reverts.Pattern,
RevertPatternMaps: opts.Reverts.PatternMaps,
NoteKeywords: opts.Notes.Keywords,
NextTag: ctx.NextTag,
TagFilterPattern: ctx.TagFilterPattern,
NoCaseSensitive: ctx.NoCaseSensitive,
CommitFilters: opts.Commits.Filters,
CommitSortBy: opts.Commits.SortBy,
CommitGroupBy: opts.CommitGroups.GroupBy,
CommitGroupSortBy: opts.CommitGroups.SortBy,
CommitGroupTitleMaps: opts.CommitGroups.TitleMaps,
HeaderPattern: opts.Header.Pattern,
HeaderPatternMaps: opts.Header.PatternMaps,
IssuePrefix: opts.Issues.Prefix,
RefActions: opts.Refs.Actions,
MergePattern: opts.Merges.Pattern,
MergePatternMaps: opts.Merges.PatternMaps,
RevertPattern: opts.Reverts.Pattern,
RevertPatternMaps: opts.Reverts.PatternMaps,
NoteKeywords: opts.Notes.Keywords,
JiraUsername: orValue(ctx.JiraUsername, opts.Jira.ClintInfo.Username),
JiraToken: orValue(ctx.JiraToken, opts.Jira.ClintInfo.Token),
JiraUrl: orValue(ctx.JiraUrl, opts.Jira.ClintInfo.URL),
JiraTypeMaps: opts.Jira.Issue.TypeMaps,
JiraIssueDescriptionPattern: opts.Jira.Issue.DescriptionPattern,
},
}
}

View file

@ -10,6 +10,8 @@ type CLIContext struct {
Stdout io.Writer
Stderr io.Writer
ConfigPath string
Template string
RepositoryUrl string
OutputPath string
Silent bool
NoColor bool
@ -18,6 +20,9 @@ type CLIContext struct {
Query string
NextTag string
TagFilterPattern string
JiraUsername string
JiraToken string
JiraUrl string
}
// InitContext ...

View file

@ -8,7 +8,7 @@ import (
// Generator ...
type Generator interface {
Generate(io.Writer, string, *chglog.Config) error
Generate(*chglog.Logger, io.Writer, string, *chglog.Config) error
}
type generatorImpl struct{}
@ -19,6 +19,6 @@ func NewGenerator() Generator {
}
// Generate ...
func (*generatorImpl) Generate(w io.Writer, query string, config *chglog.Config) error {
return chglog.NewGenerator(config).Generate(w, query)
func (*generatorImpl) Generate(logger *chglog.Logger, w io.Writer, query string, config *chglog.Config) error {
return chglog.NewGenerator(logger, config).Generate(w, query)
}

View file

@ -10,6 +10,6 @@ type mockGeneratorImpl struct {
ReturnGenerate func(io.Writer, string, *chglog.Config) error
}
func (m *mockGeneratorImpl) Generate(w io.Writer, query string, config *chglog.Config) error {
func (m *mockGeneratorImpl) Generate(logger *chglog.Logger, w io.Writer, query string, config *chglog.Config) error {
return m.ReturnGenerate(w, query, config)
}

View file

@ -2,6 +2,7 @@ package main
import (
"fmt"
"github.com/git-chglog/git-chglog"
"path/filepath"
"github.com/fatih/color"
@ -13,7 +14,7 @@ type Initializer struct {
ctx *InitContext
client gitcmd.Client
fs FileSystem
logger *Logger
logger *chglog.Logger
questioner Questioner
configBuilder ConfigBuilder
templateBuilderFactory TemplateBuilderFactory
@ -29,7 +30,7 @@ func NewInitializer(
return &Initializer{
ctx: ctx,
fs: fs,
logger: NewLogger(ctx.Stdout, ctx.Stderr, false, false),
logger: chglog.NewLogger(ctx.Stdout, ctx.Stderr, false, false),
questioner: questioner,
configBuilder: configBuilder,
templateBuilderFactory: tplBuilderFactory,

View file

@ -83,6 +83,18 @@ func CreateApp(actionFunc cli.ActionFunc) *cli.App {
Value: ".chglog/config.yml",
},
// template
&cli.StringFlag{
Name: "template, t",
Usage: "specifies a template file to pick up. If not specified, use the one in config",
},
// repository url
&cli.StringFlag{
Name: "repository-url",
Usage: "specifies git repo URL. If not specified, use 'repository_url' in config",
},
// output
&cli.StringFlag{
Name: "output, o",
@ -102,15 +114,15 @@ func CreateApp(actionFunc cli.ActionFunc) *cli.App {
// no-color
&cli.BoolFlag{
Name: "no-color",
Usage: "disable color output",
Name: "no-color",
Usage: "disable color output",
EnvVars: []string{"NO_COLOR"},
},
// no-emoji
&cli.BoolFlag{
Name: "no-emoji",
Usage: "disable emoji output",
Name: "no-emoji",
Usage: "disable emoji output",
EnvVars: []string{"NO_EMOJI"},
},
@ -122,10 +134,31 @@ func CreateApp(actionFunc cli.ActionFunc) *cli.App {
// tag-filter-pattern
&cli.StringFlag{
Name: "tag-filter-pattern, p",
Name: "tag-filter-pattern",
Usage: "Regular expression of tag filter. Is specified, only matched tags will be picked",
},
// jira-url
&cli.StringFlag{
Name: "jira-url",
Usage: "Jira URL",
EnvVars: []string{"JIRA_URL"},
},
// jira-username
&cli.StringFlag{
Name: "jira-username",
Usage: "Jira username",
EnvVars: []string{"JIRA_USERNAME"},
},
// jira-token
&cli.StringFlag{
Name: "jira-token",
Usage: "Jira token",
EnvVars: []string{"JIRA_TOKEN"},
},
// help & version
cli.HelpFlag,
cli.VersionFlag,
@ -180,6 +213,9 @@ func AppAction(c *cli.Context) error {
Query: c.Args().First(),
NextTag: c.String("next-tag"),
TagFilterPattern: c.String("tag-filter-pattern"),
JiraUsername: c.String("jira-username"),
JiraToken: c.String("jira-token"),
JiraUrl: c.String("jira-url"),
},
fs,
NewConfigLoader(),

View file

@ -1,12 +1,13 @@
package chglog
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
gitcmd "github.com/tsuyoshiwada/go-gitcmd"
"github.com/tsuyoshiwada/go-gitcmd"
)
var (
@ -47,18 +48,21 @@ func joinAndQuoteMeta(list []string, sep string) string {
}
type commitParser struct {
client gitcmd.Client
config *Config
reHeader *regexp.Regexp
reMerge *regexp.Regexp
reRevert *regexp.Regexp
reRef *regexp.Regexp
reIssue *regexp.Regexp
reNotes *regexp.Regexp
reMention *regexp.Regexp
logger *Logger
client gitcmd.Client
jiraClient JiraClient
config *Config
reHeader *regexp.Regexp
reMerge *regexp.Regexp
reRevert *regexp.Regexp
reRef *regexp.Regexp
reIssue *regexp.Regexp
reNotes *regexp.Regexp
reMention *regexp.Regexp
reJiraIssueDescription *regexp.Regexp
}
func newCommitParser(client gitcmd.Client, config *Config) *commitParser {
func newCommitParser(logger *Logger, client gitcmd.Client, jiraClient JiraClient, config *Config) *commitParser {
opts := config.Options
joinedRefActions := joinAndQuoteMeta(opts.RefActions, "|")
@ -66,15 +70,18 @@ func newCommitParser(client gitcmd.Client, config *Config) *commitParser {
joinedNoteKeywords := joinAndQuoteMeta(opts.NoteKeywords, "|")
return &commitParser{
client: client,
config: config,
reHeader: regexp.MustCompile(opts.HeaderPattern),
reMerge: regexp.MustCompile(opts.MergePattern),
reRevert: regexp.MustCompile(opts.RevertPattern),
reRef: regexp.MustCompile("(?i)(" + joinedRefActions + ")\\s?([\\w/\\.\\-]+)?(?:" + joinedIssuePrefix + ")(\\d+)"),
reIssue: regexp.MustCompile("(?:" + joinedIssuePrefix + ")(\\d+)"),
reNotes: regexp.MustCompile("^(?i)\\s*(" + joinedNoteKeywords + ")[:\\s]+(.*)"),
reMention: regexp.MustCompile("@([\\w-]+)"),
logger: logger,
client: client,
jiraClient: jiraClient,
config: config,
reHeader: regexp.MustCompile(opts.HeaderPattern),
reMerge: regexp.MustCompile(opts.MergePattern),
reRevert: regexp.MustCompile(opts.RevertPattern),
reRef: regexp.MustCompile("(?i)(" + joinedRefActions + ")\\s?([\\w/\\.\\-]+)?(?:" + joinedIssuePrefix + ")(\\d+)"),
reIssue: regexp.MustCompile("(?:" + joinedIssuePrefix + ")(\\d+)"),
reNotes: regexp.MustCompile("^(?i)\\s*(" + joinedNoteKeywords + ")[:\\s]+(.*)"),
reMention: regexp.MustCompile("@([\\w-]+)"),
reJiraIssueDescription: regexp.MustCompile(opts.JiraIssueDescriptionPattern),
}
}
@ -206,6 +213,11 @@ func (p *commitParser) processHeader(commit *Commit, input string) {
// refs & mentions
commit.Refs = p.parseRefs(input)
commit.Mentions = p.parseMentions(input)
// Jira
if commit.JiraIssueId != "" {
p.processJiraIssue(commit, commit.JiraIssueId)
}
}
func (p *commitParser) processBody(commit *Commit, input string) {
@ -344,6 +356,28 @@ func (p *commitParser) uniqMentions(mentions []string) []string {
return arr
}
func (p *commitParser) processJiraIssue(commit *Commit, issueId string) {
issue, err := p.jiraClient.GetJiraIssue(commit.JiraIssueId)
if err != nil {
p.logger.Error(fmt.Sprintf("Failed to parse Jira story %s: %s\n", issueId, err))
return
}
commit.Type = p.config.Options.JiraTypeMaps[issue.Fields.Type.Name]
commit.JiraIssue = &JiraIssue{
Type: issue.Fields.Type.Name,
Summary: issue.Fields.Summary,
Description: issue.Fields.Description,
Labels: issue.Fields.Labels,
}
if p.config.Options.JiraIssueDescriptionPattern != "" {
res := p.reJiraIssueDescription.FindStringSubmatch(commit.JiraIssue.Description)
if len(res) > 1 {
commit.JiraIssue.Description = res[1]
}
}
}
var (
fenceTypes = []string{
"```",

View file

@ -4,10 +4,12 @@ import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
agjira "github.com/andygrunwald/go-jira"
"github.com/stretchr/testify/assert"
)
@ -27,51 +29,52 @@ func TestCommitParserParse(t *testing.T) {
},
}
parser := newCommitParser(mock, &Config{
Options: &Options{
CommitFilters: map[string][]string{
"Type": []string{
"feat",
parser := newCommitParser(NewLogger(os.Stdout, os.Stderr, false, true),
mock, nil, &Config{
Options: &Options{
CommitFilters: map[string][]string{
"Type": []string{
"feat",
"fix",
"perf",
"refactor",
},
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
IssuePrefix: []string{
"#",
"gh-",
},
RefActions: []string{
"close",
"closes",
"closed",
"fix",
"perf",
"refactor",
"fixes",
"fixed",
"resolve",
"resolves",
"resolved",
},
MergePattern: "^Merge pull request #(\\d+) from (.*)$",
MergePatternMaps: []string{
"Ref",
"Source",
},
RevertPattern: "^Revert \"([\\s\\S]*)\"$",
RevertPatternMaps: []string{
"Header",
},
NoteKeywords: []string{
"BREAKING CHANGE",
},
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
IssuePrefix: []string{
"#",
"gh-",
},
RefActions: []string{
"close",
"closes",
"closed",
"fix",
"fixes",
"fixed",
"resolve",
"resolves",
"resolved",
},
MergePattern: "^Merge pull request #(\\d+) from (.*)$",
MergePatternMaps: []string{
"Ref",
"Source",
},
RevertPattern: "^Revert \"([\\s\\S]*)\"$",
RevertPatternMaps: []string{
"Header",
},
NoteKeywords: []string{
"BREAKING CHANGE",
},
},
})
})
commits, err := parser.Parse("HEAD")
assert.Nil(err)
@ -308,3 +311,102 @@ Closes username/repository#456`, "```", "```"),
},
}, commits)
}
type mockJiraClient struct {
}
func (jira mockJiraClient) GetJiraIssue(id string) (*agjira.Issue, error) {
return &agjira.Issue{
ID: id,
Fields: &agjira.IssueFields{
Expand: "",
Type: agjira.IssueType{Name: "Story"},
Project: agjira.Project{},
Resolution: nil,
Priority: nil,
Resolutiondate: agjira.Time{},
Created: agjira.Time{},
Duedate: agjira.Date{},
Watches: nil,
Assignee: nil,
Updated: agjira.Time{},
Description: fmt.Sprintf("description of %s", id),
Summary: fmt.Sprintf("summary of %s", id),
Creator: nil,
Reporter: nil,
Components: nil,
Status: nil,
Progress: nil,
AggregateProgress: nil,
TimeTracking: nil,
TimeSpent: 0,
TimeEstimate: 0,
TimeOriginalEstimate: 0,
Worklog: nil,
IssueLinks: nil,
Comments: nil,
FixVersions: nil,
AffectsVersions: nil,
Labels: []string{"GA"},
Subtasks: nil,
Attachments: nil,
Epic: nil,
Sprint: nil,
Parent: nil,
AggregateTimeOriginalEstimate: 0,
AggregateTimeSpent: 0,
AggregateTimeEstimate: 0,
Unknowns: nil,
},
}, nil
}
func TestCommitParserParseWithJira(t *testing.T) {
assert := assert.New(t)
assert.True(true)
mock := &mockClient{
ReturnExec: func(subcmd string, args ...string) (string, error) {
if subcmd != "log" {
return "", errors.New("")
}
bytes, _ := ioutil.ReadFile(filepath.Join("testdata", "gitlog_jira.txt"))
return string(bytes), nil
},
}
parser := newCommitParser(NewLogger(os.Stdout, os.Stderr, false, true),
mock, mockJiraClient{}, &Config{
Options: &Options{
CommitFilters: map[string][]string{
"Type": []string{
"feat",
"fix",
"perf",
"refactor",
},
},
HeaderPattern: "^(?:(\\w*)|(?:\\[(.*)\\])?)\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"JiraIssueId",
"Subject",
},
JiraTypeMaps: map[string]string{
"Story": "feat",
},
},
})
commits, err := parser.Parse("HEAD")
assert.Nil(err)
commit := commits[0]
assert.Equal(commit.JiraIssueId, "JIRA-1111")
assert.Equal(commit.JiraIssue.Type, "Story")
assert.Equal(commit.JiraIssue.Summary, "summary of JIRA-1111")
assert.Equal(commit.JiraIssue.Description, "description of JIRA-1111")
assert.Equal(commit.JiraIssue.Labels, []string{"GA"})
assert.Equal(commit.Type, "feat")
}

View file

@ -4,56 +4,58 @@ import (
"bytes"
"fmt"
"log"
"os"
)
func Example() {
gen := NewGenerator(&Config{
Bin: "git",
WorkingDir: ".",
Template: "CHANGELOG.tpl.md",
Info: &Info{
Title: "CHANGELOG",
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{
CommitFilters: map[string][]string{
"Type": []string{
"feat",
"fix",
gen := NewGenerator(NewLogger(os.Stdout, os.Stderr, false, true),
&Config{
Bin: "git",
WorkingDir: ".",
Template: "CHANGELOG.tpl.md",
Info: &Info{
Title: "CHANGELOG",
RepositoryURL: "https://github.com/git-chglog/git-chglog",
},
Options: &Options{
CommitFilters: map[string][]string{
"Type": []string{
"feat",
"fix",
},
},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{
"feat": "Features",
"fix": "Bug Fixes",
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
IssuePrefix: []string{
"#",
"gh-",
},
RefActions: []string{},
MergePattern: "^Merge pull request #(\\d+) from (.*)$",
MergePatternMaps: []string{
"Ref",
"Source",
},
RevertPattern: "^Revert \"([\\s\\S]*)\"$",
RevertPatternMaps: []string{
"Header",
},
NoteKeywords: []string{
"BREAKING CHANGE",
},
},
CommitSortBy: "Scope",
CommitGroupBy: "Type",
CommitGroupSortBy: "Title",
CommitGroupTitleMaps: map[string]string{
"feat": "Features",
"fix": "Bug Fixes",
},
HeaderPattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$",
HeaderPatternMaps: []string{
"Type",
"Scope",
"Subject",
},
IssuePrefix: []string{
"#",
"gh-",
},
RefActions: []string{},
MergePattern: "^Merge pull request #(\\d+) from (.*)$",
MergePatternMaps: []string{
"Ref",
"Source",
},
RevertPattern: "^Revert \"([\\s\\S]*)\"$",
RevertPatternMaps: []string{
"Header",
},
NoteKeywords: []string{
"BREAKING CHANGE",
},
},
})
})
buf := &bytes.Buffer{}
err := gen.Generate(buf, "")

View file

@ -52,21 +52,31 @@ type NoteGroup struct {
Notes []*Note
}
// JiraIssue
type JiraIssue struct {
Type string
Summary string
Description string
Labels []string
}
// Commit data
type Commit struct {
Hash *Hash
Author *Author
Committer *Committer
Merge *Merge // If it is not a merge commit, `nil` is assigned
Revert *Revert // If it is not a revert commit, `nil` is assigned
Refs []*Ref
Notes []*Note
Mentions []string // Name of the user included in the commit header or body
Header string // (e.g. `feat(core): Add new feature`)
Type string // (e.g. `feat`)
Scope string // (e.g. `core`)
Subject string // (e.g. `Add new feature`)
Body string
Hash *Hash
Author *Author
Committer *Committer
Merge *Merge // If it is not a merge commit, `nil` is assigned
Revert *Revert // If it is not a revert commit, `nil` is assigned
Refs []*Ref
Notes []*Note
Mentions []string // Name of the user included in the commit header or body
JiraIssue *JiraIssue // If no issue id found in header, `nil` is assigned
Header string // (e.g. `feat(core)[RNWY-310]: Add new feature`)
Type string // (e.g. `feat`)
Scope string // (e.g. `core`)
Subject string // (e.g. `Add new feature`)
JiraIssueId string // (e.g. `RNWY-310`)
Body string
}
// CommitGroup is a collection of commits grouped according to the `CommitGroupBy` option

1
go.mod
View file

@ -4,6 +4,7 @@ go 1.15
require (
github.com/AlecAivazis/survey/v2 v2.2.8
github.com/andygrunwald/go-jira v1.13.0
github.com/fatih/color v1.10.0
github.com/imdario/mergo v0.3.11
github.com/kyokomi/emoji/v2 v2.2.8

15
go.sum
View file

@ -3,13 +3,23 @@ github.com/AlecAivazis/survey/v2 v2.2.8/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/andygrunwald/go-jira v1.13.0 h1:vvIImGgX32bHfoiyUwkNo+/YrPnRczNarvhLOncP6dE=
github.com/andygrunwald/go-jira v1.13.0/go.mod h1:jYi4kFDbRPZTJdJOVJO4mpMMIwdB+rcZwSO58DzPd2I=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/structs v1.0.0 h1:BrX964Rv5uQ3wwS+KRUAJCBBw5PQmgJfJ6v4yly5QwU=
github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
@ -28,6 +38,8 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
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/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
@ -38,11 +50,14 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/trivago/tgo v1.0.1 h1:bxatjJIXNIpV18bucU4Uk/LaoxvxuOlp/oowRHyncLQ=
github.com/trivago/tgo v1.0.1/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc=
github.com/tsuyoshiwada/go-gitcmd v0.0.0-20180205145712-5f1f5f9475df h1:Y2l28Jr3vOEeYtxfVbMtVfOdAwuUqWaP9fvNKiBVeXY=
github.com/tsuyoshiwada/go-gitcmd v0.0.0-20180205145712-5f1f5f9475df/go.mod h1:pnyouUty/nBr/zm3GYwTIt+qFTLWbdjeLjZmJdzJOu8=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=

36
jira.go Normal file
View file

@ -0,0 +1,36 @@
package chglog
import (
agjira "github.com/andygrunwald/go-jira"
)
type JiraClient interface {
GetJiraIssue(id string) (*agjira.Issue, error)
}
type jiraClient struct {
username string
token string
url string
}
func NewJiraClient(config *Config) JiraClient {
return jiraClient{
username: config.Options.JiraUsername,
token: config.Options.JiraToken,
url: config.Options.JiraUrl,
}
}
func (jira jiraClient) GetJiraIssue(id string) (*agjira.Issue, error) {
tp := agjira.BasicAuthTransport{
Username: jira.username,
Password: jira.token,
}
client, err := agjira.NewClient(tp.Client(), jira.url)
if err != nil {
return nil, err
}
issue, _, err := client.Issue.Get(id, nil)
return issue, err
}

42
jira_test.go Normal file
View file

@ -0,0 +1,42 @@
package chglog
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestJira(t *testing.T) {
assert := assert.New(t)
config := &Config {
Options: &Options{
Processor: nil,
NextTag: "",
TagFilterPattern: "",
CommitFilters: nil,
CommitSortBy: "",
CommitGroupBy: "",
CommitGroupSortBy: "",
CommitGroupTitleMaps: nil,
HeaderPattern: "",
HeaderPatternMaps: nil,
IssuePrefix: nil,
RefActions: nil,
MergePattern: "",
MergePatternMaps: nil,
RevertPattern: "",
RevertPatternMaps: nil,
NoteKeywords: nil,
JiraUsername: "uuu",
JiraToken: "ppp",
JiraUrl: "http://jira.com",
JiraTypeMaps: nil,
JiraIssueDescriptionPattern: "",
},
}
jira := NewJiraClient(config)
issue, err := jira.GetJiraIssue("fake")
assert.Nil(issue)
assert.Error(err)
}

View file

@ -1,4 +1,4 @@
package main
package chglog
import (
"fmt"

View file

@ -1,4 +1,4 @@
package main
package chglog
import (
"bytes"

1
testdata/gitlog_jira.txt vendored Normal file
View file

@ -0,0 +1 @@
@@__CHGLOG__@@HASH:65cf1add9735dcc4810dda3312b0792236c97c4e 65cf1add@@__CHGLOG_DELIMITER__@@AUTHOR:tsuyoshi wada mail@example.com 1514808000@@__CHGLOG_DELIMITER__@@COMMITTER:tsuyoshi wada mail@example.com 1514808000@@__CHGLOG_DELIMITER__@@SUBJECT:[JIRA-1111]: Add new feature #123@@__CHGLOG_DELIMITER__@@BODY: This is body message.