Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better config #28

Merged
merged 2 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions cmd/new.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
package cmd

import (
"fmt"
"time"

"github.com/briandowns/spinner"
"github.com/chelnak/gh-changelog/internal/pkg/changelog"
"github.com/chelnak/gh-changelog/internal/pkg/writer"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var newCmd = &cobra.Command{
Use: "new",
Short: "Creates a new changelog from activity in the current repository",
Long: "Creates a new changelog from activity the current repository.",
RunE: func(command *cobra.Command, args []string) error {
s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
_ = s.Color("green")
s.FinalMSG = fmt.Sprintf("✅ Open %s or run 'gh changelog show' to view your changelog.\n", viper.GetString("file_name"))

changeLog, err := changelog.MakeFullChangelog(s)
changeLog, err := changelog.NewChangelog()
if err != nil {
return err
}

s.Stop()
return writer.Write(changeLog)
},
}
212 changes: 212 additions & 0 deletions internal/pkg/changelog/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package changelog

import (
"fmt"
"strings"
"time"

"github.com/briandowns/spinner"
"github.com/chelnak/gh-changelog/internal/pkg/gitclient"
"github.com/chelnak/gh-changelog/internal/pkg/githubclient"
"github.com/chelnak/gh-changelog/internal/pkg/utils"
"github.com/google/go-github/v43/github"
"github.com/spf13/viper"
)

type Entry struct {
Tag string
NextTag string
Date time.Time
Added []string
Changed []string
Deprecated []string
Removed []string
Fixed []string
Security []string
Other []string
}

func (e *Entry) Append(section string, entry string) error {
switch strings.ToLower(section) {
case "added":
e.Added = append(e.Added, entry)
case "changed":
e.Changed = append(e.Changed, entry)
case "deprecated":
e.Deprecated = append(e.Deprecated, entry)
case "removed":
e.Removed = append(e.Removed, entry)
case "fixed":
e.Fixed = append(e.Fixed, entry)
case "security":
e.Security = append(e.Security, entry)
case "other":
e.Other = append(e.Other, entry)
default:
return fmt.Errorf("unknown entry type '%s'", section)
}

return nil
}

type ChangeLog struct {
RepoName string
RepoOwner string
Entries []Entry
}

func NewChangeLogBuilder(gitClient *gitclient.GitClient, githubClient *githubclient.GitHubClient, tags []*gitclient.Ref) *changeLogBuilder {
s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
_ = s.Color("green")
s.FinalMSG = fmt.Sprintf("✅ Open %s or run 'gh changelog show' to view your changelog.\n", viper.GetString("file_name"))

return &changeLogBuilder{
spinner: s,
gitClient: gitClient,
githubClient: githubClient,
tags: tags,
}
}

type changeLogBuilder struct {
spinner *spinner.Spinner
gitClient *gitclient.GitClient
githubClient *githubclient.GitHubClient
tags []*gitclient.Ref
}

func (builder *changeLogBuilder) Build() (*ChangeLog, error) {
changeLog := &ChangeLog{
RepoName: builder.githubClient.RepoContext.Name,
RepoOwner: builder.githubClient.RepoContext.Owner,
Entries: []Entry{},
}

builder.spinner.Start()
err := builder.buildChangeLog(changeLog)
if err != nil {
builder.spinner.FinalMSG = ""
builder.spinner.Stop()
return nil, err
}

builder.spinner.Stop()
return changeLog, nil
}

func (builder *changeLogBuilder) buildChangeLog(changeLog *ChangeLog) error {
for idx, currentTag := range builder.tags {
builder.spinner.Suffix = fmt.Sprintf(" Processing tags: 🏷️ %s", currentTag.Name)

var nextTag *gitclient.Ref
var err error
if idx+1 == len(builder.tags) {
nextTag, err = builder.gitClient.GetFirstCommit()
if err != nil {
return fmt.Errorf("could not get first commit: %v", err)
}
} else {
nextTag = builder.tags[idx+1]
}

pullRequests, err := builder.githubClient.GetPullRequestsBetweenDates(nextTag.Date, currentTag.Date)
if err != nil {
return fmt.Errorf(
"could not get pull requests for range '%s - %s': %v",
nextTag.Date,
currentTag.Date,
err,
)
}

entry, err := builder.populateEntry(
currentTag.Name,
nextTag.Name,
currentTag.Date,
pullRequests,
)
if err != nil {
return fmt.Errorf("could not process pull requests: %v", err)
}

changeLog.Entries = append(changeLog.Entries, *entry)
}

return nil
}

func (builder *changeLogBuilder) populateEntry(currentTag string, nextTag string, date time.Time, pullRequests []*github.Issue) (*Entry, error) {
entry := &Entry{
Tag: currentTag,
NextTag: nextTag,
Date: date,
Added: []string{},
Changed: []string{},
Deprecated: []string{},
Removed: []string{},
Fixed: []string{},
Security: []string{},
Other: []string{},
}

excludedLabels := viper.GetStringSlice("excluded_labels")
for _, pr := range pullRequests {
if !hasExcludedLabel(excludedLabels, pr) {
line := fmt.Sprintf(
"%s [#%d](https://github.com/%s/%s/pull/%d) ([%s](https://github.com/%s))\n",
pr.GetTitle(),
pr.GetNumber(),
builder.githubClient.RepoContext.Owner,
builder.githubClient.RepoContext.Name,
pr.GetNumber(),
pr.GetUser().GetLogin(),
pr.GetUser().GetLogin(),
)

section := getSection(pr.Labels)
if section != "" {
err := entry.Append(section, line)
if err != nil {
return nil, err
}
}
}
}

return entry, nil
}

func hasExcludedLabel(excludedLabels []string, pr *github.Issue) bool {
for _, label := range pr.Labels {
if utils.Contains(excludedLabels, label.GetName()) {
return true
}
}

return false
}

func getSection(labels []*github.Label) string {
sections := viper.GetStringMapStringSlice("sections")

lookup := make(map[string]string)
for k, v := range sections {
for _, label := range v {
lookup[label] = k
}
}

section := ""
skipUnlabelledEntries := viper.GetBool("skip_entries_without_label")
for _, label := range labels {
if _, ok := lookup[label.GetName()]; ok {
section = lookup[label.GetName()]
} else {
if !skipUnlabelledEntries {
section = "Other"
}
}
}

return section
}
118 changes: 9 additions & 109 deletions internal/pkg/changelog/changelog.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@ package changelog

import (
"fmt"
"time"

"github.com/briandowns/spinner"
"github.com/chelnak/gh-changelog/internal/pkg/gitclient"
"github.com/chelnak/gh-changelog/internal/pkg/githubclient"
"github.com/chelnak/gh-changelog/internal/pkg/utils"
"github.com/google/go-github/v43/github"
"github.com/spf13/viper"
)

func MakeFullChangelog(spinner *spinner.Spinner) (*ChangeLogProperties, error) {
client, err := githubclient.NewGitHubClient()
func NewChangelog() (*ChangeLog, error) {
githubClient, err := githubclient.NewGitHubClient()
if err != nil {
return nil, fmt.Errorf("❌ %s", err)
}
Expand All @@ -23,115 +18,20 @@ func MakeFullChangelog(spinner *spinner.Spinner) (*ChangeLogProperties, error) {
return nil, fmt.Errorf("❌ %s", err)
}

changeLog := NewChangeLogProperties(client.RepoContext.Owner, client.RepoContext.Name)

spinner.Suffix = " Gathering all tags"
spinner.Start()

tags, err := gitClient.GetTags()
if err != nil {
return nil, fmt.Errorf("❌ could not get tags: %v", err)
}

spinner.Suffix = " Gathering all pull requests"
for idx, currentTag := range tags {
spinner.Suffix = fmt.Sprintf(" Processing tags: 🏷️ %s", currentTag.Name)

var nextTag *gitclient.Ref
if idx+1 == len(tags) {
nextTag, err = gitClient.GetFirstCommit()
if err != nil {
return nil, fmt.Errorf("❌ could not get first commit: %v", err)
}
} else {
nextTag = tags[idx+1]
}

pullRequests, err := client.GetPullRequestsBetweenDates(nextTag.Date, currentTag.Date)
if err != nil {
return nil, fmt.Errorf(
"❌ could not get pull requests for range '%s - %s': %v",
nextTag.Date,
currentTag.Date,
err,
)
}

tagProperties, err := getTagProperties(
currentTag.Name,
nextTag.Name,
currentTag.Date,
pullRequests,
viper.GetStringSlice("excluded_labels"),
client.RepoContext,
)
if err != nil {
return nil, fmt.Errorf("❌ could not process pull requests: %v", err)
}

changeLog.Tags = append(changeLog.Tags, *tagProperties)
}

return changeLog, nil
}

func getTagProperties(currentTag string, nextTag string, date time.Time, pullRequests []*github.Issue, excludedLabels []string, repoContext githubclient.RepoContext) (*TagProperties, error) {
tagProperties := NewTagProperties(currentTag, nextTag, date)
for _, pr := range pullRequests {
if !hasExcludedLabel(excludedLabels, pr) {
entry := fmt.Sprintf(
"%s [#%d](https://github.com/%s/%s/pull/%d) ([%s](https://github.com/%s))\n",
pr.GetTitle(),
pr.GetNumber(),
repoContext.Owner,
repoContext.Name,
pr.GetNumber(),
pr.GetUser().GetLogin(),
pr.GetUser().GetLogin(),
)

section := getSection(pr.Labels)
err := tagProperties.Append(section, entry)
if err != nil {
return nil, err
}
}
}

return tagProperties, nil
}

func hasExcludedLabel(excludedLabels []string, pr *github.Issue) bool {
for _, label := range pr.Labels {
if utils.Contains(excludedLabels, label.GetName()) {
return true
}
if len(tags) < 1 {
return nil, fmt.Errorf("💡 no tags found in the current repository")
}

return false
}

func getSection(labels []*github.Label) string {
sections := viper.GetStringMapStringSlice("sections")

lookup := make(map[string]string)
for k, v := range sections {
for _, label := range v {
lookup[label] = k
}
}

section := ""
for _, label := range labels {
if _, ok := lookup[label.GetName()]; ok {
section = lookup[label.GetName()]
}
}

skipUnlabelledEntries := viper.GetBool("skip_entries_without_label")
if !skipUnlabelledEntries {
section = "Other"
builder := NewChangeLogBuilder(gitClient, githubClient, tags)
changeLog, err := builder.Build()
if err != nil {
return nil, fmt.Errorf("❌ %s", err)
}

return section
return changeLog, nil
}
Loading