Repository navigation
fix(ci): 服务器同步改用 Windows 原生 ssh/scp(appleboy 不支持 MSYS) #53
Workflow file for this run
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
| name: Release Tauri App | |
| # 由 v* tag 触发:构建 Tauri 安装包并发布到 GitHub Release + 自建服务器 + OSS | |
| # 适配说明(2026-08-21):原 Electron 发布链适配为 Tauri—— | |
| # electron-builder → cargo tauri build(nsis);更新源 latest.yml → Tauri 安装包直链 | |
| # (updater 端点未启用,TODO:启用 tauri.conf updater 后补 latest.json 同步) | |
| # 2026-08-27 重构:build 完成后直接发布(gh release + SCP + OSS), | |
| # 移除 artifact 中转——artifact 存储配额(500MB)无法容纳 349MB 安装包多次构建 | |
| on: | |
| push: | |
| tags: ['v*'] | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: release-tauri-${{ github.ref_name }} | |
| cancel-in-progress: false | |
| jobs: | |
| lint-test: | |
| # 项目含 Windows 专用模块(audio_route_probe/device_probe/gdi_capture 等未做 Linux cfg 隔离), | |
| # 测试必须在 Windows runner 执行;Linux 编译会因 E0433 失败 | |
| runs-on: windows-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: x86_64-pc-windows-msvc | |
| - name: Cache cargo registry | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: app/src-tauri | |
| # models/ 不入库(AGENTS.md 铁律),build.rs 的 bundle.resources glob 需要空目录占位 | |
| - name: Prepare models placeholder | |
| shell: bash | |
| run: mkdir -p app/src-tauri/models | |
| # .cargo/config.toml 注入的是本机 Windows 预编译库绝对路径(sherpa-archive/、ort/), | |
| # ort-sys/sherpa-onnx-sys 只要见到 ORT_LIB_LOCATION/SHERPA_ONNX_ARCHIVE_DIR 存在即跳过下载—— | |
| # CI 无法 unset env,直接移除 config 使注入消失,两库回退在线下载(CI 网络可达) | |
| - name: Remove local cargo config (CI online download) | |
| shell: bash | |
| run: rm -f app/src-tauri/.cargo/config.toml | |
| # oar-ocr 未启用 ort-sys 的 download-binaries feature(本地靠 ORT_LIB_LOCATION 链接), | |
| # CI 必须手动提供 ONNX Runtime 库,与本地 ORT_LIB_LOCATION 同机制(CPU 包即可,测试不初始化 CUDA) | |
| - name: Prepare ONNX Runtime for CI | |
| shell: bash | |
| run: | | |
| mkdir -p app/src-tauri/ort/ci | |
| curl -sL https://github.com/microsoft/onnxruntime/releases/download/v1.28.0/onnxruntime-win-x64-1.28.0.zip -o /tmp/ort.zip | |
| unzip -q /tmp/ort.zip -d /tmp/ortx | |
| cp /tmp/ortx/onnxruntime-win-x64-1.28.0/lib/onnxruntime.* app/src-tauri/ort/ci/ | |
| - name: Test | |
| # ORT_PREFER_DYNAMIC_LINK=1:与本地一致走动态链接,规避 static MT 与项目 MD 运行时的 LNK2038 冲突 | |
| env: | |
| ORT_LIB_LOCATION: ${{ github.workspace }}/app/src-tauri/ort/ci | |
| ORT_PREFER_DYNAMIC_LINK: "1" | |
| # 跳过 3 个预存基线失败(CHANGELOG 已登记,与本版无关): | |
| # ai_client 默认 Provider 断言 / note_filter 两条黄金用例——待后续版本修复 | |
| run: | | |
| cd app/src-tauri | |
| cargo test -- --skip ai_client::tests::from_settings_resolves_default_provider_when_set --skip note_filter::golden_tests::session29_live_ui_excluded_from_points --skip note_filter::tests::ocr_points_exclude_watermark_junk_and_dupes | |
| build: | |
| needs: lint-test | |
| runs-on: windows-latest | |
| # Tauri 打包 + 发布(GitHub Release + 服务器 + OSS):上限 90 分钟 | |
| timeout-minutes: 90 | |
| env: | |
| HAS_ALIYUN_AK: ${{ secrets.ALIYUN_ACCESS_KEY_ID != '' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: 'npm' | |
| cache-dependency-path: app/package-lock.json | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: x86_64-pc-windows-msvc | |
| - name: Cache cargo registry | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: app/src-tauri | |
| # 模型资源占位:models/ 不入库(AGENTS.md 铁律),CI 构建时创建空目录 | |
| # 避免 tauri bundle 资源 glob 报错;运行时缺失模型有下载/降级路径兜底 | |
| - name: Prepare models placeholder | |
| shell: bash | |
| run: mkdir -p app/src-tauri/models | |
| - name: Install frontend deps | |
| run: cd app && npm ci | |
| - name: Build Tauri app (NSIS) | |
| shell: bash | |
| # .cargo/config.toml 注入的是本机 Windows 预编译库绝对路径(sherpa-archive/、ort/), | |
| # ort-sys/sherpa-onnx-sys 只要见到 ORT_LIB_LOCATION/SHERPA_ONNX_ARCHIVE_DIR 存在即跳过下载—— | |
| # CI 无法 unset env,直接移除 config 使注入消失,两库回退在线下载(CI 网络可达) | |
| run: | | |
| rm -f app/src-tauri/.cargo/config.toml | |
| mkdir -p app/src-tauri/ort/ci | |
| curl -sL https://github.com/microsoft/onnxruntime/releases/download/v1.28.0/onnxruntime-win-x64-1.28.0.zip -o /tmp/ort.zip | |
| unzip -q /tmp/ort.zip -d /tmp/ortx | |
| cp /tmp/ortx/onnxruntime-win-x64-1.28.0/lib/onnxruntime.* app/src-tauri/ort/ci/ | |
| cd app && npx tauri build --bundles nsis | |
| # oar-ocr 未启用 ort-sys download-binaries(本地靠 ORT_LIB_LOCATION 链接), | |
| # CI 手动提供 ORT 库并复用同一机制;动态链接规避 LNK2038(同本地 config) | |
| env: | |
| ORT_LIB_LOCATION: ${{ github.workspace }}/app/src-tauri/ort/ci | |
| ORT_PREFER_DYNAMIC_LINK: "1" | |
| # --------------------------------------------------------------- | |
| # 发布阶段(不再走 artifact 中转:配额 500MB 装不下 349MB 安装包多次构建) | |
| # --------------------------------------------------------------- | |
| - name: Collect installer and generate latest.json | |
| id: meta | |
| shell: bash | |
| run: | | |
| mkdir -p release-files | |
| cp app/src-tauri/target/release/bundle/nsis/*.exe release-files/ | |
| FILE=$(ls release-files/*.exe | head -1 | xargs basename) | |
| SIZE=$(stat -c%s "release-files/$FILE") | |
| SHA256=$(sha256sum "release-files/$FILE" | awk '{print $1}') | |
| DATE=$(date -u +%F) | |
| printf '{"version":"%s","fileName":"%s","size":%s,"sha256":"%s","releaseDate":"%s"}\n' \ | |
| "${{ github.ref_name }}" "$FILE" "$SIZE" "$SHA256" "$DATE" > release-files/latest.json | |
| cat release-files/latest.json | |
| echo "file=$FILE" >> "$GITHUB_OUTPUT" | |
| SIZE_MB=$(awk "BEGIN {printf \"%.2f\", $SIZE / 1048576}") | |
| echo "📦 $FILE: ${SIZE_MB} MB" | |
| if [ "$SIZE" -gt 419430400 ]; then | |
| echo "::warning::${FILE} 超过 400MB,请检查是否误打包大文件(模型不入库)" | |
| fi | |
| - name: Upload installer to GitHub Release | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TAG: ${{ github.ref_name }} | |
| FILE_NAME: ${{ steps.meta.outputs.file }} | |
| run: | | |
| # tag 触发的 release 可能尚不存在(semantic-release 未参与时),不存在则创建 | |
| gh release view "$TAG" --json id >/dev/null 2>&1 || gh release create "$TAG" --title "$TAG" --notes "熵减 $TAG" --generate-notes | |
| gh release upload "$TAG" "release-files/$FILE_NAME" --clobber | |
| # --------------------------------------------------------------- | |
| # 同步安装包至自建服务器(国内主下载源;服务器连接继承自原项目) | |
| # appleboy/ssh-action 与 scp-action 不支持 Windows runner(MSYS 平台检测失败), | |
| # 改用 Windows 自带 OpenSSH(ssh/scp 均在 Git Bash 可用) | |
| # --------------------------------------------------------------- | |
| - name: Sync installer to server | |
| shell: bash | |
| env: | |
| SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }} | |
| SERVER_HOST: ${{ secrets.SERVER_HOST }} | |
| SERVER_USER: ${{ secrets.SERVER_USER }} | |
| run: | | |
| mkdir -p ~/.ssh | |
| echo "$SSH_KEY" > ~/.ssh/deploy_key | |
| chmod 600 ~/.ssh/deploy_key | |
| SSH_OPTS="-i ~/.ssh/deploy_key -o StrictHostKeyChecking=no -o ConnectTimeout=30" | |
| ssh $SSH_OPTS "$SERVER_USER@$SERVER_HOST" "mkdir -p /opt/Entropydecrease/downloads" | |
| scp $SSH_OPTS release-files/*.exe "$SERVER_USER@$SERVER_HOST:/opt/Entropydecrease/downloads/" | |
| # 元数据最后发布,避免 404 窗口期 | |
| scp $SSH_OPTS release-files/latest.json "$SERVER_USER@$SERVER_HOST:/opt/Entropydecrease/downloads/" | |
| # 保留策略:仅保留最近 3 个版本的安装包,防止磁盘增长 | |
| ssh $SSH_OPTS "$SERVER_USER@$SERVER_HOST" "cd /opt/Entropydecrease/downloads && ls -t -- *.exe 2>/dev/null | tail -n +4 | while read -r f; do rm -f -- \"\$f\"; done" | |
| rm -f ~/.ssh/deploy_key | |
| # --------------------------------------------------------------- | |
| # 同步至 OSS(CDN 源站;可选——未配置阿里云凭据时跳过) | |
| # --------------------------------------------------------------- | |
| - name: Upload to OSS (CDN origin) | |
| if: env.HAS_ALIYUN_AK == 'true' | |
| shell: bash | |
| env: | |
| AK_ID: ${{ secrets.ALIYUN_ACCESS_KEY_ID }} | |
| AK_SECRET: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }} | |
| OSS_ENDPOINT_IN: ${{ secrets.OSS_ENDPOINT }} | |
| FILE_NAME: ${{ steps.meta.outputs.file }} | |
| run: | | |
| set -eo pipefail | |
| OSS_ENDPOINT="${OSS_ENDPOINT_IN:-oss-cn-hangzhou.aliyuncs.com}" | |
| # build job 为 windows runner,需下载 Windows 版 aliyun CLI | |
| curl -sL https://aliyuncli.alicdn.com/aliyun-cli-windows-latest-amd64.zip -o /tmp/aliyun.zip | |
| unzip -q /tmp/aliyun.zip -d /tmp/aliyun | |
| /tmp/aliyun/aliyun.exe configure set --profile ossci --mode AK --region cn-hangzhou \ | |
| --access-key-id "$AK_ID" --access-key-secret "$AK_SECRET" | |
| /tmp/aliyun/aliyun.exe oss cp "release-files/$FILE_NAME" \ | |
| "oss://${{ secrets.OSS_BUCKET }}/downloads/$FILE_NAME" \ | |
| --force -e "$OSS_ENDPOINT" --profile ossci | |
| /tmp/aliyun/aliyun.exe oss cp release-files/latest.json \ | |
| "oss://${{ secrets.OSS_BUCKET }}/downloads/latest.json" \ | |
| --force -e "$OSS_ENDPOINT" --profile ossci | |
| echo "OSS sync done" | |
| # --------------------------------------------------------------- | |
| # 发布结果校验:防止"空 Release"(历史事故 v0.27.0/v0.28.7) | |
| # --------------------------------------------------------------- | |
| - name: Verify release assets | |
| if: startsWith(github.ref, 'refs/tags/') | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| TAG: ${{ github.ref_name }} | |
| run: | | |
| set -eo pipefail | |
| names=$(gh release view "$TAG" --json assets --jq '.assets[].name') | |
| echo "Assets of $TAG:" | |
| echo "$names" | |
| if ! echo "$names" | grep -qE '\.exe$'; then | |
| echo "::error::Release $TAG 缺少 .exe 安装包资产" | |
| exit 1 | |
| fi | |
| echo "发布资产完整校验通过" |