Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 30 additions & 31 deletions .github/workflows/track-releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ name: Track plugin releases
#
# - organization: plugins whose repository belongs to the Lithe-IDEA organization,
# including first listings. After the archives are verified, the workflow
# merges the pull request itself by fast-forwarding main with the
# STORE_BOT_DEPLOY_KEY deploy key, the only bot credential that the
# "Only owners merge main" ruleset lets through.
# merges the pull request itself with a token of the Lithe Store Bot GitHub
# App, the only bot that the "Only owners merge main" ruleset lets through.
# - external: every other plugin. The pull request waits for a maintainer
# review, and this scope never receives the deploy key.
# review, and this scope never receives the app token.
on:
schedule:
- cron: "41 */6 * * *"
Expand Down Expand Up @@ -57,20 +56,27 @@ jobs:
SCOPE: ${{ matrix.scope }}
BRANCH: ${{ matrix.branch }}
TITLE: ${{ matrix.title }}
# A PR opened with GITHUB_TOKEN does not trigger other workflows. Set the
# STORE_BOT_TOKEN secret (fine-grained PAT: contents + pull requests) so
# `Validate index` runs on the bot PR as well.
GH_TOKEN: ${{ secrets.STORE_BOT_TOKEN || github.token }}
steps:
# Only the organization scope may merge into main, so only it asks for the
# app token. Without the app, both scopes fall back to the tokens below.
- name: Create the Lithe Store Bot token
id: bot
if: matrix.scope == 'organization' && vars.STORE_BOT_APP_CLIENT_ID != ''
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.STORE_BOT_APP_CLIENT_ID }}
private-key: ${{ secrets.STORE_BOT_APP_PRIVATE_KEY }}

# Every step below that pushes or calls gh uses the same token, chosen in
# this order: the app token, then the optional STORE_BOT_TOKEN secret
# (fine-grained PAT: contents + pull requests), then GITHUB_TOKEN. A pull
# request opened with GITHUB_TOKEN does not trigger `Validate index`.
- uses: actions/checkout@v7
with:
# The latest main, not the commit that triggered the run: the external
# scope must start from the main that the organization scope just updated.
ref: main
token: ${{ secrets.STORE_BOT_TOKEN || github.token }}
# Git then pushes over SSH with the deploy key. The external scope
# gets an empty key and keeps using the token, which cannot write main.
ssh-key: ${{ matrix.scope == 'organization' && secrets.STORE_BOT_DEPLOY_KEY || '' }}
token: ${{ steps.bot.outputs.token || secrets.STORE_BOT_TOKEN || github.token }}
- uses: actions/setup-node@v7
with:
node-version: 22
Expand All @@ -88,6 +94,8 @@ jobs:

- name: Close the obsolete pull request
if: steps.track.outputs.added == '0' && inputs.plugin == ''
env:
GH_TOKEN: ${{ steps.bot.outputs.token || secrets.STORE_BOT_TOKEN || github.token }}
run: |
number=$(gh pr list --head "$BRANCH" --state open --json number -q '.[0].number')
if [ -n "$number" ]; then
Expand All @@ -103,6 +111,7 @@ jobs:
id: pr
if: steps.track.outputs.added != '0'
env:
GH_TOKEN: ${{ steps.bot.outputs.token || secrets.STORE_BOT_TOKEN || github.token }}
SUMMARY: ${{ steps.track.outputs.summary }}
NEW_PLUGINS: ${{ steps.track.outputs.new_plugins }}
run: |
Expand Down Expand Up @@ -142,28 +151,18 @@ jobs:
- name: Merge the organization pull request
if: matrix.scope == 'organization' && steps.track.outputs.added != '0'
env:
# Only the app can pass the ruleset, so this step never falls back to another token.
GH_TOKEN: ${{ steps.bot.outputs.token }}
NUMBER: ${{ steps.pr.outputs.number }}
HAS_DEPLOY_KEY: ${{ secrets.STORE_BOT_DEPLOY_KEY != '' }}
run: |
if [ "$HAS_DEPLOY_KEY" != true ]; then
echo "::warning::STORE_BOT_DEPLOY_KEY is not set, so pull request #$NUMBER stays open for a maintainer to merge."
if [ -z "$GH_TOKEN" ]; then
echo "::warning::The Lithe Store Bot app is not configured (STORE_BOT_APP_CLIENT_ID, STORE_BOT_APP_PRIVATE_KEY), so pull request #$NUMBER stays open for a maintainer to merge."
exit 0
fi
# Fast-forward main to the verified commit. Without --force, git refuses
# the push if main moved after the checkout; the next run then rebuilds
# the branch from the new main.
if ! git push origin "HEAD:main"; then
echo "::warning::Could not fast-forward main, so pull request #$NUMBER stays open for a maintainer to merge."
# --match-head-commit makes GitHub refuse the merge if the branch no
# longer points at the commit that this run verified.
if ! gh pr merge "$NUMBER" --squash --match-head-commit "$(git rev-parse HEAD)"; then
echo "::warning::Could not merge pull request #$NUMBER, so it stays open for a maintainer to merge."
exit 0
fi
# GitHub marks the pull request merged once its head commit is on main.
# Delete the branch only after that, or the pull request would show as
# closed instead of merged.
for _ in $(seq 1 12); do
if [ "$(gh pr view "$NUMBER" --json state -q .state)" = MERGED ]; then
git push origin --delete "$BRANCH"
exit 0
fi
sleep 5
done
echo "::warning::main contains pull request #$NUMBER, but GitHub has not marked it merged after 60 seconds. Delete $BRANCH once it has."
git push origin --delete "$BRANCH"
35 changes: 20 additions & 15 deletions docs/maintainers.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,30 +45,35 @@ bot 首次收录的组织插件(组织 PR 描述里的“首次收录”)合
组织插件的自动合入分三步:

