diff --git a/cmd/generate.go b/cmd/generate.go index 554793b..3f2d8ef 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -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) diff --git a/cmd/validate.go b/cmd/validate.go index 07a21dd..c09650a 100644 --- a/cmd/validate.go +++ b/cmd/validate.go @@ -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" ) @@ -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() { @@ -49,4 +49,3 @@ func validate(cmd *cobra.Command, args []string) { fmt.Printf("pull request #%d has valid labels\n", prNumber) } - diff --git a/generate/generate.go b/generate/generate.go index 6293f69..d12c159 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -122,7 +122,7 @@ func Validate(labels []string) bool { for _, validLabel := range ValidLabels { validLabelsMap[validLabel] = true } - + for _, label := range labels { if _, exists := validLabelsMap[label]; exists { return true @@ -130,4 +130,4 @@ func Validate(labels []string) bool { } return false -} \ No newline at end of file +} diff --git a/generate/template.go b/generate/template.go index df69dfd..896d85c 100644 --- a/generate/template.go +++ b/generate/template.go @@ -31,7 +31,7 @@ const rawTemplate = ` ## {{$section.Icon}} {{$section.Title}} {{ range $pr := $section.PRs }} -* **{{$pr.Title}} (#{{$pr.Number}})** @{{$pr.Author}} :link: +* {{$pr.Title}} (#{{$pr.Number}}) @{{$pr.Author}} :link: {{ $pr.ReleaseNote | indent 2 }} {{end}} {{end}} diff --git a/github/github.go b/github/github.go index fbc9e70..2ba57f6 100644 --- a/github/github.go +++ b/github/github.go @@ -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 { @@ -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) { @@ -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 +}