🪟 Windows Build Validation #1
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
| # This workflow validates the complete product build and tests on Windows to catch platform-specific issues | |
| name: 🪟 Windows Build Validation | |
| on: | |
| schedule: | |
| # Runs every day at 12:00 AM IST (which is 18:30 UTC of the previous day) | |
| - cron: '30 18 * * *' | |
| workflow_dispatch: # Allow manual triggering | |
| permissions: | |
| contents: read | |
| issues: write | |
| env: | |
| GOFLAGS: "-mod=readonly" | |
| PRODUCT_NAME: "Thunder" | |
| PRODUCT_NAME_LOWER: "thunder" | |
| jobs: | |
| dependency-guard: | |
| name: 🛡️ Dependency Guard | |
| uses: ./.github/workflows/dependency-guard.yml | |
| with: | |
| detection-mode: commit | |
| build-windows: | |
| name: 🛠️ Build Product (Windows) | |
| needs: dependency-guard | |
| if: ${{ always() && (needs.dependency-guard.result == 'success' || needs.dependency-guard.result == 'skipped') }} | |
| runs-on: windows-latest | |
| steps: | |
| - name: 📥 Checkout Code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: ⚙️ Set up Go Environment | |
| uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 | |
| with: | |
| go-version-file: backend/go.mod | |
| cache: true | |
| - name: ⚙️ Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 'lts/*' | |
| - name: 📦 Install pnpm | |
| uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0 | |
| with: | |
| version: 'latest' | |
| run_install: false | |
| - name: 🗄️ Get pnpm store directory | |
| shell: pwsh | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path --silent)" >> $env:GITHUB_ENV | |
| - name: 🗄️ Setup pnpm cache | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: 📦 Install SQLite3 | |
| shell: pwsh | |
| run: | | |
| choco install sqlite -y | |
| # Add only the sqlite3 directory to PATH for subsequent steps | |
| $sqlitePath = (Get-Command sqlite3 -ErrorAction SilentlyContinue).Source | |
| if (-not $sqlitePath) { | |
| # Refresh PATH from registry to find newly installed sqlite3 | |
| $machinePath = [System.Environment]::GetEnvironmentVariable("Path","Machine") | |
| $userPath = [System.Environment]::GetEnvironmentVariable("Path","User") | |
| $env:Path = "$machinePath;$userPath" | |
| $sqlitePath = (Get-Command sqlite3 -ErrorAction SilentlyContinue).Source | |
| if (-not $sqlitePath) { | |
| Write-Host "❌ sqlite3 not found after PATH refresh. Installation may have failed." | |
| exit 1 | |
| } | |
| } | |
| $sqliteDir = Split-Path -Parent $sqlitePath | |
| echo "$sqliteDir" >> $env:GITHUB_PATH | |
| sqlite3 --version | |
| - name: 📦 Install Backend Dependencies | |
| shell: pwsh | |
| run: | | |
| Set-Location backend | |
| go mod download | |
| Set-Location ../tests/integration | |
| go mod download | |
| - name: 🧹 Clean Previous Builds | |
| shell: pwsh | |
| run: ./build.ps1 clean | |
| - name: 🔨 Build Frontend | |
| shell: pwsh | |
| run: ./build.ps1 build_frontend | |
| - name: 🔨 Build Backend and Package | |
| shell: pwsh | |
| run: ./build.ps1 build_backend | |
| - name: ✅ Verify Build Artifacts | |
| shell: pwsh | |
| run: | | |
| $distPath = "target/dist" | |
| if (Test-Path $distPath) { | |
| $artifacts = Get-ChildItem -Path $distPath -Filter "$env:PRODUCT_NAME_LOWER-*.zip" | |
| if ($artifacts.Count -gt 0) { | |
| Write-Host "✅ Build artifacts found:" | |
| $artifacts | ForEach-Object { Write-Host " - $($_.Name)" } | |
| } else { | |
| Write-Host "❌ No distribution zip files found in $distPath" | |
| exit 1 | |
| } | |
| } else { | |
| Write-Host "❌ Distribution directory not found: $distPath" | |
| exit 1 | |
| } | |
| # TODO: Re-enable frontend tests after E2E issues are resolved | |
| # - name: 🧪 Run Console Tests | |
| # working-directory: frontend/apps/thunder-console | |
| # run: pnpm test:coverage | |
| # - name: 🧪 Run Gate Tests | |
| # working-directory: frontend/apps/thunder-gate | |
| # run: pnpm test:coverage | |
| - name: 📦 Upload Windows Distribution | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: ${{ env.PRODUCT_NAME_LOWER }}-windows-distribution-${{ github.run_id }} | |
| path: target/dist/${{ env.PRODUCT_NAME_LOWER }}-*-win-*.zip | |
| if-no-files-found: error | |
| build-samples-windows: | |
| name: 🛠️ Build Sample Apps (Windows) | |
| needs: dependency-guard | |
| if: ${{ always() && (needs.dependency-guard.result == 'success' || needs.dependency-guard.result == 'skipped') }} | |
| runs-on: windows-latest | |
| steps: | |
| - name: 📥 Checkout Code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: ⚙️ Set up Go Environment | |
| uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 | |
| with: | |
| go-version-file: backend/go.mod | |
| - name: ⚙️ Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 'lts/*' | |
| - name: 🧩 Build and Package Sample Apps | |
| shell: pwsh | |
| run: | | |
| ./build.ps1 build_samples | |
| ./build.ps1 package_samples | |
| - name: 📦 Upload Built Sample App | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: sample-app-react-sdk-windows-${{ github.run_id }} | |
| path: target/dist/sample-app-react-sdk-*.zip | |
| if-no-files-found: error | |
| test-integration-windows: | |
| name: 🧪 Integration Tests (SQLite) | |
| needs: build-windows | |
| runs-on: windows-latest | |
| steps: | |
| - name: 📥 Checkout Code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: ⚙️ Set up Go Environment | |
| uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 | |
| with: | |
| go-version-file: backend/go.mod | |
| cache: true | |
| - name: 📦 Install SQLite3 | |
| shell: pwsh | |
| run: | | |
| choco install sqlite -y | |
| $sqlitePath = (Get-Command sqlite3 -ErrorAction SilentlyContinue).Source | |
| if (-not $sqlitePath) { | |
| $machinePath = [System.Environment]::GetEnvironmentVariable("Path","Machine") | |
| $userPath = [System.Environment]::GetEnvironmentVariable("Path","User") | |
| $env:Path = "$machinePath;$userPath" | |
| $sqlitePath = (Get-Command sqlite3 -ErrorAction SilentlyContinue).Source | |
| if (-not $sqlitePath) { | |
| Write-Host "❌ sqlite3 not found after PATH refresh. Installation may have failed." | |
| exit 1 | |
| } | |
| } | |
| $sqliteDir = Split-Path -Parent $sqlitePath | |
| echo "$sqliteDir" >> $env:GITHUB_PATH | |
| sqlite3 --version | |
| - name: 📥 Download Built Distribution | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| name: ${{ env.PRODUCT_NAME_LOWER }}-windows-distribution-${{ github.run_id }} | |
| path: target/dist/ | |
| - name: 📦 Install Dependencies | |
| shell: pwsh | |
| run: | | |
| Set-Location backend | |
| go mod download | |
| Set-Location ../tests/integration | |
| go mod download | |
| - name: 📝 Configure Test Database (SQLite) | |
| shell: pwsh | |
| run: ./tests/integration/resources/scripts/setup-test-config.ps1 -DbType sqlite | |
| - name: 🧪 Run Integration Tests (SQLite) | |
| shell: pwsh | |
| run: ./build.ps1 test_integration | |
| env: | |
| DB_TYPE: sqlite | |
| test-e2e-windows: | |
| name: 🎭 Playwright E2E Tests (Windows) | |
| needs: [build-windows, build-samples-windows] | |
| runs-on: windows-latest | |
| steps: | |
| - name: 📥 Checkout Code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: ⚙️ Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 'lts/*' | |
| - name: 📥 Download Built Distribution | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| name: ${{ env.PRODUCT_NAME_LOWER }}-windows-distribution-${{ github.run_id }} | |
| path: target/dist/ | |
| - name: 📥 Download Built Sample App | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| name: sample-app-react-sdk-windows-${{ github.run_id }} | |
| path: target/dist/ | |
| - name: 📂 Extract Server | |
| shell: pwsh | |
| run: | | |
| New-Item -Path "tests/e2e/server" -ItemType Directory -Force | Out-Null | |
| $zipFile = Get-ChildItem -Path "target/dist" -Filter "$env:PRODUCT_NAME_LOWER-*.zip" | Select-Object -First 1 | |
| if (-not $zipFile) { | |
| Write-Host "❌ Distribution zip not found in target/dist" | |
| Get-ChildItem -Path "target/dist" | |
| exit 1 | |
| } | |
| Write-Host "Extracting $($zipFile.FullName)..." | |
| Expand-Archive -Path $zipFile.FullName -DestinationPath "tests/e2e/server" -Force | |
| # Move contents from the extracted subfolder to server root | |
| $extractedDir = Get-ChildItem -Path "tests/e2e/server" -Directory -Filter "$env:PRODUCT_NAME_LOWER-*" | Select-Object -First 1 | |
| if ($extractedDir) { | |
| Get-ChildItem -Path $extractedDir.FullName -Force | Move-Item -Destination "tests/e2e/server" -Force | |
| Remove-Item -Path $extractedDir.FullName -Recurse -Force | |
| } | |
| - name: 🚀 Run Setup | |
| shell: pwsh | |
| run: | | |
| Set-Location "tests/e2e/server" | |
| ./setup.ps1 | |
| - name: 🚀 Start Server (Without Security) | |
| shell: bash | |
| run: | | |
| cd tests/e2e/server | |
| SKIP_SECURITY=true ./${PRODUCT_NAME_LOWER}.exe > server.log 2>&1 & | |
| echo "SERVER_PID=$!" >> $GITHUB_ENV | |
| sleep 5 | |
| # Wait for server to be ready | |
| echo "Waiting for server to be ready on https://localhost:8090..." | |
| for i in $(seq 1 60); do | |
| if curl -sk https://localhost:8090/health/liveness > /dev/null 2>&1; then | |
| echo "Server is UP!" | |
| break | |
| fi | |
| echo "Still waiting ($i/60)..." | |
| sleep 2 | |
| done | |
| # Verify server is running | |
| if ! curl -sk https://localhost:8090/health/liveness > /dev/null 2>&1; then | |
| echo "❌ Server failed to start within 2 minutes" | |
| cat server.log || true | |
| exit 1 | |
| fi | |
| echo "Server started successfully!" | |
| - name: 🔍 Extract Sample App ID | |
| id: extract-app-id | |
| shell: bash | |
| run: | | |
| echo "Fetching applications from server..." | |
| response=$(curl -sk https://localhost:8090/applications) | |
| sample_app_id=$(echo "$response" | jq -r '.applications[] | select(.name == "Sample App") | .id') | |
| if [ -n "$sample_app_id" ] && [ "$sample_app_id" != "null" ]; then | |
| echo "✅ Sample App ID extracted: $sample_app_id" | |
| echo "sample_app_id=$sample_app_id" >> $GITHUB_OUTPUT | |
| else | |
| echo "❌ Sample App not found in applications list" | |
| echo "Response: $response" | |
| exit 1 | |
| fi | |
| - name: 🔄 Restart Server with Security Enabled | |
| shell: bash | |
| run: | | |
| echo "Stopping server..." | |
| kill $SERVER_PID || true | |
| sleep 5 | |
| cd tests/e2e/server | |
| ./${PRODUCT_NAME_LOWER}.exe > server-secure.log 2>&1 & | |
| echo "SERVER_PID=$!" >> $GITHUB_ENV | |
| sleep 5 | |
| # Wait for server to be ready | |
| echo "Waiting for server to be ready on https://localhost:8090..." | |
| for i in $(seq 1 60); do | |
| if curl -sk https://localhost:8090/health/liveness > /dev/null 2>&1; then | |
| echo "Server is UP with security enabled!" | |
| break | |
| fi | |
| echo "Still waiting ($i/60)..." | |
| sleep 2 | |
| done | |
| # Verify server is running | |
| if ! curl -sk https://localhost:8090/health/liveness > /dev/null 2>&1; then | |
| echo "❌ Server failed to restart within 2 minutes" | |
| cat server-secure.log || true | |
| exit 1 | |
| fi | |
| echo "Server restarted successfully with security enabled!" | |
| - name: 📂 Extract Sample App | |
| shell: pwsh | |
| run: | | |
| New-Item -Path "tests/e2e/sample-app" -ItemType Directory -Force | Out-Null | |
| $sampleZip = Get-ChildItem -Path "target/dist" -Filter "sample-app-react-sdk-*.zip" | Select-Object -First 1 | |
| if (-not $sampleZip) { | |
| Write-Host "❌ Sample app zip not found in target/dist" | |
| Get-ChildItem -Path "target/dist" | |
| exit 1 | |
| } | |
| Write-Host "Extracting $($sampleZip.Name)..." | |
| Expand-Archive -Path $sampleZip.FullName -DestinationPath "tests/e2e/sample-app" -Force | |
| # Move contents from extracted subfolder | |
| $extractedDir = Get-ChildItem -Path "tests/e2e/sample-app" -Directory -Filter "sample-app-*" | Select-Object -First 1 | |
| if ($extractedDir) { | |
| Get-ChildItem -Path $extractedDir.FullName -Force | Move-Item -Destination "tests/e2e/sample-app" -Force | |
| Remove-Item -Path $extractedDir.FullName -Recurse -Force | |
| } | |
| - name: 📦 Install serve globally | |
| shell: bash | |
| run: npm install -g serve | |
| - name: 📱 Start Sample App | |
| shell: bash | |
| run: | | |
| cd tests/e2e/sample-app | |
| echo "Starting Sample App..." | |
| dist_path="$(pwd)/dist" | |
| cert_file="$dist_path/server.cert" | |
| key_file="$dist_path/server.key" | |
| if [ -f "$cert_file" ] && [ -f "$key_file" ]; then | |
| echo "Using HTTPS with SSL certificates" | |
| serve -s "$dist_path" -l 3000 --ssl-cert "$cert_file" --ssl-key "$key_file" > sample-app.log 2>&1 & | |
| protocol="https" | |
| else | |
| echo "No SSL certificates found, using HTTP" | |
| serve -s "$dist_path" -l 3000 > sample-app.log 2>&1 & | |
| protocol="http" | |
| fi | |
| sleep 5 | |
| # Wait for sample app to be ready | |
| echo "Waiting for sample app to be ready on ${protocol}://localhost:3000..." | |
| for i in $(seq 1 60); do | |
| if curl -sk "${protocol}://localhost:3000" > /dev/null 2>&1; then | |
| echo "Sample App is UP!" | |
| break | |
| fi | |
| echo "Still waiting ($i/60)..." | |
| sleep 2 | |
| done | |
| # Verify sample app is running | |
| if ! curl -sk "${protocol}://localhost:3000" > /dev/null 2>&1; then | |
| echo "❌ Sample app failed to start within 2 minutes" | |
| cat sample-app.log || true | |
| exit 1 | |
| fi | |
| echo "Sample App started successfully!" | |
| - name: 📦 Install E2E Dependencies | |
| working-directory: tests/e2e | |
| run: npm ci | |
| - name: 🎭 Install Playwright Browsers | |
| working-directory: tests/e2e | |
| run: npx playwright install --with-deps chromium | |
| - name: 🔍 Verify Services Before Tests | |
| shell: pwsh | |
| run: | | |
| Write-Host "Checking server..." | |
| $serverProcs = Get-Process -Name "$env:PRODUCT_NAME_LOWER" -ErrorAction SilentlyContinue | |
| if ($serverProcs) { | |
| Write-Host "✅ Server process running (PID: $($serverProcs.Id -join ', '))" | |
| } else { | |
| Write-Host "❌ Server process NOT found" | |
| Write-Host "Server log:" | |
| $logPath = "tests/e2e/server/server-secure.log" | |
| if (Test-Path $logPath) { Get-Content $logPath } else { Write-Host "(no log)" } | |
| $errPath = "tests/e2e/server/server-secure-err.log" | |
| if (Test-Path $errPath) { Get-Content $errPath } else { Write-Host "(no error log)" } | |
| } | |
| try { | |
| $response = Invoke-WebRequest -Uri "https://localhost:8090/health/liveness" -SkipCertificateCheck -TimeoutSec 5 | |
| Write-Host "✅ Server responding (status: $($response.StatusCode))" | |
| } catch { | |
| Write-Host "❌ Server not responding: $_" | |
| } | |
| Write-Host "Checking Sample App..." | |
| try { | |
| Invoke-WebRequest -Uri "https://localhost:3000" -SkipCertificateCheck -TimeoutSec 5 | Out-Null | |
| Write-Host "✅ Sample App responding" | |
| } catch { | |
| Write-Host "❌ Sample App not responding: $_" | |
| $appLog = "tests/e2e/sample-app/sample-app.log" | |
| if (Test-Path $appLog) { Write-Host "App log:"; Get-Content $appLog } | |
| } | |
| - name: 🎭 Run Playwright Tests (Chromium) | |
| working-directory: tests/e2e | |
| run: npx playwright test --project=chromium | |
| env: | |
| NODE_TLS_REJECT_UNAUTHORIZED: '0' | |
| BASE_URL: ${{ secrets.PLAYWRIGHT_BASE_URL || vars.PLAYWRIGHT_BASE_URL || 'https://localhost:8090' }} | |
| ADMIN_USERNAME: ${{ secrets.PLAYWRIGHT_ADMIN_USERNAME || 'admin' }} | |
| ADMIN_PASSWORD: ${{ secrets.PLAYWRIGHT_ADMIN_PASSWORD || 'admin' }} | |
| TEST_USER_USERNAME: ${{ secrets.PLAYWRIGHT_TEST_USER_USERNAME || 'testuser' }} | |
| TEST_USER_PASSWORD: ${{ secrets.PLAYWRIGHT_TEST_USER_PASSWORD || 'admin' }} | |
| PLAYWRIGHT_WORKERS: ${{ vars.PLAYWRIGHT_WORKERS || 1 }} | |
| DEBUG_AUTH: ${{ vars.PLAYWRIGHT_DEBUG_AUTH || 'false' }} | |
| SAMPLE_APP_ID: ${{ steps.extract-app-id.outputs.sample_app_id }} | |
| SAMPLE_APP_URL: 'https://localhost:3000' | |
| SERVER_URL: 'https://localhost:8090' | |
| AUTO_SETUP_MFA: ${{ vars.AUTO_SETUP_MFA || 'true' }} | |
| MOCK_SMS_SERVER_PORT: ${{ vars.MOCK_SMS_SERVER_PORT || 8098 }} | |
| SAMPLE_APP_USERNAME: ${{ secrets.SAMPLE_APP_USERNAME || 'e2e-test-user' }} | |
| SAMPLE_APP_PASSWORD: ${{ secrets.SAMPLE_APP_PASSWORD || 'e2e-test-password' }} | |
| - name: 📊 Upload Playwright Report | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: playwright-report-windows-${{ github.run_id }} | |
| path: tests/e2e/playwright-report/ | |
| retention-days: 30 | |
| report-results: | |
| name: 📊 Report Results | |
| runs-on: ubuntu-latest | |
| needs: [build-windows, build-samples-windows, test-integration-windows, test-e2e-windows] | |
| if: always() | |
| steps: | |
| - name: 📊 Check Overall Status | |
| id: check_status | |
| run: | | |
| BUILD_STATUS="${{ needs.build-windows.result }}" | |
| SAMPLES_STATUS="${{ needs.build-samples-windows.result }}" | |
| INTEGRATION_STATUS="${{ needs.test-integration-windows.result }}" | |
| E2E_STATUS="${{ needs.test-e2e-windows.result }}" | |
| echo "Build: $BUILD_STATUS" | |
| echo "Sample Apps: $SAMPLES_STATUS" | |
| echo "Integration Tests: $INTEGRATION_STATUS" | |
| echo "E2E Tests: $E2E_STATUS" | |
| if [ "$BUILD_STATUS" = "success" ] && [ "$SAMPLES_STATUS" = "success" ] && [ "$INTEGRATION_STATUS" = "success" ] && [ "$E2E_STATUS" = "success" ]; then | |
| echo "status=success" >> $GITHUB_OUTPUT | |
| echo "message=✅ All Windows build validations passed successfully!" >> $GITHUB_OUTPUT | |
| elif [ "$BUILD_STATUS" = "cancelled" ] || [ "$SAMPLES_STATUS" = "cancelled" ] || [ "$INTEGRATION_STATUS" = "cancelled" ] || [ "$E2E_STATUS" = "cancelled" ]; then | |
| echo "status=cancelled" >> $GITHUB_OUTPUT | |
| echo "message=⚠️ Windows build validation was cancelled." >> $GITHUB_OUTPUT | |
| elif [ "$BUILD_STATUS" = "skipped" ] || [ "$SAMPLES_STATUS" = "skipped" ] || [ "$INTEGRATION_STATUS" = "skipped" ] || [ "$E2E_STATUS" = "skipped" ]; then | |
| echo "status=skipped" >> $GITHUB_OUTPUT | |
| echo "message=⚠️ Some Windows build validation jobs were skipped." >> $GITHUB_OUTPUT | |
| else | |
| echo "status=failure" >> $GITHUB_OUTPUT | |
| echo "message=❌ Some Windows build validations failed. Please check the workflow logs." >> $GITHUB_OUTPUT | |
| fi | |
| - name: 📝 Generate Summary | |
| run: | | |
| echo "# 🪟 Windows Build Validation Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Status:** ${{ steps.check_status.outputs.status }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Message:** ${{ steps.check_status.outputs.message }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Job Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- Build Product: ${{ needs.build-windows.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Build Sample Apps: ${{ needs.build-samples-windows.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Integration Tests (SQLite): ${{ needs.test-integration-windows.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Playwright E2E Tests: ${{ needs.test-e2e-windows.result }}" >> $GITHUB_STEP_SUMMARY | |
| - name: 🔔 Notify on Failure | |
| if: steps.check_status.outputs.status == 'failure' | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | |
| env: | |
| BUILD_RESULT: ${{ needs.build-windows.result }} | |
| SAMPLES_RESULT: ${{ needs.build-samples-windows.result }} | |
| INTEGRATION_RESULT: ${{ needs.test-integration-windows.result }} | |
| E2E_RESULT: ${{ needs.test-e2e-windows.result }} | |
| WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| BRANCH_NAME: ${{ github.ref_name }} | |
| COMMIT_SHA: ${{ github.sha }} | |
| with: | |
| script: | | |
| const issue_title = '🪟 Windows Build Validation Failed'; | |
| const issue_body = `## Windows Build Validation Failure | |
| The nightly Windows build validation workflow has detected failures. | |
| **Workflow Run:** ${process.env.WORKFLOW_URL} | |
| **Branch:** ${process.env.BRANCH_NAME} | |
| **Commit:** ${process.env.COMMIT_SHA} | |
| ### Job Results | |
| - Build Product: ${process.env.BUILD_RESULT} | |
| - Build Sample Apps: ${process.env.SAMPLES_RESULT} | |
| - Integration Tests (SQLite): ${process.env.INTEGRATION_RESULT} | |
| - Playwright E2E Tests: ${process.env.E2E_RESULT} | |
| Please investigate and fix the build issues. | |
| --- | |
| *This issue was automatically created by the Windows build validation workflow.*`; | |
| const issues = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| labels: 'windows-build-failure', | |
| per_page: 1 | |
| }); | |
| if (issues.data.length === 0) { | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: issue_title, | |
| body: issue_body, | |
| labels: ['windows-build-failure', 'bug'] | |
| }); | |
| console.log('Created new issue for Windows build failure'); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issues.data[0].number, | |
| body: issue_body | |
| }); | |
| console.log('Added comment to existing Windows build failure issue'); | |
| } | |
| - name: 🔔 Send Google Chat Notification on Failure | |
| if: steps.check_status.outputs.status == 'failure' | |
| shell: bash | |
| env: | |
| GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} | |
| WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| BUILD_STATUS: ${{ needs.build-windows.result }} | |
| SAMPLES_STATUS: ${{ needs.build-samples-windows.result }} | |
| INTEGRATION_STATUS: ${{ needs.test-integration-windows.result }} | |
| E2E_STATUS: ${{ needs.test-e2e-windows.result }} | |
| BRANCH: ${{ github.ref_name }} | |
| COMMIT: ${{ github.sha }} | |
| run: | | |
| if [ -z "$GOOGLE_CHAT_WEBHOOK" ]; then | |
| echo "GOOGLE_CHAT_WEBHOOK secret is not set. Skipping Google Chat notification." | |
| exit 0 | |
| fi | |
| get_status_emoji() { | |
| case "$1" in | |
| success) echo "✅" ;; | |
| failure) echo "❌" ;; | |
| *) echo "⚠️" ;; | |
| esac | |
| } | |
| BUILD_EMOJI=$(get_status_emoji "$BUILD_STATUS") | |
| SAMPLES_EMOJI=$(get_status_emoji "$SAMPLES_STATUS") | |
| INTEGRATION_EMOJI=$(get_status_emoji "$INTEGRATION_STATUS") | |
| E2E_EMOJI=$(get_status_emoji "$E2E_STATUS") | |
| jq -n \ | |
| --arg branch "$BRANCH" \ | |
| --arg build_status "$BUILD_EMOJI $BUILD_STATUS" \ | |
| --arg samples_status "$SAMPLES_EMOJI $SAMPLES_STATUS" \ | |
| --arg integration_status "$INTEGRATION_EMOJI $INTEGRATION_STATUS" \ | |
| --arg e2e_status "$E2E_EMOJI $E2E_STATUS" \ | |
| --arg commit "${COMMIT:0:8}" \ | |
| --arg workflow_url "$WORKFLOW_URL" \ | |
| --arg issues_url "${{ github.server_url }}/${{ github.repository }}/issues?q=is%3Aissue+is%3Aopen+label%3Awindows-build-failure" \ | |
| '{ | |
| "cards": [{ | |
| "header": { | |
| "title": "🪟 Windows Build Validation Failed", | |
| "subtitle": ("Branch: " + $branch), | |
| "imageUrl": "https://cdn-icons-png.flaticon.com/512/595/595067.png", | |
| "imageStyle": "AVATAR" | |
| }, | |
| "sections": [ | |
| { | |
| "header": "Job Results", | |
| "widgets": [ | |
| { | |
| "keyValue": { | |
| "topLabel": "Build Product", | |
| "content": $build_status | |
| } | |
| }, | |
| { | |
| "keyValue": { | |
| "topLabel": "Build Sample Apps", | |
| "content": $samples_status | |
| } | |
| }, | |
| { | |
| "keyValue": { | |
| "topLabel": "Integration Tests (SQLite)", | |
| "content": $integration_status | |
| } | |
| }, | |
| { | |
| "keyValue": { | |
| "topLabel": "Playwright E2E Tests", | |
| "content": $e2e_status | |
| } | |
| }, | |
| { | |
| "keyValue": { | |
| "topLabel": "Commit", | |
| "content": $commit | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "widgets": [ | |
| { | |
| "buttons": [ | |
| { | |
| "textButton": { | |
| "text": "VIEW WORKFLOW RUN", | |
| "onClick": { | |
| "openLink": { | |
| "url": $workflow_url | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "textButton": { | |
| "text": "VIEW ISSUES", | |
| "onClick": { | |
| "openLink": { | |
| "url": $issues_url | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| ] | |
| }] | |
| }' > payload.json | |
| if curl -f -X POST -H "Content-Type: application/json" -d @payload.json "$GOOGLE_CHAT_WEBHOOK"; then | |
| echo "✅ Google Chat notification sent successfully" | |
| else | |
| echo "⚠️ Failed to send Google Chat notification" | |
| fi |