Skip to content

fix(weixin): clear poll interval on timeout to prevent accessing destroyed BrowserWindow #1650

fix(weixin): clear poll interval on timeout to prevent accessing destroyed BrowserWindow

fix(weixin): clear poll interval on timeout to prevent accessing destroyed BrowserWindow #1650

Workflow file for this run

name: '✅ PR Checks'
on:
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to run checks on'
required: true
type: string
skip_build_test:
description: 'Skip the build test job (saves ~45 min)'
type: boolean
required: false
default: false
pull_request:
types: [opened, synchronize, edited, closed]
branches: [main, dev]
paths-ignore:
- '**/*.md'
- 'docs/**'
- '.vscode/**'
- '.github/ISSUE_TEMPLATE/**'
concurrency:
group: pr-checks-${{ github.event.pull_request.number || inputs.pr_number }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
issues: read
checks: write
id-token: write
env:
BUN_INSTALL_REGISTRY: 'https://registry.npmjs.org/'
jobs:
# Cancel in-progress runs when PR is closed (merged or manually closed)
cancel-if-closed:
name: Cancel if PR closed
if: github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- run: echo "PR closed, cancelling in-progress runs via concurrency."
# Job 1: Code quality checks (TypeScript, Oxlint, Oxfmt)
code-quality:
name: Code Quality
if: github.event_name == 'workflow_dispatch' || (github.event.action != 'edited' && github.event.action != 'closed')
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve PR context
uses: ./.github/actions/checkout-pr
with:
pr_number: ${{ inputs.pr_number }}
github_token: ${{ github.token }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --no-frozen-lockfile
- name: Run postinstall
run: npm run postinstall || true
- name: Install prek
run: npm install -g @j178/prek
- name: Run prek checks
run: prek run --from-ref origin/${{ env.PR_BASE_REF }} --to-ref HEAD
# Job 2: Unit tests across all platforms
unit-tests:
name: Unit Tests (${{ matrix.os }})
if: github.event_name == 'workflow_dispatch' || (github.event.action != 'edited' && github.event.action != 'closed')
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-14, windows-2022]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Resolve PR context
uses: ./.github/actions/checkout-pr
with:
pr_number: ${{ inputs.pr_number }}
github_token: ${{ github.token }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --no-frozen-lockfile
- name: Run postinstall
run: npm run postinstall || true
- name: Run extension system tests
run: bunx vitest run
# Job 3: Coverage test (Linux only)
coverage-tests:
name: Coverage Test
if: github.event_name == 'workflow_dispatch' || (github.event.action != 'edited' && github.event.action != 'closed')
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Resolve PR context
uses: ./.github/actions/checkout-pr
with:
pr_number: ${{ inputs.pr_number }}
github_token: ${{ github.token }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --no-frozen-lockfile
- name: Run postinstall
run: npm run postinstall || true
- name: Run coverage tests
id: coverage
continue-on-error: true
run: bun run test:coverage
- name: Upload coverage to Codecov (Linux coverage only)
if: always() && hashFiles('coverage/lcov.info') != '' && github.repository == 'iOfficeAI/AionUi'
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
use_oidc: ${{ secrets.CODECOV_TOKEN == '' }}
files: coverage/lcov.info
fail_ci_if_error: false
verbose: true
- name: Skip Codecov upload when preconditions are not met
if: always() && hashFiles('coverage/lcov.info') != '' && github.repository != 'iOfficeAI/AionUi'
shell: bash
run: |
echo 'Skipping Codecov upload due to unmet preconditions.'
echo "Repository: ${{ github.repository }}"
- name: Coverage result summary
if: always()
shell: bash
run: |
if [ "${{ steps.coverage.outcome }}" = "failure" ]; then
echo "::warning::Coverage tests failed (non-blocking). Check logs for failed test details."
echo "## Coverage check (non-blocking warning)" >> $GITHUB_STEP_SUMMARY
echo "Coverage command failed in this run. Please review test failures in logs." >> $GITHUB_STEP_SUMMARY
if [ -f coverage/lcov.info ]; then
echo "lcov.info exists, Codecov upload can still run." >> $GITHUB_STEP_SUMMARY
else
echo "lcov.info not found, Codecov upload was skipped." >> $GITHUB_STEP_SUMMARY
fi
else
echo "## Coverage check" >> $GITHUB_STEP_SUMMARY
echo "Coverage command passed." >> $GITHUB_STEP_SUMMARY
if [ -f coverage/lcov.info ]; then
echo "Uploaded Linux (ubuntu-latest) coverage to Codecov." >> $GITHUB_STEP_SUMMARY
else
echo "Coverage passed but lcov.info is missing; Codecov upload was skipped." >> $GITHUB_STEP_SUMMARY
fi
fi
- name: Upload coverage artifacts
if: always() && hashFiles('coverage/lcov.info') != ''
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
if-no-files-found: warn
i18n-check:
name: I18n Check
if: github.event_name == 'workflow_dispatch' || (github.event.action != 'edited' && github.event.action != 'closed')
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --no-frozen-lockfile
- name: Run postinstall
run: npm run postinstall || true
- name: Run i18n validation (warning-only)
id: i18n
shell: bash
run: |
set -o pipefail
node scripts/check-i18n.js 2>&1 | tee i18n-check.log
- name: Publish i18n summary
if: always()
shell: bash
run: |
echo "## i18n validation" >> $GITHUB_STEP_SUMMARY
if grep -q "⚠️" i18n-check.log; then
echo "Missing/incomplete translations found. Please review warnings below." >> $GITHUB_STEP_SUMMARY
else
echo "No i18n warnings detected." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "<details><summary>i18n log</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```text' >> $GITHUB_STEP_SUMMARY
cat i18n-check.log >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
# Job 4: Build test across all platforms (parallel with code-quality and unit-tests)
build-test:
name: Build Test (${{ matrix.platform }})
if: github.event.action != 'edited' && github.event.action != 'closed' && inputs.skip_build_test != true
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- platform: 'macos-arm64'
os: 'macos-14'
arch: 'arm64'
target: '--mac'
build_args: '--mac --arm64'
unpacked_dir: 'mac-arm64'
- platform: 'macos-x64'
os: 'macos-14'
arch: 'x64'
target: '--mac'
build_args: '--mac --x64'
unpacked_dir: 'mac'
- platform: 'windows-x64'
os: 'windows-2022'
arch: 'x64'
target: '--win'
build_args: '--win --x64'
unpacked_dir: 'win-unpacked'
- platform: 'windows-arm64'
os: 'windows-2022'
arch: 'arm64'
target: '--win'
build_args: '--win --arm64'
unpacked_dir: 'win-arm64-unpacked'
- platform: 'linux'
os: 'ubuntu-latest'
arch: 'x64'
target: '--linux'
build_args: '--linux --x64'
unpacked_dir: 'linux-unpacked'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Setup just
uses: extractions/setup-just@v2
- name: Install dependencies
run: bun install --no-frozen-lockfile
- name: Run postinstall
run: npm run postinstall || true
- name: Install Linux dependencies
if: matrix.platform == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y build-essential python3 python3-pip pkg-config libsqlite3-dev fakeroot dpkg-dev rpm libnss3-dev libatk-bridge2.0-dev libdrm2 libxkbcommon-dev libxss1 libatspi2.0-dev libgtk-3-dev libxrandr2 libasound2-dev
- name: Get Electron version
id: electron-version
shell: bash
run: |
ELECTRON_VERSION=$(node -p "require('./package.json').devDependencies.electron.replace(/[\^~]/g, '')")
echo "version=$ELECTRON_VERSION" >> $GITHUB_OUTPUT
echo "Electron version: $ELECTRON_VERSION"
# Restore Electron/Electron-Builder caches before install-app-deps
- name: Cache Electron artifacts
id: electron-cache
uses: actions/cache@v4
with:
path: |
${{ runner.temp }}/.cache/electron
${{ runner.temp }}/.cache/electron-builder
~/.cache/electron
~/.cache/electron-builder
${{ env.LOCALAPPDATA }}\electron-builder\Cache
key: electron-cache-${{ matrix.platform }}-${{ matrix.arch }}-${{ hashFiles('package.json', 'bun.lock') }}
restore-keys: |
electron-cache-${{ matrix.platform }}-${{ matrix.arch }}-
electron-cache-${{ matrix.platform }}-
- name: Cache status
run: echo "electron-cache-hit=${{ steps.electron-cache.outputs.cache-hit }}"
- name: Rebuild native modules for Electron (non-Windows)
if: "!startsWith(matrix.platform, 'windows')"
run: bunx electron-builder install-app-deps
env:
npm_config_runtime: electron
npm_config_disturl: https://electronjs.org/headers
ELECTRON_CACHE: ${{ runner.temp }}/.cache/electron
ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/.cache/electron-builder
- name: Setup MSBuild (Windows only)
if: startsWith(matrix.platform, 'windows')
uses: microsoft/setup-msbuild@v2
with:
vs-version: '17.0'
- name: Build test (Windows x64)
if: matrix.platform == 'windows-x64'
shell: pwsh
run: |
Write-Host "=========================================="
Write-Host "BUILD TEST: ${{ matrix.platform }}"
Write-Host "=========================================="
just build-win-x64
Write-Host "✓Build test passed for ${{ matrix.platform }}"
env:
NODE_OPTIONS: '--max-old-space-size=8192'
MSVS_VERSION: 2022
GYP_MSVS_VERSION: 2022
WindowsTargetPlatformVersion: 10.0.19041.0
CI: true
- name: Build test (Windows arm64)
if: matrix.platform == 'windows-arm64'
shell: pwsh
run: |
Write-Host "=========================================="
Write-Host "BUILD TEST: ${{ matrix.platform }}"
Write-Host "=========================================="
just build-win-arm64
Write-Host "✓Build test passed for ${{ matrix.platform }}"
env:
NODE_OPTIONS: '--max-old-space-size=8192'
MSVS_VERSION: 2022
GYP_MSVS_VERSION: 2022
WindowsTargetPlatformVersion: 10.0.19041.0
CI: true
- name: Build test (non-Windows)
if: "!startsWith(matrix.platform, 'windows')"
shell: bash
run: |
echo "=========================================="
echo "BUILD TEST: ${{ matrix.platform }}"
echo "=========================================="
node scripts/build-with-builder.js auto ${{ matrix.build_args }}
echo "✓Build test passed for ${{ matrix.platform }}"
env:
NODE_OPTIONS: '--max-old-space-size=8192'
npm_config_arch: ${{ matrix.arch }}
npm_config_target_arch: ${{ matrix.arch }}
npm_config_runtime: electron
npm_config_target: ${{ steps.electron-version.outputs.version }}
npm_config_disturl: https://electronjs.org/headers
CI: true
- name: Verify packaged bundled bun assets
run: bun run test:packaged:bun
- name: Verify build artifacts exist
shell: bash
run: |
echo "=========================================="
echo "VERIFY ARTIFACTS: ${{ matrix.platform }}"
echo "=========================================="
ls -lah out || true
case "${{ matrix.platform }}" in
windows-*)
ls out/*.exe out/*latest*.yml >/dev/null
;;
macos-*)
ls out/*.dmg out/*.zip out/*latest*.yml >/dev/null
;;
linux)
ls out/*.deb out/*latest*.yml >/dev/null
;;
esac
- name: Silent install smoke test (Windows x64)
if: matrix.platform == 'windows-x64'
shell: pwsh
run: |
Write-Host "=========================================="
Write-Host "SMOKE INSTALL: windows-x64"
Write-Host "=========================================="
$installer = Get-ChildItem -Path out -Filter "AionUi-*-win-*.exe" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $installer) {
throw "No Windows installer found in out/"
}
Write-Host "Using installer: $($installer.FullName)"
Start-Process -FilePath $installer.FullName -ArgumentList '/S' -Wait -NoNewWindow
$candidates = @(
"$env:LOCALAPPDATA\\Programs\\AionUi\\AionUi.exe",
"$env:ProgramFiles\\AionUi\\AionUi.exe",
"$env:ProgramFiles(x86)\\AionUi\\AionUi.exe"
)
$installedExe = $candidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $installedExe) {
throw "Silent install finished but app executable not found in expected locations"
}
Write-Host "Installed executable: $installedExe"
- name: Skip executable smoke for Windows arm64 cross build
if: matrix.platform == 'windows-arm64'
shell: pwsh
run: |
Write-Host "Skipping runtime install smoke for windows-arm64: runner is windows-x64, cannot reliably execute arm64 installer."
Get-ChildItem -Path out -Filter "AionUi-*-win-*.exe" | Format-Table Name, Length
- name: Install smoke test (macOS arm64)
if: matrix.platform == 'macos-arm64'
shell: bash
run: |
set -euo pipefail
echo "=========================================="
echo "SMOKE INSTALL: macos-arm64"
echo "=========================================="
DMG_FILE=$(ls out/*.dmg | head -n 1)
MOUNT_POINT="/tmp/aionui-smoke-mount"
APP_DIR="/tmp/aionui-smoke-app"
rm -rf "$MOUNT_POINT" "$APP_DIR"
mkdir -p "$MOUNT_POINT" "$APP_DIR"
hdiutil attach "$DMG_FILE" -nobrowse -mountpoint "$MOUNT_POINT"
cp -R "$MOUNT_POINT"/*.app "$APP_DIR"/
hdiutil detach "$MOUNT_POINT"
APP_PATH=$(ls -d "$APP_DIR"/*.app | head -n 1)
APP_BIN="$APP_PATH/Contents/MacOS/AionUi"
test -x "$APP_BIN"
"$APP_BIN" --version || true
- name: Skip executable smoke for macOS x64 cross build
if: matrix.platform == 'macos-x64'
shell: bash
run: |
echo "Skipping runtime launch smoke for macos-x64 cross build on arm64 runner."
ls -lah out/*.dmg out/*.zip
- name: Install smoke test (Linux)
if: matrix.platform == 'linux'
shell: bash
run: |
set -euo pipefail
echo "=========================================="
echo "SMOKE INSTALL: linux"
echo "=========================================="
DEB_FILE=$(ls out/*.deb | head -n 1)
PKG_NAME=$(dpkg-deb -f "$DEB_FILE" Package)
sudo dpkg -i "$DEB_FILE" || sudo apt-get install -f -y
INSTALLED_BIN=$(dpkg -L "$PKG_NAME" | grep -Ei '/(bin|opt)/.*(aionui)$' | head -n 1 || true)
if [ -z "$INSTALLED_BIN" ]; then
echo "Package files:"
dpkg -L "$PKG_NAME" | head -n 50
echo "No installed executable path matched expected pattern"
exit 1
fi
test -x "$INSTALLED_BIN"
# Job 5: Test release scripts (fast, no build required)
release-script-test:
name: Release Script Test
if: github.event.action != 'edited' && github.event.action != 'closed'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Create mock build artifacts
shell: bash
run: bash scripts/create-mock-release-artifacts.sh build-artifacts
- name: Run prepare-release-assets script
shell: bash
run: bash scripts/prepare-release-assets.sh build-artifacts release-assets
- name: Validate script output
shell: bash
run: bash scripts/verify-release-assets.sh release-assets