Repository navigation
update-plugin-versions #30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================================ | |
| # 每日插件版本检查:拉取所有已录入插件的 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 |