跳到正文
SL Blog 技术探索 · 工程实践 · AI 时代思考
返回

把一个 Obsidian 插件上架到社区目录:私有源码仓 + 公开分发仓的完整流程

文章目录
  1. 一、整体形状:双仓 + 一个后台
  2. 二、上架前,仓库里必须有什么
  3. 三、GitHub 仓库配置:私有源码 + 公开分发
  4. 3.1 私有源码仓长什么样
  5. 3.2 公开分发仓只放安装所需文件
  6. 3.3 Actions 权限:默认只读,按需提权
  7. 3.4 私有仓要能推公开仓:RELEASE_TOKEN
  8. 3.5 打 tag 自动发布
  9. 四、私有仓库怎么和社区目录对接
  10. 五、在 community.obsidian.md 提交
  11. 5.1 登录并绑定 GitHub
  12. 5.2 新建插件条目
  13. 六、上架后的自动检验
  14. 6.1 不想等也能先预检
  15. 6.2 通过后的公开页面
  16. 七、踩坑与经验
  17. 7.1 工件证明(attestation):私有仓模式下天然缺失
  18. 7.2 no-unsafe-* 一堆警告,但本地 lint 却过
  19. 7.3 Clipboard 是「行为声明」不是「报错」
  20. 7.4 插件改名撞车是真实风险
  21. 7.5 扫描器会忽略哪些文件
  22. 八、可复用的上架清单

结论先行:Obsidian 现在的插件上架入口已经不是给 obsidian-releases 提 PR,而是 community.obsidian.md 这个社区目录后台。源码可以放在私有仓库,只要公开一个只放构建产物的分发仓,再用官方 GitHub App 授权扫私有源码即可。提交后目录会自动扫描 manifest.json、Release 资产、源码和构建一致性,按 Error / Warning / Recommendation / Pass 给反馈。本文把 Scholiast 走通这条路的仓库配置、私有仓授权、CI 发布、提交与整改全过程记下来。

一、整体形状:双仓 + 一个后台

Scholiast(插件 ID scholiast,中文名「笺注」)的最终结构是两个 GitHub 仓库 + 一个社区目录条目:

角色仓库 / 地址可见性内容
源码仓(开发)github-oysl/obsidian-annotation-plugin-srcPrivateTypeScript 源码、测试、设计文档、CI
分发仓(安装)github-oysl/obsidian-annotation-pluginPublicmain.js、manifest.json、styles.css、README、LICENSE、Releases
目录后台community.obsidian.md/account/plugins/scholiast登录可见提交表单、自动检查结果、发布管理

这样拆的好处是:开发过程、提交历史、内部设计文档都留在私有仓,公开仓只呈现「用户安装真正需要的那几个文件」,同时社区目录仍然能通过授权读到私有源码做一致性校验。

整条链路的触发点是打一个和 manifest.json 版本号一致的 tag:CI 构建 → 同步产物到公开仓 → 在公开仓建 Release。之后目录发现新版本,重新扫描。

二、上架前,仓库里必须有什么

官方要求很简单,但每一条都有坑。根目录必须有:

  • README.md:说明用途与用法,目录列表页会摘取一部分展示。
  • LICENSE:明确的许可证文件,否则扫描会报「没有识别到许可证」。
  • manifest.json:插件元数据,必须是默认分支 HEAD 上的最新版本。

Scholiast 的 manifest.json:

{
  "id": "scholiast",
  "name": "Scholiast",
  "version": "0.7.0",
  "minAppVersion": "1.13.0",
  "description": "Read and annotate Markdown, PDF and ebooks with highlights, notes and tags without editing the source.",
  "author": "slouyang",
  "isDesktopOnly": false
}

几个容易踩的点:

  • description 必须以句号结尾、250 字符以内、不能以 “This is a plugin” 开头。Scholiast 在上架过程中就因描述细节被要求改过。
  • id 全局唯一,且不能包含 obsidian 字样,发布后无法修改(改 ID 会清空下载量、要求所有用户重装)。
  • minAppVersion 要贴合你用到的 API。Scholiast 用了 Obsidian 1.13 的声明式设置 API,所以填 1.13.0。
  • 用了 Node.js / Electron API 的插件必须把 isDesktopOnly 设为 true。
  • 命令 ID 不要再手动加插件 ID 前缀,Obsidian 会自动加。

另外,versions.json 用来记录「插件版本 → 最低宿主版本」,每次发版和 manifest.json 一起更新:

{
  "0.5.0": "1.13.0",
  "0.6.0": "1.13.0",
  "0.7.0": "1.13.0"
}

三、GitHub 仓库配置:私有源码 + 公开分发

3.1 私有源码仓长什么样

私有源码仓是完整的开发仓库:src/、runtime/、tests/、docs/、.github/workflows/ 全在这里。

scholiast-github-private-repo

注意右上角的 Private 徽标——这正是「源码不公开」的诉求所在。

3.2 公开分发仓只放安装所需文件

公开仓刻意保持精简,根目录只放用户安装和目录扫描需要的文件,外加 docs/ 里的双语手册与截图:

scholiast-github-public-repo

