结论先行: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-src | Private | TypeScript 源码、测试、设计文档、CI |
| 分发仓(安装) | github-oysl/obsidian-annotation-plugin | Public | main.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/ 全在这里。

注意右上角的 Private 徽标——这正是「源码不公开」的诉求所在。
3.2 公开分发仓只放安装所需文件
公开仓刻意保持精简,根目录只放用户安装和目录扫描需要的文件,外加 docs/ 里的双语手册与截图:

它不包含 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 按需显式提权。这样日常默认最小权限,只有发布工作流才拿到写权限:

3.4 私有仓要能推公开仓:RELEASE_TOKEN
私有源码仓是「构建现场」,但它要把产物推送到另一个(公开)仓库。默认的 GITHUB_TOKEN 只能操作当前仓库,所以需要在私有仓的 Settings → Secrets and variables → Actions 里配置一个 RELEASE_TOKEN:
- 类型:fine-grained PAT;
- 权限:仅授予公开分发仓的
contents: write; - 用途:checkout 公开仓、推送产物、创建 Release。

3.5 打 tag 自动发布
release.yml 的触发条件是推送与版本号一致的纯数字 tag(x.y.z,不带 v 前缀)。核心步骤:
- 校验 tag 与
manifest.json的version是否一致,不一致直接失败; npm ci→npm run build;npm run lint+npm test(源码侧校验);- 生成 SLSA 构建来源证明(仅公开仓可用,私有仓自动跳过);
- 检出公开分发仓,把
main.js/manifest.json/styles.css/README.md/LICENSE/versions.json/CONTEXT.md和docs/拷进去并提交推送; - 在公开仓创建(或更新)对应 tag 的 Release,附上三个资产。
一次成功的 0.7.0 发布,工作流步骤是这样的:

可以看到 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 里就有三个可下载资产:

用户侧看到的安装方式就是「下载这三个文件放进 .obsidian/plugins/scholiast/」。
四、私有仓库怎么和社区目录对接
这是整套流程里最容易被忽略、也最关键的一步:私有源码仓必须单独授权给社区目录的 GitHub App,扫描器才能读到源码。
- 提交时仍用公开分发仓的 URL 作为「GitHub repository URL」;
- 到 github.com/apps/obsidian-community-directory 安装官方 Obsidian Community Directory App,并只授权私有源码仓;
- App 会引导完成私有源码仓的登记。
装好之后,私有源码仓的 Settings → GitHub Apps 里能看到它:

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

(截图下半部分 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」会列出你已提交的条目:

点 New plugin 打开提交表单,只需要填两项:
- GitHub repository URL:填公开分发仓地址(这里读的是公开仓默认分支上的
manifest.json); - Owner:选择归属(自己或所属组织)。
然后勾选两条承诺——同意 Developer Policies、承诺持续维护(无法维护时删除或转移)——再点 Submit:

只提交初始版本一次即可。之后发新版本不需要重新提交,只要在 GitHub 打新 tag 发 Release,用户就能收到更新。
六、上架后的自动检验
提交后目录会自动扫描,结果分成四个区块:
| 区块 | 检查什么 |
|---|---|
| Manifest | 元数据规范(描述、ID、minAppVersion 等) |
| Releases | Release 资产是否齐全、是否有工件证明 |
| Source code | ESLint 规则、危险 API、依赖漏洞、代码混淆 |
| Build verification | 用源码能否逐字节重现 Release 里的 main.js |
每个结果分级为 Error(阻断)/ Warning / Recommendation / Pass。Scholiast 当前这次扫描(0.7.0)的实际结果:

- Build verification:Pass ——「Build reproduced the release
main.jsbyte-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 就能预览扫描结果。

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

到这一步,用户就可以在 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.ztag,CI 自动构建并发布 Releases。
提交与检验:
- 登录 community.obsidian.md 并绑定 GitHub;
- 用公开仓 URL 提交,勾选政策承诺;
- 给私有仓安装并授权 Obsidian Community Directory GitHub App;
- 用 Review branch 预检整改,再发新版触发正式扫描;
- 盯到 Build verification 为 Pass(逐字节重现)再算真正上架。
相关链接:
- 社区目录后台:
https://community.obsidian.md/account/plugins/scholiast - 公开列表页:
https://community.obsidian.md/plugins/scholiast - 官方文档:Submit your plugin、Manage your plugin or theme、Developer policies
- 官方 GitHub App:obsidian-community-directory