diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-05-19 02:29:46 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-05-19 02:29:46 -0600 |
commit | 632b189cfed31bf1a1f1edf0c1ae69f294ef4123 (patch) | |
tree | b375ecf1391247bcd4439b7880a69c86b042aa42 /cmd/generate/src_contribs.go | |
parent | 75d2ab4f4d2415b1ecead2361acddb1e6d6392dd (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.go | 397 |
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"}, ¬es); 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]) } |