|
|
|
package change
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ReleaseInfo struct {
|
|
|
|
Author struct {
|
|
|
|
Login string `json:"login"`
|
|
|
|
} `json:"author"`
|
|
|
|
PublishedAt string `json:"published_at"`
|
|
|
|
Body string `json:"body"`
|
|
|
|
HTMLURL string `json:"html_url"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type CommitInfo struct {
|
|
|
|
Commit struct {
|
|
|
|
Message string `json:"message"`
|
|
|
|
} `json:"commit"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type ErrorInfo struct {
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
|
|
|
type GithubAPI struct {
|
|
|
|
Owner string
|
|
|
|
Repo string
|
|
|
|
Token string
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetReleaseInfo for getting kratos release info.
|
|
|
|
func (g *GithubAPI) GetReleaseInfo(version string) ReleaseInfo {
|
|
|
|
api := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", g.Owner, g.Repo)
|
|
|
|
if version != "latest" {
|
|
|
|
api = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", g.Owner, g.Repo, version)
|
|
|
|
}
|
|
|
|
resp, code := requestGithubAPI(api, "GET", nil, g.Token)
|
|
|
|
if code != http.StatusOK {
|
|
|
|
printGithubErrorInfo(resp)
|
|
|
|
}
|
|
|
|
releaseInfo := ReleaseInfo{}
|
|
|
|
err := json.Unmarshal(resp, &releaseInfo)
|
|
|
|
if err != nil {
|
|
|
|
fatal(err)
|
|
|
|
}
|
|
|
|
return releaseInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCommitsInfo for getting kratos commits info.
|
|
|
|
func (g *GithubAPI) GetCommitsInfo() []CommitInfo {
|
|
|
|
info := g.GetReleaseInfo("latest")
|
|
|
|
page := 1
|
|
|
|
var list []CommitInfo
|
|
|
|
for {
|
|
|
|
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/commits?pre_page=100&page=%d&since=%s", g.Owner, g.Repo, page, info.PublishedAt)
|
|
|
|
resp, code := requestGithubAPI(url, "GET", nil, g.Token)
|
|
|
|
if code != http.StatusOK {
|
|
|
|
printGithubErrorInfo(resp)
|
|
|
|
}
|
|
|
|
var res []CommitInfo
|
|
|
|
err := json.Unmarshal(resp, &res)
|
|
|
|
if err != nil {
|
|
|
|
fatal(err)
|
|
|
|
}
|
|
|
|
list = append(list, res...)
|
|
|
|
if len(res) < http.StatusContinue {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
page++
|
|
|
|
}
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
|
|
|
func printGithubErrorInfo(body []byte) {
|
|
|
|
errorInfo := &ErrorInfo{}
|
|
|
|
err := json.Unmarshal(body, errorInfo)
|
|
|
|
if err != nil {
|
|
|
|
fatal(err)
|
|
|
|
}
|
|
|
|
fatal(errors.New(errorInfo.Message))
|
|
|
|
}
|
|
|
|
|
|
|
|
func requestGithubAPI(url string, method string, body io.Reader, token string) ([]byte, int) {
|
|
|
|
cli := &http.Client{Timeout: 60 * time.Second}
|
|
|
|
request, err := http.NewRequest(method, url, body)
|
|
|
|
if err != nil {
|
|
|
|
fatal(err)
|
|
|
|
}
|
|
|
|
if token != "" {
|
|
|
|
request.Header.Add("Authorization", token)
|
|
|
|
}
|
|
|
|
resp, err := cli.Do(request)
|
|
|
|
if err != nil {
|
|
|
|
fatal(err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
resBody, err := io.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
fatal(err)
|
|
|
|
}
|
|
|
|
return resBody, resp.StatusCode
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseCommitsInfo(info []CommitInfo) string {
|
|
|
|
group := map[string][]string{
|
|
|
|
"fix": {},
|
|
|
|
"feat": {},
|
|
|
|
"deps": {},
|
|
|
|
"break": {},
|
|
|
|
"chore": {},
|
|
|
|
"other": {},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, commitInfo := range info {
|
|
|
|
msg := commitInfo.Commit.Message
|
|
|
|
index := strings.Index(fmt.Sprintf("%q", msg), `\n`)
|
|
|
|
if index != -1 {
|
|
|
|
msg = msg[:index-1]
|
|
|
|
}
|
|
|
|
prefix := []string{"fix", "feat", "deps", "break", "chore"}
|
|
|
|
var matched bool
|
|
|
|
for _, v := range prefix {
|
|
|
|
msg = strings.TrimPrefix(msg, " ")
|
|
|
|
if strings.HasPrefix(msg, v) {
|
|
|
|
group[v] = append(group[v], msg)
|
|
|
|
matched = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !matched {
|
|
|
|
group["other"] = append(group["other"], msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
md := make(map[string]string)
|
|
|
|
for key, value := range group {
|
|
|
|
var text string
|
|
|
|
switch key {
|
|
|
|
case "break":
|
|
|
|
text = "### Breaking Changes\n"
|
|
|
|
case "deps":
|
|
|
|
text = "### Dependencies\n"
|
|
|
|
case "feat":
|
|
|
|
text = "### New Features\n"
|
|
|
|
case "fix":
|
|
|
|
text = "### Bug Fixes\n"
|
|
|
|
case "chore":
|
|
|
|
text = "### Chores\n"
|
|
|
|
case "other":
|
|
|
|
text = "### Others\n"
|
|
|
|
}
|
|
|
|
if len(value) > 0 {
|
|
|
|
md[key] += text
|
|
|
|
for _, value := range value {
|
|
|
|
md[key] += fmt.Sprintf("- %s\n", value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fmt.Sprint(md["break"], md["deps"], md["feat"], md["fix"], md["chore"], md["other"])
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseReleaseInfo(info ReleaseInfo) string {
|
|
|
|
reg := regexp.MustCompile(`(?m)^\s*$[\r\n]*|[\r\n]+\s+\z|<[\S\s]+?>`)
|
|
|
|
body := reg.ReplaceAll([]byte(info.Body), []byte(""))
|
|
|
|
if string(body) == "" {
|
|
|
|
body = []byte("no release info")
|
|
|
|
}
|
|
|
|
splitters := "--------------------------------------------"
|
|
|
|
return fmt.Sprintf(
|
|
|
|
"Author: %s\nDate: %s\nUrl: %s\n\n%s\n\n%s\n\n%s\n",
|
|
|
|
info.Author.Login,
|
|
|
|
info.PublishedAt,
|
|
|
|
info.HTMLURL,
|
|
|
|
splitters,
|
|
|
|
body,
|
|
|
|
splitters,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseGithubURL(url string) (owner string, repo string) {
|
|
|
|
var start int
|
|
|
|
start = strings.Index(url, "//")
|
|
|
|
if start == -1 {
|
|
|
|
start = strings.Index(url, ":") + 1
|
|
|
|
} else {
|
|
|
|
start += 2
|
|
|
|
}
|
|
|
|
end := strings.LastIndex(url, "/")
|
|
|
|
gitIndex := strings.LastIndex(url, ".git")
|
|
|
|
if gitIndex == -1 {
|
|
|
|
repo = url[strings.LastIndex(url, "/")+1:]
|
|
|
|
} else {
|
|
|
|
repo = url[strings.LastIndex(url, "/")+1 : gitIndex]
|
|
|
|
}
|
|
|
|
tmp := url[start:end]
|
|
|
|
owner = tmp[strings.Index(tmp, "/")+1:]
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func fatal(err error) {
|
|
|
|
fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|