Skip to content

chore: track tests in repository #180

chore: track tests in repository

chore: track tests in repository #180

Workflow file for this run

name: Build/Release
on:
push:
branches: [main]
tags:
- 'v*'
workflow_dispatch:
permissions:
contents: write # 允许创建 GitHub Release 和上传 Release 附件
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
build_args: --mac --arm64 --dir
- os: windows-signer
build_args: --win --x64
steps:
# Windows: 必须在 checkout 之前启用长路径,否则 git 操作可能被截断
- name: Enable Git long paths (Windows)
if: runner.os == 'Windows'
run: git config --global core.longpaths true
- name: Check out Git repository
uses: actions/checkout@v4
with:
# fetch-depth: 0 获取完整历史和所有标签
# electron-builder 在 publish 时需要访问 git 标签来确定版本和发布信息
fetch-depth: 0
submodules: false
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 24
- name: Verify tag matches package version (non-Windows)
if: startsWith(github.ref, 'refs/tags/v') && runner.os != 'Windows'
run: |
node -e 'const tag=process.env.GITHUB_REF_NAME||""; const version=require("./package.json").version; const expected="v"+version; if(tag!==expected){console.error("Tag/package version mismatch: tag="+tag+" package="+version+" expected="+expected); process.exit(1);} console.log("Tag/package version aligned: "+tag);'
- name: Verify tag matches package version (Windows)
if: startsWith(github.ref, 'refs/tags/v') && runner.os == 'Windows'
shell: powershell
run: |
$tag = if ($env:GITHUB_REF_NAME) { $env:GITHUB_REF_NAME } else { "" }
$packageJson = Get-Content -Raw -Path "package.json" | ConvertFrom-Json
$version = $packageJson.version
$expected = "v$version"
if ($tag -ne $expected) {
Write-Error ("Tag/package version mismatch: tag={0} package={1} expected={2}" -f $tag, $version, $expected)
exit 1
}
Write-Host ("Tag/package version aligned: {0}" -f $tag)
- name: Install dependencies
run: npm install
- name: Build renderer (Vite)
run: npm run build
- name: Compile Electron main process
run: npm run compile:electron
- name: Validate runtime dependency contracts
run: node --test tests/runtimeDependencyContract.test.mjs
- name: Install web-search skill dependencies
working-directory: SKILLs/web-search
run: npm install
- name: Build web-search skill
run: npm run build:skill:web-search
# Windows: 下载捆绑的 PortableGit
- name: Setup MinGit (Windows)
if: runner.os == 'Windows'
run: node scripts/setup-mingit.js
# 非 Windows: 创建 mingit 占位目录,避免 electron-builder 报"file source doesn't exist"错误
- name: Create mingit placeholder (non-Windows)
if: runner.os != 'Windows'
shell: bash
run: mkdir -p resources/mingit
# 根据平台启用签名配置(避免 Windows 缺少 bash)
- name: Configure code signing (macOS)
if: runner.os == 'macOS'
shell: bash
env:
SECRET_CSC_LINK: ${{ secrets.CSC_LINK }}
SECRET_CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
run: |
if [ -n "${SECRET_CSC_LINK}" ] && [ -n "${SECRET_CSC_KEY_PASSWORD}" ]; then
echo "CSC_LINK=${SECRET_CSC_LINK}" >> "$GITHUB_ENV"
echo "CSC_KEY_PASSWORD=${SECRET_CSC_KEY_PASSWORD}" >> "$GITHUB_ENV"
echo "CSC_IDENTITY_AUTO_DISCOVERY=true" >> "$GITHUB_ENV"
else
echo "CSC_IDENTITY_AUTO_DISCOVERY=false" >> "$GITHUB_ENV"
fi
- name: Configure code signing (Windows)
if: runner.os == 'Windows'
shell: powershell
env:
SECRET_WIN_CERT_SHA1: ${{ secrets.WIN_CERT_SHA1 }}
run: |
if ($env:SECRET_WIN_CERT_SHA1) {
"CSC_IDENTITY_AUTO_DISCOVERY=true" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
} else {
"CSC_IDENTITY_AUTO_DISCOVERY=false" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
}
- name: Prepare macOS signing keychain
if: runner.os == 'macOS'
shell: bash
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
run: |
if [ -z "$CSC_LINK" ] || [ -z "$CSC_KEY_PASSWORD" ]; then
echo "CSC_LINK/CSC_KEY_PASSWORD not configured for macOS signing." >&2
exit 1
fi
KEYCHAIN_PATH="$RUNNER_TEMP/idbots.keychain-db"
KEYCHAIN_PASSWORD="$(openssl rand -base64 32)"
CERT_PATH="$RUNNER_TEMP/idbots-signing.p12"
printf '%s' "$CSC_LINK" | base64 --decode > "$CERT_PATH" 2>/dev/null || printf '%s' "$CSC_LINK" | base64 -D > "$CERT_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 "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$CSC_KEY_PASSWORD" -T /usr/bin/codesign -T /usr/bin/productsign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH"
echo "CSC_KEYCHAIN=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
echo "CSC_KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> "$GITHUB_ENV"
- name: Build Electron app package (macOS)
if: runner.os == 'macOS'
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
IDBOTS_SKIP_NOTARIZE: 1
run: >
npx electron-builder ${{ matrix.build_args }}
--publish never
- name: Build Electron app package (Windows signed)
if: runner.os == 'Windows'
env:
WIN_CERT_SHA1: ${{ secrets.WIN_CERT_SHA1 }}
WIN_SIGNTOOL_PATH: ${{ vars.WIN_SIGNTOOL_PATH }}
WIN_TIMESTAMP_URL: ${{ vars.WIN_TIMESTAMP_URL }}
shell: powershell
run: |
if (-not $env:WIN_CERT_SHA1) {
Write-Error "Missing WIN_CERT_SHA1 secret for Windows code signing."
exit 1
}
npx electron-builder ${{ matrix.build_args }} --publish never
- name: Verify Windows signatures
if: runner.os == 'Windows'
env:
WIN_SIGNTOOL_PATH: ${{ vars.WIN_SIGNTOOL_PATH }}
shell: powershell
run: |
$signTool = if ($env:WIN_SIGNTOOL_PATH) { $env:WIN_SIGNTOOL_PATH } else { 'C:\sign\signtool.exe' }
if (-not (Test-Path $signTool)) {
Write-Error "signtool not found at: $signTool"
exit 1
}
$installer = Get-ChildItem -Path release -Filter 'IDBots Setup *.exe' | Select-Object -First 1
if (-not $installer) {
Write-Error "Windows installer not found in release directory."
exit 1
}
& $signTool verify /pa /all $installer.FullName
- name: Notarize app (macOS)
if: runner.os == 'macOS'
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
APP_ZIP_PATH="$(node scripts/release-artifact-paths.cjs zip)"
echo "Using APP_ZIP_PATH=$APP_ZIP_PATH"
ditto -c -k --keepParent "release/mac-arm64/IDBots.app" "$APP_ZIP_PATH"
xcrun notarytool submit "$APP_ZIP_PATH" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
--wait
xcrun stapler staple "release/mac-arm64/IDBots.app"
- name: Build DMG from prepackaged app (macOS)
if: runner.os == 'macOS'
env:
IDBOTS_SKIP_NOTARIZE: 1
PYTHON_PATH: /usr/bin/python3
run: |
PATH="$PWD/.tmp-bin:$PATH"
mkdir -p .tmp-bin
ln -sf /usr/bin/python3 .tmp-bin/python
npx electron-builder --mac dmg --arm64 --prepackaged "release/mac-arm64/IDBots.app" --publish never
- name: Codesign + notarize DMG (macOS)
if: runner.os == 'macOS'
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
MAC_CODESIGN_IDENTITY: ${{ secrets.MAC_CODESIGN_IDENTITY }}
CSC_KEYCHAIN: ${{ env.CSC_KEYCHAIN }}
CSC_KEYCHAIN_PASSWORD: ${{ env.CSC_KEYCHAIN_PASSWORD }}
run: |
if [ -z "$MAC_CODESIGN_IDENTITY" ]; then
echo "MAC_CODESIGN_IDENTITY is required for DMG signing." >&2
exit 1
fi
DMG_PATH="$(node scripts/release-artifact-paths.cjs dmg)"
echo "Using DMG_PATH=$DMG_PATH"
if [ -n "$CSC_KEYCHAIN" ] && [ -n "$CSC_KEYCHAIN_PASSWORD" ]; then
security unlock-keychain -p "$CSC_KEYCHAIN_PASSWORD" "$CSC_KEYCHAIN"
codesign --force --sign "$MAC_CODESIGN_IDENTITY" --keychain "$CSC_KEYCHAIN" "$DMG_PATH"
else
codesign --force --sign "$MAC_CODESIGN_IDENTITY" "$DMG_PATH"
fi
xcrun notarytool submit "$DMG_PATH" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
--wait
xcrun stapler staple "$DMG_PATH"
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.os }}
path: |
release/*.dmg
release/*.zip
release/*.AppImage
release/*.exe
release/*.blockmap
release/latest*.yml
if-no-files-found: error
retention-days: 7
release:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Download macOS artifacts
uses: actions/download-artifact@v4
with:
name: dist-macos-latest
path: release-assets/macos
- name: Download Windows artifacts
uses: actions/download-artifact@v4
with:
name: dist-windows-signer
path: release-assets/windows
- name: Verify release assets
run: |
echo "Collected assets:"
find release-assets -type f | sort
- name: Publish GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: IDBots ${{ github.ref_name }}
draft: false
prerelease: false
generate_release_notes: true
body: |
Cross-platform desktop installers for IDBots.
Included packages:
- macOS DMG package (.dmg, signed & notarized)
- Windows NSIS installer (.exe)
Notes:
- macOS DMG is signed and notarized in GitHub Actions.
fail_on_unmatched_files: true
files: |
release-assets/macos/*.dmg
release-assets/macos/*.blockmap
release-assets/macos/latest-mac.yml
release-assets/windows/*.exe
release-assets/windows/*.blockmap
release-assets/windows/latest*.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}