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  }