Files
openless/.github/workflows/release-tauri.yml
T
baiqing ac2edbe961 ci(release): wxs 用 env 绝对路径定位 IME DLL
之前 light.exe 失败的根因:openless-ime.wxs 的 Source 写的相对路径
src-tauri\target\windows-ime-msvc\... ,要从 openless-all/app/ 才能
resolve;但 Tauri 内置 wix bundler 的 candle/light cwd 是 src-tauri/,
路径错位,文件找不到 → light 失败。

本地 windows-package-msvc.ps1 通过 Repair-TauriMsiBundle 从 appRoot 手动
跑 light 兜底,CI 没有这个兜底所以挂掉。

修法:wxs Source 改用 $(env.OPENLESS_IME_DLL_X64) / _X86 绝对路径变量;
工作流的 IME build 步骤把绝对路径写到 $GITHUB_ENV。这样无论 candle/light
cwd 在哪,都能拿到正确的 DLL 路径。

本地脚本本就 set 这两个 env var(windows-package-msvc.ps1:213),改后
本地 + CI 共用同一条 absolute-path 路径,Repair fallback 不再必需。
2026-05-04 20:04:18 +08:00

385 lines
17 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Release Tauri (cross-platform)
# 触发条件:
# - 推 v*.*.*-tauri 形式的 tag(与老 Swift 版的 vX.Y.Z 区分开,不冲突)
# - 手动 dispatch(用于测试构建,不发版)
#
# 输出:
# macOS arm64/x64 .dmg + Windows x64 .msi/.exe + Linux x64 .deb/.rpm/.AppImage,自动作为 GitHub Release 资产上传。
#
# macOS 分发:
# - 配好 APPLE_CERTIFICATE / APPLE_CERTIFICATE_PASSWORD / APPLE_ID /
# APPLE_PASSWORD / APPLE_TEAM_ID 后,Tauri 会做 Developer ID 签名和公证。
# 用户从浏览器下载后不需要手工 xattr。
# - 未配置 Apple secrets 时自动回退 ad-hoc 签名,GitHub Actions 会打印 warning。
# - Windows 没签名(无证书),Win 11 SmartScreen 会警告 "未识别的发布者",用户点"仍要运行"。
# - 任意一个 platform 失败不影响另一个继续构建(fail-fast: false)。
on:
push:
tags:
- 'v*-tauri'
workflow_dispatch:
jobs:
build:
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
rust-target: aarch64-apple-darwin
updater-target: darwin
updater-arch: aarch64
- platform: macos-13
rust-target: x86_64-apple-darwin
updater-target: darwin
updater-arch: x86_64
- platform: windows-latest
rust-target: x86_64-pc-windows-msvc
updater-target: windows
updater-arch: x86_64
- platform: ubuntu-22.04
rust-target: x86_64-unknown-linux-gnu
updater-target: linux
updater-arch: x86_64
runs-on: ${{ matrix.platform }}
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: 'openless-all/app/package-lock.json'
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust-target }}
- name: Cache Cargo
uses: swatinem/rust-cache@v2
with:
workspaces: 'openless-all/app/src-tauri -> target'
- name: Install Linux bundle deps
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
curl \
file \
libasound2-dev \
libayatana-appindicator3-dev \
libfuse2 \
librsvg2-dev \
libssl-dev \
libwebkit2gtk-4.1-dev \
libxdo-dev \
patchelf \
rpm \
wget
- name: Install npm deps
working-directory: 'openless-all/app'
run: npm ci
- name: Check updater signing availability
if: startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-tauri')
shell: bash
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
run: |
if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then
echo "::error::TAURI_SIGNING_PRIVATE_KEY is required for signed auto-update artifacts."
exit 1
fi
- name: Check Apple signing availability
if: startsWith(matrix.platform, 'macos') && startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-tauri')
shell: bash
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
missing=()
for name in APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_ID APPLE_PASSWORD APPLE_TEAM_ID; do
if [ -z "${!name:-}" ]; then
missing+=("$name")
fi
done
if [ "${#missing[@]}" -gt 0 ]; then
echo "::warning::macOS release will use ad-hoc signing because Apple signing/notarization secrets are missing: ${missing[*]}"
fi
- name: Import Apple Developer ID certificate
if: startsWith(matrix.platform, 'macos')
shell: bash
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
if [ -z "${APPLE_CERTIFICATE:-}" ] || [ -z "${APPLE_CERTIFICATE_PASSWORD:-}" ]; then
echo "No Apple certificate secrets configured; macOS build will use ad-hoc signing."
exit 0
fi
KEYCHAIN_PASSWORD="${KEYCHAIN_PASSWORD:-$(openssl rand -base64 32)}"
CERT_PATH="$RUNNER_TEMP/openless-certificate.p12"
KEYCHAIN_PATH="$RUNNER_TEMP/openless-build.keychain-db"
echo "$APPLE_CERTIFICATE" | base64 --decode > "$CERT_PATH"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security default-keychain -s "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -t 3600 -u "$KEYCHAIN_PATH"
security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
DEVELOPER_ID_INFO="$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" | grep 'Developer ID Application' | head -n 1)"
if [ -n "$DEVELOPER_ID_INFO" ]; then
CERT_INFO="$DEVELOPER_ID_INFO"
else
CERT_INFO="$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" | grep -E 'Apple Distribution|Apple Development' | head -n 1)"
fi
if [ -z "$CERT_INFO" ]; then
echo "Apple certificate imported, but no usable code-signing identity was found."
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
exit 1
fi
CERT_ID="$(echo "$CERT_INFO" | awk -F'"' '{print $2}')"
echo "APPLE_SIGNING_IDENTITY=$CERT_ID" >> "$GITHUB_ENV"
echo "Imported Apple signing identity: $CERT_ID"
- name: Configure Apple notarization
if: startsWith(matrix.platform, 'macos')
shell: bash
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_PROVIDER_SHORT_NAME: ${{ secrets.APPLE_PROVIDER_SHORT_NAME }}
run: |
for name in APPLE_ID APPLE_PASSWORD APPLE_TEAM_ID APPLE_PROVIDER_SHORT_NAME; do
value="${!name:-}"
if [ -n "$value" ]; then
echo "$name=$value" >> "$GITHUB_ENV"
fi
done
# ── macOS:用我们自己的 build-mac.sh,统一处理签名、公证和 artifact 清理 ──
- name: Build (macOS)
if: startsWith(matrix.platform, 'macos')
working-directory: 'openless-all/app'
env:
INSTALL: '0' # CI 不要装到 /Applications,也不要 reset TCC
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: bash scripts/build-mac.sh
# ── Windows:先 build OpenLessIme.dll(x64+x86),再跑 tauri bundle。
# openless-ime.wxs 用 $(env.OPENLESS_IME_DLL_X64) / _X86 拿绝对路径,
# 跨 candle/light cwd 都能 resolve(Tauri wix bundler cwd 不固定)。
- name: Build Windows IME native DLLs
if: matrix.platform == 'windows-latest'
shell: pwsh
working-directory: 'openless-all/app'
run: |
$appRoot = (Resolve-Path .).Path
foreach ($t in @(
@{ Platform = 'x64'; Folder = 'x64'; EnvName = 'OPENLESS_IME_DLL_X64' },
@{ Platform = 'Win32'; Folder = 'x86'; EnvName = 'OPENLESS_IME_DLL_X86' }
)) {
$out = Join-Path $appRoot "src-tauri\target\windows-ime-msvc\$($t.Folder)\Release"
$obj = Join-Path $appRoot "src-tauri\target\windows-ime-msvc\obj\$($t.Folder)\Release"
./scripts/windows-ime-build.ps1 -Configuration Release -Platform $t.Platform -OutputDirectory $out -IntermediateDirectory $obj
if ($LASTEXITCODE -ne 0) {
throw "OpenLessIme $($t.Platform) build failed with exit $LASTEXITCODE"
}
$dll = (Resolve-Path (Join-Path $out 'OpenLessIme.dll')).Path
if (-not (Test-Path $dll)) {
throw "OpenLessIme.dll not produced at $dll"
}
"$($t.EnvName)=$dll" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
Write-Host "[ok] built $dll (exported $($t.EnvName))"
}
# ── Windows:仅 Windows 跑通用 tauri build(Linux 用下方专用 step,避免重复构建)──
- name: Build (Windows)
if: matrix.platform == 'windows-latest'
shell: bash
working-directory: 'openless-all/app'
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
if [ -n "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then
npm run tauri -- build --config '{"bundle":{"createUpdaterArtifacts":true}}'
else
npm run tauri build
fi
# ── Linux:产 deb / rpm / AppImage ──
- name: Build (Linux)
if: matrix.platform == 'ubuntu-22.04'
shell: bash
working-directory: 'openless-all/app'
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
if [ -n "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then
npm run tauri -- build --bundles deb,rpm,appimage --config '{"bundle":{"createUpdaterArtifacts":true}}'
else
npm run tauri -- build --bundles deb,rpm,appimage
fi
- name: Disambiguate macOS updater bundle filename
if: startsWith(matrix.platform, 'macos') && env.TAURI_SIGNING_PRIVATE_KEY != ''
shell: bash
working-directory: 'openless-all/app/src-tauri/target/release/bundle/macos'
run: |
if [ -f OpenLess.app.tar.gz ]; then
mv OpenLess.app.tar.gz "OpenLess_${{ matrix.updater-arch }}.app.tar.gz"
fi
if [ -f OpenLess.app.tar.gz.sig ]; then
mv OpenLess.app.tar.gz.sig "OpenLess_${{ matrix.updater-arch }}.app.tar.gz.sig"
fi
- name: Write updater manifest
if: env.TAURI_SIGNING_PRIVATE_KEY != ''
shell: bash
working-directory: 'openless-all/app'
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
OPENLESS_UPDATE_TARGET: ${{ matrix.updater-target }}
OPENLESS_UPDATE_ARCH: ${{ matrix.updater-arch }}
OPENLESS_UPDATE_REPO: appergb/openless
OPENLESS_UPDATE_MIRROR_BASE_URL: https://fastgit.cc/https://github.com
run: node scripts/write-updater-manifest.mjs
# ── 收集产物 ──
- name: List artifacts (debug)
shell: bash
working-directory: 'openless-all/app/src-tauri/target/release/bundle'
run: ls -la macos/ dmg/ nsis/ msi/ deb/ rpm/ appimage/ 2>/dev/null || true
# 防御性步骤:剥掉 macOS 产物上任何残留扩展属性 / quarantine。
# 理论上 GitHub Actions 输出的 .app/.dmg 不会带 com.apple.quarantine
# (xattr 也不会通过 actions/upload-artifact 跨机器持久化),但保留这一步
# 让"云端 artifact 一定干净"成为可验证的承诺。用户下载后再被本地浏览器
# 加 quarantine 时,按 release notes 的 `xattr -cr` 一行即可消除。
- name: Strip xattr / quarantine on macOS bundles
if: startsWith(matrix.platform, 'macos')
shell: bash
working-directory: 'openless-all/app/src-tauri/target/release/bundle'
run: |
for path in macos/*.app dmg/*.dmg; do
if [ -e "$path" ]; then
echo "▶ stripping xattr: $path"
xattr -cr "$path" || true
xattr -lr "$path" || true
fi
done
- name: Upload macOS artifacts
if: startsWith(matrix.platform, 'macos')
uses: actions/upload-artifact@v4
with:
name: openless-macos-${{ matrix.updater-arch }}
path: |
openless-all/app/src-tauri/target/release/bundle/dmg/*.dmg
if-no-files-found: error
- name: Upload macOS updater artifacts
if: startsWith(matrix.platform, 'macos') && env.TAURI_SIGNING_PRIVATE_KEY != ''
uses: actions/upload-artifact@v4
with:
name: openless-macos-${{ matrix.updater-arch }}-updater
path: |
openless-all/app/src-tauri/target/release/bundle/macos/*.app.tar.gz
openless-all/app/src-tauri/target/release/bundle/macos/*.app.tar.gz.sig
openless-all/app/src-tauri/target/release/bundle/latest-darwin-${{ matrix.updater-arch }}.json
openless-all/app/src-tauri/target/release/bundle/latest-darwin-${{ matrix.updater-arch }}-mirror.json
if-no-files-found: error
- name: Upload Windows artifacts
if: matrix.platform == 'windows-latest'
uses: actions/upload-artifact@v4
with:
name: openless-windows-x64
path: |
openless-all/app/src-tauri/target/release/bundle/nsis/*.exe
openless-all/app/src-tauri/target/release/bundle/msi/*.msi
if-no-files-found: error
- name: Upload Windows updater artifacts
if: matrix.platform == 'windows-latest' && env.TAURI_SIGNING_PRIVATE_KEY != ''
uses: actions/upload-artifact@v4
with:
name: openless-windows-x64-updater
path: |
openless-all/app/src-tauri/target/release/bundle/nsis/*.exe.sig
openless-all/app/src-tauri/target/release/bundle/msi/*.msi.sig
openless-all/app/src-tauri/target/release/bundle/latest-windows-x86_64.json
openless-all/app/src-tauri/target/release/bundle/latest-windows-x86_64-mirror.json
if-no-files-found: error
- name: Upload Linux artifacts
if: matrix.platform == 'ubuntu-22.04'
uses: actions/upload-artifact@v4
with:
name: openless-linux-x64
path: |
openless-all/app/src-tauri/target/release/bundle/deb/*.deb
openless-all/app/src-tauri/target/release/bundle/rpm/*.rpm
openless-all/app/src-tauri/target/release/bundle/appimage/*.AppImage
if-no-files-found: error
- name: Upload Linux updater artifacts
if: matrix.platform == 'ubuntu-22.04' && env.TAURI_SIGNING_PRIVATE_KEY != ''
uses: actions/upload-artifact@v4
with:
name: openless-linux-x64-updater
path: |
openless-all/app/src-tauri/target/release/bundle/appimage/*.AppImage.sig
openless-all/app/src-tauri/target/release/bundle/latest-linux-x86_64.json
openless-all/app/src-tauri/target/release/bundle/latest-linux-x86_64-mirror.json
if-no-files-found: error
# ── tag 推送时,同步上传到 GitHub Release ──
- name: Create / update release
if: startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-tauri')
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: 'OpenLess ${{ github.ref_name }}'
draft: false
prerelease: false
# Matrix jobs all upload assets to the same release. Generate notes once
# so macOS, Windows, and Linux jobs do not duplicate the release body.
generate_release_notes: ${{ matrix.updater-target == 'darwin' && matrix.updater-arch == 'aarch64' }}
files: |
openless-all/app/src-tauri/target/release/bundle/dmg/*.dmg
openless-all/app/src-tauri/target/release/bundle/macos/*.app.tar.gz
openless-all/app/src-tauri/target/release/bundle/macos/*.app.tar.gz.sig
openless-all/app/src-tauri/target/release/bundle/nsis/*.exe
openless-all/app/src-tauri/target/release/bundle/nsis/*.exe.sig
openless-all/app/src-tauri/target/release/bundle/msi/*.msi
openless-all/app/src-tauri/target/release/bundle/msi/*.msi.sig
openless-all/app/src-tauri/target/release/bundle/deb/*.deb
openless-all/app/src-tauri/target/release/bundle/rpm/*.rpm
openless-all/app/src-tauri/target/release/bundle/appimage/*.AppImage
openless-all/app/src-tauri/target/release/bundle/appimage/*.AppImage.sig
openless-all/app/src-tauri/target/release/bundle/latest-*.json