536 lines
18 KiB
YAML
536 lines
18 KiB
YAML
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, ready_for_review]
|
|
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 != 'closed' && github.event.pull_request.draft == false)
|
|
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
|
|
cache: true
|
|
|
|
- name: Install dependencies
|
|
run: bun install --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 != 'closed' && github.event.pull_request.draft == false)
|
|
runs-on: ${{ matrix.os }}
|
|
timeout-minutes: 20
|
|
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
|
|
cache: true
|
|
|
|
- name: Disable Windows Defender (Windows only)
|
|
if: matrix.os == 'windows-2022'
|
|
shell: pwsh
|
|
continue-on-error: true
|
|
run: |
|
|
try { Set-MpPreference -DisableRealtimeMonitoring $true } catch { Write-Host "::warning::Failed to disable real-time monitoring: $_" }
|
|
try { Add-MpPreference -ExclusionPath "${{ github.workspace }}" } catch { Write-Host "::warning::Failed to add exclusion path: $_" }
|
|
|
|
- name: Install dependencies
|
|
run: bun install --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 != 'closed' && github.event.pull_request.draft == false)
|
|
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
|
|
cache: true
|
|
|
|
- name: Install dependencies
|
|
run: bun install --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 != 'closed' && github.event.pull_request.draft == false)
|
|
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
|
|
cache: true
|
|
|
|
- name: Install dependencies
|
|
run: bun install --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 representative PR platforms (parallel with code-quality and unit-tests)
|
|
build-test:
|
|
name: Build Test (${{ matrix.platform }})
|
|
if: github.event.action != 'closed' && github.event.pull_request.draft == false && 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: 'windows-x64'
|
|
os: 'windows-2022'
|
|
arch: 'x64'
|
|
target: '--win'
|
|
build_args: '--win --x64'
|
|
unpacked_dir: 'win-unpacked'
|
|
- platform: 'linux-x64'
|
|
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
|
|
cache: true
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.12'
|
|
|
|
- name: Install dependencies
|
|
run: bun install --frozen-lockfile
|
|
|
|
- name: Run postinstall
|
|
run: npm run postinstall || true
|
|
|
|
- name: Install Linux dependencies
|
|
if: matrix.platform == 'linux-x64'
|
|
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 "=========================================="
|
|
node scripts/build-with-builder.js x64 --win --x64
|
|
Write-Host "✓Build test passed for ${{ matrix.platform }}"
|
|
env:
|
|
NODE_OPTIONS: '--max-old-space-size=8192'
|
|
npm_config_runtime: electron
|
|
npm_config_arch: x64
|
|
npm_config_target_arch: x64
|
|
npm_config_disturl: https://electronjs.org/headers
|
|
npm_config_build_from_source: true
|
|
MSVS_VERSION: 2022
|
|
GYP_MSVS_VERSION: 2022
|
|
WindowsTargetPlatformVersion: 10.0.19041.0
|
|
CI: true
|
|
GH_TOKEN: ${{ github.token }}
|
|
|
|
- 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
|
|
GH_TOKEN: ${{ github.token }}
|
|
|
|
- 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-x64)
|
|
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: 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: Install smoke test (Linux)
|
|
if: matrix.platform == 'linux-x64'
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
echo "=========================================="
|
|
echo "SMOKE INSTALL: linux-x64"
|
|
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 != 'closed' && github.event.pull_request.draft == false
|
|
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
|
|
env:
|
|
MOCK_VERSION: '1.0.0'
|
|
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
|