Refine MCP Client architecture diagram #873
Workflow file for this run
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
| # Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. | |
| # SPDX-License-Identifier: MIT | |
| name: Test Electron Framework and Apps | |
| on: | |
| workflow_call: | |
| push: | |
| branches: ["main"] | |
| pull_request: | |
| branches: ["main"] | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| merge_group: | |
| workflow_dispatch: | |
| # Cancel in-progress runs when a new run is triggered | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| test-electron-framework: | |
| name: Test Electron Framework | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| uses: ./.github/actions/free-disk-space | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: tests/electron/package.json | |
| - name: Install test dependencies | |
| run: | | |
| cd tests/electron | |
| npm ci | |
| - name: Run Electron framework unit tests | |
| run: | | |
| cd tests/electron | |
| npm test | |
| - name: Upload test coverage | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: electron-framework-coverage | |
| path: tests/electron/coverage/ | |
| retention-days: 7 | |
| test-apps-integration: | |
| name: Test Apps Integration | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| uses: ./.github/actions/free-disk-space | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: tests/electron/package.json | |
| - name: Install test dependencies | |
| run: | | |
| cd tests/electron | |
| npm ci | |
| - name: Run app integration tests | |
| run: | | |
| cd tests/electron | |
| # Run structure tests for all apps and framework integration | |
| npm test -- test_electron_jira_app.js test_electron_example_app.js test_electron_emr_dashboard.js test_electron_framework_integration.js | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: apps-integration-test-results | |
| path: tests/electron/coverage/ | |
| retention-days: 7 | |
| test-apps-functional: | |
| name: Test Apps Functionality | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| uses: ./.github/actions/free-disk-space | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Install test dependencies | |
| run: | | |
| cd tests/electron | |
| npm ci | |
| - name: Run functional tests (builds and installs apps) | |
| run: | | |
| cd tests/electron | |
| # These tests actually install deps and try to build the apps | |
| npm test -- test_electron_functional.js --verbose | |
| timeout-minutes: 15 | |
| - name: Upload functional test results | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: apps-functional-test-results | |
| path: tests/electron/coverage/ | |
| retention-days: 7 | |
| test-apps-build: | |
| name: Test Apps Build (${{ matrix.app.name }}) | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| app: | |
| - name: jira | |
| path: src/gaia/apps/jira/webui | |
| has_package_script: true | |
| - name: example | |
| path: src/gaia/apps/example/webui | |
| has_package_script: true | |
| - name: emr-dashboard | |
| path: src/gaia/agents/emr/dashboard/electron | |
| has_package_script: false | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| uses: ./.github/actions/free-disk-space | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: ${{ matrix.app.path }}/package-lock.json | |
| - name: Install app dependencies | |
| run: | | |
| cd ${{ matrix.app.path }} | |
| npm ci --prefix . | |
| - name: Check for high-severity vulnerabilities | |
| run: | | |
| cd ${{ matrix.app.path }} | |
| # Allow command to fail but capture output | |
| npm audit --json > audit-results.json || true | |
| # Check for high/critical vulnerabilities | |
| HIGH=$(cat audit-results.json | jq -r '.metadata.vulnerabilities.high // 0') | |
| CRITICAL=$(cat audit-results.json | jq -r '.metadata.vulnerabilities.critical // 0') | |
| echo "High severity vulnerabilities: $HIGH" | |
| echo "Critical severity vulnerabilities: $CRITICAL" | |
| if [ "$HIGH" -gt 0 ] || [ "$CRITICAL" -gt 0 ]; then | |
| echo "::warning::Found $HIGH high and $CRITICAL critical vulnerabilities in ${{ matrix.app.name }}" | |
| cat audit-results.json | jq '.vulnerabilities' | |
| fi | |
| continue-on-error: true | |
| - name: Verify package scripts | |
| run: | | |
| cd ${{ matrix.app.path }} | |
| npm run --if-present lint || echo "No lint script" | |
| - name: Test app packaging (Linux) | |
| if: matrix.app.has_package_script | |
| run: | | |
| cd ${{ matrix.app.path }} | |
| # Package the app for Linux - validates dependencies work together | |
| # Note: This runs on ubuntu-latest, so only Linux packaging is tested. | |
| # Windows/macOS packaging is tested separately in build-electron-apps.yml | |
| # BLOCKING: Packaging failures indicate broken dependencies from Dependabot updates | |
| npm run package | |
| env: | |
| # Electron Forge will auto-detect Linux platform | |
| DEBUG: electron-forge:* | |
| - name: Verify build artifacts exist | |
| if: matrix.app.has_package_script | |
| run: | | |
| cd ${{ matrix.app.path }} | |
| if [ -d "out" ]; then | |
| echo "✅ Build artifacts created successfully" | |
| ls -R out/ | head -20 | |
| else | |
| echo "❌ No build artifacts found - packaging failed" | |
| echo "This indicates a dependency compatibility issue from the update." | |
| exit 1 | |
| fi | |
| - name: Validate Electron can be required (EMR Dashboard) | |
| if: matrix.app.name == 'emr-dashboard' | |
| run: | | |
| cd ${{ matrix.app.path }} | |
| # Verify Electron module can be loaded | |
| node -e "require('electron'); console.log('✅ Electron module loads successfully')" | |
| dependency-audit: | |
| name: Audit Dependencies | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') | |
| strategy: | |
| matrix: | |
| package: | |
| - name: electron-framework | |
| path: src/gaia/electron | |
| - name: jira-app | |
| path: src/gaia/apps/jira/webui | |
| - name: example-app | |
| path: src/gaia/apps/example/webui | |
| - name: emr-dashboard | |
| path: src/gaia/agents/emr/dashboard/electron | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| uses: ./.github/actions/free-disk-space | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| - name: Audit ${{ matrix.package.name }} dependencies | |
| run: | | |
| cd ${{ matrix.package.path }} | |
| npm audit --audit-level=high || true | |
| npm audit --json > audit-results.json || true | |
| continue-on-error: true | |
| - name: Upload audit results | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: audit-${{ matrix.package.name }} | |
| path: ${{ matrix.package.path }}/audit-results.json | |
| retention-days: 30 | |
| # Headless E2E tests - actually launches Electron apps to catch runtime breakage | |
| test-electron-runtime: | |
| name: Test Electron Runtime (${{ matrix.app.name }}) | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| app: | |
| - name: jira | |
| path: src/gaia/apps/jira/webui | |
| - name: example | |
| path: src/gaia/apps/example/webui | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| uses: ./.github/actions/free-disk-space | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: ${{ matrix.app.path }}/package-lock.json | |
| - name: Install app dependencies | |
| run: | | |
| cd ${{ matrix.app.path }} | |
| npm ci --prefix . | |
| - name: Install xvfb for headless display | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y xvfb | |
| - name: Test Electron app launches (headless) | |
| run: | | |
| cd ${{ matrix.app.path }} | |
| # Start xvfb for headless display | |
| export DISPLAY=:99 | |
| Xvfb :99 -screen 0 1024x768x24 & | |
| sleep 2 | |
| # Create a test script in app directory so it can find electron in node_modules | |
| cat > test-launch.js << 'EOF' | |
| const { spawn } = require('child_process'); | |
| const path = require('path'); | |
| /** | |
| * Electron App Launch Test | |
| * | |
| * This script launches an Electron app in headless mode and verifies | |
| * it starts without crashing. Used to catch breaking changes from | |
| * Dependabot dependency updates. | |
| * | |
| * Timeout Configuration: | |
| * - ELECTRON_LAUNCH_TIMEOUT_MS: Time to wait for app initialization (default: 5000ms) | |
| * - 5 seconds is sufficient for most GAIA apps which have minimal startup logic | |
| * - Apps with heavy initialization (database, network) may need longer timeouts | |
| * - The timeout only needs to be long enough to detect startup crashes | |
| */ | |
| const LAUNCH_TIMEOUT_MS = parseInt(process.env.ELECTRON_LAUNCH_TIMEOUT_MS || '5000', 10); | |
| console.log('Starting Electron app launch test...'); | |
| console.log(`Launch timeout: ${LAUNCH_TIMEOUT_MS}ms`); | |
| const electronPath = require('electron'); | |
| const appPath = process.cwd(); | |
| // Get the main entry point from package.json | |
| const pkg = require(path.join(appPath, 'package.json')); | |
| const mainFile = pkg.main || 'src/main.js'; | |
| console.log(`Launching: ${electronPath} ${mainFile}`); | |
| const proc = spawn(electronPath, [mainFile], { | |
| cwd: appPath, | |
| env: { | |
| ...process.env, | |
| ELECTRON_DISABLE_SANDBOX: '1', | |
| ELECTRON_NO_ATTACH_CONSOLE: '1', | |
| GAIA_TEST_MODE: '1' | |
| }, | |
| stdio: ['ignore', 'pipe', 'pipe'] | |
| }); | |
| let stdout = ''; | |
| let stderr = ''; | |
| let crashed = false; | |
| proc.stdout.on('data', (data) => { | |
| stdout += data.toString(); | |
| console.log('[stdout]', data.toString().trim()); | |
| }); | |
| proc.stderr.on('data', (data) => { | |
| stderr += data.toString(); | |
| const msg = data.toString(); | |
| console.log('[stderr]', msg.trim()); | |
| // Check for crash indicators | |
| if (msg.includes('crashed') || msg.includes('SIGSEGV') || msg.includes('SIGABRT')) { | |
| crashed = true; | |
| } | |
| }); | |
| // Wait for app to initialize, then terminate | |
| // This timeout is configurable via ELECTRON_LAUNCH_TIMEOUT_MS | |
| setTimeout(() => { | |
| if (!crashed) { | |
| console.log('✅ App launched successfully without crashing'); | |
| proc.kill('SIGTERM'); | |
| process.exit(0); | |
| } else { | |
| console.log('❌ App crashed during startup'); | |
| proc.kill('SIGTERM'); | |
| process.exit(1); | |
| } | |
| }, LAUNCH_TIMEOUT_MS); | |
| proc.on('error', (err) => { | |
| console.log('❌ Failed to start Electron:', err.message); | |
| process.exit(1); | |
| }); | |
| proc.on('exit', (code, signal) => { | |
| if (code !== null && code !== 0 && !signal) { | |
| console.log(`❌ Electron exited with code ${code}`); | |
| process.exit(1); | |
| } | |
| }); | |
| EOF | |
| node test-launch.js | |
| rm test-launch.js | |
| timeout-minutes: 3 | |
| test-summary: | |
| name: Test Summary | |
| runs-on: ubuntu-latest | |
| if: always() | |
| needs: [test-electron-framework, test-apps-integration, test-apps-functional, test-apps-build, dependency-audit, test-electron-runtime] | |
| steps: | |
| - name: Check test results | |
| run: | | |
| echo "==========================================" | |
| echo " ELECTRON TEST RESULTS " | |
| echo "==========================================" | |
| echo "" | |
| echo "Test Electron Framework: ${{ needs.test-electron-framework.result }}" | |
| echo "Test Apps Integration: ${{ needs.test-apps-integration.result }}" | |
| echo "Test Apps Functionality: ${{ needs.test-apps-functional.result }}" | |
| echo "Test Apps Build: ${{ needs.test-apps-build.result }}" | |
| echo "Test Electron Runtime: ${{ needs.test-electron-runtime.result }}" | |
| echo "Dependency Audit: ${{ needs.dependency-audit.result }}" | |
| echo "" | |
| FAILED=false | |
| if [[ "${{ needs.test-electron-framework.result }}" == "failure" ]]; then | |
| echo "❌ FAILED: Electron Framework tests" | |
| FAILED=true | |
| fi | |
| if [[ "${{ needs.test-apps-integration.result }}" == "failure" ]]; then | |
| echo "❌ FAILED: Apps Integration tests" | |
| FAILED=true | |
| fi | |
| if [[ "${{ needs.test-apps-functional.result }}" == "failure" ]]; then | |
| echo "❌ FAILED: Apps Functional tests" | |
| FAILED=true | |
| fi | |
| if [[ "${{ needs.test-apps-build.result }}" == "failure" ]]; then | |
| echo "❌ FAILED: Apps Build tests - dependencies may be broken" | |
| FAILED=true | |
| fi | |
| if [[ "${{ needs.test-electron-runtime.result }}" == "failure" ]]; then | |
| echo "❌ FAILED: Electron Runtime tests - apps crash on startup" | |
| FAILED=true | |
| fi | |
| echo "" | |
| if [[ "$FAILED" == "true" ]]; then | |
| echo "==========================================" | |
| echo " ❌ SOME ELECTRON TESTS FAILED " | |
| echo "==========================================" | |
| echo "" | |
| echo "If this is a Dependabot PR, the dependency update" | |
| echo "may have introduced breaking changes." | |
| exit 1 | |
| else | |
| echo "==========================================" | |
| echo " ✅ ALL ELECTRON TESTS PASSED " | |
| echo "==========================================" | |
| fi |