当前版本 1.5.0 · SDK 1.5.0
VelaShell 官方维护的插件,一个解决方案管起来: Redis、S3、Telnet、串口,外加示例插件 HelloWorld。
三个仓库各管一摊,别串:
| 仓库 | 管什么 |
|---|---|
| joesdu/VelaShell | 主程序(宿主) |
| joesdu/velashell-plugin-toolchain | 插件 SDK 与工具链:契约程序集、测试替身、构建包、vela-plugin、dotnet new 模板 |
| 本仓库 | 第一方插件本身(AI 插件除外,见下) |
本仓库的插件与第三方插件走完全同一条路径 —— 从 nuget.org 引用
VelaShell.PluginSdk.Build,没有任何"仓库内特权"。所以第一方插件天然是 SDK 包的
第一个用户:包坏了我们自己先撞上,而不是等插件作者来报。
AI 插件
velashell.ai不在这里:它住在主仓库的plugins/下,随主程序同仓构建、 同版发布 —— 它借宿主的 AvaloniaEdit 作输入框,只能进程内装载,而进程内装载要求编译时 引用的 Avalonia 与宿主逐字同版;面板还要跟着宿主的主题、语言、字体走。分仓的话每改一行 UI 都要"发一次 Release → 回主仓库抬 pin → 才看得到效果"。详见主仓库plugins/README.md。
| 目录 | 内容 |
|---|---|
plugins/ |
五个插件,一个子目录一个 csproj(见 plugins/README.md) |
tests/ |
每个插件一个测试工程(MSTest + VelaShell.PluginSdk.Testing 替身) |
build/PluginBundle.proj |
批量出 .vpx;并把可分发插件收成 plugins/ 布局(只用于 CI 体检) |
scripts/Set-Version.ps1 |
把发行版本号写进仓库里所有落点(发版时由流水线自动跑) |
Directory.Packages.props |
中央包管理:所有 NuGet 版本只在这一处 |
dotnet build VelaShell.Plugins.slnx
dotnet test VelaShell.Plugins.slnx -c Debug本仓库不出 NuGet 包、不做强名称签名(那是工具链仓库的事),本地构建不需要任何密钥,
-c Debug 也只是跑测试的习惯而非硬性要求。唯一用到密钥的地方是给 .vpx 签名
(见下面的签名密钥),不传就是未签名包,本地开发够用。
构建后插件输出会镜像到 artifacts/plugins/<目录名>/。要直接铺进本机 VelaShell,
指一下应用目录即可:
$env:VELASHELL_DEV_APP_DIR = 'G:\VelaShell\src\VelaShell\bin\Debug\net11.0'
dotnet build plugins/VelaShell.Plugin.Redis反向也行 —— 在主仓库跑
pwsh scripts/Fetch-Plugins.ps1 -FromPluginsRepo G:\velashell-plugins,
由宿主主动来取,不用改这边任何设置。
dotnet build plugins/VelaShell.Plugin.Redis -t:PackVpx # 落 bin/vpx/*.vpxPackVpx 与打包器都来自 VelaShell.PluginSdk.Build 包 —— 与第三方插件用的是同一个。
在工具链仓库 dotnet pack ... -p:VelaSdkVersion=1.5.0-dev -o artifacts/nuget,
然后打开本仓库 nuget.config 里那条注释掉的本地源,再
dotnet build -p:VelaSdkVersion=1.5.0-dev。用完记得把本地源注释回去再提交。
本仓库的插件同上同下 —— 一次 Release,所有插件都是那个版本号。Release 标签是唯一
的输入,scripts/Set-Version.ps1 把它写进三处:
| 落点 | 决定什么 |
|---|---|
Directory.Build.props 的 VelaPluginsVersion |
程序集版本 |
README.md 版本横幅 |
给人看的 |
各 plugins/*/plugin.json 的 version |
.vpx 文件名与宿主里显示的插件版本 |
最后那一处最容易漏:打包器出的是 <id>-<plugin.json 的 version>.vpx,与 MSBuild 那边的
VelaPluginsVersion 毫无关系 —— 只改前者的话,发 1.4.0 出来的仍旧叫
velashell.redis-0.1.0.vpx。脚本会动态枚举 plugins/ 下的每个 plugin.json,
新增插件时不用回来改它。
统一列车的代价是没改过的插件也跟着涨版本,用户那边会看到一次"更新";换来的是 "这台机器上装的是哪一批插件"只有一个答案 —— 排查问题时不必逐个去问版本。
这个号只回答"这一批插件是哪次发布出去的";发出去的资产是每个插件各自的 .vpx,
不再有整批打包的 zip。
它与 SDK 版本(VelaSdkVersion)是两回事:SDK 发 1.5.0 不代表插件必须跟着发,
插件发 1.4.1 也不代表契约动了。
在 GitHub 上发布 Release(标签形如 v1.4.0),流水线会:
- 把版本号写进仓库(
scripts/Set-Version.ps1),因此产物永远与标签一致; - 全量测试 + 构建;
- 打包并逐个核对签名(未签名或签名不自洽就直接失败,不会挂上去);
- 产出并挂到该 Release 的下载列表:
- 每个插件一份
.vpx(已签名) —— 从 Release 下载后即可手工安装,或推进插件商店; SHA256SUMS.txt;
- 每个插件一份
- 开一个
chore/version-<版本>的 PR 把版本号回写main,等你手动合。
本地也可以先跑一遍:
pwsh scripts/Set-Version.ps1 1.5.0 # 落盘
pwsh scripts/Set-Version.ps1 1.5.0 -Check # 只报告(CI 每次 push/PR 都跑这个).vpx 一律签名 —— 它是用户手工安装与插件商店分发的形态,没有签名,"这个包确实来自我们"
就无从谈起。密钥是 vela-plugin keygen 生成的 P-256 PKCS#8 PEM 私钥。
仓库机密 KEY_PEM_FILE 里存这份 PEM 的 base64。生成它(PowerShell,直接进剪贴板,
不落文件也不进终端回滚):
[Convert]::ToBase64String([IO.File]::ReadAllBytes('C:\Users\Joe\OneDrive\文件\密码\velashell.pem')) | Set-Clipboard然后 GitHub → Settings → Secrets and variables → Actions → New repository secret,
名字填 KEY_PEM_FILE,内容直接粘贴(是一整行,没有换行)。
粘之前想确认没贴错,可以就地验一次往返 —— 解出来的第一行应当是 -----BEGIN PRIVATE KEY-----:
[Text.Encoding]::ASCII.GetString([Convert]::FromBase64String((Get-Clipboard))).Split("`n")[0]
⚠️ 别用certutil -encode:它产出的是带页眉页脚与换行的 PEM 式 base64, 流水线里的[Convert]::FromBase64String读不了。要的是裸 base64,单行。
缺这个机密时发布会直接失败并给出这段说明 —— 未签名的 .vpx 与签过的在文件名、大小、
构建日志上都看不出区别,不能让它悄悄发出去。CI(ci.yml)则是可选:仓库自己的
push/PR 拿得到机密就顺带签一次,验证签名链路;来自 fork 的 PR 拿不到,不签名照常跑。
本地想出签名包:
dotnet build build/PluginBundle.proj -c Release -t:PackAllVpx -p:VelaSigningKey=<你的 key.pem>不传 -p:VelaSigningKey 就是未签名包,本地开发够用。
Avalonia 版本必须与宿主一致。这个版本号的权威在工具链仓库:
VelaShell.PluginSdk.Build 用精确区间 [x.y.z] 锁给插件工程,
VelaShell.PluginSdk 再把同一个值经 buildTransitive 导出成
VelaSdkPinnedAvaloniaVersion。本仓库的 VelaAvaloniaVersion 只服务于测试工程
(测试宿主要扮演装载方),由 Directory.Build.targets 的 VerifyAvaloniaMatchesSdk
在构建期与 SDK 锁的值核对 —— 漂了当场报 VELAP1000,而不是等用户装上插件才炸。
AGPL-3.0-only,与主仓库一致。商业授权见主仓库的 LICENSE-COMMERCIAL.md。