基于 Go 实现 Deno upgrade

洛竹 等级 357 0 0

书接上篇,我在向 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, _ := http.Get(releaseURL)
    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 就是用协程去执行检查更新的动作,这样执行命令还是会立马得到反馈,程序则会在后台执行检查更新,当检测到有新版本则强制更新。

完整代码在go-release/example,对 Cobra 不熟悉的同学可以看一下 Cobra 中文文档

执行 shell 命令推荐 go-sh,它对 exec 包做了封装,提供了更好地使用体验( PS:还不是因为我菜)

// 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 核心代码翻译自 Deno 的 upgrade,开源项目,点赞是我维护的动力,在此求个 Star。

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

林小帅

基于 Go 实现 Deno upgrade

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

基于 Go 实现 Deno upgrade

justjavac 大佬:

基于 Go 实现 Deno upgrade

基于 Go 实现 Deno upgrade

收藏
评论区

相关推荐

Golang高阶:Golang1.5到Golang1.12包管理
版权所有,转载请注明:http://www.lenggirl.com/go/gomod.html(https://links.jianshu.com/go?tohttp%3A%2F%2Fwww.lenggirl.com%2Fgo%2Fgomod.html) 1. 前言 Golang 是一门到如今有十年的静态高级语言了,2009年的时
Golang高并发抓取HTML图片
版权所有,转载请注明:http://www.lenggirl.com/language/gopicture.html(https://links.jianshu.com/go?tohttp%3A%2F%2Fwww.lenggirl.com%2Flanguage%2Fgopicture.html) 使用准备 1.安装Golang 2.
Golang指南:顶级Golang框架、IDE和工具列表
自推出以来,Google的Go编程语言(Golang)越来越受主流用户的欢迎。在2016年12月的一份调研中,3,595名受访者中有89%表明他们在工作中或工作以外用Go语言编程。 此外,在编程语言中,Go语言在专业知识和偏好方面排名最高。2017年7月,在Tiobe的年度编程语言排名(https://www.tiobe.com/tiobeindex
【Golang】Golang + jwt 实现简易用户认证
<p本文已同步发布到我的个人博客:<a href"https://links.jianshu.com/go?tohttps%3A%2F%2Fglorin.xyz%2F2019%2F11%2F23%2FGolangjwtsimpleauth%2F" target"_blank"https://glorin.xyz/2019/11/23/Golang
golang 中神奇的 slice
声明:本文仅限于简书发布,其他第三方网站均为盗版,原文地址: golang 中神奇的 slice(https://links.jianshu.com/go?tohttps%3A%2F%2Fliqiang.io%2Fpost%2Fimagesliceingolang) 在 golang 中,似乎人们都不太喜欢使用 Linked List,甚至于原
godoc 命令和 golang 代码文档管理
介绍 godoc 是 golang 自带的文档查看器,更多的提供部署服务 go doc 和 godoc 在 golang 1.13 被移除了,可以自行安装 golang.org go1.13 godoc(https://links.jianshu.com/go?tohttps%3A%2F%2Fgolang.org%2Fdoc%2Fg
为什么GOPROXY对Golang开发如此重要
为什么GOPROXY对Golang开发如此重要 引言 从Go 1.13开始,Go Module作为Golang中的标准包管理器,在安装时自动启用,并附带一个默认的GOPROXY。 但是对于其他的GOPROXY选项,比如JFrog GoCenter,以及你自己的Go Module包,你需要在公众视野中保持安全,你应该选择什么样的配置? 你怎样才能
【Golang】Go入门及进阶书籍推荐
Go入门教程全集 链接: https://pan.baidu.com/s/1mWD7DpRa56WXi7WmNaohOg(https://pan.baidu.com/s/1mWD7DpRa56WXi7WmNaohOg) 提取码: ki1e Cloud.Native.Go.pdf C和指针.pdf C面向对象多线程编程.pdf Design Patt
【Golang】GoWeb框架之Gin-简明教程
Gin 简介 Gin is a HTTP web framework written in Go (Golang). It features a
Golang中常用的字符串操作
Golang中常用的字符串操作 一、标准库相关的Package go import( "strings" ) 二、常用字符串操作 1. 判断是否为空字符串 1.1 使用“”进行判断 思路:直接判断是否等于""空字符串,由于Golang中字符串不能为 nil,且为值类型,所以直接与空字符串比较即可。 举例: go
深入理解 Go Slice
(https://imghelloworld.osscnbeijing.aliyuncs.com/0ce8a8773a658d4b843e5796a0dbf001.png) image 原文地址:深入理解 Go Slice(https://github.com/EDDYCJY/blog/blob/master/golang/pkg/20
我的golang基础
库查询 https://gowalker.org/你应该$HOME/.profile文件增加下面设置。 搭建go的环境 step1:去golang的官网下载go的安装包 windows:go1.9.2.....msi mac:go1.9.2......pkg 双击傻瓜式安装 linux:go1.9.2.linuxamd64.tar.gz 默认到下
向 Deno 学习脚本的管理
前言 🌱如果你使用过 Deno、Go 或者配置过 Android Studio,那么你一定对配置环境变量不陌生。那么如果我们自己写了一个脚本或者命令行工具,如何分享给朋友们玩呢?最简单的当然是直接把脚本放出去,供别人手动下载和手动配置环境变量。但这既不优雅,也不利于传播,本文就是研读了 Deno 的安装机制后,总结出的一套可用的二进制可执行文件分发教程
基于 Go 实现 Deno upgrade
书接上篇,我在中向大家介绍了 Deno 是如何管理它的安装包的——以 Github release 的形式发布、执行编写好的 shell 脚本安装程序以及基于 tags 的版本管理。有版本管理就会有更新的需求,本文就是在研读了 Deno 的 upgrade 命令后使用 Go 语言实现了自己的 upgrade 命令。 获取最新版本我们先来看下 Deno
go get下载包失败问题
关于我由于某些不可抗力的原因,国内使用go get命令安装包时会经常会出现timeout的问题。本文介绍几个常用的解决办法。 从github克隆golang在github上建立了一个镜像库,如https://github.com/golang/net就对应是 https://golang.org/x/net的镜像库。 要下载golang.org/x/net包