Skip to content

update-plugin-versions #30

update-plugin-versions

update-plugin-versions #30

# ============================================================
# 每日插件版本检查:拉取所有已录入插件的 PyPI 最新版本,
# 存在新版本时复用与 plugin-submission.yml 相同的验证逻辑重新验证,
# 验证通过后更新 assess 元数据并推送。
# ============================================================
name: update-plugin-versions
on:
schedule:
# 每天 UTC 02:00(北京时间 10:00)
- cron: "0 2 * * *"
workflow_dispatch:
permissions:
contents: write
jobs:
update:
name: 检查并更新插件版本
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: 检出仓库代码
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: 安装依赖
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq jq >/dev/null 2>&1
python3 -m pip install --upgrade pip >/dev/null 2>&1
- name: 检查 PyPI 新版本
id: check
run: |
python3 .github/scripts/check_pypi_updates.py > updates.json
echo "count=$(jq 'length' updates.json)" >> "$GITHUB_OUTPUT"
echo "fail_count=$(jq 'length' failures.json)" >> "$GITHUB_OUTPUT"
jq -r '.[] | "\(.pypi): \(.current_version) -> \(.latest_version)"' updates.json || true
- name: 重新验证并更新版本
id: update
if: steps.check.outputs.count != '0'
run: |
set +e
UPDATED=0
echo '[]' > updated.json
echo '[]' > failed.json
for entry in $(jq -c '.[]' updates.json); do
PYPI=$(echo "$entry" | jq -r '.pypi')
REPO=$(echo "$entry" | jq -r '.repo')
CUR=$(echo "$entry" | jq -r '.current_version')
LATEST=$(echo "$entry" | jq -r '.latest_version')
echo "===== 处理 ${PYPI}: ${CUR} -> ${LATEST} ====="
if [ -z "$REPO" ]; then
echo "[skip] ${PYPI}: 缺少仓库地址"
jq --arg p "$PYPI" --arg c "$CUR" --arg l "$LATEST" --arg r "缺少仓库地址" \
'. += [{pypi:$p, current:$c, latest:$l, reason:$r}]' failed.json > tmp_failed.json && mv tmp_failed.json failed.json
continue
fi
# 克隆插件仓库(与提交流程一致)
git clone --depth 1 "${REPO}.git" plugin_repo 2>&1 | tail -3
if [ ! -d plugin_repo ]; then
echo "[skip] ${PYPI}: 克隆仓库失败"
jq --arg p "$PYPI" --arg c "$CUR" --arg l "$LATEST" --arg r "克隆仓库失败" \
'. += [{pypi:$p, current:$c, latest:$l, reason:$r}]' failed.json > tmp_failed.json && mv tmp_failed.json failed.json
continue
fi
# 定位插件源码目录
PYPI_UNDER=$(echo "$PYPI" | tr '-' '_')
PLUGIN_DIR=$(find plugin_repo -type d -regex ".*/src/${PYPI_UNDER}\$" | head -n1 || true)
if [ -z "$PLUGIN_DIR" ]; then
PLUGIN_DIR=$(find plugin_repo -type d -regex '.*/src/axtbot_plugin_[^/]+$' | head -n1 || true)
fi
if [ -z "$PLUGIN_DIR" ] || [ ! -f "$PLUGIN_DIR/__init__.py" ]; then
echo "[skip] ${PYPI}: 找不到 src/.../__init__.py"
jq --arg p "$PYPI" --arg c "$CUR" --arg l "$LATEST" --arg r "找不到插件源码目录" \
'. += [{pypi:$p, current:$c, latest:$l, reason:$r}]' failed.json > tmp_failed.json && mv tmp_failed.json failed.json
rm -rf plugin_repo
continue
fi
# 解析最新 __meta__
python3 .github/scripts/parse_meta.py "$PLUGIN_DIR/__init__.py" 2>&1 | tail -3
if [ ! -f meta.json ]; then
echo "[skip] ${PYPI}: __meta__ 解析失败"
jq --arg p "$PYPI" --arg c "$CUR" --arg l "$LATEST" --arg r "__meta__ 解析失败" \
'. += [{pypi:$p, current:$c, latest:$l, reason:$r}]' failed.json > tmp_failed.json && mv tmp_failed.json failed.json
rm -rf plugin_repo
continue
fi
# pip 安装最新版
python3 -m pip install --quiet "$PYPI" 2>&1 | tail -3
# 克隆 AxTBot 主仓库并验证加载
rm -rf axtbot
git clone --depth 1 https://github.com/AxT-Team/AxTBot.git axtbot 2>&1 | tail -3
REL_DIR="${PLUGIN_DIR#plugin_repo/}"
mkdir -p "axtbot/$(dirname "$REL_DIR")"
cp -r "$PLUGIN_DIR" "axtbot/$REL_DIR"
cd axtbot
python3 -m pip install --quiet -e . 2>&1 | tail -3
PKG="${PLUGIN_DIR##*/}" python3 ../.github/scripts/verify_plugin.py 2>&1 | tail -3
cd ..
VERIFY_OK="false"
if [ -f axtbot/verify.json ]; then
VERIFY_OK=$(jq -r '.ok' axtbot/verify.json)
fi
if [ "$VERIFY_OK" != "true" ]; then
echo "[skip] ${PYPI}: 验证失败,跳过更新"
# 摘取验证错误日志末尾,供 Webhook 通知展示
VERR=""
if [ -f axtbot/verify.json ]; then
VERR=$(jq -r '.error // ""' axtbot/verify.json | python3 -c "import sys;print(sys.stdin.read()[-800:].strip())")
fi
jq --arg p "$PYPI" --arg c "$CUR" --arg l "$LATEST" --arg r "加载验证失败" --arg e "$VERR" \
'. += [{pypi:$p, current:$c, latest:$l, reason:$r, error:$e}]' failed.json > tmp_failed.json && mv tmp_failed.json failed.json
rm -rf plugin_repo axtbot meta.json form.json
continue
fi
# 构造表单并合成新元数据
PYPI_NAME="$PYPI" python3 .github/scripts/build_form_for_update.py
python3 .github/scripts/build_metadata.py
if [ ! -f axtbot-plugin-tmp.json ]; then
echo "[skip] ${PYPI}: 合成元数据失败"
jq --arg p "$PYPI" --arg c "$CUR" --arg l "$LATEST" --arg r "合成元数据失败" \
'. += [{pypi:$p, current:$c, latest:$l, reason:$r}]' failed.json > tmp_failed.json && mv tmp_failed.json failed.json
rm -rf plugin_repo axtbot meta.json form.json
continue
fi
# 版本号以 PyPI 最新版为准
jq --arg v "$LATEST" '.version = $v' axtbot-plugin-tmp.json > tmp.json \
&& mv tmp.json axtbot-plugin-tmp.json
cp axtbot-plugin-tmp.json "assess/${PYPI}.json"
PYPI_NAME="$PYPI" python3 .github/scripts/update_index.py >/dev/null 2>&1 || true
echo "[ok] ${PYPI} 已更新到 ${LATEST}"
jq --arg p "$PYPI" --arg v "$LATEST" \
'. += [{pypi:$p, version:$v}]' updated.json > tmp_updated.json && mv tmp_updated.json updated.json
UPDATED=1
rm -rf plugin_repo axtbot meta.json form.json axtbot-plugin-tmp.json
done
echo "updated=$UPDATED" >> "$GITHUB_OUTPUT"
- name: 提交并推送版本更新
if: steps.update.outputs.updated == '1'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add assess/
git diff --cached --quiet || git commit -m "chore(assess): 自动更新插件版本"
git push
- name: 推送版本检查结果到 Webhook
if: always()
env:
NOTIFY_WEBHOOK_URL: ${{ secrets.AUTO_REVIEW_WEBHOOK_URL }}
NOTIFY_WEBHOOK_TOKEN: ${{ secrets.AUTO_REVIEW_WEBHOOK_TOKEN }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set +e
# 汇总 updated / failed 数组(update step 未执行时为默认空数组)
UPDATED_JSON='[]'
FAILED_JSON='[]'
if [ -f updated.json ]; then
UPDATED_JSON=$(cat updated.json)
fi
if [ -f failed.json ]; then
FAILED_JSON=$(cat failed.json)
fi
# 把 PyPI 查询失败的插件也并入 failed 数组
if [ -f failures.json ]; then
PYPI_QUERY_FAILED=$(jq -c '[.[] | {pypi, current: .current_version, latest: "?", reason: "PyPI 查询失败"}]' failures.json)
FAILED_JSON=$(jq -nc --argjson a "$FAILED_JSON" --argjson b "$PYPI_QUERY_FAILED" '$a + $b')
fi
# 无任何更新与失败时不推送,避免每日空消息
UP_COUNT=$(jq 'length' <<<"$UPDATED_JSON")
FD_COUNT=$(jq 'length' <<<"$FAILED_JSON")
echo "updated=${UP_COUNT} failed=${FD_COUNT}"
if [ "$UP_COUNT" = "0" ] && [ "$FD_COUNT" = "0" ]; then
echo "插件无更新且无失败,跳过 Webhook 推送"
exit 0
fi
NOTIFY_JSON=$(jq -nc \
--arg ev "plugin_version_check" \
--arg src "plugin-store" \
--arg run "$RUN_URL" \
--argjson up "$UPDATED_JSON" \
--argjson fd "$FAILED_JSON" \
'{event:$ev, source:$src, updated:$up, failed:$fd, run:($run|if length>0 then . else null end)}')
NOTIFY_JSON="$NOTIFY_JSON" python3 .github/scripts/notify_webhook.py