1. 开 PR 之前运行 `validate-index.mjs --verify-archives`,与 `Validate index` 对新增归档的检查相同;
2. 用 `STORE_BOT_DEPLOY_KEY` 把 main 快进到 PR 的提交,GitHub 据此把 PR 标为已合并;
3. PR 标为已合并后删除分支。
2. 用 Lithe Store Bot 的 token 推送分支、开 PR,再 squash 合并。合并时指定本轮校验过的提交
(`gh pr merge --match-head-commit`),分支在校验之后被改动过,GitHub 就拒绝合并;
3. 合并后删除分支。

main 在扫描期间被别人更新时快进会失败;没有配置 `STORE_BOT_DEPLOY_KEY` 时工作流不尝试合入。
这两种情况都只在工作流里给出警告,PR 保持打开,由维护者按审查清单审查后合并,下一轮扫描也会基于新的 main 重建它。
删除 deploy key 或 secret 即可停止自动合入。
没有配置 Lithe Store Bot,或者合并失败(例如 main 在扫描期间被更新并与 PR 冲突)时,工作流只给出警告,
PR 保持打开,由维护者按审查清单审查后合并,下一轮扫描也会基于新的 main 重建它。
删除仓库变量 `STORE_BOT_APP_CLIENT_ID`,或者卸载 Lithe Store Bot,即可停止自动合入。

信任边界:在任一 Lithe-IDEA 组织插件仓库能发布 Release 的人,以及能在组织里创建公开
`lithe-plugin-*` 仓库的人,都可以不经 Store 维护者审查把插件发布到 Lithe Store。
`lithe-plugin-*` 仓库的人,都可以不经 Store 维护者审查把插件发布到 Lithe Store。组织允许成员创建公开仓库时
(Organization settings → Member privileges → Repository creation),每个组织成员都在这个范围内。
授予组织成员身份或插件仓库写权限时按这个标准判断。

需要的仓库设置:

1. **Settings → Actions → General → Workflow permissions**:勾选
“Allow GitHub Actions to create and approve pull requests”。
2. **Settings → Deploy keys**:一把允许写入的 deploy key
(`lithe-store-bot (track organization releases)`),私钥保存为 `STORE_BOT_DEPLOY_KEY` secret。
只有组织插件那一轮的 checkout 会拿到这把 key,组织外那一轮仍用 `GITHUB_TOKEN`,写不了 main。
2. **Lithe Store Bot**:Lithe-IDEA 组织拥有的私有 GitHub App,只有 Contents 与 Pull requests 读写权限,
不接收 webhook,只安装在本仓库。仓库变量 `STORE_BOT_APP_CLIENT_ID` 保存它的 Client ID,
secret `STORE_BOT_APP_PRIVATE_KEY` 保存它的私钥。只有组织插件那一轮会生成 app token;
组织外那一轮用 `STORE_BOT_TOKEN` 或 `GITHUB_TOKEN`,写不了 main。用 app token 开的 PR 会触发
`Validate index`,合并到 main 也会触发 main 上的 `Validate index` 和 `Sync README`(总览已在 PR 里生成,不会再开同步 PR)。
3. **Settings → Rules → Rulesets**:规则集 “Only owners merge main” 限制 `main` 的更新、删除和强制推送,
只有组织管理员和 deploy key 可以绕过。GitHub 不允许把 GitHub Actions 加入仓库规则集的绕过名单,
所以工作流用 deploy key 写 main。这项绕过对仓库的所有 deploy key 生效,新增其他 deploy key 前先评估。
4. 可选:创建 `STORE_BOT_TOKEN` secret(fine-grained PAT,仅本仓库,Contents 与 Pull requests 读写)。
使用默认 `GITHUB_TOKEN` 开的 PR 不会触发 `Validate index`;工作流已在开 PR 前完成同样的校验。
deploy key 推送到 main 会正常触发 main 上的 `Validate index` 和 `Sync README`。
只有组织管理员和 Lithe Store Bot 可以绕过。GitHub 不允许把 GitHub Actions(`GITHUB_TOKEN`)加入仓库规则集的
绕过名单,Lithe-IDEA 组织也禁用了 deploy key,所以工作流用组织自己的 GitHub App 写 main。
绕过名单按 App 指定,不会放行其他 App 或 deploy key。
4. 可选:创建 `STORE_BOT_TOKEN` secret(fine-grained PAT,仅本仓库,Contents 与 Pull requests 读写),
让组织外 PR 和 README 同步 PR 也触发 `Validate index`。使用默认 `GITHUB_TOKEN` 开的 PR 不会触发它;
工作流已在开 PR 前完成同样的校验。

## 自动同步 README 总览

Expand All @@ -78,7 +83,7 @@ README 的插件总览由 `scripts/render-readme.mjs` 根据索引生成。`main
合并这个 PR 后工作流会再运行一次,发现总览已经一致就结束。总览已经一致时,上一轮没人合并的同步 PR 会被自动关闭。

仓库设置与追踪新版本相同(“Allow GitHub Actions to create and approve pull requests”,
可选的 `STORE_BOT_TOKEN`),同步 PR 不使用 deploy key,始终由维护者合并。没有配置 `STORE_BOT_TOKEN` 时,
可选的 `STORE_BOT_TOKEN`),同步 PR 不使用 Lithe Store Bot,始终由维护者合并。没有配置 `STORE_BOT_TOKEN` 时,
这个 PR 也不会触发 `Validate index`。同步 PR 只应该包含 `README.md`;如果还改了别的文件,不要合并。

## 下架与撤回
Expand Down
Loading