它不包含 src/、tests/ 等源码目录。也就是说,公开的只是「构建产物 + 使用说明」。

3.3 Actions 权限:默认只读,按需提权

官方 Release 工作流文档建议把仓库的 Workflow permissions 设为 Read and write,这样工作流里的 GITHUB_TOKEN 才有权限创建 Release。

Scholiast 走的是更小权限的路子:仓库默认保持 Read repository contents and packages permissions(只读),而在 release.yml 里用 permissions: contents: write / id-token: write 按需显式提权。这样日常默认最小权限,只有发布工作流才拿到写权限:

scholiast-github-workflow-permissions

3.4 私有仓要能推公开仓:RELEASE_TOKEN

私有源码仓是「构建现场」,但它要把产物推送到另一个(公开)仓库。默认的 GITHUB_TOKEN 只能操作当前仓库,所以需要在私有仓的 Settings → Secrets and variables → Actions 里配置一个 RELEASE_TOKEN:

  • 类型:fine-grained PAT;
  • 权限:仅授予公开分发仓的 contents: write;
  • 用途:checkout 公开仓、推送产物、创建 Release。

scholiast-github-secrets

3.5 打 tag 自动发布

release.yml 的触发条件是推送与版本号一致的纯数字 tag(x.y.z,不带 v 前缀)。核心步骤:

  1. 校验 tag 与 manifest.json 的 version 是否一致,不一致直接失败;
  2. npm ci → npm run build;
  3. npm run lint + npm test(源码侧校验);
  4. 生成 SLSA 构建来源证明(仅公开仓可用,私有仓自动跳过);
  5. 检出公开分发仓,把 main.js / manifest.json / styles.css / README.md / LICENSE / versions.json / CONTEXT.md 和 docs/ 拷进去并提交推送;
  6. 在公开仓创建(或更新)对应 tag 的 Release,附上三个资产。

一次成功的 0.7.0 发布,工作流步骤是这样的:

scholiast-github-actions-steps

可以看到 Verify tag matches manifest version、Build、Validate source、Sync distribution files to public repo、Create release in public distribution repo 依次跑完,全程 38 秒。

注意 Attest build provenance 这一步是跳过的:GitHub 的工件证明只对公开仓库开放,私有源码仓拿不到,工作流里用 if: ${{ !github.event.repository.private }} 主动跳过。这一步的取舍在后面「踩坑」里详细说。

发布完成后,公开仓的 Release 里就有三个可下载资产:

scholiast-github-release

用户侧看到的安装方式就是「下载这三个文件放进 .obsidian/plugins/scholiast/」。

四、私有仓库怎么和社区目录对接

这是整套流程里最容易被忽略、也最关键的一步:私有源码仓必须单独授权给社区目录的 GitHub App,扫描器才能读到源码。

  1. 提交时仍用公开分发仓的 URL 作为「GitHub repository URL」;
  2. 到 github.com/apps/obsidian-community-directory 安装官方 Obsidian Community Directory App,并只授权私有源码仓;
  3. App 会引导完成私有源码仓的登记。

装好之后,私有源码仓的 Settings → GitHub Apps 里能看到它:

scholiast-github-app

授权成功后,目录后台的检查结果里,源码相关问题会带上 PRIVATE SOURCE 标记,并且仓库区块会出现一行 Source verified 和验证日期——这表示扫描器把公开 Release 的构建产物和私有源码做了比对:

scholiast-community-review

(截图下半部分 Repository 区块可见:GitHub 指向公开仓,Source repository 指向私有仓并标注 PRIVATE,Source verified 时间为 Oct 8, 2026。)

如果哪天 App 失去了私有仓的读权限,条目页会出现警告,直到重新授权为止。所以私有仓这条路不是「装一次就完事」,权限维护要持续。

五、在 community.obsidian.md 提交

5.1 登录并绑定 GitHub

用 Obsidian 账号登录 community.obsidian.md,在 Profile 里把 GitHub 账号连上(只读授权,用来校验仓库归属)。这一步是提交的前置条件。

5.2 新建插件条目

侧栏 Plugins 页面右上角有 New plugin,下方「Your entries」会列出你已提交的条目:

scholiast-community-plugins-list

点 New plugin 打开提交表单,只需要填两项:

  • GitHub repository URL:填公开分发仓地址(这里读的是公开仓默认分支上的 manifest.json);
  • Owner:选择归属(自己或所属组织)。

然后勾选两条承诺——同意 Developer Policies、承诺持续维护(无法维护时删除或转移)——再点 Submit:

scholiast-community-new-plugin

只提交初始版本一次即可。之后发新版本不需要重新提交,只要在 GitHub 打新 tag 发 Release,用户就能收到更新。

六、上架后的自动检验

提交后目录会自动扫描,结果分成四个区块:

区块检查什么
Manifest元数据规范(描述、ID、minAppVersion 等)
ReleasesRelease 资产是否齐全、是否有工件证明
Source codeESLint 规则、危险 API、依赖漏洞、代码混淆
Build verification用源码能否逐字节重现 Release 里的 main.js

每个结果分级为 Error(阻断)/ Warning / Recommendation / Pass。Scholiast 当前这次扫描(0.7.0)的实际结果:

