Skip to content

Commit

Permalink
add support for generating release note from branch
Browse files Browse the repository at this point in the history
Previously, the generate command would only work for generating a
release note using the latest release created from the repository. This
was changed so that release branches that create older releases can
still generate release notes from the release branch. For example, if
the latest release is v6.0.0 and I have a v5.5.x release branch with the
latest release on that branch being v5.5.5, I can generate a release
note for that release branch for my next release of v5.5.6.

It achieves this by grabbing all the last 50 releases made to the
repository and then getting the commit SHAs that are tagged for those
releases. It then goes through all the commits on the branch and tries
to match it to one of the 50 commit release SHAs. If it finds one, it
will fetch all the prs made after that commit.

Also changed some formatting of the release note template so that it
will not bold all the text for each pr.

Signed-off-by: Clara Fu <[email protected]>
Co-authored-by: Alex Suraci <[email protected]>
  • Loading branch information
clarafu and vito committed Jul 30, 2020
1 parent 3b9f979 commit 4a8abf6
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 72 deletions.
9 changes: 7 additions & 2 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ func generateReleaseNote(cmd *cobra.Command, args []string) {
githubOwner, _ := cmd.Flags().GetString("github-owner")
githubRepo, _ := cmd.Flags().GetString("github-repo")

commitSHA, err := client.FetchLatestReleaseCommitSHA(githubOwner, githubRepo)
releaseSHAs, err := client.FetchCommitsFromReleases(githubOwner, githubRepo)
if err != nil {
failf("failed to fetch latest release commit SHA from github: %s", err)
failf("failed to fetch release commit SHAs from github: %s", err)
}

githubBranch, _ := cmd.Flags().GetString("github-branch")

commitSHA, err := client.FetchLatestReleaseCommitFromBranch(githubOwner, githubRepo, githubBranch, releaseSHAs)
if err != nil {
failf("failed to fetch latest release commit from branch: %s", err)
}

pullRequests, err := client.FetchPullRequestsAfterCommit(githubOwner, githubRepo, githubBranch, commitSHA)
if err != nil {
failf("failed to fetch pull requests: %s", err)
Expand Down
5 changes: 2 additions & 3 deletions cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"fmt"
"strconv"

"github.com/clarafu/release-me/github"
"github.com/clarafu/release-me/generate"
"github.com/clarafu/release-me/github"
"github.com/spf13/cobra"
)

Expand All @@ -15,7 +15,7 @@ var validateCmd = &cobra.Command{
Long: `Ensures that the pull request given has at least one of the labels
required to properly generate a release note using the "generate"
command.`,
Run: validate,
Run: validate,
}

func init() {
Expand Down Expand Up @@ -49,4 +49,3 @@ func validate(cmd *cobra.Command, args []string) {

fmt.Printf("pull request #%d has valid labels\n", prNumber)
}

4 changes: 2 additions & 2 deletions generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,12 @@ func Validate(labels []string) bool {
for _, validLabel := range ValidLabels {
validLabelsMap[validLabel] = true
}

for _, label := range labels {
if _, exists := validLabelsMap[label]; exists {
return true
}
}

return false
}
}
2 changes: 1 addition & 1 deletion generate/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const rawTemplate = `
## {{$section.Icon}} {{$section.Title}}
{{ range $pr := $section.PRs }}
* **{{$pr.Title}} (#{{$pr.Number}})** @{{$pr.Author}} <sub><sup><a name="{{$pr.Number}}" href="#{{$pr.Number}}">:link:</a></sup></sub>
* {{$pr.Title}} (#{{$pr.Number}}) @{{$pr.Author}} <sub><sup><a name="{{$pr.Number}}" href="#{{$pr.Number}}">:link:</a></sup></sub>
{{ $pr.ReleaseNote | indent 2 }}
{{end}}
{{end}}
Expand Down
149 changes: 85 additions & 64 deletions github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,40 +42,8 @@ func New(token string) GitHub {
}
}

func (g GitHub) FetchLabelsForPullRequest(owner, repo string, pullRequestNumber int) ([]string, error) {
var PullRequestlabelsQuery struct {
Repository struct {
PullRequest struct {
Labels struct {
Nodes []struct {
Name string
}
} `graphql:"labels(first: 10)"`
} `graphql:"pullRequest(number: $prNumber)"`
} `graphql:"repository(owner: $owner, name: $name)"`
}

PRVariables := map[string]interface{}{
"owner": githubv4.String(owner),
"name": githubv4.String(repo),
"prNumber": githubv4.Int(pullRequestNumber),
}

err := g.client.Query(context.Background(), &PullRequestlabelsQuery, PRVariables)
if err != nil {
return nil, err
}

var labels []string
for _, node := range PullRequestlabelsQuery.Repository.PullRequest.Labels.Nodes {
labels = append(labels, node.Name)
}

return labels, nil
}

func (g GitHub) FetchLatestReleaseCommitSHA(owner, repo string) (string, error) {
var releaseSHAQuery struct {
func (g GitHub) FetchCommitsFromReleases(owner, repo string) (map[string]bool, error) {
var releaseSHAsQuery struct {
Repository struct {
Releases struct {
Nodes []struct {
Expand All @@ -85,60 +53,81 @@ func (g GitHub) FetchLatestReleaseCommitSHA(owner, repo string) (string, error)
}
}
}
} `graphql:"releases(last: 1, orderBy:{direction: ASC, field: NAME})"`
} `graphql:"releases(first: 50, orderBy: {direction: DESC, field: CREATED_AT})"`
} `graphql:"repository(owner: $owner, name: $name)"`
}

releaseSHAVariables := map[string]interface{}{
releaseSHAsVariables := map[string]interface{}{
"owner": githubv4.String(owner),
"name": githubv4.String(repo),
}

err := g.client.Query(context.Background(), &releaseSHAQuery, releaseSHAVariables)
err := g.client.Query(context.Background(), &releaseSHAsQuery, releaseSHAsVariables)
if err != nil {
return "", err
return nil, err
}

// If the repository does not have a release, return an empty string
var releaseSHA string
if len(releaseSHAQuery.Repository.Releases.Nodes) > 0 {
releaseSHA = releaseSHAQuery.Repository.Releases.Nodes[0].Tag.Target.Oid
releaseSHAs := map[string]bool{}
for _, release := range releaseSHAsQuery.Repository.Releases.Nodes {
releaseSHAs[release.Tag.Target.Oid] = true
}

return releaseSHA, nil
return releaseSHAs, nil
}

func (g GitHub) FetchCommitFromTag(owner, repo, tag string) (string, error) {
var releaseSHAQuery struct {
func (g GitHub) FetchLatestReleaseCommitFromBranch(owner, repo, branch string, releaseSHAs map[string]bool) (string, error) {
var commitsQuery struct {
Repository struct {
Refs struct {
Nodes []struct {
Target struct {
Oid string
}
Ref struct {
Target struct {
Commit struct {
History struct {
Nodes []struct {
Oid string
}
PageInfo struct {
EndCursor githubv4.String
HasNextPage bool
}
} `graphql:"history(first: 100, after: $commitCursor)"`
} `graphql:"... on Commit"`
}
} `graphql:"refs(refPrefix: "refs/tags/", first: 1, query: $tag)"`
} `graphql:"ref(qualifiedName: $branch)"`
} `graphql:"repository(owner: $owner, name: $name)"`
}

releaseSHAVariables := map[string]interface{}{
"owner": githubv4.String(owner),
"name": githubv4.String(repo),
"tag": githubv4.String(tag),
commitsVariables := map[string]interface{}{
"owner": githubv4.String(owner),
"name": githubv4.String(repo),
"branch": githubv4.String(branch),
"commitCursor": (*githubv4.String)(nil),
}

err := g.client.Query(context.Background(), &releaseSHAQuery, releaseSHAVariables)
if err != nil {
return "", err
}
var lastCommit string
for {
err := g.client.Query(context.Background(), &commitsQuery, commitsVariables)
if err != nil {
return "", fmt.Errorf("failed to fetch commits from github: %w", err)
}

history := commitsQuery.Repository.Ref.Target.Commit.History
for _, commit := range history.Nodes {
lastCommit = commit.Oid

if _, found := releaseSHAs[commit.Oid]; found {
return commit.Oid, nil
}
}

if !history.PageInfo.HasNextPage {
fmt.Printf("could not find a commit from the latest release, generating release note using all commits in branch %s\n", branch)
break
}

// If the repository does not have a release, return an empty string
var releaseSHA string
if len(releaseSHAQuery.Repository.Refs.Nodes) > 0 {
releaseSHA = releaseSHAQuery.Repository.Refs.Nodes[0].Target.Oid
commitsVariables["commitCursor"] = history.PageInfo.EndCursor
}

return releaseSHA, nil
return lastCommit, nil
}

func (g GitHub) FetchPullRequestsAfterCommit(owner, repo, branch, commitSHA string) ([]PullRequest, error) {
Expand Down Expand Up @@ -241,3 +230,35 @@ func (g GitHub) FetchPullRequestsAfterCommit(owner, repo, branch, commitSHA stri
}
return pullRequests, nil
}

func (g GitHub) FetchLabelsForPullRequest(owner, repo string, pullRequestNumber int) ([]string, error) {
var PullRequestlabelsQuery struct {
Repository struct {
PullRequest struct {
Labels struct {
Nodes []struct {
Name string
}
} `graphql:"labels(first: 10)"`
} `graphql:"pullRequest(number: $prNumber)"`
} `graphql:"repository(owner: $owner, name: $name)"`
}

PRVariables := map[string]interface{}{
"owner": githubv4.String(owner),
"name": githubv4.String(repo),
"prNumber": githubv4.Int(pullRequestNumber),
}

err := g.client.Query(context.Background(), &PullRequestlabelsQuery, PRVariables)
if err != nil {
return nil, err
}

var labels []string
for _, node := range PullRequestlabelsQuery.Repository.PullRequest.Labels.Nodes {
labels = append(labels, node.Name)
}

return labels, nil
}

0 comments on commit 4a8abf6

Please sign in to comment.