Skip to content

fix(ci): upgrade macOS runners to v15 #60

fix(ci): upgrade macOS runners to v15

fix(ci): upgrade macOS runners to v15 #60

Workflow file for this run

name: Build and Release
on:
push:
tags:
- "v*"
workflow_dispatch:
jobs:
build:
strategy:
matrix:
include:
# macOS: Use Intel runner for x64, Apple Silicon runner for arm64
# CRITICAL: macos-15-intel is x86_64, macos-15 is Apple Silicon (arm64)
# better-sqlite3 native module must be built on matching architecture
- os: macos-15-intel # Intel (x86_64) runner for x64 builds
platform: mac
arch: x64
- os: macos-15 # Apple Silicon (arm64) runner for arm64 builds
platform: mac
arch: arm64
- os: windows-latest
platform: win
arch: x64
- os: windows-latest
platform: win
arch: arm64
- os: ubuntu-latest
platform: linux
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
# 显式使用与 package.json 中一致的 pnpm 版本
version: 9.15.0
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-${{ matrix.arch || 'default' }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch || 'default' }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Rebuild native modules for Electron
shell: bash
run: |
# Rebuild native modules for the target architecture
# 为目标架构重建原生模块
# First, remove any prebuilt binaries to force rebuild from source
# 首先删除预编译二进制,强制从源码重新编译
rm -rf node_modules/better-sqlite3/build || true
rm -rf node_modules/better-sqlite3/prebuilds || true
if [ "${{ matrix.platform }}" = "mac" ]; then
echo "Rebuilding better-sqlite3 for macOS ${{ matrix.arch }}"
# Force rebuild with explicit architecture
npx electron-rebuild -f -w better-sqlite3 --arch ${{ matrix.arch }}
elif [ "${{ matrix.platform }}" = "win" ]; then
echo "Rebuilding better-sqlite3 for Windows ${{ matrix.arch }}"
npx electron-rebuild -f -w better-sqlite3 --arch ${{ matrix.arch }}
else
pnpm run rebuild
fi
- name: Verify native module architecture (macOS)
if: matrix.platform == 'mac'
shell: bash
run: |
# Verify the better-sqlite3 native module is built for correct architecture
# 验证 better-sqlite3 原生模块是否为正确架构编译
NATIVE_MODULE="node_modules/better-sqlite3/build/Release/better_sqlite3.node"
if [ -f "$NATIVE_MODULE" ]; then
echo "Checking architecture of $NATIVE_MODULE"
file "$NATIVE_MODULE"
ARCH=$(file "$NATIVE_MODULE" | grep -o 'arm64\|x86_64')
echo "Detected architecture: $ARCH"
if [ "${{ matrix.arch }}" = "arm64" ] && [ "$ARCH" != "arm64" ]; then
echo "ERROR: Expected arm64 but got $ARCH"
exit 1
elif [ "${{ matrix.arch }}" = "x64" ] && [ "$ARCH" != "x86_64" ]; then
echo "ERROR: Expected x86_64 but got $ARCH"
exit 1
fi
echo "Architecture verification passed!"
else
echo "WARNING: Native module not found at $NATIVE_MODULE"
fi
- name: Clean previous build artifacts
shell: bash
run: |
rm -rf dist || true
- name: Build for ${{ matrix.platform }} ${{ matrix.arch }}
shell: bash
run: |
if [ -n "${{ matrix.arch }}" ]; then
# Build specific architecture (Windows or macOS with arch specified)
# 构建特定架构(Windows 或指定了 arch 的 macOS)
if [ "${{ matrix.platform }}" = "win" ]; then
pnpm vite build && pnpm electron-builder --win --${{ matrix.arch }} --publish never
elif [ "${{ matrix.platform }}" = "mac" ]; then
pnpm vite build && pnpm electron-builder --mac --${{ matrix.arch }} --publish never
fi
else
pnpm electron:build:${{ matrix.platform }}
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Normalize Windows update metadata
if: matrix.platform == 'win' && matrix.arch != ''
shell: bash
run: |
# Generate appropriate yml files for each architecture
# 为每个架构生成适当的 yml 文件
if [ "${{ matrix.arch }}" = "arm64" ]; then
if [ -f "dist/latest.yml" ]; then
node scripts/fix-latest-yml.js --file dist/latest.yml --arch arm64
# Rename to latest-arm64.yml for arm64 builds
mv dist/latest.yml dist/latest-arm64.yml
echo "Renamed latest.yml to latest-arm64.yml for ARM64"
fi
else
# x64: Generate latest-x64.yml for backward compatibility with older versions
# x64: 生成 latest-x64.yml 以兼容旧版本
# electron-builder already generates correct filenames via artifactName
# electron-builder 已通过 artifactName 配置生成正确的文件名
if [ -f "dist/latest.yml" ]; then
node scripts/fix-latest-yml.js --file dist/latest.yml --arch x64
# Create latest-x64.yml as a copy for backward compatibility with old versions
# 创建 latest-x64.yml 副本以兼容旧版本
cp dist/latest.yml dist/latest-x64.yml
echo "Created latest-x64.yml for backward compatibility"
fi
fi
# Remove duplicate NSIS blockmap without arch (prevents release upload errors)
shopt -s nullglob
for file in dist/*.exe.blockmap; do
case "$file" in
*-x64.exe.blockmap|*-arm64.exe.blockmap) ;;
*) rm -f "$file" ;;
esac
done
- name: Normalize macOS update metadata
if: matrix.platform == 'mac' && matrix.arch != ''
shell: bash
run: |
# macOS: Rename latest-mac.yml to arch-specific versions
# macOS: 将 latest-mac.yml 重命名为架构特定版本
if [ -f "dist/latest-mac.yml" ]; then
if [ "${{ matrix.arch }}" = "arm64" ]; then
mv dist/latest-mac.yml dist/latest-mac-arm64.yml
echo "Renamed latest-mac.yml to latest-mac-arm64.yml"
else
# x64: Keep as latest-mac.yml (default for updater) and also create latest-mac-x64.yml
cp dist/latest-mac.yml dist/latest-mac-x64.yml
echo "Created latest-mac-x64.yml for x64"
fi
fi
- name: Remove debug files
shell: bash
run: |
# 删除 builder-debug.yml
find dist -name "builder-debug.yml" -delete || true
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.platform }}-${{ matrix.arch || 'default' }}-build
path: |
dist/*.dmg
dist/*.zip
dist/*-x64.exe
dist/*-arm64.exe
dist/*.AppImage
dist/*.deb
dist/*.blockmap
dist/latest*.yml
if-no-files-found: ignore
release:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Set release version
shell: bash
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV"
- name: List artifacts for debugging
shell: bash
run: |
echo "=== Artifact structure ==="
find artifacts -type f -name "*.yml" -o -name "*.exe" -o -name "*.dmg" | head -50
- name: Create Release with gh CLI
shell: bash
run: |
# 1. Prepare Release Notes
cat > release_notes.md <<EOF
## 📦 下载安装
| 平台 | 下载 |
|------|------|
| Windows | [![Windows x64](https://img.shields.io/badge/Windows_x64-0078D6?style=for-the-badge&logo=windows&logoColor=white)](https://github.com/legeling/PromptHub/releases/latest/download/PromptHub-Setup-${VERSION}-x64.exe) [![Windows arm64](https://img.shields.io/badge/Windows_arm64-0078D6?style=for-the-badge&logo=windows&logoColor=white)](https://github.com/legeling/PromptHub/releases/latest/download/PromptHub-Setup-${VERSION}-arm64.exe) |
| macOS | [![macOS Apple Silicon](https://img.shields.io/badge/macOS_Apple_Silicon-000000?style=for-the-badge&logo=apple&logoColor=white)](https://github.com/legeling/PromptHub/releases/latest/download/PromptHub-${VERSION}-arm64.dmg) [![macOS Intel](https://img.shields.io/badge/macOS_Intel-000000?style=for-the-badge&logo=apple&logoColor=white)](https://github.com/legeling/PromptHub/releases/latest/download/PromptHub-${VERSION}-x64.dmg) |
| Linux | [![Linux AppImage](https://img.shields.io/badge/Linux_AppImage-FCC624?style=for-the-badge&logo=linux&logoColor=black)](https://github.com/legeling/PromptHub/releases/latest/download/PromptHub-${VERSION}-x64.AppImage) [![Linux deb](https://img.shields.io/badge/Linux_deb-FCC624?style=for-the-badge&logo=linux&logoColor=black)](https://github.com/legeling/PromptHub/releases/latest/download/prompthub_${VERSION}_amd64.deb) |
### ⚠️ macOS 首次启动
如果提示"无法打开"或"已损坏",请在终端执行:
\`\`\`bash
sudo xattr -rd com.apple.quarantine /Applications/PromptHub.app
\`\`\`
---
## 🔄 本次更新
详细更新日志请查看 [更新日志](https://github.com/legeling/PromptHub#-更新日志)
EOF
# 2. Collect and Deduplicate Assets
echo "Collecting and deduplicating assets..."
mkdir -p release_assets
# Find all matching files and copy them to a single directory
# This handles duplicates by overwriting (latest version wins)
find artifacts -type f \( \
-name "PromptHub-*-x64.dmg" -o \
-name "PromptHub-*-arm64.dmg" -o \
-name "PromptHub-Setup-*-x64.exe" -o \
-name "PromptHub-Setup-*-arm64.exe" -o \
-name "PromptHub-*-x64.AppImage" -o \
-name "prompthub_*_amd64.deb" -o \
-name "PromptHub-*.zip" -o \
-name "latest*.yml" -o \
-name "*.blockmap" \
\) -exec cp -f {} release_assets/ \;
echo "Files to upload (deduplicated):"
ls -1 release_assets/
# 3. Clean up existing release
echo "Checking for existing release..."
gh release delete "${GITHUB_REF_NAME}" --yes --cleanup-tag || true
# IMPORTANT: Add delay to allow GitHub API to propagate the deletion
echo "Waiting for GitHub API consistency..."
sleep 10
# 4. Re-create Tag (Ensure it exists for the release)
git tag -f "${GITHUB_REF_NAME}"
git push origin "${GITHUB_REF_NAME}" --force
# 5. Create Release and Upload
echo "Creating release..."
gh release create "${GITHUB_REF_NAME}" \
release_assets/* \
--title "PromptHub ${VERSION}" \
--notes-file release_notes.md \
--draft=false \
--prerelease=${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }} \
--generate-notes
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}