最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 基于 Go 实现 Deno upgrade

    正文概述 掘金(俊宁也是洛竹)   2021-02-04   740

    书接上篇,我在向 Deno 学习优秀的脚本管理中向大家介绍了 Deno 是如何管理它的安装包的——以 Github release 的形式发布、执行编写好的 shell 脚本安装程序以及基于 tags 的版本管理。有版本管理就会有更新的需求,本文就是在研读了 Deno 的 upgrade 命令后使用 Go 语言实现了自己的 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", ""))
    }
    

    分析出以上代码做了以下几件事:

    1. 由于是异步函数,用 println 函数提示正在查找新版本
    2. 使用 GET 方式请求 https://github.com/denoland/deno/releases/latest
    3. 由于 https://github.com/denoland/deno/releases/latest 会重定向到最新的 release https://github.com/denoland/deno/releases/tag/v1.7.1,通过一顿字符串操作我们便取出了 v1.7.1
    4. 调用字符串的 replace 方法删除 v 得到 ""

    让我们用 Go 来实现它:

    package release
    
    import (
    	"net/http"
    	"strings"
    )
    
    func getLatestVersion() string{
    	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() // 为了防止内存泄漏
    
    	pathArr := strings.Split(resp.Request.URL.Path, "/")
    	latest := strings.Replace(pathArr[len(pathArr)-1], "v", "", 1)
    	return latest
    }
    

    版本检测

    版本的处理少不了SemVer。我们使用 go-version 来处理版本相关工作,有了版本比较我们便可以实现一个版本检查的功能。以下代码是 我封装的 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 实现 upgrade

    下面的代码中,我们基于 go-release 实现了一个带 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()
    }
    

    强制更新方案

    Node.js 命令行工具检查更新的正确姿势 对这个问题做了很深入的研究并实现了 Node 版的工具。核心逻辑就是每次执行命令时异步去执行检查更新。这句话翻译成 Go 就是用协程去执行检查更新的动作,这样执行命令还是会立马得到反馈,程序则会在后台执行检查更新,当检测到有新版本则强制更新。

    // rootCmd 代表没有调用子命令时的基础命令
    var rootCmd = &cobra.Command{
    	Use:     "tpc",
    	Version: Version,
    	Run: func(cmd *cobra.Command, args []string) {
    		sh.Command("tpc", "-h").Run()
      },
      // 每次执行完命令后去执行检查更新,Start 表示不阻塞原来的任务,还有一个 Run 方法则是会阻塞
    	PersistentPostRun: func(cmd *cobra.Command, args []string) {
    		sh.Command("bash", "-c", "tpc upgrade --force=false").Start()
    	},
    }
    

    go-release 的诞生

    go-release 的诞生源自我工作中要实现一个 CLI 工具的版本管理功能,由于是个菜鸟,中间遇到了各种各样的困难,幸得大佬无私地解答,才能很快实现了这么一个功能,在此鸣谢:

    林小帅:

    基于 Go 实现 Deno upgrade

    漫画 Go 语言 纯手绘版 作者好家伙:

    基于 Go 实现 Deno upgrade

    justjavac 大佬:

    基于 Go 实现 Deno upgrade


    起源地下载网 » 基于 Go 实现 Deno upgrade

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元