51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

上来就对标 20k Star 的开源项目,是自不量力还是后起之秀?

先来一段紧箍咒:nvm、fvm、gvm、sdkman、fnm、n、g、rvm、jenv、phpbrew、rustup、swiftenv、pyenv、rbenv...![](https://img1.51tbox.com/static/2024-03-11/col/a1c1f158fde5b53d82a5c94915d67608/9cee381e9e404e5097962e21394e5e16.jpg) 这些都是用来解决编程语言多版本管理的工具,如果你是个程序员肯定认识或是用过几个,但是刚接触编程的小白,就会有些挠头了。 啥是编程语言版本管理工具?它们有什么用呢? 举个例子,用 Java 的开发者可能会遇见的问题,公司的项目是万年不变 JDK 8,但个人项目用的是最新的 JDK 21。这种情况下,在一台电脑上开发公司和个人项目的时候,就需要切换一下当前开发环境对应的 JDK 版本,否则项目跑不起来。编程语言版本管理工具就是用来**切换/管理编程语言不同版本的工具** ,比如 Java 语言对应的工具是 `jenv`。 每一种编程语言都有一个对应的版本管理工具,**对于多语言开发者来说就需要安装、配置、学习各种版本管理工具,记忆不同工具的使用命令** ,这和紧箍咒无异。那咋办啊?![](https://img1.51tbox.com/static/2024-03-11/col/a1c1f158fde5b53d82a5c94915d67608/36305320a4a64881b8f9bc507bfa043b.jpg) 莫慌,今天 HelloGitHub 带来的是一款跨平台版本、支持多语言的版本管理工具------vfox,让你无忧应对多编程语言、不同版本的开发环境。该项目由国人(99 年的小伙)开发,更贴合国内开发者的使用习惯。![](https://img1.51tbox.com/static/2024-03-11/col/a1c1f158fde5b53d82a5c94915d67608/100e7f44d3b349cda9e325b319f71fac.jpg) GitHub 地址:https://github.com/version-fox/vfox 接下来,让我们一起走近 vfox 了解它的功能、上手使用、技术原理和强大的插件系统吧! 一、介绍 ---- vfox 是一个类 nvm、fvm、sdkman、asdf 的版本管理工具,具有**跨平台** 、**通用** 、**易拓展**的特性: * 简单:安装简单,一套命令管理所有语言 * 跨平台:支持 **Windows**、Linux、macOS * 人性化:换项目时**自动切换到对应编程语言**、支持自动补全 * 扩展性:容易上手的插件系统,添加冷门的编程语言 * 作用域:支持 Global、Project、Session 三种作用域 质疑声:同类型的项目挺多的啊,不能一个国人开发、开源就来求 Star 吧? 下面,我们就来**和在 GitHub 上有 20k Star 的同类型工具 asdf PK 一下**,看看 vfox 是不是重复造轮子,到底能不能打! 二、对比 asdf --------- 这里主要从**操作系统兼容性、性能和插件换源**三个方面进行对比。 ### 2.1 兼容性 \| 兼容性 \| Windows \| Linux \| macOS \| \|------\|---------\|-------\|-------\| \| asdf \| ❌ \| ✅ \| ✅ \| \| vfox \| ✅ \| ✅ \| ✅ \| 首先,asdf 是用 shell 脚本实现的工具,所以并**不支持原生 Windows 环境**。而 vfox 是用 Go + Lua 实现的,因此天生支持 Windows 和其他操作系统。 ### 2.2 性能 ![](https://img1.51tbox.com/static/2024-03-11/col/a1c1f158fde5b53d82a5c94915d67608/8c9f8b48c82a4c2c9734db0aaf624c3a.jpg) 上图是对两个工具最核心的切换版本功能进行基准测试的结果,很容易就能得出结论:vfox 比 asdf **快 5 倍**。 \| 速度 \| 平均 \| 最快 \| 最慢 \| \|------\|----------\|---------\|----------\| \| asdf \| 158.7 ms \| 154 ms \| 168.4 ms \| \| vfox \| 28.1ms \| 27.1 ms \| 32.3 ms \| **技术解析** :asdf 执行切换版本的速度之所以较慢,主要是由于其**垫片机制** 。简单来说,当你尝试运行如 node 这样的命令时,asdf 会首先查找对应的垫片,然后根据 `.tool-versions` 文件或全局设置来确定使用哪个版本的 node 。这个查找和确定版本的过程会消耗一定的时间,从而影响了命令的执行速度。 相比之下,vfox 则采用了直接操作环境变量的方式来管理版本,它会直接设置和切换环境变量,从而避免了查找和确定版本的过程。因此,在执行速度上要比使用垫片机制的 asdf 快得多。 虽然 asdf 很强,但是它对 Windows 原生无能为力。虽然 vfox 很新,但在**性能和跨平台方面做得更好**。 ### 2.3 插件换源 大多数时候,我们会被网络问题而困扰,所以切换下载源的操作是必不可少的。 下面以切换 Node.js 源为例,对比 asdf 和 vfox 在换源时的区别。 asdf 是通过 `asdf-vm/asdf-nodejs` 插件实现了对于 Node.js 的支持,但该插件是需要手动预定义一个环境变量来修改下载源,多语言换源还需要设置多个不同的环境变量。 * 优点:可以灵活切换任何镜像源 * 缺点:需要手动设置,操作不友好 vfox 选择了另一种方法,即一个镜像源对应一个插件。 ``` $ vfox add nodejs/nodejs # 使用官方下载源 $ vfox add nodejs/npmmirror # 使用 npmmirror 镜像 $ vfox add python/python # 官方下载源 $ vfox add python/npmmirror ``` 虽然这样会使仓库的插件变多,但使用起来降低了负担,也**没有乱七八糟的环境变量需要配置**,对用户非常友好! 三、上手 ---- 说了这么多,还没上手玩一下简直忍不了。 ### 3.1. 安装 **Windows** 用户只需要下载安装器进行安装即可,**Linux** 用户可以使用 APT 或 YUM 来快速安装,**macOS** 用户可以使用 Homebrew 安装。更详细的安装方式可查看文档 ``` $ brew tap version-fox/tap $ brew install vfox ``` 安装完成之后,需要将 vfox 挂载到你的 shell 中,从下面条目中选择一条适合你 shell 的。 ``` echo 'eval "$(vfox activate bash)"' >> ~/.bashrc echo 'eval "$(vfox activate zsh)"' >> ~/.zshrc echo 'vfox activate fish | source' >> ~/.config/fish/config.fish # 对于 Powershell 用户,将下面行添加到你的 $PROFILE 文件中 Invoke-Expression "$(vfox activate pwsh)" ``` ### 3.2 使用 安装好了,但你还做不了任何事情,因为 vfox 是使用插件作为扩展,按需安装。 > 不知道应该添加哪些插件,可以用 `vfox available` 命令查看所有可用插件 所以你还需要安装插件,以 Node.js 为例,为了获得更好的体验,我们添加 npmmirror 镜像源插件:`vfox add nodejs/npmmirror`。![](https://img1.51tbox.com/static/2024-03-11/col/a1c1f158fde5b53d82a5c94915d67608/297bbce873714890af3c0f696a845aee.jpg) 在插件成功安装之后,你就可以玩起来了! * 安装指定版本:`vfox install nodejs@` * 安装最新版本:`vfox install nodejs@latest` * 切换版本:`vfox use nodejs[@]` 文字表达远不如图片来的更直观,我们直接上效果图。![](https://img1.51tbox.com/static/2024-03-11/col/a1c1f158fde5b53d82a5c94915d67608/39fcbe8a0530415da62d5100bcdb0d56.jpg) 四、技术原理 ------ vfox 支持 Global、Session、Project 三种作用域,这三种作用域能够满足我们日常开发所需的场景。 \| 作用域 \| 命令 \| 说明 \| \|---------\|--------------------------\|---------------\| \| Global \| `vfox use -g ` \| 全局范围有效 \| \| Session \| `vfox use -s ` \| 当前 shell 会话有效 \| \| Project \| `vfox use -p ` \| 当前项目下有效 \| 那么你对它们的实现原理感兴趣吗?咱们废话不多说,直接看原理图!![](https://img1.51tbox.com/static/2024-03-11/col/a1c1f158fde5b53d82a5c94915d67608/4e6ad4cc506b45229687f1bce6be1695.jpg) vfox 是基于 shell 的 hook 机制实现的,hook 机制简单来说就是每当我们执行完命令之后,shell 都会调用一下你配置的钩子函数(hook),即 `vfox env ` 命令,我们后面解释这个命令是干什么的。 说回到作用域上来,vofox 是通过 `.tool-versions` 文件来记录每个 SDK 对应的版本号信息。对于三种作用域,会分别在不同的地方创建 `.tool-versions` 文件,用于记录作用域内所需要的 SDK 版本信息。 * `Global` -\> `$HOME/.version-fox/.tool-versions` * `Project` -\> `当前项目目录` * `Session` -\> `$HOME/.version-fox/tmp//.tool-versions` 代码如下: ``` func newSdkManagerWithSource(sources ...RecordSource) *Manager {     meta, err := newPathMeta()     if err != nil {        panic("Init path meta error")     }     var paths []string     for _, source := range sources {         // 根据不同的作用域选择性加载不同位置的.tool-versions文件        switch source {        case GlobalRecordSource:           paths = append(paths, meta.ConfigPath)        case ProjectRecordSource:            // 当前目录           curDir, err := os.Getwd()           if err != nil {              panic("Get current dir error")           }           paths = append(paths, curDir)        case SessionRecordSource:            // Shell会话临时目录           paths = append(paths, meta.CurTmpPath)        }     }     // env.Record是用来专门操作.tool-versions文件的, 增删改查     var record env.Record     if len(paths) == 0 {        record = env.EmptyRecord     } else if len(paths) == 1 {        r, err := env.NewRecord(paths[0])        if err != nil {           panic(err)        }        record = r     } else {        r, err := env.NewRecord(paths[0], paths[1:]...)        if err != nil {           panic(err)        }        record = r     }     // SdkManager是用来专门管理Sdk的组件, 到这里Manager就可以通过Record来获取和修改Sdk版本信息咯     return newSdkManager(record, meta) } ``` 上面提到,最核心的其实是 hook 机制调用的 `vfox env ` 命令,那它到底干了件什么事情呢? ``` func envCmd(ctx *cli.Context) error {     ...         // 拿到对应shell的组件        s := shell.NewShell(shellName)        if s == nil {           return fmt.Errorf("unknow target shell %s", shellName)        }        // 上面提到的加载.tool-versions信息到Manager中        manager := internal.NewSdkManagerWithSource(internal.SessionRecordSource, internal.ProjectRecordSource)        defer manager.Close()        // 获取需要配置的环境变量信息        envKeys, err := manager.EnvKeys()        if err != nil {           return err        }        // 将环境变量信息, 翻译成符合对应shell的命令        exportStr := s.Export(envKeys)        fmt.Println(exportStr)        return nil     } } func (m *Manager) EnvKeys() (env.Envs, error) {     shellEnvs := make(env.Envs)     var paths []string     // 这里就是前面说的, Record包含了所有的版本信息, 只需要取出来即可     for k, v := range m.Record.Export() {        if lookupSdk, err := m.LookupSdk(k); err == nil {           if keys, err := lookupSdk.EnvKeys(Version(v)); err == nil {              for key, value := range keys {                 if key == "PATH" {                    paths = append(paths, *value)                 } else {                    shellEnvs[key] = value                 }              }           }        }     }    ...     return shellEnvs, nil } ``` 没看懂代码没关系,用一句话概括这段代码的功能:**将 `.tool-versions` 记录的 SDK 版本信息,翻译成具体 shell 可执行的命令**,其实核心技术就这么朴实无华。 五、插件系统 ------ 插件系统是 vfox 的核心,它赋予 vfox 无限的可能性,不仅仅局限于单一的 SDK。通过插件系统,vfox 能够灵活地适应任何 SDK 的需求,无论是现有的还是未来可能出现的。 更重要的是,插件系统使用 Lua 作为插件的开发语言,内置了一些常用模块,如 `http`、`json`、`html`、`file` 等,这使得插件系统不仅功能强大,而且**易于开发和自定义**。用户可以根据自己的需求,轻松编写和定制自己的脚本,从而实现更多的功能。 口说无凭,我们直接写一个简单的插件来体验一下,以写一个 **Windows 环境下可用的 Python 插件**为例。 ### 5.1 插件模板结构 在开工之前,我们首先需要了解一下插件结构是什么样子,以及都提供了哪些钩子函数供我们实现。 ``` --- 内置全局变量: 操作系统和架构类型 OS_TYPE = "" ARCH_TYPE = "" --- 描述当前插件的基本信息, 插件名称、版本、最低运行时版本等信息 PLUGIN = {     name = "xxx",     author = "xxx",     version = "0.0.1",     description = "xxx",     updateUrl = "https://localhost/xxx.lua",     minRuntimeVersion = "0.2.3", } --- 1.预安装钩子函数。vfox 会根据提供的元信息, 帮你提前下载好所需的文件(如果是压缩包,会帮你解压)放到指定目录。 function PLUGIN:PreInstall(ctx)     return {       version = "0.1.1",       sha256 = "xxx", --- 可选       sha1 = "xxx", --- 可选       url = "文件地址"     } end --- 2.后置钩子函数。这里主要是做一些额外操作, 例如编译源码。 function PLUGIN:PostInstall(ctx) end --- 3.可用钩子函数。 告诉 vfox 当前插件都有哪些可用版本。 function PLUGIN:Available(ctx)  end --- 4.环境信息钩子函数。 告诉 vfox 当前SDK所需要配置的环境变量有哪些。 function PLUGIN:EnvKeys(ctx) end ``` 总共就 4 个钩子函数,是不是非常简单。 ### 5.2 Python 插件实现 OK,万事俱备那我们正式开始实现 Python 插件咯\~ ``` --- vfox 提供的库 local http = require("http") --- 发起 http 请求 local html = require("html") --- 解析 html OS_TYPE = "" ARCH_TYPE = "" --- python 下载源地址信息 local PYTHON_URL = "https://www.python.org/ftp/python/" local DOWNLOAD_SOURCE = {     --- ...     EXE = "https://www.python.org/ftp/python/%s/python-%s%s.exe",     SOURCE = "https://www.python.org/ftp/python/%s/Python-%s.tar.xz" } PLUGIN = {     name = "python",     author = "aooohan",     version = "0.0.1",     minRuntimeVersion = "0.2.3",  } function PLUGIN:PreInstall(ctx)     --- 拿到用户输入版本号, 解析成具体版本号     local version = ctx.version     if version == "latest" then         version = self:Available({})[1].version     end     if OS_TYPE == "windows" then         local url, filename = checkAvailableReleaseForWindows(version)         return {             version = version,             url = url,             note = filename         }     else         --- 非 Windows 环境实现, 略     end end function checkAvailableReleaseForWindows(version)     --- 处理架构类型, 同一架构的不同名称     local archType = ARCH_TYPE     if ARCH_TYPE == "386" then         archType = ""     else         archType = "-" .. archType     end     --- 检查是否存在 exe 安装器, 当然 Python 还提供了其他安装器, 例如 msi、web-installer 等     local url = DOWNLOAD_SOURCE.EXE:format(version, version, archType)     local resp, err = http.head({         url = url     })     if err ~= nil or resp.status_code ~= 200 then         error("No available installer found for current version")     end     return url, "python-" .. version .. archType .. ".exe" end --- vfox 会在 PreInstall 执行完之后, 执行当前钩子函数. function PLUGIN:PostInstall(ctx)     if OS_TYPE == "windows" then         return windowsCompile(ctx)     else         --- 略     end end function windowsCompile(ctx)     local sdkInfo = ctx.sdkInfo['python']     --- vfox 分配的安装路径     local path = sdkInfo.path     local filename = sdkInfo.note     --- exe 安装器路径     local qInstallFile = path .. "\\" .. filename     local qInstallPath = path     --- 执行安装器     local exitCode = os.execute(qInstallFile .. ' /quiet InstallAllUsers=0 PrependPath=0 TargetDir=' .. qInstallPath)     if exitCode ~= 0 then         error("error installing python")     end     --- 清理安装器     os.remove(qInstallFile) end --- 告诉 vfox 可用版本 function PLUGIN:Available(ctx)     return parseVersion() end function parseVersion()     --- 这里就是解析对应的 html 页面, 通过正则匹配具体版本号了     local resp, err = http.get({         url = PYTHON_URL     })     if err ~= nil or resp.status_code ~= 200 then         error("paring release info failed." .. err)     end     local result = {}     --- 解析 html 略      return result end --- 配置环境变量, 主要是 PATH, 但是注意 Windows 和 Unix-like 路径不一致, 所以要区分 function PLUGIN:EnvKeys(ctx)     local mainPath = ctx.path     if OS_TYPE == "windows" then         return {             {                 key = "PATH",                 value = mainPath             }         }     else         return {             {                 key = "PATH",                 value = mainPath .. "/bin"             }         }     end end ``` 至此,我们就完成了一个 Windows 环境下可用的 Python 插件啦~🎉![](https://img1.51tbox.com/static/2024-03-11/col/a1c1f158fde5b53d82a5c94915d67608/cee073d7d6d14ea2be98198d7e6b8510.jpg) 当然,这只是为了方便演示如何自己实现插件,vfox 目前已经提供了完善的 Python 插件,可以通过 `vfox add python/npmmirror` 命令直接安装使用哦。 vfox 目前已支持 12 种插件,还在努力丰富中💪💪💪 * Python ✅ -\> `python/npmmirror` * Nodejs ✅ -\> `nodejs/npmmirror` * Java ✅ -\> `java/adoptium-jdk` * Golang ✅ -\> `golang/golang` * Dart ✅ -\> `dart/dart` * Flutter ✅ -\> `flutter/flutter-cn` * .Net ✅ -\> `dotnet/dotnet` * Deno ✅ -\> `deno/deno` * Zig ✅ -\> `zig/zig` * Maven ✅ -\> `maven/maven` * Graalvm ✅ -\> `java/graalvm` * Kotlin ✅ -\> `kotlin/kotlin` * Ruby ⌛️ * PHP ⌛️ 六、结束 ---- 我的初衷是不管什么语言,只要是需要版本管理,只需要一个工具就能简单高效的完成。所以我创建了 vfox,它是一款专注于**多语言** 、**多版本** 管理的生态工具,目标只有一个:**让所有的编程语言版本管理变得简单易用**。无论你是 JavaScript、Java 还是 Python 的开发者,vfox 都能为你提供一站式的解决方案。 我们的愿景是创建一个适合国人使用的、简单易用的**多语言** 、**多版本** 管理工具。我们相信,只有真正理解开发者的需求,才能创造出真正有价值的工具。vfox 就是这样的工具,它是为了解决开发者在日常工作中遇到的**版本管理问题**而生。 > GitHub 地址:https://github.com/version-fox/vfox
赞(4)
未经允许不得转载:工具盒子 » 上来就对标 20k Star 的开源项目,是自不量力还是后起之秀?