scholiast-community-review

  • Build verification:Pass ——「Build reproduced the release main.js byte-for-byte」,这是最有说服力的一条,证明用户装的产物确实来自仓库里的源码;
  • Dependencies:Pass —— 无漏洞依赖;
  • Code obfuscation:Pass —— 无混淆;
  • Behavior:Vault Read / Vault Write 均 Pass,Clipboard Access 是 Recommendation(下面细说);
  • Releases:一条 Recommendation —— 缺少 Release 资产的工件证明;
  • Source code:三条 Warning,都是 TypeScript 的 no-unsafe-* 规则。

6.1 不想等也能先预检

目录是周期性检查新 Release 的。想立刻验证某个分支的整改效果,有两条路:

  • 条目页的 … 菜单 → Check for new releases(立刻去查有没有新 Release);
  • 条目页 → Review branch → 填分支 / tag / commit(留空用默认分支)→ Run preview scan,不用发 Release 就能预览扫描结果。

scholiast-community-review-branch

6.2 通过后的公开页面

审核通过、目录发布后,插件就有了公开列表页,带「Private source reviewed」徽标、健康度与评分、版本、下载量,以及 Add to Obsidian 按钮:

scholiast-community-listing

到这一步,用户就可以在 Obsidian 里直接搜索插件名安装了。截图时这条 0.7.0 的记录是:Health Excellent、Review Satisfactory、46 次下载、10 个 Release。

七、踩坑与经验

7.1 工件证明(attestation):私有仓模式下天然缺失

官方 Release 工作流会生成 SLSA 构建来源证明。但 GitHub 的工件证明只对公开仓库生成,私有源码仓用不了——工作流里只能主动跳过。而 Release 又是在另一个公开仓里通过 PAT 创建的,那一步没有跑证明生成。

结果就是:私有源码 + 公开分发这套结构,很容易收到「Missing GitHub artifact attestations for release assets」这条 Recommendation。

它只是建议、不阻断,而且 Build verification 的逐字节比对其实是更强的保证。如果想让这条也变绿,得在公开仓侧的发布环节补上证明生成——这是私有仓方案要额外考虑的成本。

7.2 no-unsafe-* 一堆警告,但本地 lint 却过

三条 Warning 都指向同一处 src/reader.ts,报的是 no-unsafe-call / no-unsafe-assignment / no-unsafe-argument——某处拿到了 any / error 类型的值直接调用。

这类问题本地 npm run lint 不一定拦得住:目录扫描用的是官方 eslint-plugin-obsidianmd 的规则集,和仓库自己的 ESLint 配置未必完全一致。想提前对齐,最好在仓库里也装上这个官方 ESLint 插件。

7.3 Clipboard 是「行为声明」不是「报错」

Clipboard Access 那条 Recommendation 的意思是:插件会读写系统剪贴板,可能暴露从 Obsidian 外复制的内容。按 Developer Policies,“访问知识库之外的文件” 属于必须在 README 里明确披露的事项。Scholiast 的剪贴板用途是「复制摘录」,属于功能本身的一部分,在 README 中说明即可,不需要改代码。

7.4 插件改名撞车是真实风险

Scholiast 曾短暂定名 Marginalia,因为和别人重名,很快改成了 Scholiast(ID scholiast)。教训是:定名之前先在社区目录里搜一遍,因为条目发布后 ID 就不能改了。

7.5 扫描器会忽略哪些文件

目录扫描有固定的忽略清单(dist、node_modules、tests、*.test.*、docs、scripts、*.mjs 等)。所以测试文件、构建脚本、文档不会被当成插件代码分析,只要命名规范。反过来说,如果某个文件本该被忽略却出现在扫描结果里,先检查名字是否落在清单内。

八、可复用的上架清单

准备阶段:

  • README.md、LICENSE、manifest.json、versions.json 都在默认分支根目录;
  • manifest.json 的 description 以句号结尾、≤250 字符、无 emoji;
  • id 唯一、不含 obsidian,且已确认不撞车;
  • minAppVersion 与用到的 API 匹配;用到 Node/Electron API 时 isDesktopOnly: true;
  • 删除模板示例代码。

仓库与 CI:

  • 私有源码仓 + 公开分发仓结构清晰;
  • 确认 Actions 权限可用:默认只读时,在 release.yml 用 permissions: 显式提权(本仓库就是这么做的);
  • 配置只对公开仓有 contents: write 的 RELEASE_TOKEN;
  • 打与版本号一致的 x.y.z tag,CI 自动构建并发布 Releases。

提交与检验:

  • 登录 community.obsidian.md 并绑定 GitHub;
  • 用公开仓 URL 提交,勾选政策承诺;
  • 给私有仓安装并授权 Obsidian Community Directory GitHub App;
  • 用 Review branch 预检整改,再发新版触发正式扫描;
  • 盯到 Build verification 为 Pass(逐字节重现)再算真正上架。

相关链接:


RAG 智能问答

针对本文继续提问:《把一个 Obsidian 插件上架到社区目录:私有源码仓 + 公开分发仓的完整流程》