基于 Go 實(shí)現(xiàn) Deno upgrade
怕什么真理無窮,進(jìn)一寸有一寸的歡喜。大家好,我是@洛竹,一名熱愛編程、熱愛生活的終身學(xué)習(xí)實(shí)踐者。
回復(fù)“Go語言”即可獲贈Python從入門到進(jìn)階共10本電子書
書接上篇,我在向 Deno 學(xué)習(xí)優(yōu)秀的腳本管理中向大家介紹了 Deno 是如何管理它的安裝包的——以 Github release 的形式發(fā)布、執(zhí)行編寫好的 shell 腳本安裝程序以及基于 tags 的版本管理。有版本管理就會有更新的需求,本文就是在研讀了 Deno 的 upgrade 命令后使用 Go 語言實(shí)現(xiàn)了自己的 upgrade 命令。
獲取最新版本
我們先來看下 Deno 的源碼:
use?deno_runtime::deno_fetch::reqwest::Client;
const?RELEASE_URL:?&str?=?"https://github.com/denoland/deno/releases";
...
async?fn?get_latest_release_version(
??client:?&Client,
)?->?Result<String,?AnyError>?{
??println!("Looking?up?latest?version");
??let?res?=?client
????.get(&format!("{}/latest",?RELEASE_URL))
????.send()
????.await?;
??let?version?=?res.url().path_segments().unwrap().last().unwrap();
??Ok(version.replace("v",?""))
}
分析出以上代碼做了以下幾件事:
由于是異步函數(shù),用 println函數(shù)提示正在查找新版本使用 GET方式請求https://github.com/denoland/deno/releases/latest由于 https://github.com/denoland/deno/releases/latest會重定向到最新的 releasehttps://github.com/denoland/deno/releases/tag/v1.7.1,通過一頓字符串操作我們便取出了v1.7.1調(diào)用字符串的 replace方法替換v為""得到版本號
讓我們用 Go 來實(shí)現(xiàn)它:
代碼看起來差不多,比如模板字符串寫法、字符串處理
package?release
import?(
?"net/http"
?"strings"
)
func?getLatestVersion()?string{
?releaseURL?:=?fmt.Sprintf("https://github.com/%s/%s/releases/latest",?user,?repo)
?resp,?_?:=?http.Get(releaseURL)
?defer?resp.Body.Close()?//?為了防止內(nèi)存泄漏
?pathArr?:=?strings.Split(resp.Request.URL.Path,?"/")
?latest?:=?strings.Replace(pathArr[len(pathArr)-1],?"v",?"",?1)
?return?latest
}
版本檢測
版本的處理少不了SemVer。我們使用 go-version 來處理版本相關(guān)工作,有了版本比較我們便可以實(shí)現(xiàn)一個版本檢查的功能。以下代碼是 我封裝的 go-release 中的一段代碼:
type?UpdateInfo?struct?{
?IsUpdate??????bool
?LatestVersion?string
}
func?CheckUpdate(user?string,?repo?string,?current?string)?(updateInfo?*UpdateInfo,?err?error)?{
?releaseURL?:=?fmt.Sprintf("https://github.com/%s/%s/releases/latest",?user,?repo)
?resp,?err?:=?http.Get(releaseURL)
?if?err?!=?nil?{
??return?nil,?err
?}
?defer?resp.Body.Close()
?current?=?strings.Replace(current,?"v",?"",?1)
?pathArr?:=?strings.Split(resp.Request.URL.Path,?"/")
?latest?:=?strings.Replace(pathArr[len(pathArr)-1],?"v",?"",?1)
?currentVersion,?err?:=?version.NewVersion(current)
?if?err?!=?nil?{
??return?nil,?err
?}
?latestVersion,?err?:=?version.NewVersion(latest)
?if?err?!=?nil?{
??return?nil,?err
?}
?updateInfo?=?&UpdateInfo{
??IsUpdate:??????currentVersion.LessThan(latestVersion),
??LatestVersion:?latest,
?}
?return?updateInfo,?nil
}
cobra 實(shí)現(xiàn) upgrade
下面的代碼中,我們基于 go-release 實(shí)現(xiàn)了一個帶 upgrade 命令的簡易 CLI,核心邏輯便是比較本地版本是否是最新的,如果不是則安裝最新的。
const?Version?=?"0.0.1"
func?checkUpgrade(current?string)?{
??fmt.Println("Looking?up?latest?version")
?update,?err?:=?release.CheckUpdate("youngjuning",?"tpc",?current)
?if?err?!=?nil?{
??panic(err)
?}
?if?update.IsUpdate?{
??fmt.Printf("Found?latest?version?%v?\n",?update.LatestVersion)
??sh.Command("bash",?"-c",?"curl?-fsSL?https://raw.githubusercontent.com/youngjuning/tpc/main/install.sh?|?sh").Run()
?}?else?{
??fmt.Printf("Local?version?%v?is?the?most?recent?release?\n",?current)
?}
}
var?rootCmd?=?&cobra.Command{
?Use:?????"app",
?Version:?Version,
}
var?cmdUpgrade?=?&cobra.Command{
?Use:?"upgrade",
?Run:?func(cmd?*cobra.Command,?args?[]string)?{
??checkUpgrade(Version,?force)
?},
}
func?main()?{
?rootCmd.AddCommand(cmdUpgrade)
?rootCmd.Execute()
}
強(qiáng)制更新方案
Node.js 命令行工具檢查更新的正確姿勢 對這個問題做了很深入的研究并實(shí)現(xiàn)了 Node 版的工具。核心邏輯就是每次執(zhí)行命令時異步去執(zhí)行檢查更新。這句話翻譯成 Go 就是用協(xié)程去執(zhí)行檢查更新的動作,這樣執(zhí)行命令還是會立馬得到反饋,程序則會在后臺執(zhí)行檢查更新,當(dāng)檢測到有新版本則強(qiáng)制更新。
完整代碼在go-release/example,對 Cobra 不熟悉的同學(xué)可以看一下 Cobra 中文文檔。
執(zhí)行 shell 命令推薦 go-sh,它對 exec 包做了封裝,提供了更好地使用體驗(yàn)( PS:還不是因?yàn)槲也耍?/p>
//?rootCmd?代表沒有調(diào)用子命令時的基礎(chǔ)命令
var?rootCmd?=?&cobra.Command{
?Use:?????"tpc",
?Version:?Version,
?Run:?func(cmd?*cobra.Command,?args?[]string)?{
??sh.Command("tpc",?"-h").Run()
??},
??//?每次執(zhí)行完命令后去執(zhí)行檢查更新,Start?表示不阻塞原來的任務(wù),還有一個?Run?方法則是會阻塞
?PersistentPostRun:?func(cmd?*cobra.Command,?args?[]string)?{
??sh.Command("bash",?"-c",?"tpc?upgrade?--force=false").Start()
?},
}
go-release 的誕生
go-release 核心代碼翻譯自 Deno 的
upgrade,開源項(xiàng)目,點(diǎn)贊是我維護(hù)的動力,在此求個 Star。
go-release 的誕生源自我工作中要實(shí)現(xiàn)一個 CLI 工具的版本管理功能,由于是個菜鳥,中間遇到了各種各樣的困難,幸得大佬無私地解答,才能很快實(shí)現(xiàn)了這么一個功能,在此鳴謝:
林小帥:

漫畫 Go 語言 純手繪版 作者好家伙:

justjavac 大佬:

-------------------?End?-------------------
往期精彩文章推薦:

歡迎大家點(diǎn)贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持
想加入Go學(xué)習(xí)群請?jiān)诤笈_回復(fù)【入群】
萬水千山總是情,點(diǎn)個【在看】行不行
