github.com/mloves0824/enron/cmd/enron@v0.0.0-20230830012320-113bbf6be3c8/internal/change/get.go (about) 1 package change 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "net/http" 9 "os" 10 "regexp" 11 "strings" 12 "time" 13 ) 14 15 type ReleaseInfo struct { 16 Author struct { 17 Login string `json:"login"` 18 } `json:"author"` 19 PublishedAt string `json:"published_at"` 20 Body string `json:"body"` 21 HTMLURL string `json:"html_url"` 22 } 23 24 type CommitInfo struct { 25 Commit struct { 26 Message string `json:"message"` 27 } `json:"commit"` 28 } 29 30 type ErrorInfo struct { 31 Message string 32 } 33 34 type GithubAPI struct { 35 Owner string 36 Repo string 37 Token string 38 } 39 40 // GetReleaseInfo for getting enron release info. 41 func (g *GithubAPI) GetReleaseInfo(version string) ReleaseInfo { 42 api := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", g.Owner, g.Repo) 43 if version != "latest" { 44 api = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", g.Owner, g.Repo, version) 45 } 46 resp, code := requestGithubAPI(api, http.MethodGet, nil, g.Token) 47 if code != http.StatusOK { 48 printGithubErrorInfo(resp) 49 } 50 releaseInfo := ReleaseInfo{} 51 err := json.Unmarshal(resp, &releaseInfo) 52 if err != nil { 53 fatal(err) 54 } 55 return releaseInfo 56 } 57 58 // GetCommitsInfo for getting enron commits info. 59 func (g *GithubAPI) GetCommitsInfo() []CommitInfo { 60 info := g.GetReleaseInfo("latest") 61 page := 1 62 prePage := 100 63 var list []CommitInfo 64 for { 65 url := fmt.Sprintf("https://api.github.com/repos/%s/%s/commits?pre_page=%d&page=%d&since=%s", g.Owner, g.Repo, prePage, page, info.PublishedAt) 66 resp, code := requestGithubAPI(url, http.MethodGet, nil, g.Token) 67 if code != http.StatusOK { 68 printGithubErrorInfo(resp) 69 } 70 var res []CommitInfo 71 err := json.Unmarshal(resp, &res) 72 if err != nil { 73 fatal(err) 74 } 75 list = append(list, res...) 76 if len(res) < prePage { 77 break 78 } 79 page++ 80 } 81 return list 82 } 83 84 func printGithubErrorInfo(body []byte) { 85 errorInfo := &ErrorInfo{} 86 err := json.Unmarshal(body, errorInfo) 87 if err != nil { 88 fatal(err) 89 } 90 fatal(errors.New(errorInfo.Message)) 91 } 92 93 func requestGithubAPI(url string, method string, body io.Reader, token string) ([]byte, int) { 94 cli := &http.Client{Timeout: 60 * time.Second} 95 request, err := http.NewRequest(method, url, body) 96 if err != nil { 97 fatal(err) 98 } 99 if token != "" { 100 request.Header.Add("Authorization", token) 101 } 102 resp, err := cli.Do(request) 103 if err != nil { 104 fatal(err) 105 } 106 defer resp.Body.Close() 107 resBody, err := io.ReadAll(resp.Body) 108 if err != nil { 109 fatal(err) 110 } 111 return resBody, resp.StatusCode 112 } 113 114 func ParseCommitsInfo(info []CommitInfo) string { 115 group := map[string][]string{ 116 "fix": {}, 117 "feat": {}, 118 "deps": {}, 119 "break": {}, 120 "chore": {}, 121 "other": {}, 122 } 123 124 for _, commitInfo := range info { 125 msg := commitInfo.Commit.Message 126 index := strings.Index(fmt.Sprintf("%q", msg), `\n`) 127 if index != -1 { 128 msg = msg[:index-1] 129 } 130 prefix := []string{"fix", "feat", "deps", "break", "chore"} 131 var matched bool 132 for _, v := range prefix { 133 msg = strings.TrimPrefix(msg, " ") 134 if strings.HasPrefix(msg, v) { 135 group[v] = append(group[v], msg) 136 matched = true 137 } 138 } 139 if !matched { 140 group["other"] = append(group["other"], msg) 141 } 142 } 143 144 md := make(map[string]string) 145 for key, value := range group { 146 var text string 147 switch key { 148 case "break": 149 text = "### Breaking Changes\n" 150 case "deps": 151 text = "### Dependencies\n" 152 case "feat": 153 text = "### New Features\n" 154 case "fix": 155 text = "### Bug Fixes\n" 156 case "chore": 157 text = "### Chores\n" 158 case "other": 159 text = "### Others\n" 160 } 161 if len(value) == 0 { 162 continue 163 } 164 md[key] += text 165 for _, value := range value { 166 md[key] += fmt.Sprintf("- %s\n", value) 167 } 168 } 169 return fmt.Sprint(md["break"], md["deps"], md["feat"], md["fix"], md["chore"], md["other"]) 170 } 171 172 func ParseReleaseInfo(info ReleaseInfo) string { 173 reg := regexp.MustCompile(`(?m)^\s*$[\r\n]*|[\r\n]+\s+\z|<[\S\s]+?>`) 174 body := reg.ReplaceAll([]byte(info.Body), []byte("")) 175 if string(body) == "" { 176 body = []byte("no release info") 177 } 178 splitters := "--------------------------------------------" 179 return fmt.Sprintf( 180 "Author: %s\nDate: %s\nUrl: %s\n\n%s\n\n%s\n\n%s\n", 181 info.Author.Login, 182 info.PublishedAt, 183 info.HTMLURL, 184 splitters, 185 body, 186 splitters, 187 ) 188 } 189 190 func ParseGithubURL(url string) (owner string, repo string) { 191 var start int 192 start = strings.Index(url, "//") 193 if start == -1 { 194 start = strings.Index(url, ":") + 1 195 } else { 196 start += 2 197 } 198 end := strings.LastIndex(url, "/") 199 gitIndex := strings.LastIndex(url, ".git") 200 if gitIndex == -1 { 201 repo = url[strings.LastIndex(url, "/")+1:] 202 } else { 203 repo = url[strings.LastIndex(url, "/")+1 : gitIndex] 204 } 205 tmp := url[start:end] 206 owner = tmp[strings.Index(tmp, "/")+1:] 207 return 208 } 209 210 func fatal(err error) { 211 fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err) 212 os.Exit(1) 213 }