summaryrefslogtreecommitdiff
path: root/cmd/generate/src_contribs.go
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-05-19 02:29:46 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-05-19 02:29:46 -0600
commit632b189cfed31bf1a1f1edf0c1ae69f294ef4123 (patch)
treeb375ecf1391247bcd4439b7880a69c86b042aa42 /cmd/generate/src_contribs.go
parent75d2ab4f4d2415b1ecead2361acddb1e6d6392dd (diff)
cmd/generate: Factor forge_*.go files out of src_contribs.go
Diffstat (limited to 'cmd/generate/src_contribs.go')
-rw-r--r--cmd/generate/src_contribs.go397
1 files changed, 28 insertions, 369 deletions
diff --git a/cmd/generate/src_contribs.go b/cmd/generate/src_contribs.go
index 2ace0fd..b5345e3 100644
--- a/cmd/generate/src_contribs.go
+++ b/cmd/generate/src_contribs.go
@@ -2,9 +2,7 @@ package main
import (
"fmt"
- "net/url"
"os"
- "regexp"
"strings"
"time"
@@ -96,163 +94,48 @@ func classifyStatus(status string) (string, error) {
}
}
-var (
- reGoLangGerritCL = regexp.MustCompile(`https://go-review\.googlesource\.com/c/([^/?#]+)/\+/([0-9]+)(?:\?[^#]*)?(?:#.*)?$`)
- reGitHubPR = regexp.MustCompile(`^https://github\.com/([^/?#]+)/([^/?#]+)/pull/([0-9]+)(?:\?[^#]*)?(?:#.*)?$`)
- reGitHubCommit = regexp.MustCompile(`^https://github\.com/([^/?#]+)/([^/?#]+)/commit/([0-9a-f]+)(?:\?[^#]*)?(?:#.*)?$`)
- reGitLabMR = regexp.MustCompile(`^https://([^/]+)/([^?#]+)/-/merge_requests/([0-9]+)(?:\?[^#]*)?(?:#.*)?$`)
- rePiperMailDate = regexp.MustCompile(`^\s*<I>([^<]+)</I>\s*$`)
-)
-
const (
statusOpen = "open"
statusMerged = "merged, not yet in a release"
statusReleasedFmt = "merged, released in %s"
)
-func (c Contribution) fetchStatus() (string, error) {
- if m := reGitHubPR.FindStringSubmatch(c.URLs[0]); m != nil {
- user := m[1]
- repo := m[2]
- prnum := m[3]
-
- urlStr := "https://api.github.com/repos/" + user + "/" + repo + "/pulls/" + prnum
-
- var obj struct {
- // State values are "open" and "closed".
- State string `json:"state"`
- Merged bool `json:"merged"`
- MergeCommitSha string `json:"merge_commit_sha"`
- }
- if err := httpGetJSON(urlStr, nil, &obj); err != nil {
- return "", err
- }
- ret := obj.State
- if obj.Merged {
- ret = statusMerged
- tag, err := getGitTagThatContainsAll("https://github.com/"+user+"/"+repo, obj.MergeCommitSha)
- if err != nil {
- return "", err
- }
- if tag != "" {
- ret = fmt.Sprintf(statusReleasedFmt, tag)
- }
- }
-
- return ret, nil
- }
- if m := reGitLabMR.FindStringSubmatch(c.URLs[0]); m != nil {
- authority := m[1]
- projectID := m[2]
- mrnum := m[3]
+type Forge interface {
+ FetchStatus(urls []string) (string, error)
+ FetchSubmittedAt(urls []string) (time.Time, error)
+ FetchLastUpdated(urls []string) (time.Time, User, error)
+}
- urlStr := "https://" + authority + "/api/v4/projects/" + url.QueryEscape(projectID) + "/merge_requests/" + mrnum
+var forges = []Forge{
+ // highest precedence
+ Gerrit{}, // must be higher than GitHub because of golang
+ GitHub{},
+ GitLab{},
+ PiperMail{},
+ // lowest precedence
+}
- var obj struct {
- // State values are "opened", "closed", "locked", and "merged".
- State string `json:"state"`
- MergeCommitSha string `json:"merge_commit_sha"`
- SquashCommitSha string `json:"squash_commit_sha"`
- }
- if err := httpGetJSON(urlStr, nil, &obj); err != nil {
+func (c Contribution) fetchStatus() (string, error) {
+ for _, forge := range forges {
+ status, err := forge.FetchStatus(c.URLs)
+ if err != nil {
return "", err
}
-
- ret := obj.State
- if ret == "opened" {
- ret = statusOpen
- }
-
- if ret == "merged" {
- ret = statusMerged
- var mergeCommit string
- if obj.MergeCommitSha != "" {
- mergeCommit = obj.MergeCommitSha
- }
- if obj.SquashCommitSha != "" {
- mergeCommit = obj.SquashCommitSha
- }
- if mergeCommit != "" {
- tag, err := getGitTagThatContainsAll("https://"+authority+"/"+projectID+".git", mergeCommit)
- if err != nil {
- return "", err
- }
- if tag != "" {
- ret = fmt.Sprintf(statusReleasedFmt, tag)
- }
- }
- }
-
- return ret, nil
- }
- if len(c.URLs) > 1 {
- var gitURL string
- var gitCommits []string
- for _, u := range c.URLs[1:] {
- if m := reGitHubCommit.FindStringSubmatch(u); m != nil {
- user := m[1]
- repo := m[2]
- hash := m[3]
-
- gitURL = "https://github.com/" + user + "/" + repo
- gitCommits = append(gitCommits, hash)
- }
- }
- if len(gitCommits) > 0 {
- ret := statusMerged
- tag, err := getGitTagThatContainsAll(gitURL, gitCommits...)
- if err != nil {
- return "", err
- }
- if tag != "" {
- ret = fmt.Sprintf(statusReleasedFmt, tag)
- }
- return ret, nil
+ if status != "" {
+ return status, nil
}
}
return "", fmt.Errorf("idk how to get status for %q", c.URLs[0])
}
func (c Contribution) fetchSubmittedAt() (time.Time, error) {
- if m := reGitHubPR.FindStringSubmatch(c.URLs[0]); m != nil {
- user := m[1]
- repo := m[2]
- prnum := m[3]
-
- urlStr := "https://api.github.com/repos/" + user + "/" + repo + "/pulls/" + prnum
-
- var obj struct {
- CreatedAt time.Time `json:"created_at"`
- }
- if err := httpGetJSON(urlStr, nil, &obj); err != nil {
- return time.Time{}, err
- }
- return obj.CreatedAt, nil
- }
- if m := reGitLabMR.FindStringSubmatch(c.URLs[0]); m != nil {
- authority := m[1]
- projectID := m[2]
- mrnum := m[3]
-
- urlStr := "https://" + authority + "/api/v4/projects/" + url.QueryEscape(projectID) + "/merge_requests/" + mrnum
-
- var obj struct {
- CreatedAt time.Time `json:"created_at"`
- }
- if err := httpGetJSON(urlStr, nil, &obj); err != nil {
- return time.Time{}, err
- }
- return obj.CreatedAt, nil
- }
- if strings.Contains(c.URLs[0], "/pipermail/") {
- htmlStr, err := httpGet(c.URLs[0], nil)
+ for _, forge := range forges {
+ submittedAt, err := forge.FetchSubmittedAt(c.URLs)
if err != nil {
return time.Time{}, err
}
- for _, line := range strings.Split(htmlStr, "\n") {
- if m := rePiperMailDate.FindStringSubmatch(line); m != nil {
- return time.Parse(time.UnixDate, m[1])
- }
+ if !submittedAt.IsZero() {
+ return submittedAt, nil
}
}
return time.Time{}, fmt.Errorf("idk how to get created timestamp for %q", c.URLs[0])
@@ -267,238 +150,14 @@ func withinOneSecond(a, b time.Time) bool {
}
func (c Contribution) fetchLastUpdated() (time.Time, User, error) {
- for _, u := range c.URLs {
- if m := reGoLangGerritCL.FindStringSubmatch(u); m != nil {
- projectID := m[1]
- changeID := m[2]
-
- urlStr := "https://go-review.googlesource.com/changes/" + projectID + "~" + changeID + "?o=MESSAGES&o=DETAILED_ACCOUNTS"
-
- var obj struct {
- Updated GerritTime `json:"updated"`
- Messages []struct {
- Author struct {
- AccountID int `json:"_account_id"`
- Name string `json:"name"`
- DisplayName string `json:"display_name"`
- } `json:"author"`
- Date GerritTime `json:"date"`
- } `json:"messages"`
- }
- if err := httpGetGerritJSON(urlStr, nil, &obj); err != nil {
- return time.Time{}, User{}, err
- }
- retUpdatedAt := obj.Updated.Val
- var retUser User
- for _, message := range obj.Messages {
- if withinOneSecond(message.Date.Val, retUpdatedAt) {
- if message.Author.DisplayName != "" {
- retUser.Name = message.Author.DisplayName
- } else {
- retUser.Name = message.Author.Name
- }
- retUser.URL = fmt.Sprintf("https://go-review.googlesource.com/dashboard/%d", message.Author.AccountID)
- break
- }
- }
- return retUpdatedAt, retUser, nil
- }
- }
- if m := reGitHubPR.FindStringSubmatch(c.URLs[0]); m != nil {
- user := m[1]
- repo := m[2]
- prnum := m[3]
-
- var obj struct {
- UpdatedAt time.Time `json:"updated_at"`
-
- CreatedAt time.Time `json:"created_at"`
- CreatedBy struct {
- Login string `json:"login"`
- HTMLURL string `json:"html_url"`
- } `json:"user"`
-
- MergedAt time.Time `json:"merged_at"`
- MergedBy struct {
- Login string `json:"login"`
- HTMLURL string `json:"html_url"`
- } `json:"merged_by"`
- }
- if err := httpGetJSON("https://api.github.com/repos/"+user+"/"+repo+"/pulls/"+prnum, nil, &obj); err != nil {
- return time.Time{}, User{}, err
- }
-
- retUpdatedAt := obj.UpdatedAt
- var retUser User
-
- if retUser == (User{}) && withinOneSecond(obj.CreatedAt, retUpdatedAt) {
- retUser.Name = obj.CreatedBy.Login
- retUser.URL = obj.CreatedBy.HTMLURL
- }
- if retUser == (User{}) && withinOneSecond(obj.MergedAt, retUpdatedAt) {
- retUser.Name = obj.MergedBy.Login
- retUser.URL = obj.MergedBy.HTMLURL
- }
- if retUser == (User{}) {
- // "normal" comments
- var comments []struct {
- UpdatedAt time.Time `json:"updated_at"`
- User struct {
- Login string `json:"login"`
- HTMLURL string `json:"html_url"`
- } `json:"user"`
- }
- if err := httpGetPaginatedJSON("https://api.github.com/repos/"+user+"/"+repo+"/issues/"+prnum+"/comments", nil, &comments, githubPagination); err != nil {
- return time.Time{}, User{}, err
- }
- for _, comment := range comments {
- if withinOneSecond(comment.UpdatedAt, retUpdatedAt) {
- retUser.Name = comment.User.Login
- retUser.URL = comment.User.HTMLURL
- break
- }
- }
- }
- if retUser == (User{}) {
- // comments on a specific part of the diff
- var reviewComments []struct {
- UpdatedAt time.Time `json:"updated_at"`
- User struct {
- Login string `json:"login"`
- HTMLURL string `json:"html_url"`
- } `json:"user"`
- }
- if err := httpGetPaginatedJSON("https://api.github.com/repos/"+user+"/"+repo+"/pulls/"+prnum+"/comments", nil, &reviewComments, githubPagination); err != nil {
- return time.Time{}, User{}, err
- }
- for _, comment := range reviewComments {
- if withinOneSecond(comment.UpdatedAt, retUpdatedAt) {
- retUser.Name = comment.User.Login
- retUser.URL = comment.User.HTMLURL
- break
- }
- }
- }
- if retUser == (User{}) {
- var events []struct {
- CreatedAt time.Time `json:"created_at"`
- Actor struct {
- Login string `json:"login"`
- HTMLURL string `json:"html_url"`
- } `json:"actor"`
- }
- if err := httpGetJSON("https://api.github.com/repos/"+user+"/"+repo+"/issues/"+prnum+"/events", nil, &events); err != nil {
- return time.Time{}, User{}, err
- }
- for _, event := range events {
- if withinOneSecond(event.CreatedAt, retUpdatedAt) {
- retUser.Name = event.Actor.Login
- retUser.URL = event.Actor.HTMLURL
- break
- }
- }
- }
-
- return retUpdatedAt, retUser, nil
- }
- if m := reGitLabMR.FindStringSubmatch(c.URLs[0]); m != nil {
- authority := m[1]
- projectID := m[2]
- mrnum := m[3]
-
- urlStr := "https://" + authority + "/api/v4/projects/" + url.QueryEscape(projectID) + "/merge_requests/" + mrnum
-
- var obj struct {
- ID int `json:"id"`
-
- UpdatedAt time.Time `json:"updated_at"`
-
- CreatedAt time.Time `json:"created_at"`
- CreatedBy struct {
- Username string `json:"username"`
- WebURL string `json:"web_url"`
- } `json:"author"`
-
- MergedAt time.Time `json:"merged_at"`
- MergedBy struct {
- Username string `json:"username"`
- WebURL string `json:"web_url"`
- } `json:"merged_by"`
- }
- if err := httpGetJSON(urlStr, nil, &obj); err != nil {
+ for _, forge := range forges {
+ updatedAt, updatedBy, err := forge.FetchLastUpdated(c.URLs)
+ if err != nil {
return time.Time{}, User{}, err
}
-
- retUpdatedAt := obj.UpdatedAt
- var retUser User
-
- if retUser == (User{}) && withinOneSecond(obj.CreatedAt, retUpdatedAt) {
- retUser.Name = obj.CreatedBy.Username
- retUser.URL = obj.CreatedBy.WebURL
- }
- if retUser == (User{}) && withinOneSecond(obj.MergedAt, retUpdatedAt) {
- retUser.Name = obj.MergedBy.Username
- retUser.URL = obj.MergedBy.WebURL
- }
- if retUser == (User{}) {
- var notes struct {
- Notes []struct {
- UpdatedAt time.Time `json:"updated_at"`
- Author struct {
- Username string `json:"username"`
- WebURL string `json:"web_url"`
- } `json:"author"`
- } `json:"notes"`
- }
- if err := httpGetJSON(fmt.Sprintf("https://%s/%s/noteable/merge_request/%d/notes", authority, projectID, obj.ID), map[string]string{"X-Last-Fetched-At": "0"}, &notes); err != nil {
- return time.Time{}, User{}, err
- }
- for _, note := range notes.Notes {
- if withinOneSecond(note.UpdatedAt, retUpdatedAt) {
- retUser.Name = note.Author.Username
- retUser.URL = note.Author.WebURL
- break
- }
- }
+ if !updatedAt.IsZero() {
+ return updatedAt, updatedBy, nil
}
-
- return retUpdatedAt, retUser, nil
}
-
- var ret time.Time
- if len(c.URLs) > 1 {
- for _, u := range c.URLs[1:] {
- if m := reGitHubCommit.FindStringSubmatch(u); m != nil {
- user := m[1]
- repo := m[2]
- hash := m[3]
-
- urlStr := "https://api.github.com/repos/" + user + "/" + repo + "/commits/" + hash
- var obj struct {
- Commit struct {
- Author struct {
- Date time.Time `json:"date"`
- } `json:"author"`
- Committer struct {
- Date time.Time `json:"date"`
- } `json:"committer"`
- } `json:"commit"`
- }
- if err := httpGetJSON(urlStr, nil, &obj); err != nil {
- return time.Time{}, User{}, err
- }
- if obj.Commit.Author.Date.After(ret) {
- ret = obj.Commit.Author.Date
- }
- if obj.Commit.Committer.Date.After(ret) {
- ret = obj.Commit.Committer.Date
- }
- }
- }
- }
- if !ret.IsZero() {
- return ret, User{}, nil
- }
-
return time.Time{}, User{}, nil //fmt.Errorf("idk how to get updated timestamp for %q", c.URLs[0])
}