mirror of
https://github.com/go-kratos/kratos.git
synced 2025-01-18 03:21:57 +02:00
feat(cmd): add kratos changelog command (#1140)
This commit is contained in:
parent
c268f98404
commit
7755bd36dc
@ -1,9 +1,17 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GoGet go get path.
|
||||
@ -19,3 +27,200 @@ func GoGet(path ...string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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 != 200 {
|
||||
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?page=%d&since=%s", g.Owner, g.Repo, page, info.PublishedAt)
|
||||
resp, code := requestGithubAPI(url, "GET", nil, g.Token)
|
||||
if code != 200 {
|
||||
printGithubErrorInfo(resp)
|
||||
}
|
||||
var res []CommitInfo
|
||||
err := json.Unmarshal(resp, &res)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
list = append(list, res...)
|
||||
if len(res) < 30 || len(res) == 0 {
|
||||
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 := ioutil.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": {},
|
||||
"other": {},
|
||||
}
|
||||
for i, commitInfo := range info {
|
||||
msg := commitInfo.Commit.Message
|
||||
index := strings.Index(fmt.Sprintf("%q", msg), `\n`)
|
||||
if index != -1 {
|
||||
msg = commitInfo.Commit.Message[:index-1]
|
||||
}
|
||||
prefix := []string{"fix", "feat", "deps", "break"}
|
||||
for _, v := range prefix {
|
||||
if strings.HasPrefix(msg, v) {
|
||||
group[v] = append(group[v], msg)
|
||||
info = append(info[:i], info[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
// other
|
||||
for _, value := range info {
|
||||
msg := value.Commit.Message
|
||||
index := strings.Index(fmt.Sprintf("%q", msg), `\n`)
|
||||
if index != -1 {
|
||||
msg = value.Commit.Message[:index-1]
|
||||
}
|
||||
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 "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["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)
|
||||
}
|
||||
|
43
cmd/kratos/internal/change/change.go
Normal file
43
cmd/kratos/internal/change/change.go
Normal file
@ -0,0 +1,43 @@
|
||||
package change
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CmdNew represents the new command.
|
||||
var CmdNew = &cobra.Command{
|
||||
Use: "changelog",
|
||||
Short: "Get a kratos change log",
|
||||
Long: "Get a kratos release or commits info. Example: kratos changelog dev or kratos changelog {version}",
|
||||
Run: run,
|
||||
}
|
||||
var token string
|
||||
var repoURL string
|
||||
|
||||
func init() {
|
||||
if repoURL = os.Getenv("KRATOS_REPO"); repoURL == "" {
|
||||
repoURL = "https://github.com/go-kratos/kratos.git"
|
||||
}
|
||||
CmdNew.Flags().StringVarP(&repoURL, "repo-url", "r", repoURL, "github repo")
|
||||
token = os.Getenv("GITHUB_TOKEN")
|
||||
}
|
||||
|
||||
func run(cmd *cobra.Command, args []string) {
|
||||
owner, repo := base.ParseGithubUrl(repoURL)
|
||||
api := base.GithubApi{Owner: owner, Repo: repo, Token: token}
|
||||
version := "latest"
|
||||
if len(args) > 0 {
|
||||
version = args[0]
|
||||
}
|
||||
if version == "dev" {
|
||||
info := api.GetCommitsInfo()
|
||||
fmt.Print(base.ParseCommitsInfo(info))
|
||||
} else {
|
||||
info := api.GetReleaseInfo(version)
|
||||
fmt.Print(base.ParseReleaseInfo(info))
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/change"
|
||||
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/project"
|
||||
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto"
|
||||
"github.com/go-kratos/kratos/cmd/kratos/v2/internal/upgrade"
|
||||
@ -25,10 +26,11 @@ func init() {
|
||||
rootCmd.AddCommand(project.CmdNew)
|
||||
rootCmd.AddCommand(proto.CmdProto)
|
||||
rootCmd.AddCommand(upgrade.CmdUpgrade)
|
||||
rootCmd.AddCommand(change.CmdNew)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user