feat(plugins): 插件市场索引与签名发布流水线

- plugins/registry/<id>.toml 指向作者仓库的固定 commit(repo、commit、path)
- scripts/plugin_registry.py:check 在 PR 上校验改动的条目、运行作者测试、
  从 web-src/ 构建前端并写审查摘要;build 在合并后增量打包(commit 未变的
  条目复用已发布的 zip)
- plugin-registry.yml:check / build 不读 secret,sign 只用 PLUGIN_SIGNING_KEY
  签名,不运行作者代码;发布到 release plugins 并删除下架的资源
- 插件目录改用独立的插件公钥 ab_plugin_pubkey.pem,与在线更新密钥分开
- catalog 条目新增 authors、permissions、has_web、repo、commit、readme
- 6 个示例插件上架;上架文档(中 / 英 / 日)、PR 模板与 CODEOWNERS
This commit is contained in:
Estrella Pan
2026-10-09 10:13:30 +02:00
parent f3359b17fd
commit eb1598c21e
27 changed files with 1161 additions and 56 deletions

2
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,2 @@
/plugins/registry/ @EstrellaXD
/plugins/revoked.toml @EstrellaXD

28
.github/PULL_REQUEST_TEMPLATE/plugin.md vendored Normal file
View File

@@ -0,0 +1,28 @@
<!-- Plugin listing PR. Base branch: 4.0-dev. Docs: https://autobangumi.org/dev/plugins/publish -->
## Plugin
- id:
- repo:
- commit (40-char SHA):
- New plugin / update (old version -> new version):
## Author checklist
- [ ] This PR changes only files under `plugins/registry/`.
- [ ] `commit` is a full 40-character SHA, and it is pushed to a public repository.
- [ ] The file name equals the `id` in `plugin.toml`.
- [ ] `tests/` has at least 1 test, and all tests pass.
- [ ] `ab-plugin validate` passes.
- [ ] For an update, `version` is higher than the listed version, and `repo` is unchanged.
- [ ] If the plugin has a frontend, its source is in `web-src/` with `package.json` and `pnpm-lock.yaml`.
- [ ] The plugin depends only on the standard library and packages that AutoBangumi ships. Other pure-Python libraries are in `vendor/`.
- [ ] I agree that AutoBangumi redistributes the packed plugin in its release.
## Maintainer review checklist
- [ ] I read all code at the compare link (update) or tree link (new plugin) in the Job Summary.
- [ ] The plugin connects only to documented hosts.
- [ ] The plugin writes nothing outside `config/`.
- [ ] If there is a frontend, I read `web-src/` line by line. It runs on the main origin.
- [ ] The declared `permissions` match what the code does.

119
.github/workflows/plugin-registry.yml vendored Normal file
View File

@@ -0,0 +1,119 @@
name: Plugin Registry
# 插件市场:plugins/registry/<id>.toml 指向作者仓库的固定 commit。
# - check:PR 上运行作者的代码(测试、pnpm 构建),所以只读权限、不读任何 secret。
# - build:合并后打包,同样没有密钥;commit 未变的条目复用已发布的 zip。
# - sign:只有这个 job 读 PLUGIN_SIGNING_KEY,它不运行作者的任何代码,
# 只用 build_plugin_catalog.py 读 zip 里的 plugin.toml 并签名。
on:
pull_request:
branches: ["4.0-dev"]
paths: ["plugins/**", "scripts/plugin_registry.py", ".github/workflows/plugin-registry.yml"]
push:
branches: ["4.0-dev"]
paths: ["plugins/**"]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: plugin-registry-${{ github.event_name == 'pull_request' && github.ref || 'publish' }}
cancel-in-progress: false
jobs:
check:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 需要 base 分支上的旧条目
- &python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- &uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"
- &pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: false
- &node
uses: actions/setup-node@v4
with:
node-version: 20
- &deps
name: Install backend
run: mkdir -p backend/config && cd backend && uv sync --group dev
- name: Check changed entries
run: |
set -o pipefail
uv run --project backend python scripts/plugin_registry.py check \
--base "origin/${{ github.base_ref }}" --out dist | tee -a "$GITHUB_STEP_SUMMARY"
build:
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- *python
- *uv
- *pnpm
- *node
- *deps
- name: Download current catalog
env:
GH_TOKEN: ${{ github.token }}
run: gh release download plugins -p catalog.json -D old || echo "release plugins 还不存在,全部重新打包"
- name: Build
run: uv run --project backend python scripts/plugin_registry.py build --old old/catalog.json --out dist
- uses: actions/upload-artifact@v4
with:
name: plugins-dist
path: dist/
retention-days: 7
sign:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with:
version: "latest"
- uses: actions/download-artifact@v4
with:
name: plugins-dist
path: dist
- name: Sign catalog
env:
PLUGIN_SIGNING_KEY: ${{ secrets.PLUGIN_SIGNING_KEY }}
run: |
if [ -z "$PLUGIN_SIGNING_KEY" ]; then
echo "::error::PLUGIN_SIGNING_KEY secret is not set" >&2
exit 1
fi
umask 077
printf '%s\n' "$PLUGIN_SIGNING_KEY" > "$RUNNER_TEMP/key.pem"
uv run --no-project --with cryptography python scripts/build_plugin_catalog.py \
--key "$RUNNER_TEMP/key.pem" --sources dist/sources.json --out release-assets dist/*.zip
rm -f "$RUNNER_TEMP/key.pem"
- name: Publish
env:
GH_TOKEN: ${{ github.token }}
run: |
# tag 名不含 beta / alpha,不会触发 build.yml 的镜像构建
gh release view plugins >/dev/null 2>&1 || gh release create plugins \
--target "${{ github.sha }}" --latest=false --title "Plugin catalog" \
--notes "AutoBangumi 插件市场的签名目录,由 plugin-registry 工作流维护。"
gh release upload plugins release-assets/* --clobber
# 下架:删除不在新目录中的旧 zip 与签名
for asset in $(gh release view plugins --json assets -q '.assets[].name'); do
[ -e "release-assets/$asset" ] || gh release delete-asset plugins "$asset" -y
done