TREK 插件发布完全指南:从 GitHub 仓库到官方注册表的完整流水线
【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREK
插件是 TREK 生态的扩展单元,而发布则是把它们交给全世界 TREK 实例的方式。本文基于 TREK 仓库的官方文档(wiki/Plugin-Publishing.md)与
trek-plugin-sdk的真实源码,系统讲解 TREK 插件从本地开发、离线校验、打包、创建 GitHub Release、生成注册表条目到提交 PR 的完整发布流水线——你只需维护一个公开 GitHub 仓库和一行publish命令,其余全部由 SDK 自动完成。读完本文,你将掌握trek-pluginCLI 的五个发布步骤、所有离线/网络检查闸门、签名机制与更新策略,能够独立发布一个可被发现、可被更新、可被验证的 TREK 插件。
TREK 插件的分发不走传统的上传服务器模式:它基于一个静态注册表——TREK-Plugins GitHub 仓库。没有上传服务器,没有账户体系。插件作者把代码托管在自己的公开 GitHub 仓库,把构建好的plugin.zip挂到 Release 上,再通过一个 Pull Request 把条目登记进注册表。trek-plugin-sdk包内置的trek-pluginCLI 几乎承包了所有机械性工作——你几乎不需要手写 hash、大小、commit 或任何 JSON 字段。
一、发布模型:静态注册表 + 不可变 Release
理解 TREK 插件发布,先要理解它的两条根本约束:
- GitHub Release 实质上是不可变的。注册表为每个版本的插件固定记录其 sha256,一旦你覆盖已发布的 Release 字节,就会破坏所有已安装该版本实例的校验和。这正是
publish命令中步骤顺序的由来(下文详述)。 - 注册表只存放元数据。TREK 实例从你的 GitHub Release 下载
plugin.zip,用注册表条目中的sha256和可选签名验证字节后再安装。注册表本身(registry/plugins/<id>.json)只是这些元数据的集合。
你唯一需要的外部依赖是git和一个已认证的gh(GitHub CLI)。SDK 内部的 fork、release 创建、PR 提交全部通过gh完成(见 plugin-sdk/src/cli/submit.ts 中的gh('repo','fork', ...)、gh('pr','create', ...)等调用)。
二、一步发布:publish的五个步骤与顺序哲学
最简单的发布路径是单条命令:
npx trek-plugin-sdk publish --repo you/trek-plugin-flight-tracker --tag v1.0.0publish按严格顺序执行整个发布流程,共五步(对应源码 plugin-sdk/src/cli/publish.ts 中[1/5]到[5/5]的日志标记):
- check—— 在本地跑全部注册表闸门;
- pack—— 构建
plugin.zip; - release—— git tag、push、用
gh release create创建带附件的 GitHub Release; - preflight—— 运行需要 tag 和 Release 已存在的网络闸门;
- submit—— 向注册表打开 PR。
顺序本身就是重点。早期版本是 pack → release → preflight → submit,意味着"README 没写"这类问题是在 GitHub Release 已创建之后才被发现——Release 不可变,作者为此烧掉了v1.0.0这个 tag,只能弃掉重来一个只改了 README 的v1.0.1。现在的实现把能在工作区回答的闸门全部放到第 1 步:任一失败,就什么都不会打包、打 tag、推送或发布,修好之后可以对着同一版本号重跑。
命令末尾会打印 PR 链接。在交互式终端中,它会主动提议签名(没有密钥时自动创建);脚本和 CI 环境不会被提示,需显式传--sign。两个逃生舱口:
--no-checks:跳过第 1 步(重跑时用);--no-preflight:跳过第 4 步(重跑时用)。
它们只是重跑时的出口,首次发布不要使用。此外,publish在打出 Release 前会做签名状态断言(plugin-sdk/src/cli/signing.ts 的assertSigningAllowed):如果插件此前是以签名形式发布的,未签名的发布会在第 1 步直接被拒绝,避免在不可变的 Release 上浪费 tag。
如果你希望分步手工执行,release(pack → GitHub Release → entry)、preflight、submit(开 PR)三个子命令仍然独立存在。
三、第一步:托管并构建你的插件
插件必须放在一个公开 GitHub 仓库中(约定命名trek-plugin-<id>)。用create-trek-plugin脚手架生成项目布局:
npx trek-plugin-sdk create flight-tracker --type widget # integration | page | widget | trip-page一个可发布的插件,仓库根目录必须包含:
trek-plugin.json—— 清单文件,详见 Plugin Development;package.json—— 必须带 CommonJS 标记("type": "commonjs"),SDK 最多只能作为 devDependency;server/index.js—— 构建后的服务端入口(必需,即使是纯 UI 插件也从这里经definePlugin()加载,见 plugin-sdk/src/cli/checks/offline.ts 的codeServerEntry检查);client/—— 构建后的前端(仅page/widget/trip-page类型需要);README.md—— 填写完整,且包含真实截图(质量闸门很严格,见下文);docs/screenshot.png—— 商店卡片图,必须提交到仓库。
需要特别提醒:create生成的项目能跑、能打包,但过不了validate——README 还是模板,也没有截图。这两件事只能由你自己完成。
四、本地验证:status与validate
npx trek-plugin-sdk status # 我在哪?还差什么?——永不失败 npx trek-plugin-sdk validate . # 同样的检查,但失败时以非零码退出status与validate运行的是同一套注册表闸门,只是深度不同(plugin-sdk/src/cli/checks/index.ts 中二者共享runOffline):status把它们按阶段(Manifest / Code / Docs / Release / Repo)分组打印成清单,并提示下一步该执行哪条命令,用于定位,因此永不失败;validate是闸门本身——同样的检查、同样的退出码,是脚本和 CI 应使用的形式。
离线就能抓到几乎所有注册表拒绝项(完整清单见 plugin-sdk/src/cli/checks/offline.ts):
icon不是真实的 lucide 名称——TREK 会静默回退到Blocks图标,本地打字错误看不出来,但 CI 会拒绝;- README 缺少四个必需章节中的任何一个、还残留脚手架占位符、或真实正文不足 400 个字符(
MIN_PROSE_CHARS = 400,统计时剔除标题、代码、图片、表格,见 plugin-sdk/src/cli/checks/readme.ts); - 截图不能解析为磁盘上真实存在的文件(仅 README 里有链接不算,文件必须存在);
- 清单声明了某个权限但 README 从未解释它(每个权限字符串必须原样出现在 README 中);
name/description/author超出注册表长度限制,或名称混用拉丁字母与西里尔/希腊同形字(homoglyph 仿冒攻击);description缺省会以空字符串进入条目,触发注册表 schema 的 minLength 检查;egress[]中的主机没有对应的http:outbound:<host>权限——TREK 的网络允许列表与 iframe CSP 只从http:outbound权限构建,运行期从不读取egress[],因此这样的主机在运行时会被静默拦截(源码注释中称其为 "the egress trap");name/description中出现 emoji(TREK 渲染时会剥离 emoji);trek版本范围无界(如">=3.0.0"),无法阻止插件在从未测试过的主机版本上运行。
真正需要网络的闸门只有四个,且都要求 Release 已存在:tag 是否解析到条目固定的 commit、已发布的工件能否下载且哈希与固定的 sha256 一致(并能用你的密钥验证)、插件 id 是否已被其他 GitHub 所有者绑定、以及更新是否丢弃或轮换了已发布过的签名密钥。这些都在preflight(publish的第 4 步)中运行。
截图的获取
npm i -D playwright && npx playwright install chromium # 一次性安装——不是 SDK 的依赖 npx trek-plugin-sdk shot # 加 --dark 渲染暗色主题shot会启动 dev server,在与 TREK 相同的主题化沙箱框架(/preview)中渲染你的插件,输出 1600×900 的docs/screenshot.png(plugin-sdk/src/cli/shot.ts)。Playwright 约 300MB,因此不是 SDK 的依赖,被按需惰性加载,缺少时会提示安装命令。integration类型插件没有 UI 可渲染,shot帮不上忙——此时应截图你的插件所改变的 TREK 界面。
务必提交截图。注册表在固定的 commit上解析它,只存在于工作区、未提交的图片在 CI 中必然失败,哪怕本地status是绿的。
五、打包:pack
npx trek-plugin-sdk pack . # 生成 ./plugin.zip npx trek-plugin-sdk pack . --out dist.zip # 自定义输出路径 npx trek-plugin-sdk pack . --json # 机器可读结果pack按安装器要求的精确布局构建plugin.zip,并打印手工计算麻烦的sha256和size。它拒绝打包无法加载的插件——清单损坏、缺少server/index.js、包含原生二进制——但故意不强制发布闸门(没写 README、缺截图也可以打包),因为打包同样是向本地 TREK 侧载(sideload)试用插件的方式,文档只需在真正发布时齐全(对应源码 plugin-sdk/src/cli/pack.ts 中blocking(runOffline(...), 'artifact')只取blocks: 'artifact'的阻断项)。
打包只包含运行时需要的文件——trek-plugin.json、README.md、LICENSE、package.json以及server/和client/目录树——并剔除node_modules、.git、source map(.map)和.ts源码。它拒绝原生二进制(.node、binding.gyp、prebuilds/),并强制执行与安装器相同的体积上限:
| 限制项 | 上限 |
|---|---|
| 单文件 | 25 MB |
| 归档总大小 | 50 MB |
| 条目数 | 4000 |
docs/故意不随包发布。商店直接从固定 commit 处的仓库拉取docs/screenshot.png,因此请把图片提交到 GitHub 但留在 zip 之外——pack会自动处理。
pack产出的plugin.zip同时也是侧载的工件:把它交给实例管理员(或直接拖到Admin → Plugins),即可完全绕过注册表安装——无需 PR、无需审查、无需 SHA-256/签名固定。侧载插件会被标记,且永不自动更新,因此下面的注册表 PR 仍是让插件可被发现、可更新的正途。更多背景见 Plugins。
六、创建 GitHub Release
tag 采用vX.Y.Z格式,其中X.Y.Z必须等于清单中的version,并把打包好的plugin.zip作为 Release 资产上传:
gh release create v1.0.0 plugin.zip --repo you/trek-plugin-flight-tracker优先上传plugin.zip资产——它就是你打包时的原始字节,注册表会固定其哈希。不要依赖 GitHub 自动生成的源码归档(source archives),它们不是安装器布局,字节也不稳定。
七、生成注册表条目:entry
npx trek-plugin-sdk entry \ --repo you/trek-plugin-flight-tracker \ --tag v1.0.0 \ --out registry/plugins/flight-tracker.jsonentry读取你的清单和plugin.zip,生成完整条目(plugin-sdk/src/cli/entry.ts),自动推导:
commitSha—— 通过git rev-parse <tag>^{commit}解析(^{commit}会对带注释的 tag 解引用到实际 commit,见resolveCommit);downloadUrl—— 由 repo、tag 和资产名拼出;sha256—— 对plugin.zip的字节做 SHA-256;size—— 字节数;apiVersion—— 默认 1,可从清单覆盖;trek—— 清单中的版本范围,原样照抄。
trek范围是条目中唯一的兼容性字段,TREK 用它闸控安装与激活。旧的minTrekVersion/maxTrekVersion已被弃用且不再生成:前者只是重复了范围的下界,后者是包含语义、无法表达<4.0.0。注册表仍接受在trek字段出现之前发布的旧条目携带这两个字段,但不会为新条目生成它们。entry会拒绝为没有可用trek范围的清单生成条目——因为 TREK 本来就会拒绝安装它。
可用旗标:--zip(默认plugin.zip)、--commit <sha>(覆盖 commit 解析)、--asset(指定不同名字的 Release 资产)、--merge(更新场景,见下文)、--out(写出文件)。
buildEntry还会把清单中的requiredAddons、pluginDependencies和operatorEgress镜像到条目(仅非空时生成,保持普通条目字节不变)。这一点至关重要:TREK 在下载工件之前就从注册表索引解析依赖,条目遗漏它们会让 CI 报 "manifest requiredAddons != entry requiredAddons"——而这在早期版本中确实发生过(release 已创建、字节已固定之后才发现)。
一条命令:release
npx trek-plugin-sdk release . --repo you/trek-plugin-flight-tracker --tag v1.0.0release一步完成pack →gh release create→ entry,并把条目打印到 stdout。接受--out、--notes、--commit和--merge(需要已认证的ghCLI)。
八、Preflight:需要 Release 存在的检查
在打开 PR 之前,先对已推送的 Release 运行注册表剩余的 CI 检查,避免一次审查往返:
npx trek-plugin-sdk preflight --repo you/trek-plugin-flight-tracker --tag v1.0.0preflight运行validate的全部检查,外加四个真正需要网络的闸门(plugin-sdk/src/cli/checks/network.ts):
- tag 解析到固定的
commitSha(network.tag-resolves,带注释的 tag 会被解引用到 commit 再比对); - 该 commit 处的清单和 README 与条目一致且通过质量闸门(
network.manifest-at-commit、network.readme-at-commit); - 发布的工件可下载、哈希等于固定的 sha256、不含原生二进制、能用你的密钥验证(
network.artifact,大小允许 4096 字节的 GitHub 资产填充余量;签名会做 Ed25519 校验); - 插件 id 未被绑定到其他 GitHub 所有者(
network.owner-binding,读取注册表的OWNERS.json),且更新不会丢弃或轮换已发布的签名密钥(network.signing-downgrade)。
在固定 commit 上重新评定清单和 README 并非与本地通过的重复:作者写完 README 却忘记提交,就会出现"绿树红 tag"——而 CI 评分的正是 tag 指向的版本。--entry <file.json>可检查手写条目,--all检查所有版本而非仅最新版。一次全绿的 preflight 意味着 CI 也会绿。SDK 的 plugin-sdk/test/checks-parity.test.ts 专门守护这一承诺——注册表有而 SDK 没有的闸门就是"假绿",这是被禁止的。
九、打开注册表 PR
最快捷的路径——submit代劳 fork/branch/commit/PR 全程:
npx trek-plugin-sdk submit --repo you/trek-plugin-flight-tracker --tag v1.0.0它会 fork TREK-Plugins(只 fork 一次),基于当前上游main开分支,写入(更新时合并进)registry/plugins/<id>.json,推送并打开 PR,最后打印 PR 链接(plugin-sdk/src/cli/submit.ts)。加--draft创建草稿 PR,--registry <owner/name>指定镜像仓库(需要已认证的gh)。
手工方式:fork 注册表,把生成的文件添加为registry/plugins/<id>.json,向main开 PR。只添加这一个文件——dist/在合并时自动生成,CI 会拒绝手工修改它。
条目遵循注册表的schema/plugin-entry.schema.json;schema/example-entry.json是标准形状。size是必填的(常被遗漏),commitSha、downloadUrl、sha256、trek、apiVersion以及每个版本上的nativeModules: false也都是必填项——全部由trek-plugin entry代填。CI 会拒绝trek与固定 commit 处清单不一致的条目(对仍携带minTrekVersion的旧条目,还会检查其下限与范围一致)。
十、CI 到底强制什么
注册表 CI 对每个变更的registry/plugins/*.json运行scripts/validate-entry.mjs和scripts/check-readme.mjs。几乎所有规则都是你的trek-plugin.json和README.md的纯函数,因此trek-plugin validate在打任何 tag 之前就能离线检查——你永远不该从 CI 那里第一次听说这些规则。
条目检查(validate-entry.mjs):schema 合法 ·id与文件名一致且匹配 slug 模式^[a-z][a-z0-9-]{2,39}$· 首次注册时id绑定到你的 GitHub 所有者,之后任何人无法改指(所有权变更需要维护者覆盖)· homoglyph/混合脚本名称检查 · Release tag 存在且解析到commitSha· 该 commit 处清单对齐(id、version、type、apiVersion、nativeModules不得为true)·下载工件的 SHA-256 与固定值一致且大小在界内 · 归档内无原生二进制· 声明http:outbound时egress[]必须存在(且不能是裸*)。任何唯一 slug 都可用,但registry、install、rescan除外——安装加载器会拒绝它们(与 admin API 路由段冲突)。
README 检查(check-readme.mjs,在固定 commit 处获取):仓库根目录必须存在 · 包含What it does / Screenshots / Permissions / Setup四个章节 · 至少有一张能解析为真实图片的截图(相对路径docs/screenshot.png相对该 commit 解析)· 真实正文 ≥ 400 字符(剔除标题/代码/图片/表格后)· 无残留脚手架占位符 ·解释清单声明的每个权限。
十一、出处与完整性
commitSha固定了维护者审查的确切源码(git tag 是可移动的);sha256固定了 TREK 将要运行的确切工件字节(Release 资产是可变的)。
TREK 安装时会将下载字节与sha256比对,不匹配则拒绝安装。条目上的reviewedAt日期表示维护者查看了那个确切 commit——它不是持续保证。reviewedAt与boundOwner由 CI 在合并时维护,不要自行设置。
十二、签名你的 Release(可选,但推荐)
sha256证明字节是注册表担保的那些字节;作者签名则额外证明字节是你签的——即使注册表被攻破,也无法以你的名义分发攻击者代码。条目 schema 允许两个可选字段:条目上的authorPublicKey和每个版本上的signature。TREK 离线验证签名(minisign / Ed25519,不依赖外部服务),并在首次安装时固定你的密钥(信任首次使用,TOFU):之后用不同密钥签名的版本会被拒绝,直到管理员重新信任。
最省事的方式:让 SDK 代劳
SDK 内置无依赖的 Ed25519 实现,不需要 minisign。在终端中,publish会主动提议签名:解释利弊、没有密钥时自动创建、然后签名。脚本和 CI 不会被提示,直接传--sign:
npx trek-plugin-sdk publish --repo you/repo --tag v1.2.0 --sign--sign对确切工件字节签名,并同时填好authorPublicKey(条目级)和signature(版本级)。若插件此前以签名形式发布,publish会在第 1 步拒绝未签名的 Release——在任何打包、打 tag、发布之前——因为 GitHub Release 不可变,到第 4 步才发现问题就浪费了 tag。请备份~/.trek-plugin/signing.key——丢失它意味着你再也无法发布签名的更新。
手工 minisign 方式
minisign -G # 生成 minisign.key(保密)+ minisign.pub把minisign.pub中的 base64 载荷行放入条目作为authorPublicKey(跨版本稳定),然后每个版本执行:
minisign -Sm plugin.zip # 生成 plugin.zip.minisig把plugin.zip.minisig中的 base64 行作为该版本的signature,放在其sha256旁边:
{ "id": "flight-tracker", "authorPublicKey": "RWQ…base64 minisign public key…", "versions": [{ "version": "1.2.0", "sha256": "3b2a…", "signature": "RUR…base64 .minisig payload…" }] }签名是可选的,而且是一扇可以晚些时候再走、但永远回不来的单向门。没有authorPublicKey/signature的条目只靠sha256安装;从"未签名"到"签名"不会破坏任何人——因为在有签名版本安装之前,没有任何东西被固定,在 v1.4.0 才加密钥是真实可行的选择。但反过来永远不行:插件一旦以签名形式发布,未签名的更新会在所有已安装实例上被拒绝(SIGNATURE_MISSING),密钥轮换需要注册表维护者覆盖(allow-key-change)加每个实例的管理员重新信任。所以——随时可以签名,但请把密钥备份好。
十三、更新你的插件
修改清单中的version,提交,然后用新 tag 再次运行publish——它会检测到已有条目并把新版本前插,保持最新在前。手工方式:重新pack,打新的vX.Y.ZRelease,再用--merge把新版本折叠到既有条目上:
npx trek-plugin-sdk entry --repo you/trek-plugin-flight-tracker --tag v1.1.0 \ --merge registry/plugins/flight-tracker.json \ --out registry/plugins/flight-tracker.json--merge前插新版本(数组保持最新在前)并保留条目其余部分,且会校验签名一致性——签名密钥与已发布的不同、或已签名插件发未签名更新都会被拒绝(见 plugin-sdk/src/cli/entry.ts 的mergePath分支)。然后把更新后的文件提 PR。各实例在下一次注册表轮询时看到更新;是否应用始终是管理员的显式操作,若新版本申请了更多权限,管理员必须重新批准——详见 Plugin Permissions。
小结:发布核对清单
- 公开 GitHub 仓库,仓库根目录含
trek-plugin.json、package.json(CommonJS 标记)、构建后的server/index.js、需要的client/、写好的README.md、已提交的docs/screenshot.png; trek-plugin status/validate全绿(离线,含 README ≥ 400 字符、四章节齐全、截图可解析、权限全部解释);trek-plugin pack生成plugin.zip(sha256 / size 由此得到);- 打 tag
vX.Y.Z(等于清单version)并gh release create上传plugin.zip; trek-plugin entry生成条目(或release一步完成);trek-plugin preflight验证 tag、commit、工件哈希、所有者绑定、签名状态;trek-plugin submit开 PR(或手工 fork + PR)。
整个流程的核心设计是:先在本地、后动远端,先校验、后发布。任何能被工作区回答的问题都不该等到 GitHub Release 创建之后才发现——这既是publish五步顺序的由来,也是validate/preflight/CI 三级闸门存在的意义。遵循这份指南,你的插件就能以可验证、可更新、可信赖的方式进入 TREK 的官方注册表。
【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREK
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考