chore: track tests in repository #180
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }} |