Skip to content

Commit

Permalink
Merge pull request #87314 from cockroachdb/blathers/backport-release-…
Browse files Browse the repository at this point in the history
…21.2-85490
  • Loading branch information
nickvigilante authored Sep 12, 2022
2 parents 361c9dd + cd3d484 commit a4b0f48
Show file tree
Hide file tree
Showing 2 changed files with 747 additions and 120 deletions.
162 changes: 115 additions & 47 deletions pkg/cmd/docs-issue-generation/docs_issue_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,29 @@ import (
"time"
)

// commit contains details about each formatted commit
type commit struct {
sha string
title string
releaseNote string
// docsIssue contains details about each formatted commit to be committed to the docs repo
type docsIssue struct {
sourceCommitSha string
title string
body string
}

// pr contains details about the pull request
// pr contains details about the cockroach pull request
type pr struct {
number int
mergeBranch string
commits []commit
docsIssues []docsIssue
}

// ghSearch contains a list of search items (in this case, PRs) from the GitHub API
type ghSearch struct { // this struct holds the search results of all PRs based on a given commit
// ghSearch contains a list of search items (in this case, cockroach PRs) based on a given commit from the GitHub API.
type ghSearch struct {
Items []ghSearchItem `json:"items"`
}

// ghSearchItem contains details about a specific PR, including its number, from the GitHub API
// ghSearchItem contains details about a specific cockroach PR,
// including its number, from the GitHub API.
type ghSearchItem struct {
Number int `json:"number"` // PR number
PRNumber int `json:"number"`
PullRequest ghSearchItemPr `json:"pull_request"`
}

Expand All @@ -53,7 +54,9 @@ type ghSearchItemPr struct {
MergedAt *time.Time `json:"merged_at"`
}

// this struct holds details about the PR from the GitHub API
// ghPull holds the base branch for a cockroach PR from the GitHub API.
// It directly mirrors the structure of the PR where the branch name
// field (Ref) is nested within the Base field.
type ghPull struct {
Base struct {
Ref string `json:"ref"` // this is the destination branch of the PR
Expand All @@ -66,7 +69,7 @@ type ghPullCommit struct {
Commit ghPullCommitMsg `json:"commit"`
}

// ghPullCommitMsg holds the commit message from the GitHub API
// ghPullCommitMsg holds the commit message in the cockroach PR from the GitHub API
type ghPullCommitMsg struct {
Message string `json:"message"` // the commit message
}
Expand All @@ -81,21 +84,36 @@ type parameters struct {
func docsIssueGeneration(params parameters) {
var search ghSearch // a search for all PRs based on a given commit SHA
var prs []pr
if err := httpGet("https://api.github.com/search/issues?q=sha:"+params.Sha+"+repo:cockroachdb/cockroach+is:merged", params.Token, &search); err != nil {
err := httpGet(
"https://api.github.com/search/issues?q=sha:"+params.Sha+"+repo:cockroachdb/cockroach+is:merged",
params.Token,
&search,
)
if err != nil {
log.Fatal(err)
}
prs = prNums(search) // populate slice of PRs of type pr
for _, pr := range prs { // for each PR in the list, get the merge branch and the list of eligible commits
var pull ghPull
if err := httpGet("https://api.github.com/repos/cockroachdb/cockroach/pulls/"+strconv.Itoa(pr.number), params.Token, &pull); err != nil {
err := httpGet(
"https://api.github.com/repos/cockroachdb/cockroach/pulls/"+strconv.Itoa(pr.number),
params.Token,
&pull,
)
if err != nil {
log.Fatal(err)
}
pr.mergeBranch = pull.Base.Ref
var commits []ghPullCommit
if err := httpGet("https://api.github.com/repos/cockroachdb/cockroach/pulls/"+strconv.Itoa(pr.number)+"/commits?per_page:250", params.Token, &commits); err != nil {
err = httpGet(
"https://api.github.com/repos/cockroachdb/cockroach/pulls/"+strconv.Itoa(pr.number)+"/commits?per_page:250",
params.Token,
&commits,
)
if err != nil {
log.Fatal(err)
}
pr.commits = getCommits(commits, pr.number)
pr.docsIssues = getIssues(commits, pr.number)
pr.createDocsIssues(params.Token)
}
}
Expand All @@ -115,7 +133,8 @@ func httpGet(url string, token string, out interface{}) error {
if err != nil {
return err
}
if err := json.Unmarshal(bs, out); err != nil { // unmarshal (convert) the byte slice into an interface
// unmarshal (convert) the byte slice into an interface
if err := json.Unmarshal(bs, out); err != nil {
return err
}
return nil
Expand All @@ -124,61 +143,110 @@ func httpGet(url string, token string, out interface{}) error {
// prNums returns an array of PRS for the given GH search of PRs against a given commit.
func prNums(search ghSearch) []pr {
var result []pr
for _, x := range search.Items { // each PR returned from the search is iterated through, and a new pr object is created for each
// each PR returned from the search is iterated through, and a new pr object is created for each
for _, x := range search.Items {
if x.PullRequest.MergedAt != nil {
result = append(result, pr{number: x.Number}) // a new object of type pr containing the PR number is appended to the result slice
// a new object of type pr containing the PR number is appended to the result slice
result = append(result, pr{number: x.PRNumber})
}
}
return result // return a slice of prs
}

// getCommits takes a list of commits from GitHub as well as the PR associated with those commits and outputs a formatted list of commits with valid release notes on that PR
func getCommits(pullCommit []ghPullCommit, prNumber int) []commit {
var result []commit
// getIssues takes a list of commits from GitHub as well as the PR number associated with those commits and outputs a
// formatted list of docs issues with valid release notes
func getIssues(pullCommit []ghPullCommit, prNumber int) []docsIssue {
var result []docsIssue
for _, c := range pullCommit {
message := c.Commit.Message
rn := formatReleaseNote(message, prNumber, c.Sha) // return a slice of data that locates every instance of the phrase "release note (", case insensitive
if rn != "" { // checks to make sure a match was returned
x := commit{ // declare a new commit object
sha: c.Sha,
title: formatTitle(message),
releaseNote: rn,
// return a slice of data that locates every instance of the phrase "release note (", case-insensitive
rns := formatReleaseNotes(message, prNumber, c.Sha)
for i, rn := range rns {
{ // checks to make sure a match was returned
x := docsIssue{ // declare a new commit object
sourceCommitSha: c.Sha,
title: formatTitle(message, prNumber, i+1, len(rns)),
body: rn,
}
result = append(result, x)
}
result = append(result, x)
}
}
return result
}

func formatReleaseNote(message string, prNumber int, sha string) string {
re := regexp.MustCompile(`(?s)[rR]elease [nN]ote \(.*`) // this regex checks to make sure there's a release note within the commit
reNeg := regexp.MustCompile(`([rR]elease [nN]ote \(bug fix.*)|([rR]elease [nN]ote: [nN]one)`) // this regex is used to exclude bug fixes or releases without release notes
rn := re.FindString(message) // return the first instance of the phrase "release note (", case insensitive
if len(rn) > 0 && !reNeg.MatchString(message) { // checks to make sure the desired release note is not null and doesn't match the negating string.
return fmt.Sprintf(
"Related PR: https://github.com/cockroachdb/cockroach/pull/%s\nCommit: https://github.com/cockroachdb/cockroach/commit/%s\n\n---\n\n%s",
var (
noRNRE = regexp.MustCompile(`[rR]elease [nN]ote: [nN]one`)
allRNRE = regexp.MustCompile(`[rR]elease [nN]ote \(.*`)
bugFixRNRE = regexp.MustCompile(`([rR]elease [nN]ote \(bug fix\):.*)`)
releaseJustificationRE = regexp.MustCompile(`[rR]elease [jJ]ustification:.*`)
)

// formatReleaseNotes generates a list of docsIssue bodies for the docs repo based on a given CRDB sha
func formatReleaseNotes(message string, prNumber int, crdbSha string) []string {
rnBodySlice := []string{}
if noRNRE.MatchString(message) {
return rnBodySlice
}
splitString := strings.Split(message, "\n")
releaseNoteLines := []string{}
var rnBody string
for _, x := range splitString {
validRn := allRNRE.MatchString(x)
bugFixRn := bugFixRNRE.MatchString(x)
releaseJustification := releaseJustificationRE.MatchString(x)
if len(releaseNoteLines) > 0 && (validRn || releaseJustification) {
rnBody = fmt.Sprintf(
"Related PR: https://github.com/cockroachdb/cockroach/pull/%s\n"+
"Commit: https://github.com/cockroachdb/cockroach/commit/%s\n"+
"\n---\n\n%s",
strconv.Itoa(prNumber),
crdbSha,
strings.Join(releaseNoteLines, "\n"),
)
rnBodySlice = append(rnBodySlice, strings.TrimSuffix(rnBody, "\n"))
rnBody = ""
releaseNoteLines = []string{}
}
if (validRn && !bugFixRn) || (len(releaseNoteLines) > 0 && !bugFixRn && !releaseJustification) {
releaseNoteLines = append(releaseNoteLines, x)
}
}
if len(releaseNoteLines) > 0 { // commit whatever is left in the buffer to the rnBodySlice set
rnBody = fmt.Sprintf(
"Related PR: https://github.com/cockroachdb/cockroach/pull/%s\n"+
"Commit: https://github.com/cockroachdb/cockroach/commit/%s\n"+
"\n---\n\n%s",
strconv.Itoa(prNumber),
sha,
rn,
crdbSha,
strings.Join(releaseNoteLines, "\n"),
)
rnBodySlice = append(rnBodySlice, strings.TrimSuffix(rnBody, "\n"))
}
return ""
return rnBodySlice
}

func formatTitle(message string) string {
if i := strings.IndexRune(message, '\n'); i > 0 {
return message[:i]
func formatTitle(message string, prNumber int, index int, totalLength int) string {
var commitTitle string
if i := strings.IndexRune(message, '\n'); i > 0 { //
commitTitle = message[:i]
} else {
commitTitle = message
}
result := fmt.Sprintf("PR #%d - %s", prNumber, commitTitle)
if totalLength > 1 {
result += fmt.Sprintf(" (%d of %d)", index, totalLength)
}
return message
return result
}

func (p pr) createDocsIssues(token string) {
postURL := "https://api.github.com/repos/cockroachdb/docs/issues"
for _, commit := range p.commits {
for _, issue := range p.docsIssues {
reqBody, err := json.Marshal(map[string]interface{}{
"title": commit.title,
"title": issue.title,
"labels": []string{"C-product-change", p.mergeBranch},
"body": commit.releaseNote,
"body": issue.body,
})
if err != nil {
log.Fatal(err)
Expand Down
Loading

0 comments on commit a4b0f48

Please sign in to comment.