From baea60d195d34b98a3751fd72797f2155b8e0d82 Mon Sep 17 00:00:00 2001 From: xuthus5 Date: Fri, 5 Aug 2022 12:10:22 +0800 Subject: [PATCH] feat: support auto update feat: support run on windows --- cmd.go | 4 +- go.mod | 2 + output.go | 40 +++++++++++---- update.go | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.go | 7 +++ 5 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 update.go diff --git a/cmd.go b/cmd.go index 8a7c0bc..b595bde 100644 --- a/cmd.go +++ b/cmd.go @@ -22,7 +22,7 @@ var ( ) func init() { - // create a new mder folder + // create a new site folder rootCmd.AddCommand(initCmd()) // generate static website rootCmd.AddCommand(deployCmd()) @@ -30,6 +30,8 @@ func init() { rootCmd.AddCommand(newCmd()) // run serve locally rootCmd.AddCommand(serveCmd()) + // auto update + rootCmd.AddCommand(updateCmd()) } func main() { diff --git a/go.mod b/go.mod index 2aa8315..a89cb74 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( github.com/abhinav/goldmark-toc v0.2.1 github.com/gin-gonic/gin v1.8.1 + github.com/guonaihong/gout v0.3.1 github.com/radovskyb/watcher v1.0.7 github.com/spf13/cobra v1.5.0 github.com/yuin/goldmark v1.4.13 @@ -26,6 +27,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.2 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/ugorji/go/codec v1.2.7 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect diff --git a/output.go b/output.go index 08e75d2..c0ce6c1 100644 --- a/output.go +++ b/output.go @@ -6,6 +6,7 @@ import ( "math" "os" "os/exec" + "runtime" "sort" "text/template" ) @@ -39,15 +40,26 @@ type PostData struct { // check 检测目标 func (o *Outter) check() { + var args []string + switch runtime.GOOS { + case "windows": + args = []string{"cmd.exe", "/C", "rmdir", "/S", "/Q", "dist"} + default: + args = []string{"rm", "-rf", "./dist"} + } + + var cmd = exec.Command(args[0], args[1:]...) + + if err := cmd.Run(); err != nil { + sout(cmd.String()) + serr("clear dist directory failed: %v", err) + } + if !isExist("./dist") { if err := mkdir("./dist"); err != nil { sfault("create dist directory failed: %v", err) } } - - if err := exec.Command("rm", "-rf", "./dist/*").Run(); err != nil { - sfault("clear dist directory failed: %v", err) - } } func (o *Outter) createDir(fp string) error { @@ -61,18 +73,28 @@ func (o *Outter) createDir(fp string) error { // sourceCopy 资源拷贝 将主题的资源拷贝到目标文件夹中 func (o *Outter) sourceCopy() { - themePath := fmt.Sprintf("./themes/%s/", o.Config.Theme) - destPath := "./dist/" - cmd := exec.Command("cp", "-r", themePath+"css", destPath) + var args []string + switch runtime.GOOS { + case "windows": + args = []string{"cmd.exe", "/C", "xcopy", "/e"} + default: + args = []string{"cp", "-r"} + } + themePath := slash(fmt.Sprintf("./themes/%s/", o.Config.Theme)) + destPath := slash("./dist/") + cmd := exec.Command(args[0], append(args, themePath+"css", destPath)...) if err := cmd.Run(); err != nil { + sout(cmd.String()) sfault("copy theme css source failed: %v", err) } - cmd = exec.Command("cp", "-r", themePath+"js", destPath) + cmd = exec.Command(args[0], append(args, themePath+"js", destPath)...) if err := cmd.Run(); err != nil { + sout(cmd.String()) sfault("copy theme js source failed: %v", err) } - cmd = exec.Command("cp", "-r", themePath+"images", destPath) + cmd = exec.Command(args[0], append(args, themePath+"images", destPath)...) if err := cmd.Run(); err != nil { + sout(cmd.String()) sfault("copy theme images source failed: %v", err) } } diff --git a/update.go b/update.go new file mode 100644 index 0000000..e9ebed4 --- /dev/null +++ b/update.go @@ -0,0 +1,150 @@ +package main + +import ( + "fmt" + "github.com/spf13/cobra" + "os/exec" + "time" + + "github.com/guonaihong/gout" +) + +func updateCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "update", + Short: "mder auto update", + Example: "mder update", + Run: func(cmd *cobra.Command, args []string) { + commit, err := GetRepoLatestCommit() + if err != nil { + sfault("get mder version failed: %v", err) + } + sout("-----\nmder latest version: %s\n更新中...\n", commit.Sha) + var url = fmt.Sprintf("gitter.top/mder/mder@%s", commit.Sha) + genO, err := exec.Command("go", "install", url).CombinedOutput() + if err != nil { + serr("install: go install %s\nupdate failed: %v\n", url, err) + return + } + sout("%supdate success", string(genO)) + }, + } + + return cmd +} + +type RepoCommits []*RepoCommit + +type RepoCommit struct { + Author struct { + Active bool `json:"active"` + AvatarURL string `json:"avatar_url"` + Created time.Time `json:"created"` + Description string `json:"description"` + Email string `json:"email"` + FollowersCount int `json:"followers_count"` + FollowingCount int `json:"following_count"` + FullName string `json:"full_name"` + ID int `json:"id"` + IsAdmin bool `json:"is_admin"` + Language string `json:"language"` + LastLogin time.Time `json:"last_login"` + Location string `json:"location"` + Login string `json:"login"` + ProhibitLogin bool `json:"prohibit_login"` + Restricted bool `json:"restricted"` + StarredReposCount int `json:"starred_repos_count"` + Visibility string `json:"visibility"` + Website string `json:"website"` + } `json:"author"` + Commit struct { + Author struct { + Date string `json:"date"` + Email string `json:"email"` + Name string `json:"name"` + } `json:"author"` + Committer struct { + Date string `json:"date"` + Email string `json:"email"` + Name string `json:"name"` + } `json:"committer"` + Message string `json:"message"` + Tree struct { + Created time.Time `json:"created"` + Sha string `json:"sha"` + URL string `json:"url"` + } `json:"tree"` + URL string `json:"url"` + Verification struct { + Payload string `json:"payload"` + Reason string `json:"reason"` + Signature string `json:"signature"` + Signer struct { + Email string `json:"email"` + Name string `json:"name"` + Username string `json:"username"` + } `json:"signer"` + Verified bool `json:"verified"` + } `json:"verification"` + } `json:"commit"` + Committer struct { + Active bool `json:"active"` + AvatarURL string `json:"avatar_url"` + Created time.Time `json:"created"` + Description string `json:"description"` + Email string `json:"email"` + FollowersCount int `json:"followers_count"` + FollowingCount int `json:"following_count"` + FullName string `json:"full_name"` + ID int `json:"id"` + IsAdmin bool `json:"is_admin"` + Language string `json:"language"` + LastLogin time.Time `json:"last_login"` + Location string `json:"location"` + Login string `json:"login"` + ProhibitLogin bool `json:"prohibit_login"` + Restricted bool `json:"restricted"` + StarredReposCount int `json:"starred_repos_count"` + Visibility string `json:"visibility"` + Website string `json:"website"` + } `json:"committer"` + Created time.Time `json:"created"` + Files []struct { + Filename string `json:"filename"` + } `json:"files"` + HTMLURL string `json:"html_url"` + Parents []struct { + Created time.Time `json:"created"` + Sha string `json:"sha"` + URL string `json:"url"` + } `json:"parents"` + Sha string `json:"sha"` + Stats struct { + Additions int `json:"additions"` + Deletions int `json:"deletions"` + Total int `json:"total"` + } `json:"stats"` + URL string `json:"url"` +} + +// GetRepoLatestCommit 获取仓库最新提交记录 +func GetRepoLatestCommit() (*RepoCommit, error) { + var commits RepoCommits + + api := "https://gitter.top/api/v1/repos/mder/mder/commits" + + if err := gout.GET(api).SetHeader(gout.H{ + "Accept": "application/json", + "User-Agent": "mder/beta", + }).SetQuery(gout.H{ + "page": 1, + "limit": 5, + }).SetTimeout(time.Second * 3).BindJSON(&commits).Do(); err != nil { + return nil, err + } + + if len(commits) == 0 { + return nil, fmt.Errorf("empty commit") + } + return commits[0], nil +} diff --git a/utils.go b/utils.go index 9c3425e..a15effc 100644 --- a/utils.go +++ b/utils.go @@ -129,3 +129,10 @@ func sfault(format string, args ...interface{}) { _, _ = fmt.Fprintf(os.Stderr, "runtime: %+v\n%s\n", ri, real) os.Exit(1) } + +func slash(str string) string { + if runtime.GOOS == "windows" { + return strings.ReplaceAll(str, "/", "\\") + } + return str +}