Files
openless/.github/workflows/release-tauri.yml
T
TRIPandbaiqing b7685cadda docs(release): make macOS xattr workaround discoverable (#347)
发现的真因:v1.2.23 Stable 和 v1.2.24-1 Beta 的 macOS DMG 都是 ad-hoc 签名 + 未经 Apple 公证(仓库没配 APPLE_CERTIFICATE 等 secrets)。macOS 14+ Gatekeeper 默认拒绝运行未公证的 app,弹"OpenLess 已损坏"。
build-mac.sh:40-41 注释已经写明这是已知限制:
  "用户免手工 xattr 的根本方案是 Developer ID 签名 + Apple notarization"

之前 README 只在 brew install 段写了 xattr -cr 指令,DMG 直装路径没写。
用户首次装 Beta 不知道要做这一步,看到"已损坏"以为 release 坏了。

本 PR:
- README.md / README.zh.md 的「下载与安装」段,DMG 直装路径加上 xattr -cr 指令 + 一句解释为什么需要(ad-hoc 签名、未公证)
- release-tauri.yml 的 Create release step 加 body 模板,所有未来 release 自动 prepend 一段:macOS xattr 指令 + Beta/Stable 渠道说明。用 append_body: true 让它放在 generated notes 之前

不在本 PR:配 Apple Developer 证书 + secrets——那是根治方案,需要 $99/年 Apple Developer 账号 + 用户决定要不要走那条路。

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-08 09:27:16 +08:00

523 lines
26 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-15-intel
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 }}
# 渠道由 tag 后缀决定:
# v<v>-beta-tauri → beta 渠道(GitHub Release 标 prerelease,manifest 文件名带 -beta 后缀,
# 正式版用户的 endpoint 拿不到)
# v<v>-tauri → stable 渠道(正式版,文件名沿用旧约定,向后兼容)
# workflow_dispatch / 非 tag 触发时 github.ref_name 不是 tag 字符串,
# endsWith 返回 false,回退为 stable,不改变现有 dispatch 行为。
OPENLESS_RELEASE_CHANNEL: ${{ endsWith(github.ref_name, '-beta-tauri') && 'beta' || 'stable' }}
steps:
- uses: actions/checkout@v4
with:
# vendor/qwen-asr 是 macOS 上 build.rs 必须的 git submodule(cc-rs 编译
# antirez/qwen-asr 的 C 源),不拉就会在 mac 端 cargo build 阶段挂掉。
submodules: recursive
- 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))"
# bundle.resources 引用的是 src-tauri/openless-ime-payload/{x64,x86}/OpenLessIme.dll
# (仓库里 commit 的是 0 字节占位让 mac 本地 build 也能 resolve)。
# 这里用真 dll 覆盖占位,让 NSIS / MSI 都装上真文件;NSIS hook 的 regsvr32
# 同时会把 64 / 32 位 COM 注册到 HKLM\Software\Classes\CLSID 的
# KEY_WOW64_64KEY / KEY_WOW64_32KEY 两侧(windows_ime_profile.rs 都会查)。
$payloadDir = Join-Path $appRoot "src-tauri\openless-ime-payload\$($t.Folder)"
New-Item -ItemType Directory -Force -Path $payloadDir | Out-Null
Copy-Item -Force -Path $dll -Destination (Join-Path $payloadDir 'OpenLessIme.dll')
Write-Host "[ok] copied real $($t.Folder) dll into bundle.resources payload path"
}
# ── Windows tauri build:保持 bash shell,因为 PowerShell 调外部命令
# 会把 '{"bundle":...}' 的内部双引号吃掉、让 tauri 收到无效 JSON。
# 用 set +e + GITHUB_ENV 把 exit code 传到下一步给 Repair 判断。
- 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: |
set +e
# 拆两轮跑:Tauri 的签名 / updater artifact 阶段是 post-bundle 钩子,
# 任意 bundler 失败会让 *所有* bundle 的 .sig 跳过。MSI 必踩 ICE80,
# 所以单一 `tauri build` 永远拿不到 NSIS 的 .exe.sig。
# Pass 1:NSIS 独立跑——必须成功,产出 *_x64-setup.exe(.sig) 给 updater。
# Pass 2:MSI 独立跑——允许失败,Repair 步骤兜底 light.exe 重链。
if [ -n "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then
npm run tauri -- build --bundles nsis --config '{"bundle":{"createUpdaterArtifacts":true}}'
nsis_exit=$?
npm run tauri -- build --bundles msi --config '{"bundle":{"createUpdaterArtifacts":true}}'
msi_exit=$?
else
npm run tauri -- build --bundles nsis
nsis_exit=$?
npm run tauri -- build --bundles msi
msi_exit=$?
fi
echo "TAURI_BUILD_EXIT=$msi_exit" >> "$GITHUB_ENV"
# NSIS 是 updater 的硬依赖,挂了就直接 fail step。
if [ "$nsis_exit" -ne 0 ]; then
echo "::error::NSIS bundle failed (exit $nsis_exit) — updater artifact unavailable."
exit 1
fi
# MSI 失败不挡——下一步 Repair 用 light.exe 重链(带 -sice:ICE80)。
exit 0
# ── 如果 tauri 在 wix link 阶段失败(candle 出了 wixobj,但 light 因 cwd
# 解析 wxs Source 找不到 IME DLL),从 appRoot 手动跑 light 兜底重链。
# 这是本地 windows-package-msvc.ps1::Repair-TauriMsiBundle 的同款手术。
- name: Repair Windows MSI if Tauri failed at WiX link
if: matrix.platform == 'windows-latest'
shell: pwsh
working-directory: 'openless-all/app'
run: |
$exitCode = $env:TAURI_BUILD_EXIT
$appRoot = (Resolve-Path .).Path
$msi = Get-ChildItem "src-tauri\target\release\bundle\msi\*.msi" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($msi) {
Write-Host "[ok] MSI already produced by tauri: $($msi.FullName); no repair needed."
return
}
if (-not $exitCode -or $exitCode -eq '0') {
throw "Tauri exited 0 but no MSI found at src-tauri\target\release\bundle\msi\"
}
Write-Warning "Tauri MSI failed (exit $exitCode). Attempting manual light.exe relink from app root."
$wixRoot = Join-Path $appRoot 'src-tauri\target\release\wix\x64'
$mainObj = Join-Path $wixRoot 'main.wixobj'
$imeObj = Join-Path $wixRoot 'openless-ime.wixobj'
$locale = Join-Path $wixRoot 'locale.wxl'
foreach ($p in @($mainObj, $imeObj, $locale)) {
if (-not (Test-Path $p)) {
throw "Required WiX object missing: $p — tauri build aborted before candle ran. Check the Build (Windows) step log."
}
}
$light = (Get-ChildItem "$env:LOCALAPPDATA\tauri\WixTools314\light.exe" -ErrorAction SilentlyContinue | Select-Object -First 1).FullName
if (-not $light) { throw "WiX light.exe not found in $env:LOCALAPPDATA\tauri\WixTools314" }
$version = (Get-Content src-tauri\tauri.conf.json -Raw | ConvertFrom-Json).version
$bundleDir = Join-Path $appRoot 'src-tauri\target\release\bundle\msi'
New-Item -ItemType Directory -Force -Path $bundleDir | Out-Null
$msiPath = Join-Path $bundleDir "OpenLess_${version}_x64_en-US.msi"
Push-Location $appRoot
try {
# -sice:ICE80:x86 IME DLL 与 x64 一同打进 INSTALLDIR\windows-ime\,
# 这是 32 位组件落在 64 位 Directory 下的合法场景(DLL 路径绝对指向,
# 不依赖 SysWOW64 重定向)。Tauri 自身没有暴露 light 透传参数,所以
# 必须在这里抑制,否则 LGHT0204 必失败。
& $light -nologo -sice:ICE80 -ext WixUIExtension -ext WixUtilExtension -loc $locale -out $msiPath $mainObj $imeObj
if ($LASTEXITCODE -ne 0) { throw "light.exe relink failed with exit $LASTEXITCODE" }
} finally {
Pop-Location
}
Write-Host "[ok] MSI rebuilt at $msiPath"
- name: Verify Windows installers register TSF IME
if: matrix.platform == 'windows-latest'
shell: pwsh
working-directory: 'openless-all/app'
run: |
$nsis = Get-ChildItem "src-tauri\target\release\bundle\nsis\*.exe" -ErrorAction Stop | Select-Object -First 1
$msi = Get-ChildItem "src-tauri\target\release\bundle\msi\*.msi" -ErrorAction Stop | Select-Object -First 1
if (-not $nsis) { throw "NSIS installer not found." }
if (-not $msi) { throw "MSI installer not found." }
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\windows-ime-install-smoke.ps1 -InstallerPath $nsis.FullName -InstallerKind nsis
if ($LASTEXITCODE -ne 0) {
throw "NSIS installer smoke failed with exit $LASTEXITCODE"
}
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\windows-ime-install-smoke.ps1 -InstallerPath $msi.FullName -InstallerKind msi
if ($LASTEXITCODE -ne 0) {
throw "MSI installer smoke failed with exit $LASTEXITCODE"
}
# ── Linux:产 deb / rpm / AppImage ──
# bundle.resources 里的 Windows TSF DLL 占位对 Linux 包没意义,用空 map 覆盖跳过。
- 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":{"resources":{},"createUpdaterArtifacts":true}}'
else
npm run tauri -- build --bundles deb,rpm,appimage \
--config '{"bundle":{"resources":{}}}'
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
# beta 渠道时输出 latest-{tgt}-{arch}-beta.json,stable 沿用旧文件名。
OPENLESS_RELEASE_CHANNEL: ${{ env.OPENLESS_RELEASE_CHANNEL }}
# Beta 渠道里脚本要把 manifest.url 写成 releases/download/<tag>/...
# 而不是 releases/latest(后者永远 = Stable,Beta 用户按 url 下载会拉到错文件)。
# workflow_dispatch 时 github.ref_name 不是 tag——但那种情况下 channel=stable,
# 脚本不会读这个字段,安全。
OPENLESS_RELEASE_TAG: ${{ github.ref_name }}
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
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
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
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
# beta 渠道的 release 必须标 prerelease=true:GitHub UI 会折叠它,
# 普通用户看不到;同时只上传 latest-*-beta.json,正式版用户的
# endpoint(latest-*.json)永远不会被覆盖,保证 Beta 不溢出正式版。
prerelease: ${{ env.OPENLESS_RELEASE_CHANNEL == 'beta' }}
# body 在 generated notes 之前 prepend——所有 matrix job 设同一份字符串,
# 多次调用 action 时内容一致、idempotent。提示 macOS Gatekeeper 绕过指令 +
# 渠道说明,每个 release 自动带上,用户不用回项目 README 翻就能看到。
body: |
### macOS 用户首次安装提示
下载 DMG 拖入 `/Applications` 后,**必须**在终端运行:
```bash
xattr -cr /Applications/OpenLess.app
```
否则 Gatekeeper 会提示「OpenLess 已损坏」——这是因为当前 build 用 ad-hoc 签名、没做 Apple 公证。
### 渠道说明
- 以 `-tauri` 结尾的 tag 是**正式版**(自动推送给所有 in-app 检查更新的用户)。
- 以 `-beta-tauri` 结尾的 tag 是 **Beta 版**(GitHub 标 pre-release,**不**通过 in-app updater 推送给正式版用户;只对在「设置 → 关于 → 加入 Beta 渠道」开关切到 Beta 的用户可见,且需要手动从此页面下载安装)。
append_body: true
# 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