Skip to content

fix(ci): pin ubuntu-24.04 for Linux x64 builds #84

fix(ci): pin ubuntu-24.04 for Linux x64 builds

fix(ci): pin ubuntu-24.04 for Linux x64 builds #84

# Electron 桌面客户端 — 正式版本发布
# 与 Tauri 客户端完全分开:使用独立 tag 与独立 GitHub Release,不共用 v* tag。
#
# 触发方式:
# - 推送 tag 匹配 electron-v*(例如 electron-v0.4.0)→ 构建全平台安装包并创建 Release
#
# 支持平台:
# - macOS arm64(macos-latest)
# - macOS x64(macos-14-large)
# - Windows x64(msi)
# - Linux x64(AppImage + deb)
# - Linux arm64(AppImage + deb)
#
# 使用的 Secrets(可与 Tauri 共用 Apple 相关,也可单独配置):
# - GH_PAT(可选):创建 Release、上传资产(未配置时自动回退到 github.token,需 Contents: Write)
# - macOS 签名/公证(未配置时 Mac 构建为无签名包,用户打开会提示「已损坏」):
# - APPLE_CERTIFICATE(.p12 Base64)/ APPLE_CERTIFICATE_PASSWORD / APPLE_SIGNING_IDENTITY
# - 公证(否则 Gatekeeper 仍可能拦):APPLE_API_KEY(.p8 Base64)/ APPLE_API_KEY_ID / APPLE_API_ISSUER(或 APPLE_ISSUER_ID)
#
# 产物目录:crates/agent-electron-client/release/${version}/(由 package.json directories.output 决定)
name: Release Electron App
on:
push:
tags:
- "electron-v*"
permissions:
contents: write
jobs:
prepare:
name: Prepare release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get version from tag
id: version
run: |
VERSION="${GITHUB_REF_NAME#electron-v}"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Electron release version: $VERSION"
- name: Read release notes
id: notes
run: |
VERSION="${{ steps.version.outputs.version }}"
TAG="${{ github.ref_name }}"
NOTES_FILE="release-notes/${TAG}.md"
if [ -f "$NOTES_FILE" ]; then
echo "Using release notes from $NOTES_FILE"
cp "$NOTES_FILE" release_notes.txt
else
echo "Electron 桌面客户端 ${VERSION}。请在 Assets 中下载对应平台安装包。" > release_notes.txt
fi
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
run: |
TAG="${{ github.ref_name }}"
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" 2>/dev/null; then
echo "Release $TAG already exists"
else
gh release create "$TAG" \
--title "NuwaxBot (Electron) ${{ steps.version.outputs.version }}" \
--notes-file release_notes.txt \
--repo "$GITHUB_REPOSITORY"
echo "Created release $TAG"
fi
build-electron:
name: Build Electron (${{ matrix.platform }} ${{ matrix.arch }})
needs: prepare
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
arch: arm64
dist_cmd: dist:mac:arm64
- platform: macos-14-large
arch: x64
dist_cmd: dist:mac:x64
- platform: windows-latest
arch: x64
dist_cmd: dist:win
- platform: ubuntu-24.04
arch: x64
dist_cmd: dist:linux:x64
- platform: ubuntu-24.04-arm
arch: arm64
dist_cmd: dist:linux:arm64
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set version in package.json
shell: bash
working-directory: crates/agent-electron-client
run: |
VERSION="${{ needs.prepare.outputs.version }}"
npm pkg set version="$VERSION"
echo "package.json version: $(npm pkg get version)"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
cache-dependency-path: crates/agent-electron-client/package-lock.json
# Python 3.12+ 移除了 distutils,node-gyp(better-sqlite3 等 native 模块)依赖它;安装 setuptools 以提供 distutils
- name: Install setuptools for node-gyp (Python 3.12+)
shell: bash
run: python3 -m pip install setuptools --break-system-packages 2>/dev/null || python3 -m pip install setuptools
# Linux 构建 deb/rpm 包需要 rpm 工具;arm64 runner 需要系统 fpm(内置 fpm 仅 x86)
- name: Install Linux build tools
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y rpm ruby ruby-dev
sudo gem install fpm
echo "USE_SYSTEM_FPM=true" >> "$GITHUB_ENV"
# 使用 npm install 替代 npm ci:lock 仅含当前平台的可选依赖,其他平台(esbuild/rollup 等)会报 Missing,install 会在各 runner 上按需解析
- name: Install dependencies
working-directory: crates/agent-electron-client
run: npm install
# 为 Electron 内嵌 Node 重编 better-sqlite3,避免 Linux arm64 等平台运行时 "cannot open shared object file"
- name: Rebuild native modules for Electron
working-directory: crates/agent-electron-client
run: npm run electron-rebuild
- name: Run unit tests
working-directory: crates/agent-electron-client
run: npm run test:run
- name: Check import boundaries
working-directory: crates/agent-electron-client
run: npm run check:boundaries
# 缓存内置 Node.js(所有平台)、Git(仅 Windows)和 uv(所有平台)
- name: Cache bundled Node.js
uses: actions/cache@v4
with:
path: crates/agent-electron-client/resources/node
key: bundled-node-24-${{ matrix.platform }}-${{ hashFiles('crates/agent-electron-client/package.json') }}
- name: Cache bundled Git (Windows only)
if: matrix.platform == 'windows-latest'
uses: actions/cache@v4
with:
path: crates/agent-electron-client/resources/git
key: bundled-git-2.47.1-windows-${{ hashFiles('crates/agent-electron-client/package.json') }}
- name: Cache uv
uses: actions/cache@v4
with:
path: crates/agent-electron-client/resources/uv
key: bundled-uv-${{ matrix.platform }}-${{ hashFiles('crates/agent-electron-client/package.json') }}
- name: Prepare bundled resources (uv, node, git)
working-directory: crates/agent-electron-client
run: npm run prepare:all
timeout-minutes: 15
# ---- macOS:导入证书并配置公证(可选) ----
- name: Import Apple certificate (macOS)
if: runner.os == 'macOS'
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
if [ -z "${APPLE_CERTIFICATE:-}" ]; then
echo "APPLE_CERTIFICATE not set, signing will be skipped"
exit 0
fi
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=actions
echo -n "$APPLE_CERTIFICATE" | base64 --decode -o "$CERTIFICATE_PATH"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$CERTIFICATE_PATH" -k "$KEYCHAIN_PATH" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
security list-keychain -d user -s "$KEYCHAIN_PATH"
rm -f "$CERTIFICATE_PATH"
echo "CSC_KEYCHAIN=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
- name: Prepare Apple notarization key (macOS)
if: runner.os == 'macOS'
env:
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
run: |
if [ -z "${APPLE_API_KEY_BASE64:-}" ] || [ -z "${APPLE_API_KEY_ID:-}" ]; then
echo "Apple notarization secrets not set, notarization will be skipped"
exit 0
fi
ISSUER="${APPLE_API_ISSUER:-$APPLE_ISSUER_ID}"
if [ -z "$ISSUER" ]; then
echo "APPLE_API_ISSUER or APPLE_ISSUER_ID required for notarization"
exit 0
fi
mkdir -p "$RUNNER_TEMP/apple-api-key"
echo -n "$APPLE_API_KEY_BASE64" | base64 --decode -o "$RUNNER_TEMP/apple-api-key/AuthKey.p8"
echo "APPLE_API_KEY=$RUNNER_TEMP/apple-api-key/AuthKey.p8" >> "$GITHUB_ENV"
echo "APPLE_API_KEY_ID=$APPLE_API_KEY_ID" >> "$GITHUB_ENV"
echo "APPLE_API_ISSUER=$ISSUER" >> "$GITHUB_ENV"
# 若用户看到「已损坏」:请确认已配置 APPLE_CERTIFICATE / APPLE_SIGNING_IDENTITY 及公证用 APPLE_API_KEY 等,且构建日志中有签名与公证成功
- name: Clean previous build artifacts
shell: bash
working-directory: crates/agent-electron-client
run: |
rm -rf release node_modules/.cache
echo "Cleaned release and cache directories"
- name: Build Electron app
shell: bash
working-directory: crates/agent-electron-client
env:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
CSC_NAME: ${{ secrets.APPLE_SIGNING_IDENTITY }}
run: |
if [ "${{ runner.os }}" = "macOS" ] && [ -z "${{ secrets.APPLE_CERTIFICATE }}" ]; then
echo "No Apple certificate, building unsigned Mac package for ${{ matrix.arch }}"
npm run dist:mac:unsigned:${{ matrix.arch }} -- --publish never
else
npm run ${{ matrix.dist_cmd }} -- --publish never
fi
- name: Upload artifacts to Release
shell: bash
env:
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
run: |
TAG="${{ github.ref_name }}"
VERSION="${{ needs.prepare.outputs.version }}"
OUT_DIR="crates/agent-electron-client/release/${VERSION}"
if [ ! -d "$OUT_DIR" ]; then
echo "::error::Output directory not found: $OUT_DIR"
exit 1
fi
for f in "${OUT_DIR}"/*; do
[ -e "$f" ] || continue
[ -f "$f" ] || continue
echo "Uploading $(basename "$f") ..."
gh release upload "$TAG" "$f" --clobber --repo "$GITHUB_REPOSITORY"
done