fix: improve manual report export and linux appimage build #73
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 | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| jobs: | |
| build-tauri: | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: macos-latest | |
| args: "--target aarch64-apple-darwin" | |
| target: aarch64-apple-darwin | |
| - platform: macos-latest | |
| args: "--target x86_64-apple-darwin" | |
| target: x86_64-apple-darwin | |
| - platform: ubuntu-22.04 | |
| args: "--target x86_64-unknown-linux-gnu" | |
| target: x86_64-unknown-linux-gnu | |
| - platform: windows-latest | |
| args: "" | |
| target: x86_64-pc-windows-msvc | |
| runs-on: ${{ matrix.platform }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: "npm" | |
| - name: Install Linux build dependencies | |
| if: startsWith(matrix.platform, 'ubuntu') | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev \ | |
| libgtk-3-dev \ | |
| libayatana-appindicator3-dev \ | |
| librsvg2-dev \ | |
| patchelf \ | |
| libfuse2 | |
| - name: Install frontend dependencies | |
| run: npm install | |
| - name: Build application | |
| env: | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| run: npm run tauri build -- ${{ matrix.args }} | |
| - name: Rename macOS updater assets | |
| if: startsWith(matrix.platform, 'macos') | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| normalize_name() { | |
| local filename="$1" | |
| printf '%s' "${filename// /_}" | |
| } | |
| if [[ "${{ matrix.target }}" == "aarch64-apple-darwin" ]]; then | |
| ARCH_SUFFIX="aarch64" | |
| else | |
| ARCH_SUFFIX="x64" | |
| fi | |
| find src-tauri/target -type f -path "*/release/bundle/macos/*.app.tar.gz" | while read -r file; do | |
| dir="$(dirname "$file")" | |
| base="$(basename "$file" ".app.tar.gz")" | |
| normalized_base="$(normalize_name "$base")" | |
| renamed="${dir}/${normalized_base}_${ARCH_SUFFIX}.app.tar.gz" | |
| mv "$file" "$renamed" | |
| if [[ -f "${file}.sig" ]]; then | |
| mv "${file}.sig" "${renamed}.sig" | |
| fi | |
| done | |
| find src-tauri/target -type f -path "*/release/bundle/dmg/*.dmg" | while read -r file; do | |
| dir="$(dirname "$file")" | |
| name="$(basename "$file")" | |
| normalized_name="$(normalize_name "$name")" | |
| renamed="${dir}/${normalized_name}" | |
| if [[ "$file" != "$renamed" ]]; then | |
| mv "$file" "$renamed" | |
| fi | |
| done | |
| - name: Rename Windows installer assets | |
| if: startsWith(matrix.platform, 'windows') | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| normalize_name() { | |
| local filename="$1" | |
| printf '%s' "${filename// /_}" | |
| } | |
| find src-tauri/target -type f -path "*/release/bundle/nsis/*.exe" | while read -r file; do | |
| dir="$(dirname "$file")" | |
| name="$(basename "$file")" | |
| normalized_name="$(normalize_name "$name")" | |
| renamed="${dir}/${normalized_name}" | |
| if [[ "$file" != "$renamed" ]]; then | |
| mv "$file" "$renamed" | |
| fi | |
| if [[ -f "${file}.sig" ]]; then | |
| mv "${file}.sig" "${renamed}.sig" | |
| fi | |
| done | |
| - name: Rename Linux bundle assets | |
| if: startsWith(matrix.platform, 'ubuntu') | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| normalize_name() { | |
| local filename="$1" | |
| printf '%s' "${filename// /_}" | |
| } | |
| find src-tauri/target -type f -path "*/release/bundle/appimage/*.AppImage" | while read -r file; do | |
| dir="$(dirname "$file")" | |
| name="$(basename "$file")" | |
| normalized_name="$(normalize_name "$name")" | |
| renamed="${dir}/${normalized_name}" | |
| if [[ "$file" != "$renamed" ]]; then | |
| mv "$file" "$renamed" | |
| fi | |
| done | |
| find src-tauri/target -type f -path "*/release/bundle/deb/*.deb" | while read -r file; do | |
| dir="$(dirname "$file")" | |
| name="$(basename "$file")" | |
| normalized_name="$(normalize_name "$name")" | |
| renamed="${dir}/${normalized_name}" | |
| if [[ "$file" != "$renamed" ]]; then | |
| mv "$file" "$renamed" | |
| fi | |
| done | |
| - name: Verify required artifacts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| require_file() { | |
| local pattern="$1" | |
| local label="$2" | |
| local match | |
| match="$(find src-tauri/target -type f -path "$pattern" | sort | head -n 1 || true)" | |
| if [[ -z "$match" ]]; then | |
| echo "缺少 ${label}: ${pattern}" >&2 | |
| exit 1 | |
| fi | |
| echo "${label}: ${match}" | |
| } | |
| case "${{ matrix.target }}" in | |
| aarch64-apple-darwin) | |
| require_file "*/release/bundle/macos/*_aarch64.app.tar.gz" "macOS aarch64 自动更新包" | |
| require_file "*/release/bundle/macos/*_aarch64.app.tar.gz.sig" "macOS aarch64 自动更新签名" | |
| require_file "*/release/bundle/dmg/*.dmg" "macOS aarch64 DMG" | |
| ;; | |
| x86_64-apple-darwin) | |
| require_file "*/release/bundle/macos/*_x64.app.tar.gz" "macOS x64 自动更新包" | |
| require_file "*/release/bundle/macos/*_x64.app.tar.gz.sig" "macOS x64 自动更新签名" | |
| require_file "*/release/bundle/dmg/*.dmg" "macOS x64 DMG" | |
| ;; | |
| x86_64-unknown-linux-gnu) | |
| require_file "*/release/bundle/appimage/*.AppImage" "Linux AppImage" | |
| require_file "*/release/bundle/deb/*.deb" "Linux DEB" | |
| ;; | |
| x86_64-pc-windows-msvc) | |
| require_file "*/release/bundle/nsis/*.exe" "Windows 安装包" | |
| require_file "*/release/bundle/nsis/*.sig" "Windows 安装包签名" | |
| ;; | |
| esac | |
| - name: Upload release assets | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-assets-${{ matrix.platform }}-${{ strategy.job-index }} | |
| path: | | |
| src-tauri/target/**/release/bundle/dmg/*.dmg | |
| src-tauri/target/**/release/bundle/dmg/*.sig | |
| src-tauri/target/**/release/bundle/macos/*.app.tar.gz | |
| src-tauri/target/**/release/bundle/macos/*.app.tar.gz.sig | |
| src-tauri/target/**/release/bundle/nsis/*.exe | |
| src-tauri/target/**/release/bundle/nsis/*.sig | |
| src-tauri/target/**/release/bundle/appimage/*.AppImage | |
| src-tauri/target/**/release/bundle/deb/*.deb | |
| if-no-files-found: error | |
| publish-release: | |
| needs: build-tauri | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: all-artifacts | |
| pattern: "release-assets-*" | |
| merge-multiple: true | |
| - name: Extract changelog | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VERSION="${GITHUB_REF_NAME#v}" | |
| BODY=$(awk "/^## \[${VERSION}\]/{found=1; next} /^## \[/{if(found) exit} found{print}" CHANGELOG.md) | |
| if [[ -z "$BODY" ]]; then | |
| BODY="版本 ${VERSION} 更新" | |
| fi | |
| BODY="${BODY} | |
| ### macOS 安装说明 | |
| 首次打开可能提示「已损坏,无法打开」,请执行: | |
| \`\`\`bash | |
| sudo xattr -rd com.apple.quarantine '/Applications/Work Review.app' | |
| \`\`\`" | |
| printf '%s\n' "$BODY" > release_notes.md | |
| - name: Build updater.json from signatures | |
| shell: bash | |
| env: | |
| VERSION: ${{ github.ref_name }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| DOWNLOAD_BASE="https://github.com/${REPO}/releases/download/${VERSION}" | |
| VER="${VERSION#v}" | |
| NOTES="$(cat release_notes.md)" | |
| PUB_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| MAC_AARCH64_FILE="$(find all-artifacts -type f -name '*_aarch64.app.tar.gz' | sort | head -n 1 || true)" | |
| MAC_X64_FILE="$(find all-artifacts -type f -name '*_x64.app.tar.gz' | sort | head -n 1 || true)" | |
| WINDOWS_FILE="$(find all-artifacts -type f \( -name '*setup.exe' -o -name '*installer.exe' \) ! -name '*.sig' | sort | head -n 1 || true)" | |
| require_asset() { | |
| local asset_file="$1" | |
| local signature_file="$2" | |
| local label="$3" | |
| if [[ -z "$asset_file" || ! -f "$asset_file" ]]; then | |
| echo "缺少 ${label} 产物" >&2 | |
| exit 1 | |
| fi | |
| if [[ ! -f "$signature_file" ]]; then | |
| echo "缺少 ${label} 签名: ${signature_file}" >&2 | |
| exit 1 | |
| fi | |
| echo "${label}: ${asset_file}" | |
| } | |
| require_asset "$MAC_AARCH64_FILE" "${MAC_AARCH64_FILE}.sig" "darwin-aarch64" | |
| require_asset "$MAC_X64_FILE" "${MAC_X64_FILE}.sig" "darwin-x86_64" | |
| require_asset "$WINDOWS_FILE" "${WINDOWS_FILE}.sig" "windows-x86_64" | |
| json="$(jq -n \ | |
| --arg version "$VER" \ | |
| --arg notes "$NOTES" \ | |
| --arg pub_date "$PUB_DATE" \ | |
| '{version: $version, notes: $notes, pub_date: $pub_date, platforms: {}}')" | |
| add_platform() { | |
| local platform_key="$1" | |
| local asset_file="$2" | |
| local signature_file="$3" | |
| local base_url="$4" | |
| if [[ -z "$asset_file" || ! -f "$asset_file" || ! -f "$signature_file" ]]; then | |
| echo "skip ${platform_key}" | |
| return | |
| fi | |
| local signature | |
| signature="$(tr -d '\r\n' < "$signature_file")" | |
| local asset_name | |
| asset_name="$(basename "$asset_file")" | |
| variant_json="$(jq \ | |
| --arg key "$platform_key" \ | |
| --arg url "${base_url}/${asset_name}" \ | |
| --arg signature "$signature" \ | |
| '.platforms[$key] = { url: $url, signature: $signature }' \ | |
| <<<"$variant_json")" | |
| } | |
| # 为每个代理前缀生成独立的 updater JSON 文件 | |
| # 格式: "前缀 文件名"(前缀为空表示直连 GitHub) | |
| PROXY_VARIANTS=( | |
| " updater.json" | |
| "https://ghproxy.cn/ updater-ghproxy.json" | |
| "https://ghp.ci/ updater-ghp.json" | |
| ) | |
| for variant in "${PROXY_VARIANTS[@]}"; do | |
| proxy_prefix="${variant%% *}" | |
| output_file="${variant##* }" | |
| if [[ -z "$proxy_prefix" ]]; then | |
| VARIANT_BASE="${DOWNLOAD_BASE}" | |
| else | |
| VARIANT_BASE="${proxy_prefix}${DOWNLOAD_BASE}" | |
| fi | |
| variant_json="$json" | |
| add_platform "darwin-aarch64" "$MAC_AARCH64_FILE" "${MAC_AARCH64_FILE}.sig" "$VARIANT_BASE" | |
| add_platform "darwin-x86_64" "$MAC_X64_FILE" "${MAC_X64_FILE}.sig" "$VARIANT_BASE" | |
| add_platform "windows-x86_64" "$WINDOWS_FILE" "${WINDOWS_FILE}.sig" "$VARIANT_BASE" | |
| if [[ "$(jq '.platforms | length' <<<"$variant_json")" -ne 3 ]]; then | |
| echo "自动更新平台数量异常 (${output_file}),期望 3 个,实际为 $(jq '.platforms | length' <<<"$variant_json")" >&2 | |
| exit 1 | |
| fi | |
| printf '%s\n' "$variant_json" > "$output_file" | |
| echo "=== ${output_file} ===" | |
| cat "$output_file" | |
| done | |
| - name: Create or update GitHub Release | |
| uses: ncipollo/release-action@v1 | |
| with: | |
| allowUpdates: true | |
| name: "Work_Review ${{ github.ref_name }}" | |
| bodyFile: release_notes.md | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| artifacts: "all-artifacts/**/*,updater.json,updater-ghproxy.json,updater-ghp.json" | |
| artifactErrorsFailBuild: false | |
| replacesArtifacts: true | |
| makeLatest: legacy |