Skip to content

feat(tray): 添加托盘菜单'重启程序'选项 #171

feat(tray): 添加托盘菜单'重启程序'选项

feat(tray): 添加托盘菜单'重启程序'选项 #171

Workflow file for this run

name: "CI/CD"
# 发布Release(创建tag):git tag v1.2.0 && git push origin v1.2.0
on:
push:
branches: [main, master]
tags: ['v*']
pull_request:
branches: [main, master]
workflow_dispatch:
inputs:
release:
description: 'Create release'
required: false
default: false
type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
env:
NODE_VERSION: '20'
RUST_TOOLCHAIN: stable
# 用于缓存二进制文件的 key
RCLONE_VERSION: "current"
OPENLIST_VERSION: "v4.1.10"
jobs:
# ========== 快速检查(合并为一个 job,并行执行所有检查)==========
quick-checks:
runs-on: ubuntu-latest
timeout-minutes: 8
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
shared-key: check
# 一次性安装所有依赖
- name: Install dependencies
run: pnpm install --frozen-lockfile
# 并行运行所有检查
- name: Run checks
run: |
# 使用后台进程并行运行
pnpm run check:i18n &
I18N_PID=$!
pnpm run lint &
LINT_PID=$!
npx tsc --noEmit &
TSC_PID=$!
# 等待所有后台任务完成,并收集退出码
wait $I18N_PID
I18N_EXIT=$?
wait $LINT_PID
LINT_EXIT=$?
wait $TSC_PID
TSC_EXIT=$?
# 如果任何检查失败,返回非零退出码
if [ $I18N_EXIT -ne 0 ] || [ $LINT_EXIT -ne 0 ] || [ $TSC_EXIT -ne 0 ]; then
echo "Checks failed: i18n=$I18N_EXIT lint=$LINT_EXIT tsc=$TSC_EXIT"
exit 1
fi
# ========== 前端构建(独立 job,可并行)==========
frontend-build:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run build
# ========== Tauri 构建检查(使用缓存的二进制文件)==========
tauri-check:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
shared-key: tauri-check
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
# 缓存二进制文件(rclone/openlist)
- name: Cache binaries
uses: actions/cache@v4
id: cache-binaries
with:
path: |
src-tauri/binaries/rclone
src-tauri/binaries/openlist
src-tauri/binaries/rclone-x86_64-unknown-linux-gnu
src-tauri/binaries/openlist-x86_64-unknown-linux-gnu
key: binaries-x86_64-unknown-linux-gnu-${{ env.RCLONE_VERSION }}-${{ env.OPENLIST_VERSION }}
- name: Resolve skip-downloads flag
id: resolve-skip-downloads
shell: bash
run: |
set -euo pipefail
if [ "${{ steps.cache-binaries.outputs.cache-hit }}" != "true" ]; then
echo "skip_downloads=false" >> "$GITHUB_OUTPUT"
exit 0
fi
has_rclone=false
has_openlist=false
if [ -f "src-tauri/binaries/rclone" ] || [ -f "src-tauri/binaries/rclone-x86_64-unknown-linux-gnu" ]; then
has_rclone=true
fi
if [ -f "src-tauri/binaries/openlist" ] || [ -f "src-tauri/binaries/openlist-x86_64-unknown-linux-gnu" ]; then
has_openlist=true
fi
if [ "$has_rclone" = "true" ] && [ "$has_openlist" = "true" ]; then
echo "skip_downloads=true" >> "$GITHUB_OUTPUT"
else
echo "skip_downloads=false" >> "$GITHUB_OUTPUT"
echo "Cache hit but required binaries are missing; downloads remain enabled."
fi
- run: pnpm install --frozen-lockfile
- run: pnpm run pretauri-build
# 如果缓存命中,跳过下载
- name: Build Tauri (check only)
run: pnpm run tauri build --no-bundle
env:
RUST_BACKTRACE: 1
# Tauri 签名私钥
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
# Tauri 签名私钥密码(如果密钥已加密)
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
NETMOUNT_SKIP_BIN_DOWNLOADS: ${{ steps.resolve-skip-downloads.outputs.skip_downloads }}
# ========== Release 创建 ==========
create-release:
needs: [quick-checks, frontend-build, tauri-check]
if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && github.event.inputs.release == 'true')
permissions:
contents: write
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
release_id: ${{ steps.create-release.outputs.result }}
package_version: ${{ steps.get-version.outputs.package_version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- id: get-version
run: |
PACKAGE_VERSION=$(node -p "require('./package.json').version")
echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV
echo "package_version=$PACKAGE_VERSION" >> $GITHUB_OUTPUT
- id: create-release
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const tag = `v${process.env.PACKAGE_VERSION}`
try {
const { data } = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag
})
core.info(`Release exists for ${tag}, reusing id=${data.id}`)
return data.id
} catch {
core.info(`Creating new draft release for ${tag}`)
}
const { data } = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: tag,
target_commitish: context.sha,
name: `NetMount ${tag}`,
body: 'Take a look at the assets to download and install this app.',
draft: true,
prerelease: false
})
return data.id
# ========== 多平台构建 Tauri 应用(使用缓存优化)==========
build-tauri:
needs: [create-release]
if: needs.create-release.result == 'success'
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- platform: 'macos-latest'
target: 'aarch64-apple-darwin'
args: '--target aarch64-apple-darwin'
arch: 'aarch64'
- platform: 'macos-latest'
target: 'x86_64-apple-darwin'
args: '--target x86_64-apple-darwin'
arch: 'x86_64'
- platform: 'ubuntu-22.04'
target: 'x86_64-unknown-linux-gnu'
args: ''
arch: 'x86_64'
- platform: 'ubuntu-22.04-arm'
target: 'aarch64-unknown-linux-gnu'
args: ''
arch: 'aarch64'
- platform: 'windows-latest'
target: 'x86_64-pc-windows-msvc'
args: ''
arch: 'x86_64'
- platform: 'windows-11-arm'
target: 'aarch64-pc-windows-msvc'
args: ''
arch: 'aarch64'
runs-on: ${{ matrix.platform }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
# Rust 缓存 - 按目标平台分离缓存
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
key: ${{ matrix.target }}
cache-on-failure: true
# 缓存二进制文件
- name: Cache binaries
uses: actions/cache@v4
id: cache-binaries
with:
path: |
src-tauri/binaries/rclone
src-tauri/binaries/openlist
src-tauri/binaries/rclone-${{ matrix.target }}${{ contains(matrix.platform, 'windows') && '.exe' || '' }}
src-tauri/binaries/openlist-${{ matrix.target }}${{ contains(matrix.platform, 'windows') && '.exe' || '' }}
src-tauri/binaries/winfsp.msi
key: binaries-${{ matrix.target }}-${{ env.RCLONE_VERSION }}-${{ env.OPENLIST_VERSION }}
- name: Resolve skip-downloads flag
id: resolve-skip-downloads
shell: bash
run: |
set -euo pipefail
if [ "${{ steps.cache-binaries.outputs.cache-hit }}" != "true" ]; then
echo "skip_downloads=false" >> "$GITHUB_OUTPUT"
exit 0
fi
ext=""
if [ "${{ contains(matrix.platform, 'windows') }}" = "true" ]; then
ext=".exe"
fi
has_rclone=false
has_openlist=false
if [ -f "src-tauri/binaries/rclone${ext}" ] || [ -f "src-tauri/binaries/rclone-${{ matrix.target }}${ext}" ]; then
has_rclone=true
fi
if [ -f "src-tauri/binaries/openlist${ext}" ] || [ -f "src-tauri/binaries/openlist-${{ matrix.target }}${ext}" ]; then
has_openlist=true
fi
if [ "$has_rclone" = "true" ] && [ "$has_openlist" = "true" ]; then
echo "skip_downloads=true" >> "$GITHUB_OUTPUT"
else
echo "skip_downloads=false" >> "$GITHUB_OUTPUT"
echo "Cache hit but required binaries are missing for target ${{ matrix.target }}; downloads remain enabled."
fi
- name: Install Linux dependencies
if: contains(matrix.platform, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- run: pnpm install --frozen-lockfile
- run: pnpm run pretauri-build
# 如果二进制文件已缓存,跳过下载
- name: Build Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Tauri 签名私钥
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
# Tauri 签名私钥密码(如果密钥已加密)
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
# 使用缓存时跳过下载
NETMOUNT_SKIP_BIN_DOWNLOADS: ${{ steps.resolve-skip-downloads.outputs.skip_downloads }}
with:
releaseId: ${{ needs.create-release.outputs.release_id }}
tauriScript: pnpm tauri
args: ${{ matrix.args }}
# ========== 生成 Changelog ==========
generate-changelog:
needs: [create-release]
if: needs.create-release.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
changelog: ${{ steps.gen.outputs.changelog }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- run: git fetch --tags --force
- id: gen
env:
CURRENT_TAG: v${{ needs.create-release.outputs.package_version }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
node scripts/generate-changelog.mjs > changelog.md
cat changelog.md
{
echo 'changelog<<CHANGELOG_EOF'
cat changelog.md
echo ''
echo 'CHANGELOG_EOF'
} >> "$GITHUB_OUTPUT"
# ========== 发布 Release ==========
publish-release:
needs: [create-release, build-tauri, generate-changelog]
if: always() && needs.create-release.result == 'success' && needs.build-tauri.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
steps:
- uses: actions/github-script@v7
env:
RELEASE_ID: ${{ needs.create-release.outputs.release_id }}
CHANGELOG: ${{ needs.generate-changelog.outputs.changelog }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const changelog = process.env.CHANGELOG || '';
const prBody = context.payload.pull_request?.body || '';
await github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: process.env.RELEASE_ID,
draft: false,
prerelease: false,
body: `${changelog}\n\n${prBody}`.trim()
});