先来一段紧箍咒:nvm、fvm、gvm、sdkman、fnm、n、g、rvm、jenv、phpbrew、rustup、swiftenv、pyenv、rbenv...

这些都是用来解决编程语言多版本管理的工具,如果你是个程序员肯定认识或是用过几个,但是刚接触编程的小白,就会有些挠头了。

啥是编程语言版本管理工具?它们有什么用呢?

举个例子,用 Java 的开发者可能会遇见的问题,公司的项目是万年不变 JDK 8,但个人项目用的是最新的 JDK 21。这种情况下,在一台电脑上开发公司和个人项目的时候,就需要切换一下当前开发环境对应的 JDK 版本,否则项目跑不起来。编程语言版本管理工具就是用来切换/管理编程语言不同版本的工具,比如 Java 语言对应的工具是 jenv

每一种编程语言都有一个对应的版本管理工具,对于多语言开发者来说就需要安装、配置、学习各种版本管理工具,记忆不同工具的使用命令,这和紧箍咒无异。那咋办啊?

莫慌,今天 HelloGitHub 带来的是一款跨平台版本、支持多语言的版本管理工具——vfox,让你无忧应对多编程语言、不同版本的开发环境。该项目由国人(99 年的小伙)开发,更贴合国内开发者的使用习惯。

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 性能

上图是对两个工具最核心的切换版本功能进行基准测试的结果,很容易就能得出结论: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

在插件成功安装之后,你就可以玩起来了!

  • 安装指定版本:vfox install nodejs@<version>
  • 安装最新版本:vfox install nodejs@latest
  • 切换版本:vfox use nodejs[@<version>]

文字表达远不如图片来的更直观,我们直接上效果图。

四、技术原理

vfox 支持 Global、Session、Project 三种作用域,这三种作用域能够满足我们日常开发所需的场景。

作用域 命令 说明
Global vfox use -g <sdk-name> 全局范围有效
Session vfox use -s <sdk-name> 当前 shell 会话有效
Project vfox use -p <sdk-name> 当前项目下有效

那么你对它们的实现原理感兴趣吗?咱们废话不多说,直接看原理图!

vfox 是基于 shell 的 hook 机制实现的,hook 机制简单来说就是每当我们执行完命令之后,shell 都会调用一下你配置的钩子函数(hook),即 vfox env <shell-name> 命令,我们后面解释这个命令是干什么的。

说回到作用域上来,vofox 是通过 .tool-versions 文件来记录每个 SDK 对应的版本号信息。对于三种作用域,会分别在不同的地方创建 .tool-versions 文件,用于记录作用域内所需要的 SDK 版本信息。

  • Global -> $HOME/.version-fox/.tool-versions
  • Project -> 当前项目目录
  • Session -> $HOME/.version-fox/tmp/<shell-pid>/.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 <shell-name> 命令,那它到底干了件什么事情呢?

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 作为插件的开发语言,内置了一些常用模块,如 httpjsonhtmlfile 等,这使得插件系统不仅功能强大,而且易于开发和自定义。用户可以根据自己的需求,轻松编写和定制自己的脚本,从而实现更多的功能。

口说无凭,我们直接写一个简单的插件来体验一下,以写一个 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 插件啦~

当然,这只是为了方便演示如何自己实现插件,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

