From 3b9757324ab5bca667c89f05de89b7bc02d24993 Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Tue, 3 Sep 2019 10:58:44 +0200 Subject: [PATCH 1/9] Add bump command Signed-off-by: Simon Pasquier --- cmd/bump.go | 247 ++++++++++++++++++++++++++++++++ cmd/promu.go | 5 + cmd/release.go | 19 +-- go.sum | 1 + pkg/changelog/changelog.go | 66 ++++----- pkg/changelog/changelog_test.go | 83 ++++++++++- pkg/github/github.go | 66 +++++++++ 7 files changed, 431 insertions(+), 56 deletions(-) create mode 100644 cmd/bump.go create mode 100644 pkg/github/github.go diff --git a/cmd/bump.go b/cmd/bump.go new file mode 100644 index 00000000..2f7e5a8a --- /dev/null +++ b/cmd/bump.go @@ -0,0 +1,247 @@ +// Copyright © 2019 Prometheus Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "context" + "fmt" + "io" + "io/ioutil" + "os" + "sort" + "strings" + "text/template" + "time" + + "github.com/google/go-github/v25/github" + "github.com/pkg/errors" + + "github.com/prometheus/promu/pkg/changelog" + githubUtil "github.com/prometheus/promu/pkg/github" +) + +var ( + bumpcmd = app.Command("bump", "Update CHANGELOG.md and VERSION files to the next version") + + bumpLevel = bumpcmd.Flag("level", "Level of version to increment (should be one of major, minor, patch, pre)").Default("minor").Enum("major", "minor", "patch", "pre") + bumpPreRelease = bumpcmd.Flag("pre-release", "Pre-release identifier").Default("rc.0").String() + bumpBaseBranch = bumpcmd.Flag("base-branch", "Pre-release identifier").Default("master").String() +) + +type pullRequest struct { + Number int + Title string + Kinds changelog.Kinds +} + +var ( + labelPrefix = "changelog/" + skipLabel = labelPrefix + "skip" +) + +type changelogData struct { + Version string + Date string + PullRequests []pullRequest + Skipped []pullRequest + Contributors []string +} + +const changelogTmpl = `## {{ .Version }} / {{ .Date }} +{{ range .PullRequests }} +* [{{ .Kinds.String }}] {{ makeSentence .Title }} #{{ .Number }} +{{- end }} + + +Contributors: +{{ range .Contributors }} +* @{{ . }} +{{- end }} + +` + +func writeChangelog(w io.Writer, version string, prs, skippedPrs []pullRequest, contributors []string) error { + sort.SliceStable(prs, func(i int, j int) bool { return prs[i].Kinds.Before(prs[j].Kinds) }) + sort.SliceStable(skippedPrs, func(i int, j int) bool { return skippedPrs[i].Kinds.Before(skippedPrs[j].Kinds) }) + sort.Strings(contributors) + + tmpl, err := template.New("changelog").Funcs( + template.FuncMap{ + "makeSentence": func(s string) string { + s = strings.TrimRight(s, ".") + return s + "." + }, + }).Parse(changelogTmpl) + if err != nil { + return errors.Wrap(err, "invalid template") + } + + return tmpl.Execute(w, &changelogData{ + Version: version, + Date: time.Now().Format("2006-01-02"), + PullRequests: prs, + Skipped: skippedPrs, + Contributors: contributors, + }) +} + +func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRelease string, baseBranch string) error { + current, err := projInfo.ToSemver() + if err != nil { + return err + } + + next := *current + switch bumpLevel { + case "major": + next = current.IncMajor() + case "minor": + next = current.IncMinor() + case "patch": + next = current.IncPatch() + } + next, err = next.SetPrerelease(preRelease) + if err != nil { + return err + } + + ctx := context.Background() + if *timeout != time.Duration(0) { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, *timeout) + defer cancel() + } + client, err := githubUtil.NewClient(ctx) + if err != nil { + info(fmt.Sprintf("failed to create authenticated GitHub client: %v", err)) + info("Fallback to client without unauthentication") + client = github.NewClient(nil) + } + + lastTag := "v" + current.String() + commit, _, err := client.Repositories.GetCommit(ctx, projInfo.Owner, projInfo.Name, lastTag) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("Fail to get the GitHub commit for %s", lastTag)) + } + lastTagTime := commit.GetCommit().GetCommitter().GetDate() + lastCommitSHA := commit.GetSHA() + + // Gather all pull requests merged since the last tag. + var ( + prs, skipped []pullRequest + uniqContributors = make(map[string]struct{}) + ) + err = githubUtil.ReadAll( + func(opts *github.ListOptions) (*github.Response, error) { + ghPrs, resp, err := client.PullRequests.List(ctx, projInfo.Owner, projInfo.Name, &github.PullRequestListOptions{ + State: "closed", + Sort: "updated", + Direction: "desc", + ListOptions: *opts, + }) + if err != nil { + return nil, errors.Wrap(err, "Fail to list GitHub pull requests") + } + for _, pr := range ghPrs { + if pr.GetBase().GetRef() != baseBranch { + continue + } + if pr.GetUpdatedAt().Before(lastTagTime) { + // We've reached pull requests that haven't changed since + // the reference tag so we can stop now. + return nil, nil + } + if pr.GetMergedAt().IsZero() || pr.GetMergedAt().Before(lastTagTime) { + continue + } + if pr.GetMergeCommitSHA() == lastCommitSHA { + continue + } + + var ( + kinds []string + skip bool + ) + for _, lbl := range pr.Labels { + if lbl.GetName() == skipLabel { + skip = true + } + if strings.HasPrefix(lbl.GetName(), labelPrefix) { + kinds = append(kinds, strings.ToUpper(strings.TrimPrefix(lbl.GetName(), labelPrefix))) + } + } + p := pullRequest{ + Kinds: changelog.ParseKinds(kinds), + Title: pr.GetTitle(), + Number: pr.GetNumber(), + } + if pr.GetUser() != nil { + uniqContributors[pr.GetUser().GetLogin()] = struct{}{} + } + if skip { + skipped = append(skipped, p) + } else { + prs = append(prs, p) + } + } + return resp, nil + }, + ) + if err != nil { + return err + } + + var contributors []string + for k := range uniqContributors { + contributors = append(contributors, k) + } + + // Update the changelog file. + original, err := ioutil.ReadFile(changelogPath) + if err != nil { + return err + } + f, err := os.Create(changelogPath) + if err != nil { + return err + } + defer f.Close() + err = writeChangelog(f, next.String(), prs, skipped, contributors) + if err != nil { + return err + } + _, err = f.Write(original) + if err != nil { + return err + } + + // Update the version file (if present). + if _, err = os.Stat(versionPath); err == nil { + f, err := os.Create(versionPath) + if err != nil { + return err + } + defer f.Close() + + _, err = f.WriteString(next.String()) + if err != nil { + return err + } + } + + return nil +} diff --git a/cmd/promu.go b/cmd/promu.go index 264b3274..4006c679 100644 --- a/cmd/promu.go +++ b/cmd/promu.go @@ -125,6 +125,11 @@ func Execute() { runBuild(optArg(*binariesArg, 0, "all")) case checkLicensescmd.FullCommand(): runCheckLicenses(optArg(*checkLicLocation, 0, "."), *headerLength, *sourceExtensions) + case bumpcmd.FullCommand(): + err = runBumpVersion("CHANGELOG.md", "VERSION", *bumpLevel, *bumpPreRelease, *bumpBaseBranch) + if err != nil { + fatal(err) + } case checkChangelogcmd.FullCommand(): if err := runCheckChangelog(*checkChangelogPath, *checkChangelogVersion); err != nil { fatal(err) diff --git a/cmd/release.go b/cmd/release.go index 993f7ff1..d846ad3c 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -24,9 +24,9 @@ import ( "github.com/google/go-github/v25/github" "github.com/pkg/errors" - "golang.org/x/oauth2" "github.com/prometheus/promu/pkg/changelog" + githubUtil "github.com/prometheus/promu/pkg/github" "github.com/prometheus/promu/util/retry" ) @@ -39,25 +39,16 @@ var ( ) func runRelease(location string) { - token := os.Getenv("GITHUB_TOKEN") - if len(token) == 0 { - fatal(errors.New("GITHUB_TOKEN not defined")) - } - ctx := context.Background() if *timeout != time.Duration(0) { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, *timeout) defer cancel() } - client := github.NewClient( - oauth2.NewClient( - ctx, - oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ), - ), - ) + client, err := githubUtil.NewClient(ctx) + if err != nil { + fatal(err) + } semVer, err := projInfo.ToSemver() if err != nil { diff --git a/go.sum b/go.sum index fc55d188..795a92ae 100644 --- a/go.sum +++ b/go.sum @@ -63,6 +63,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/pkg/changelog/changelog.go b/pkg/changelog/changelog.go index bc559c09..cb943fbd 100644 --- a/pkg/changelog/changelog.go +++ b/pkg/changelog/changelog.go @@ -50,17 +50,13 @@ func (k Kind) String() string { return "" } -// Kinds is a list of Kind which implements sort.Interface. +// Kinds is a list of Kind. type Kinds []Kind -func (k Kinds) Len() int { return len(k) } -func (k Kinds) Less(i, j int) bool { return k[i] < k[j] } -func (k Kinds) Swap(i, j int) { k[i], k[j] = k[j], k[i] } - -// ParseKinds converts a slash-separated list of Kind to a list of Kind. -func ParseKinds(s string) Kinds { +// ParseKinds converts a slice of strings to a slice of Kind. +func ParseKinds(s []string) Kinds { m := make(map[Kind]struct{}) - for _, k := range strings.Split(s, "/") { + for _, k := range s { switch k { case "CHANGE": m[kindChange] = struct{}{} @@ -77,7 +73,7 @@ func ParseKinds(s string) Kinds { for k := range m { kinds = append(kinds, k) } - sort.Stable(kinds) + sort.SliceStable(kinds, func(i, j int) bool { return kinds[i] < kinds[j] }) return kinds } @@ -89,6 +85,28 @@ func (k Kinds) String() string { return strings.Join(s, "/") } +// Before returns whether the receiver should sort before the other. +func (k Kinds) Before(other Kinds) bool { + if len(k) == 0 { + return len(other) == 0 + } + if len(other) == 0 { + return true + } + + n := len(k) + if len(k) > len(other) { + n = len(other) + } + for j := 0; j < n; j++ { + if k[j] == other[j] { + continue + } + return k[j] < other[j] + } + return len(k) <= len(other) +} + // Change represents a change description. type Change struct { Text string @@ -98,33 +116,8 @@ type Change struct { type Changes []Change func (c Changes) Sorted() error { - less := func(k1, k2 Kinds) bool { - if len(k1) == 0 { - if len(k2) == 0 { - return true - } - return false - } - if len(k2) == 0 { - return true - } - - n := len(k1) - if len(k1) > len(k2) { - n = len(k2) - } - for j := 0; j < n; j++ { - if k1[j] == k2[j] { - continue - } - return k1[j] < k2[j] - } - return len(k1) <= len(k2) - } - for i := 0; i < len(c)-1; i++ { - k1, k2 := c[i].Kinds, c[i+1].Kinds - if !less(k1, k2) { + if !c[i].Kinds.Before(c[i+1].Kinds) { return errors.Errorf("%q should be after %q", c[i].Text, c[i+1].Text) } } @@ -181,7 +174,8 @@ func ReadEntry(r io.Reader, version string) (*Entry, error) { } m := reChange.FindStringSubmatch(line) if len(m) > 1 { - entry.Changes = append(entry.Changes, Change{Text: line, Kinds: ParseKinds(m[1])}) + kinds := strings.Split(m[1], "/") + entry.Changes = append(entry.Changes, Change{Text: line, Kinds: ParseKinds(kinds)}) } lines = append(lines, line) } diff --git a/pkg/changelog/changelog_test.go b/pkg/changelog/changelog_test.go index c187ec8a..b843594b 100644 --- a/pkg/changelog/changelog_test.go +++ b/pkg/changelog/changelog_test.go @@ -247,27 +247,27 @@ This is the first stable release. func TestKinds(t *testing.T) { for _, tc := range []struct { - in string + in []string exp Kinds }{ { - in: "CHANGE", + in: []string{"CHANGE"}, exp: Kinds{kindChange}, }, { - in: "BUGFIX/CHANGE", + in: []string{"BUGFIX", "CHANGE"}, exp: Kinds{kindChange, kindBugfix}, }, { - in: "BUGFIX/BUGFIX", + in: []string{"BUGFIX", "BUGFIX"}, exp: Kinds{kindBugfix}, }, { - in: "BUGFIX/INVALID", + in: []string{"BUGFIX", "INVALID"}, exp: Kinds{kindBugfix}, }, { - in: "INVALID", + in: []string{"INVALID"}, }, } { t.Run("", func(t *testing.T) { @@ -279,6 +279,77 @@ func TestKinds(t *testing.T) { } } +func TestKindsBefore(t *testing.T) { + for _, tc := range []struct { + first Kinds + second Kinds + + before bool + }{ + { + first: Kinds{}, + before: true, + }, + { + first: Kinds{kindChange}, + second: Kinds{}, + before: true, + }, + { + first: Kinds{kindChange}, + second: Kinds{kindChange}, + before: true, + }, + { + first: Kinds{kindChange}, + second: Kinds{kindBugfix}, + before: true, + }, + { + first: Kinds{kindFeature}, + second: Kinds{kindChange}, + before: false, + }, + { + first: Kinds{kindChange}, + second: Kinds{kindChange, kindBugfix}, + before: true, + }, + { + first: Kinds{kindChange, kindBugfix}, + second: Kinds{kindChange}, + before: false, + }, + { + first: Kinds{kindChange, kindFeature, kindBugfix}, + second: Kinds{kindChange, kindBugfix}, + before: true, + }, + { + first: Kinds{kindChange, kindBugfix}, + second: Kinds{kindChange, kindFeature, kindBugfix}, + before: false, + }, + { + first: Kinds{kindChange, kindFeature, kindBugfix}, + second: Kinds{kindChange, kindBugfix}, + before: true, + }, + { + first: Kinds{}, + second: Kinds{kindChange, kindBugfix}, + before: false, + }, + } { + t.Run("", func(t *testing.T) { + before := tc.first.Before(tc.second) + if before != tc.before { + t.Fatalf("expected %t but got: %t", tc.before, before) + } + }) + } +} + func TestChangesSorted(t *testing.T) { for _, tc := range []struct { in Changes diff --git a/pkg/github/github.go b/pkg/github/github.go new file mode 100644 index 00000000..0eab1c89 --- /dev/null +++ b/pkg/github/github.go @@ -0,0 +1,66 @@ +// Copyright © 2019 Prometheus Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package github + +import ( + "context" + "os" + + "github.com/google/go-github/v25/github" + "github.com/pkg/errors" + "golang.org/x/oauth2" +) + +// ReadAll iterates over GitHub pages. +func ReadAll(list func(*github.ListOptions) (*github.Response, error)) error { + opt := github.ListOptions{PerPage: 10} + for { + resp, err := list(&opt) + if err != nil { + return err + } + if resp == nil || resp.NextPage == 0 { + return nil + } + opt.Page = resp.NextPage + } +} + +// TokenVarName is the name of the environment variable containing the token. +const TokenVarName = "GITHUB_TOKEN" + +// NewClient returns a new GitHub client with an authentication token read from TokenVarName. +func NewClient(ctx context.Context) (*github.Client, error) { + token := os.Getenv(TokenVarName) + if len(token) == 0 { + return nil, errors.Errorf("%s not defined", TokenVarName) + } + + c := github.NewClient( + oauth2.NewClient( + ctx, + oauth2.StaticTokenSource( + &oauth2.Token{ + AccessToken: token, + }, + ), + ), + ) + _, _, err := c.Zen(ctx) + if err != nil { + return nil, err + } + return c, nil +} From 9f5dc8c7e4099458da39e3e021521558764e9a85 Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Fri, 13 Sep 2019 10:57:13 +0200 Subject: [PATCH 2/9] Simplify if branch Signed-off-by: Simon Pasquier --- pkg/changelog/changelog.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/changelog/changelog.go b/pkg/changelog/changelog.go index cb943fbd..7b169d09 100644 --- a/pkg/changelog/changelog.go +++ b/pkg/changelog/changelog.go @@ -87,11 +87,10 @@ func (k Kinds) String() string { // Before returns whether the receiver should sort before the other. func (k Kinds) Before(other Kinds) bool { - if len(k) == 0 { - return len(other) == 0 - } if len(other) == 0 { return true + } else if len(k) == 0 { + return false } n := len(k) From 4f7a209ef8736cb1c92764b4e01faff1be4c8987 Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Wed, 2 Oct 2019 17:12:02 +0200 Subject: [PATCH 3/9] Refactor code Signed-off-by: Simon Pasquier --- cmd/bump.go | 100 ++++++++++++++++++++----------------- pkg/changelog/changelog.go | 16 ++++++ 2 files changed, 71 insertions(+), 45 deletions(-) diff --git a/cmd/bump.go b/cmd/bump.go index 2f7e5a8a..acac47b9 100644 --- a/cmd/bump.go +++ b/cmd/bump.go @@ -41,9 +41,10 @@ var ( ) type pullRequest struct { - Number int - Title string - Kinds changelog.Kinds + Number int + Title string + Contributor string + Kinds changelog.Kinds } var ( @@ -63,7 +64,7 @@ const changelogTmpl = `## {{ .Version }} / {{ .Date }} {{ range .PullRequests }} * [{{ .Kinds.String }}] {{ makeSentence .Title }} #{{ .Number }} {{- end }} - @@ -74,9 +75,27 @@ Contributors: ` -func writeChangelog(w io.Writer, version string, prs, skippedPrs []pullRequest, contributors []string) error { - sort.SliceStable(prs, func(i int, j int) bool { return prs[i].Kinds.Before(prs[j].Kinds) }) - sort.SliceStable(skippedPrs, func(i int, j int) bool { return skippedPrs[i].Kinds.Before(skippedPrs[j].Kinds) }) +func writeChangelog(w io.Writer, version string, prs []pullRequest) error { + var ( + visible, hidden []pullRequest + uniqContributors = map[string]struct{}{"": struct{}{}} + contributors []string + ) + for _, pr := range prs { + if len(pr.Kinds) > 0 { + visible = append(visible, pr) + } else { + hidden = append(hidden, pr) + } + + if _, ok := uniqContributors[pr.Contributor]; ok { + continue + } + uniqContributors[pr.Contributor] = struct{}{} + contributors = append(contributors, pr.Contributor) + } + sort.SliceStable(visible, func(i int, j int) bool { return visible[i].Kinds.Before(visible[j].Kinds) }) + sort.SliceStable(hidden, func(i int, j int) bool { return hidden[i].Kinds.Before(hidden[j].Kinds) }) sort.Strings(contributors) tmpl, err := template.New("changelog").Funcs( @@ -93,8 +112,8 @@ func writeChangelog(w io.Writer, version string, prs, skippedPrs []pullRequest, return tmpl.Execute(w, &changelogData{ Version: version, Date: time.Now().Format("2006-01-02"), - PullRequests: prs, - Skipped: skippedPrs, + PullRequests: visible, + Skipped: hidden, Contributors: contributors, }) } @@ -141,13 +160,10 @@ func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRele lastCommitSHA := commit.GetSHA() // Gather all pull requests merged since the last tag. - var ( - prs, skipped []pullRequest - uniqContributors = make(map[string]struct{}) - ) + var ghPrs []*github.PullRequest err = githubUtil.ReadAll( func(opts *github.ListOptions) (*github.Response, error) { - ghPrs, resp, err := client.PullRequests.List(ctx, projInfo.Owner, projInfo.Name, &github.PullRequestListOptions{ + prs, resp, err := client.PullRequests.List(ctx, projInfo.Owner, projInfo.Name, &github.PullRequestListOptions{ State: "closed", Sort: "updated", Direction: "desc", @@ -156,7 +172,7 @@ func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRele if err != nil { return nil, errors.Wrap(err, "Fail to list GitHub pull requests") } - for _, pr := range ghPrs { + for _, pr := range prs { if pr.GetBase().GetRef() != baseBranch { continue } @@ -171,32 +187,7 @@ func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRele if pr.GetMergeCommitSHA() == lastCommitSHA { continue } - - var ( - kinds []string - skip bool - ) - for _, lbl := range pr.Labels { - if lbl.GetName() == skipLabel { - skip = true - } - if strings.HasPrefix(lbl.GetName(), labelPrefix) { - kinds = append(kinds, strings.ToUpper(strings.TrimPrefix(lbl.GetName(), labelPrefix))) - } - } - p := pullRequest{ - Kinds: changelog.ParseKinds(kinds), - Title: pr.GetTitle(), - Number: pr.GetNumber(), - } - if pr.GetUser() != nil { - uniqContributors[pr.GetUser().GetLogin()] = struct{}{} - } - if skip { - skipped = append(skipped, p) - } else { - prs = append(prs, p) - } + ghPrs = append(ghPrs, pr) } return resp, nil }, @@ -205,9 +196,28 @@ func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRele return err } - var contributors []string - for k := range uniqContributors { - contributors = append(contributors, k) + // Extract information from pull requests. + var prs []pullRequest + for _, pr := range ghPrs { + var ( + kinds changelog.Kinds + contributor string + ) + for _, lbl := range pr.Labels { + s := strings.TrimPrefix(strings.ToLower(lbl.GetName()), "kind/") + if k, err := changelog.FromString(s); err == nil { + kinds = append(kinds, k) + } + } + if pr.GetUser() != nil { + contributor = pr.GetUser().GetLogin() + } + prs = append(prs, pullRequest{ + Kinds: kinds, + Number: pr.GetNumber(), + Title: pr.GetTitle(), + Contributor: contributor, + }) } // Update the changelog file. @@ -220,7 +230,7 @@ func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRele return err } defer f.Close() - err = writeChangelog(f, next.String(), prs, skipped, contributors) + err = writeChangelog(f, next.String(), prs) if err != nil { return err } diff --git a/pkg/changelog/changelog.go b/pkg/changelog/changelog.go index 7b169d09..b13719b9 100644 --- a/pkg/changelog/changelog.go +++ b/pkg/changelog/changelog.go @@ -36,6 +36,22 @@ const ( kindBugfix ) +// FromString turns a string into a Kind. +func FromString(s string) (Kind, error) { + s = strings.ToUpper(s) + switch { + case strings.Contains(s, "CHANGE"): + return kindChange, nil + case strings.Contains(s, "FEATURE"): + return kindFeature, nil + case strings.Contains(s, "ENHANCEMENT"): + return kindEnhancement, nil + case strings.Contains(s, "BUGFIX") || strings.Contains(s, "BUG"): + return kindBugfix, nil + } + return 0, errors.New("Not found") +} + func (k Kind) String() string { switch k { case kindChange: From 15803e85b416075e808493d5dcb8358535b1845f Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Wed, 2 Oct 2019 17:31:46 +0200 Subject: [PATCH 4/9] Add --dry-run option Signed-off-by: Simon Pasquier --- cmd/bump.go | 7 ++++++- cmd/promu.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/bump.go b/cmd/bump.go index acac47b9..30086d38 100644 --- a/cmd/bump.go +++ b/cmd/bump.go @@ -38,6 +38,7 @@ var ( bumpLevel = bumpcmd.Flag("level", "Level of version to increment (should be one of major, minor, patch, pre)").Default("minor").Enum("major", "minor", "patch", "pre") bumpPreRelease = bumpcmd.Flag("pre-release", "Pre-release identifier").Default("rc.0").String() bumpBaseBranch = bumpcmd.Flag("base-branch", "Pre-release identifier").Default("master").String() + bumpDryRun = bumpcmd.Flag("dry-run", "Do not modify the files").Bool() ) type pullRequest struct { @@ -118,7 +119,7 @@ func writeChangelog(w io.Writer, version string, prs []pullRequest) error { }) } -func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRelease string, baseBranch string) error { +func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRelease string, baseBranch string, dryRun bool) error { current, err := projInfo.ToSemver() if err != nil { return err @@ -220,6 +221,10 @@ func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRele }) } + if dryRun { + return writeChangelog(os.Stdout, next.String(), prs) + } + // Update the changelog file. original, err := ioutil.ReadFile(changelogPath) if err != nil { diff --git a/cmd/promu.go b/cmd/promu.go index 4006c679..95f4fde3 100644 --- a/cmd/promu.go +++ b/cmd/promu.go @@ -126,7 +126,7 @@ func Execute() { case checkLicensescmd.FullCommand(): runCheckLicenses(optArg(*checkLicLocation, 0, "."), *headerLength, *sourceExtensions) case bumpcmd.FullCommand(): - err = runBumpVersion("CHANGELOG.md", "VERSION", *bumpLevel, *bumpPreRelease, *bumpBaseBranch) + err = runBumpVersion("CHANGELOG.md", "VERSION", *bumpLevel, *bumpPreRelease, *bumpBaseBranch, *bumpDryRun) if err != nil { fatal(err) } From 84227139875782b37cea590584415a22d5c67838 Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Fri, 4 Oct 2019 15:24:04 +0200 Subject: [PATCH 5/9] Fix typos + optimize for loop Signed-off-by: Simon Pasquier --- cmd/bump.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/bump.go b/cmd/bump.go index 30086d38..50e1b0b9 100644 --- a/cmd/bump.go +++ b/cmd/bump.go @@ -37,7 +37,7 @@ var ( bumpLevel = bumpcmd.Flag("level", "Level of version to increment (should be one of major, minor, patch, pre)").Default("minor").Enum("major", "minor", "patch", "pre") bumpPreRelease = bumpcmd.Flag("pre-release", "Pre-release identifier").Default("rc.0").String() - bumpBaseBranch = bumpcmd.Flag("base-branch", "Pre-release identifier").Default("master").String() + bumpBaseBranch = bumpcmd.Flag("base-branch", "Base branch").Default("master").String() bumpDryRun = bumpcmd.Flag("dry-run", "Do not modify the files").Bool() ) @@ -148,7 +148,7 @@ func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRele client, err := githubUtil.NewClient(ctx) if err != nil { info(fmt.Sprintf("failed to create authenticated GitHub client: %v", err)) - info("Fallback to client without unauthentication") + info("fallback to GitHub client without unauthentication") client = github.NewClient(nil) } @@ -174,14 +174,14 @@ func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRele return nil, errors.Wrap(err, "Fail to list GitHub pull requests") } for _, pr := range prs { - if pr.GetBase().GetRef() != baseBranch { - continue - } if pr.GetUpdatedAt().Before(lastTagTime) { // We've reached pull requests that haven't changed since // the reference tag so we can stop now. return nil, nil } + if pr.GetBase().GetRef() != baseBranch { + continue + } if pr.GetMergedAt().IsZero() || pr.GetMergedAt().Before(lastTagTime) { continue } From 193cc50e1ab3c7f5c6ccdb9269e29e62bb72181d Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Fri, 4 Oct 2019 17:07:29 +0200 Subject: [PATCH 6/9] Add SECURITY as a supported label Signed-off-by: Simon Pasquier --- pkg/changelog/changelog.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/changelog/changelog.go b/pkg/changelog/changelog.go index b13719b9..8214f307 100644 --- a/pkg/changelog/changelog.go +++ b/pkg/changelog/changelog.go @@ -30,7 +30,8 @@ import ( type Kind int const ( - kindChange = iota + kindSecurity = iota + kindChange kindFeature kindEnhancement kindBugfix @@ -40,6 +41,8 @@ const ( func FromString(s string) (Kind, error) { s = strings.ToUpper(s) switch { + case strings.Contains(s, "SECURITY"): + return kindSecurity, nil case strings.Contains(s, "CHANGE"): return kindChange, nil case strings.Contains(s, "FEATURE"): @@ -54,6 +57,8 @@ func FromString(s string) (Kind, error) { func (k Kind) String() string { switch k { + case kindSecurity: + return "SECURITY" case kindChange: return "CHANGE" case kindFeature: @@ -74,6 +79,8 @@ func ParseKinds(s []string) Kinds { m := make(map[Kind]struct{}) for _, k := range s { switch k { + case "SECURITY": + m[kindSecurity] = struct{}{} case "CHANGE": m[kindChange] = struct{}{} case "FEATURE": From 4d086ae123ad18d68fd632a1bd00a3ca9dd7a29c Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Wed, 16 Oct 2019 15:03:52 +0200 Subject: [PATCH 7/9] Add newline to VERSION Signed-off-by: Simon Pasquier --- cmd/bump.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/bump.go b/cmd/bump.go index 50e1b0b9..b595c4d1 100644 --- a/cmd/bump.go +++ b/cmd/bump.go @@ -252,7 +252,7 @@ func runBumpVersion(changelogPath, versionPath string, bumpLevel string, preRele } defer f.Close() - _, err = f.WriteString(next.String()) + _, err = f.WriteString(next.String() + "\n") if err != nil { return err } From 7a63377c8d3de041ca48eb031cce9b48ea36c7c9 Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Wed, 18 Dec 2019 12:54:23 +0100 Subject: [PATCH 8/9] Exclude prombot from contributors Signed-off-by: Simon Pasquier --- cmd/bump.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/bump.go b/cmd/bump.go index b595c4d1..44f561ff 100644 --- a/cmd/bump.go +++ b/cmd/bump.go @@ -61,7 +61,8 @@ type changelogData struct { Contributors []string } -const changelogTmpl = `## {{ .Version }} / {{ .Date }} +const ( + changelogTmpl = `## {{ .Version }} / {{ .Date }} {{ range .PullRequests }} * [{{ .Kinds.String }}] {{ makeSentence .Title }} #{{ .Number }} {{- end }} @@ -75,6 +76,8 @@ Contributors: {{- end }} ` + prombotUser = "prombot" +) func writeChangelog(w io.Writer, version string, prs []pullRequest) error { var ( @@ -93,6 +96,9 @@ func writeChangelog(w io.Writer, version string, prs []pullRequest) error { continue } uniqContributors[pr.Contributor] = struct{}{} + if pr.Contributor == prombotUser { + continue + } contributors = append(contributors, pr.Contributor) } sort.SliceStable(visible, func(i int, j int) bool { return visible[i].Kinds.Before(visible[j].Kinds) }) From f66f0f012abf3ab7a5717d68c584e97a61f33381 Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Fri, 14 Feb 2020 14:42:19 +0100 Subject: [PATCH 9/9] Comment out the contributors list by default Signed-off-by: Simon Pasquier --- cmd/bump.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/bump.go b/cmd/bump.go index 44f561ff..09368d32 100644 --- a/cmd/bump.go +++ b/cmd/bump.go @@ -70,11 +70,12 @@ const ( * [{{ .Kinds.String }}] {{ makeSentence .Title }} #{{ .Number }} {{- end }} --> + ` prombotUser = "prombot" )