mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-01-07 11:39:01 +08:00
Updates the release workflow to download and include NapCat.Shell.Windows.OneKey.zip in the release artifacts.
443 lines
17 KiB
YAML
443 lines
17 KiB
YAML
name: Release NapCat
|
||
|
||
on:
|
||
workflow_dispatch:
|
||
push:
|
||
tags:
|
||
- 'v*'
|
||
|
||
permissions: write-all
|
||
|
||
env:
|
||
OPENROUTER_API_URL: https://91vip.futureppo.top/v1/chat/completions
|
||
OPENROUTER_MODEL: "Antigravity/gemini-3-flash-preview"
|
||
RELEASE_NAME: "NapCat"
|
||
|
||
jobs:
|
||
# 验证版本号格式
|
||
validate-version:
|
||
runs-on: ubuntu-latest
|
||
outputs:
|
||
valid: ${{ steps.check.outputs.valid }}
|
||
version: ${{ steps.check.outputs.version }}
|
||
steps:
|
||
- name: Validate semantic version
|
||
id: check
|
||
run: |
|
||
TAG="${GITHUB_REF#refs/tags/}"
|
||
echo "Checking tag: $TAG"
|
||
|
||
# 语义化版本正则表达式
|
||
# 支持: v1.0.0, v1.0.0-beta, v1.0.0-rc.1, v1.0.0-alpha.1+build.123
|
||
SEMVER_REGEX="^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?$"
|
||
|
||
if [[ "$TAG" =~ $SEMVER_REGEX ]]; then
|
||
echo "✅ Valid semantic version: $TAG"
|
||
echo "valid=true" >> $GITHUB_OUTPUT
|
||
echo "version=$TAG" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "❌ Invalid version format: $TAG"
|
||
echo "Expected format: vX.Y.Z or vX.Y.Z-prerelease"
|
||
echo "Examples: v1.0.0, v1.2.3-beta, v2.0.0-rc.1"
|
||
echo "valid=false" >> $GITHUB_OUTPUT
|
||
exit 1
|
||
fi
|
||
|
||
Build-Framework:
|
||
needs: validate-version
|
||
if: needs.validate-version.outputs.valid == 'true'
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Clone Main Repository
|
||
uses: actions/checkout@v4
|
||
- name: Use Node.js 20.X
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: 20.x
|
||
- name: Build NapCat.Framework
|
||
env:
|
||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
run: |
|
||
npm i -g pnpm
|
||
pnpm i
|
||
pnpm --filter napcat-webui-frontend run build || exit 1
|
||
pnpm run build:framework
|
||
mv packages/napcat-framework/dist framework-dist
|
||
cd framework-dist
|
||
npm install --omit=dev
|
||
rm ./package-lock.json || exit 0
|
||
- name: Upload Artifact
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: NapCat.Framework
|
||
path: framework-dist
|
||
|
||
Build-Shell:
|
||
needs: validate-version
|
||
if: needs.validate-version.outputs.valid == 'true'
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Clone Main Repository
|
||
uses: actions/checkout@v4
|
||
- name: Use Node.js 20.X
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: 20.x
|
||
- name: Build NapCat.Shell
|
||
env:
|
||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
run: |
|
||
npm i -g pnpm
|
||
pnpm i
|
||
pnpm --filter napcat-webui-frontend run build || exit 1
|
||
pnpm run build:shell
|
||
mv packages/napcat-shell/dist shell-dist
|
||
cd shell-dist
|
||
npm install --omit=dev
|
||
rm ./package-lock.json || exit 0
|
||
- name: Upload Artifact
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: NapCat.Shell
|
||
path: shell-dist
|
||
Download-QNX64:
|
||
needs: Build-Shell
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Download Artifacts
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
path: ./artifacts
|
||
|
||
- name: Setup tools
|
||
run: |
|
||
sudo apt update
|
||
sudo apt install -y aria2 unzip zip p7zip-full curl jq
|
||
|
||
- name: Download QQ x64, Node.js and Assemble NapCat.Shell.Windows.Node.zip
|
||
run: |
|
||
set -euo pipefail
|
||
TMPDIR=$(mktemp -d)
|
||
cd "$TMPDIR"
|
||
|
||
# -----------------------------
|
||
# 1) 下载 QQ x64
|
||
# -----------------------------
|
||
# JS_URL="https://cdn-go.cn/qq-web/im.qq.com_new/latest/rainbow/windowsConfig.js"
|
||
# JS_URL="https://slave.docadan488.workers.dev/proxy?url=https://cdn-go.cn/qq-web/im.qq.com_new/latest/rainbow/windowsConfig.js"
|
||
# NT_URL=$(curl -fsSL "$JS_URL" | grep -oP '"ntDownloadX64Url"\s*:\s*"\K[^"]+')
|
||
NT_URL="https://dldir1v6.qq.com/qqfile/qq/QQNT/eb263b35/QQ9.9.23.42086_x64.exe"
|
||
QQ_ZIP="$(basename "$NT_URL")"
|
||
aria2c -x16 -s16 -k1M -o "$QQ_ZIP" "$NT_URL"
|
||
|
||
QQ_EXTRACT="$TMPDIR/qq_extracted"
|
||
mkdir -p "$QQ_EXTRACT"
|
||
7z x -y -o"$QQ_EXTRACT" "$QQ_ZIP" >/dev/null
|
||
|
||
# -----------------------------
|
||
# 2) 下载 Node.js Windows x64 zip 22.11.0
|
||
# -----------------------------
|
||
NODE_VER="22.11.0"
|
||
NODE_URL="https://nodejs.org/dist/v$NODE_VER/node-v$NODE_VER-win-x64.zip"
|
||
NODE_ZIP="node-v$NODE_VER-win-x64.zip"
|
||
aria2c -x1 -s1 -k1M -o "$NODE_ZIP" "$NODE_URL"
|
||
|
||
NODE_EXTRACT="$TMPDIR/node_extracted"
|
||
mkdir -p "$NODE_EXTRACT"
|
||
unzip -q "$NODE_ZIP" -d "$NODE_EXTRACT"
|
||
|
||
# -----------------------------
|
||
# 3) 创建输出目录
|
||
# -----------------------------
|
||
OUT_DIR="$GITHUB_WORKSPACE/NapCat.Shell.Windows.Node"
|
||
mkdir -p "$OUT_DIR/NapCat.Shell.Windows.Node"
|
||
|
||
# -----------------------------
|
||
# 4) 解压 NapCat.Shell.zip 到 napcat
|
||
# -----------------------------
|
||
cp -a "$GITHUB_WORKSPACE/artifacts/NapCat.Shell/." "$OUT_DIR/napcat/"
|
||
|
||
# -----------------------------
|
||
# 5) 拷贝 QQ 文件到 NapCat.Shell.Windows.Node
|
||
# -----------------------------
|
||
QQ_TARGETS=("avif_convert.dll" "broadcast_ipc.dll" "config.json" "libglib-2.0-0.dll" "libgobject-2.0-0.dll" "libvips-42.dll" "ncnn.dll" "opencv.dll" "package.json" "QBar.dll" "wrapper.node")
|
||
for name in "${QQ_TARGETS[@]}"; do
|
||
find "$QQ_EXTRACT" -iname "$name" -exec cp -a {} "$OUT_DIR" \; || true
|
||
done
|
||
|
||
# -----------------------------
|
||
# 6) 拷贝仓库文件 napcat.bat 和 index.js
|
||
# -----------------------------
|
||
cp -a "$GITHUB_WORKSPACE/packages/napcat-develop/napcat.bat" "$OUT_DIR/" || true
|
||
cp -a "$GITHUB_WORKSPACE/packages/napcat-develop/index.js" "$OUT_DIR/" || true
|
||
cp -a "$GITHUB_WORKSPACE/packages/napcat-develop/QQNT.dll" "$OUT_DIR/" || true
|
||
# -----------------------------
|
||
# 7) 拷贝 Node.exe 到 NapCat.Shell.Windows.Node
|
||
# -----------------------------
|
||
cp -a "$NODE_EXTRACT/node-v$NODE_VER-win-x64/node.exe" "$OUT_DIR/" || true
|
||
|
||
- name: Upload Artifact
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: NapCat.Shell.Windows.Node
|
||
path: NapCat.Shell.Windows.Node
|
||
|
||
release-napcat:
|
||
needs: [Build-Framework, Build-Shell, Download-QNX64]
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
- name: Download Artifacts
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
path: ./artifacts
|
||
|
||
- name: Download NapCat.Shell.Windows.OneKey.zip
|
||
run: |
|
||
curl -L -o NapCat.Shell.Windows.OneKey.zip https://github.com/NapNeko/NapCatResource/raw/main/NapCat.Shell.Windows.OneKey.zip
|
||
|
||
- name: Zip Artifacts
|
||
run: |
|
||
cd artifacts
|
||
[ -d NapCat.Framework ] && (cd NapCat.Framework && zip -qr ../../NapCat.Framework.zip .)
|
||
[ -d NapCat.Shell ] && (cd NapCat.Shell && zip -qr ../../NapCat.Shell.zip .)
|
||
[ -d NapCat.Shell.Windows.Node ] && (cd NapCat.Shell.Windows.Node && zip -qr ../../NapCat.Shell.Windows.Node.zip .)
|
||
cd ..
|
||
|
||
- name: Generate release note via OpenRouter
|
||
env:
|
||
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
|
||
OPENROUTER_API_URL: ${{ env.OPENROUTER_API_URL }}
|
||
OPENROUTER_MODEL: ${{ env.OPENROUTER_MODEL }}
|
||
GITHUB_OWNER: "NapNeko" # 替换成你的 repo owner
|
||
GITHUB_REPO: "NapCatQQ" # 替换成你的 repo 名
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
# 当前 tag
|
||
CURRENT_TAG="${GITHUB_REF#refs/tags/}"
|
||
echo "Current tag: $CURRENT_TAG"
|
||
|
||
# 从 GitHub API 获取 tag 列表
|
||
TAGS_JSON=$(curl -s "https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/tags?per_page=100")
|
||
TAGS=( $(echo "$TAGS_JSON" | jq -r '.[].name' | sort -V) )
|
||
|
||
# 找到上一个 tag
|
||
PREV_TAG=""
|
||
for i in "${!TAGS[@]}"; do
|
||
if [ "${TAGS[$i]}" = "$CURRENT_TAG" ]; then
|
||
if [ $i -gt 0 ]; then
|
||
PREV_TAG="${TAGS[$((i-1))]}"
|
||
fi
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [ -z "$PREV_TAG" ]; then
|
||
echo "⚠️ Could not find previous tag for $CURRENT_TAG, using first commit"
|
||
PREV_TAG=$(git rev-list --max-parents=0 HEAD | head -1)
|
||
fi
|
||
|
||
echo "Previous tag: $PREV_TAG"
|
||
|
||
# 强制拉取上一个 tag 和当前 tag
|
||
git fetch origin "refs/tags/$PREV_TAG:refs/tags/$PREV_TAG" --force || true
|
||
git fetch origin "refs/tags/$CURRENT_TAG:refs/tags/$CURRENT_TAG" --force || true
|
||
|
||
# 获取 commit,使用更清晰的格式
|
||
# 格式: <type>: <subject> (<hash>)
|
||
COMMITS=$(git log --pretty=format:'- %s (%h)' "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null || git log --pretty=format:'- %s (%h)' -20)
|
||
|
||
echo "Commit list from $PREV_TAG to $CURRENT_TAG:"
|
||
echo "$COMMITS"
|
||
|
||
# 获取文件变化统计
|
||
echo "Getting file change statistics..."
|
||
FILE_STATS=$(git diff --stat "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null || echo "")
|
||
|
||
# 获取总体统计(最后一行)
|
||
SUMMARY_LINE=$(echo "$FILE_STATS" | tail -1)
|
||
echo "Summary: $SUMMARY_LINE"
|
||
|
||
# 获取每个文件的变化(去掉最后一行汇总)
|
||
# 截断过长的输出(最多50个文件,每行最多80字符)
|
||
FILE_CHANGES=$(echo "$FILE_STATS" | head -n -1 | head -50 | cut -c1-80)
|
||
|
||
# 如果文件变化太多,进一步精简:只保留主要目录的变化
|
||
FILE_COUNT=$(echo "$FILE_STATS" | head -n -1 | wc -l)
|
||
if [ "$FILE_COUNT" -gt 50 ]; then
|
||
echo "Too many files ($FILE_COUNT), grouping by directory..."
|
||
# 按目录分组统计
|
||
DIR_STATS=$(git diff --stat "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null | head -n -1 | \
|
||
sed 's/|.*//g' | \
|
||
awk -F'/' '{if(NF>1) print $1"/"$2; else print $1}' | \
|
||
sort | uniq -c | sort -rn | head -20)
|
||
FILE_CHANGES="[按目录分组统计 - 共 $FILE_COUNT 个文件变更]
|
||
$DIR_STATS"
|
||
fi
|
||
|
||
echo "File changes:"
|
||
echo "$FILE_CHANGES"
|
||
|
||
# 获取具体代码变化(关键文件的diff)
|
||
echo "Getting code diff for key files..."
|
||
|
||
# 定义关键目录(优先展示这些目录的变化)
|
||
KEY_DIRS="packages/napcat-core packages/napcat-onebot packages/napcat-webui-backend"
|
||
|
||
# 获取变更的关键文件列表(排除测试、配置等)
|
||
# 使用 || true 防止 grep 无匹配时返回非零退出码
|
||
KEY_FILES=$(git diff --name-only "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null | \
|
||
grep -E "^packages/napcat-(core|onebot|webui-backend|shell)/" || true | \
|
||
grep -E "\.(ts|js)$" || true | \
|
||
grep -v -E "(test|spec|\.d\.ts|config)" || true | \
|
||
head -15) || true
|
||
|
||
CODE_DIFF=""
|
||
DIFF_CHAR_LIMIT=6000 # 总diff字符限制
|
||
CURRENT_CHARS=0
|
||
|
||
if [ -n "$KEY_FILES" ]; then
|
||
for file in $KEY_FILES; do
|
||
if [ "$CURRENT_CHARS" -ge "$DIFF_CHAR_LIMIT" ]; then
|
||
CODE_DIFF="$CODE_DIFF
|
||
[... 更多文件变化已截断 ...]"
|
||
break
|
||
fi
|
||
|
||
# 获取单个文件的diff,限制每个文件最多50行
|
||
FILE_DIFF=$(git diff "$PREV_TAG".."$CURRENT_TAG" -- "$file" 2>/dev/null | head -50) || true
|
||
FILE_DIFF_LEN=${#FILE_DIFF}
|
||
|
||
# 如果单个文件diff超过1500字符,截断
|
||
if [ "$FILE_DIFF_LEN" -gt 1500 ]; then
|
||
FILE_DIFF=$(echo "$FILE_DIFF" | head -c 1500)
|
||
FILE_DIFF="$FILE_DIFF
|
||
[... 文件 $file 变化已截断 ...]"
|
||
fi
|
||
|
||
if [ -n "$FILE_DIFF" ]; then
|
||
CODE_DIFF="$CODE_DIFF
|
||
|
||
### $file
|
||
\`\`\`diff
|
||
$FILE_DIFF
|
||
\`\`\`"
|
||
CURRENT_CHARS=$((CURRENT_CHARS + FILE_DIFF_LEN))
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# 如果没有关键文件变化,获取前5个变更文件的diff
|
||
if [ -z "$CODE_DIFF" ]; then
|
||
echo "No key files changed, getting top changed files..."
|
||
TOP_FILES=$(git diff --name-only "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null | \
|
||
grep -E "\.(ts|js|yml|md)$" | head -5) || true
|
||
|
||
if [ -n "$TOP_FILES" ]; then
|
||
for file in $TOP_FILES; do
|
||
FILE_DIFF=$(git diff "$PREV_TAG".."$CURRENT_TAG" -- "$file" 2>/dev/null | head -30) || true
|
||
if [ -n "$FILE_DIFF" ] && [ ${#FILE_DIFF} -lt 1000 ]; then
|
||
CODE_DIFF="$CODE_DIFF
|
||
|
||
### $file
|
||
\`\`\`diff
|
||
$FILE_DIFF
|
||
\`\`\`"
|
||
fi
|
||
done
|
||
fi
|
||
fi
|
||
|
||
# 如果仍然没有代码变化,添加说明
|
||
if [ -z "$CODE_DIFF" ]; then
|
||
CODE_DIFF="[本次更新主要涉及配置文件和文档变更,无核心代码变化]"
|
||
fi
|
||
|
||
echo "Code diff preview:"
|
||
echo "$CODE_DIFF" | head -50
|
||
|
||
# 读取 prompt
|
||
PROMPT_FILE=".github/prompt/release_note_prompt.txt"
|
||
SYSTEM_PROMPT=$(<"$PROMPT_FILE")
|
||
|
||
# 构建用户内容,传递更多上下文(包含文件变化和代码diff)
|
||
USER_CONTENT="当前版本: $CURRENT_TAG
|
||
上一版本: $PREV_TAG
|
||
|
||
## 提交列表
|
||
$COMMITS
|
||
|
||
## 文件变化统计
|
||
$SUMMARY_LINE
|
||
|
||
## 变更文件列表
|
||
$FILE_CHANGES
|
||
|
||
## 关键代码变化
|
||
$CODE_DIFF"
|
||
|
||
# 构建请求 JSON,增加 max_tokens 以获取更完整的输出
|
||
BODY=$(jq -n \
|
||
--arg system "$SYSTEM_PROMPT" \
|
||
--arg user "$USER_CONTENT" \
|
||
--arg model "$OPENROUTER_MODEL" \
|
||
'{model: $model, messages:[{role:"system", content:$system},{role:"user", content:$user}], temperature:0.2, max_tokens:1500}')
|
||
|
||
echo "=== OpenRouter request body ==="
|
||
echo "$BODY" | jq .
|
||
|
||
# 调用 OpenRouter
|
||
if RESPONSE=$(curl -s -X POST "$OPENROUTER_API_URL" \
|
||
-H "Authorization: Bearer $OPENAI_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$BODY"); then
|
||
echo "=== raw response ==="
|
||
echo "$RESPONSE"
|
||
echo "=== OpenRouter raw response ==="
|
||
if echo "$RESPONSE" | jq . >/dev/null 2>&1; then
|
||
echo "$RESPONSE" | jq .
|
||
else
|
||
echo "jq failed to parse response"
|
||
fi
|
||
|
||
# 提取生成内容
|
||
RELEASE_BODY=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // .choices[0].text // ""' 2>/dev/null || echo "")
|
||
|
||
if [ -z "$RELEASE_BODY" ]; then
|
||
echo "❌ OpenRouter failed to generate release note, using default.md"
|
||
# 替换默认模板中的版本占位符
|
||
sed "s/{VERSION}/$CURRENT_TAG/g" .github/prompt/default.md > CHANGELOG.md
|
||
else
|
||
# 后处理:确保版本号正确,并添加比较链接
|
||
echo -e "$RELEASE_BODY" > CHANGELOG.md
|
||
# 替换可能的占位符
|
||
sed -i "s/{VERSION}/$CURRENT_TAG/g" CHANGELOG.md
|
||
sed -i "s/{PREV_VERSION}/$PREV_TAG/g" CHANGELOG.md
|
||
fi
|
||
else
|
||
echo "❌ Curl failed, using default.md"
|
||
sed "s/{VERSION}/$CURRENT_TAG/g" .github/prompt/default.md > CHANGELOG.md
|
||
fi
|
||
echo "=== generated release note ==="
|
||
cat CHANGELOG.md
|
||
|
||
- name: Create Release Draft and Upload Artifacts
|
||
uses: softprops/action-gh-release@v1
|
||
with:
|
||
name: NapCat ${{ github.ref_name }}
|
||
token: ${{ secrets.GITHUB_TOKEN }}
|
||
body_path: CHANGELOG.md
|
||
files: |
|
||
NapCat.Shell.Windows.Node.zip
|
||
NapCat.Framework.zip
|
||
NapCat.Shell.zip
|
||
NapCat.Shell.Windows.OneKey.zip
|
||
draft: true
|