最后,感谢 HelloGitHub 提供的机会,让我能向更多人介绍 vfox。作为一个开源项目的创作者,我深感开源的力量。它不仅仅是代码的共享,更是知识和经验的共享。希望 vfox 能成为我们沟通的桥梁,欢迎各种形式的反馈和建议,让我们一起变强!

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

  1. 我的第一个 60 k+ Star Java开源项目

    JavaGuide([Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识):https://github.com/Snailclimb/JavaGuide. 人生总有各种各样的 ...

  2. 看github上有18万star的第一开源项目如何教你学前端编程的

    作为 Github | star 第一开源项目,已经超过18万 star:比之前最火的bootstrap的10万star还要多出8w,freeCodeCamp 越来越受关注,建站两年时间不到已经近40 ...

  3. Player 播放器开源项目总结

    Android开发中,我们不免会遇到播放器相关开发的需求,以下是本人之前star的开源项目,供大家参考: 一.NBPlayer 项目地址:https://github.com/renhui/NBPla ...

  4. Material Design 开源项目总结

    Android开发中,我们不免会遇到Material Design展示的需求,以下是本人之前star的开源项目,供大家参考: 一.RippleEffect 项目地址:https://github.co ...

  5. Android Tools 开发工具库开源项目总结

    在Android开发中,我们不免会遇到使用一些工具库来简化我们的工具代码的编写,以下是本人之前star的开源项目,供大家参考: 一.android_testsuite 项目地址:https://git ...

  6. Webview 浏览器开源项目总结

    在Android开发中,我们不免会遇到使用WebView实现网页展示的需求,以下是本人之前star的开源项目,供大家参考: 一.CrosswalkWebview 项目地址:https://github ...

  7. ProgressBar 进度条开源项目总结

    在Android开发中,我们不免会遇到进度条展示的需求,以下是本人之前star的开源项目,供大家参考: 一.ArcProgressBar 开源项目地址:https://github.com/zenoT ...

  8. 5 天 4000 star 的一个爆款开源项目

    今天早上起来浏览 GitHub 的时候,在周热门趋势排行榜上看到了这么一个开源项目,仅仅 5 天时间,爬到了周排行榜的第一名的位置.而在每天的排行榜上,今天一早也高高位居排行榜的第二位. 这个开源项目 ...

  9. 微人事 star 数超 10k,如何打造一个 star 数超 10k 的开源项目

    看了下,微人事(https://github.com/lenve/vhr)项目 star 数超 10k 啦,松哥第一个 star 数过万的开源项目就这样诞生了. 两年前差不多就是现在这个时候,松哥所在 ...

  10. 我的开源项目在五个月内超过了 600 star

    其实我在 2016 年年底就开始写了这个项目:Forest,一个能够将 HTTP 的所有请求信息(包括 URL .Header 以及 Body 等信息)绑定到您自定义的 Interface 方法上,能 ...

随机推荐

  1. 【主流技术】实战之 Spring Boot 中集成微信支付(小程序)

    前言 微信支付是企业级项目中经常使用到的功能,作为后端开发人员,完整地掌握该技术是十分有必要的. 以下是经过真实商业项目实践的集成步骤,包括注册流程.调用过程.代码demo(经过脱敏)等,希望我的分享 ...

  2. 蘑菇街大三Java后端暑期实习面经

    「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识.准备 Java 面试,首选 JavaGuide! 分享一位热心读者分享的实习面经给博客园的小伙伴们看看. 一面 1.自我 ...

  3. 阿里天池实验室简明教程以及Docker安装使用[一]

    1.天池notebook简介和使用 天池实验室是基于PAI DSW探索版开发的,PAI DSW (Data Science Workshop)是为算法开发者量身打造的云天池实验室是基于PAI DSW探 ...

  4. 【8】同步vscode配置和插件【导入导出】、再也不用担心换电脑重新安装插件了

    相关文章: [1]VScode中文界面方法-------超简单教程 [2]VScode搭建python和tensorflow环境 [3]VSCode 主题设置推荐,自定义配色方案,修改注释高亮颜色 [ ...

  5. 植物大战僵尸:寻找阳光掉落Call

    本次实验内容:本次实验将接触到Call调用这个概念,什么是Call调用? Call相当于你在编程时所编写的函数,而高级语言中的函数最终也是会被编译器转换为汇编格式的Call调用,这些关键Call普遍都 ...

  6. 遥感图像处理笔记之【Multi-label Land Cover Classification with Deep Learning】

    遥感图像处理学习(3) 前言 遥感图像处理方向的学习者可以参考或者复刻 本文初编辑于2023年12月14日 2024年1月24日搬运至本人博客园平台 文章标题:Multi-label Land Cov ...

  7. centos离线安装mongodb-database-tools

    mongodb-database-tools是MongoDB数据库工具的命令行的工具,用于工作与MongoDB部署.可以使用mongodump和mongoimport很方便的导入导出备份数据. 该数据 ...

  8. centos7.9离线安装MongoDB4.4.17

    前言 MongoDB 5.0开始要求CPU支持avx指令集,参考https://mp.weixin.qq.com/s/6FFXih1DEZYDFOk1hCu69w 环境 CentOS 7.9.2009 ...

  9. 一图看懂iPhone 15系列:15/Plus/Pro/Pro Max有啥区别?详细配置对比

    距离iPhone 15系列发布只剩下2天(北京时间9月13日凌晨1点),即将推出预计分别是iPhone 15.iPhone 15 Plus,以及Pro系列的iPhone 15 Pro以及iPhone ...

  10. java线程池实现多任务并发执行

    Java线程池实现多任务并发执行 1️⃣ 创建一些任务来落地多任务并发执行 每一个数组里面的数据可以看成任务,或者是需要并发的业务接口, 数组与数组之间,可以看作为他们之间有血缘关系,简单来说就是: ...