From 8ed96082f4695beff3c224395347f6865aac88c6 Mon Sep 17 00:00:00 2001 From: Cynthia J Date: Thu, 23 Apr 2026 22:13:59 -0700 Subject: [PATCH 1/6] ussing existing workflow --- .github/workflows/android.yaml | 1 + .github/workflows/e2e_tests_fdc.yaml | 1 + .github/workflows/e2e_tests_pipeline.yaml | 1 + .github/workflows/ios.yaml | 1 + .github/workflows/macos.yaml | 1 + .github/workflows/nightly.yaml | 224 +++--------------- .../scripts/nightly_issue_dashboard.dart | 22 +- .github/workflows/web.yaml | 1 + .github/workflows/windows.yaml | 1 + 9 files changed, 51 insertions(+), 202 deletions(-) diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index bcd3a290d84e..f2b5b413274b 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -23,6 +23,7 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: jobs: android: diff --git a/.github/workflows/e2e_tests_fdc.yaml b/.github/workflows/e2e_tests_fdc.yaml index 09c75e073f17..ff4036c29f0a 100644 --- a/.github/workflows/e2e_tests_fdc.yaml +++ b/.github/workflows/e2e_tests_fdc.yaml @@ -20,6 +20,7 @@ on: - 'website/**' - '**/example/**' - '**.md' + workflow_call: permissions: contents: read diff --git a/.github/workflows/e2e_tests_pipeline.yaml b/.github/workflows/e2e_tests_pipeline.yaml index f13f10f20af6..fe9f71045124 100644 --- a/.github/workflows/e2e_tests_pipeline.yaml +++ b/.github/workflows/e2e_tests_pipeline.yaml @@ -19,6 +19,7 @@ on: - 'website/**' - '**/example/**' - '**.md' + workflow_call: permissions: contents: read diff --git a/.github/workflows/ios.yaml b/.github/workflows/ios.yaml index 5e8df18080a0..bee1f26ef59e 100644 --- a/.github/workflows/ios.yaml +++ b/.github/workflows/ios.yaml @@ -23,6 +23,7 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: jobs: ios: diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index d9c41a6376c9..fb1ce245bc21 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -23,6 +23,7 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: jobs: macos: diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 263cd65b2e39..02db4f59fe58 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -14,202 +14,32 @@ permissions: issues: write jobs: - pipeline-e2e-android: - runs-on: ubuntu-latest - timeout-minutes: 45 - env: - AVD_ARCH: x86_64 - AVD_API_LEVEL: 34 - AVD_TARGET: google_apis - steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a - name: Install Node.js 20 - with: - node-version: '20' - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b - with: - distribution: 'temurin' - java-version: '21' - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff - with: - channel: 'stable' - cache: true - cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" - pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b33cc520cace360b95d02b37bf09cdaa - with: - run-bootstrap: false - melos-version: '5.3.0' - - name: Inject Firebase config for pipeline E2E - env: - FIREBASE_OPTIONS_DART: ${{ secrets.PIPELINE_E2E_FIREBASE_OPTIONS_DART }} - GOOGLE_SERVICES_JSON: ${{ secrets.PIPELINE_E2E_GOOGLE_SERVICES_JSON }} - GOOGLE_SERVICE_INFO_PLIST: ${{ secrets.PIPELINE_E2E_GOOGLE_SERVICE_INFO_PLIST }} - run: | - echo "$FIREBASE_OPTIONS_DART" > packages/cloud_firestore/cloud_firestore/pipeline_example/lib/firebase_options.dart - echo "$GOOGLE_SERVICES_JSON" > packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/google-services.json - echo "$GOOGLE_SERVICE_INFO_PLIST" > packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/GoogleService-Info.plist - - name: Bootstrap package - run: melos bootstrap --scope "cloud_firestore*" - - name: Enable KVM - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - - name: Gradle cache - uses: gradle/actions/setup-gradle@v4 - - name: Free Disk Space (Ubuntu) - uses: AdityaGarg8/remove-unwanted-software@90e01b21170618765a73370fcc3abbd1684a7793 - with: - remove-dotnet: true - remove-haskell: true - remove-codeql: true - remove-docker-images: true - remove-large-packages: true - - name: AVD cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 - continue-on-error: true - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ runner.os }}-${{ env.AVD_API_LEVEL }}-${{ env.AVD_TARGET }}-${{ env.AVD_ARCH }} - - name: Start AVD then run pipeline E2E tests - uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b - with: - api-level: ${{ env.AVD_API_LEVEL }} - target: ${{ env.AVD_TARGET }} - arch: ${{ env.AVD_ARCH }} - emulator-build: 14214601 - working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example - script: | - flutter test integration_test/pipeline/pipeline_live_test.dart --timeout 10x --dart-define=CI=true -d emulator-5554 - - name: Ensure Appium is shut down - run: | - pgrep -f appium && pkill -f appium || echo "No Appium process found" - - name: Save Android Emulator Cache - if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 - continue-on-error: true - with: - key: ${{ steps.avd-cache.outputs.cache-primary-key }} - path: | - ~/.android/avd/* - ~/.android/adb* - - pipeline-e2e-web: - runs-on: macos-latest - timeout-minutes: 25 - steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a - name: Install Node.js 20 - with: - node-version: '20' - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff - with: - channel: 'stable' - cache: true - cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" - pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa - with: - run-bootstrap: false - melos-version: '5.3.0' - - name: Inject Firebase config for pipeline E2E - env: - FIREBASE_OPTIONS_DART: ${{ secrets.PIPELINE_E2E_FIREBASE_OPTIONS_DART }} - GOOGLE_SERVICES_JSON: ${{ secrets.PIPELINE_E2E_GOOGLE_SERVICES_JSON }} - GOOGLE_SERVICE_INFO_PLIST: ${{ secrets.PIPELINE_E2E_GOOGLE_SERVICE_INFO_PLIST }} - run: | - echo "$FIREBASE_OPTIONS_DART" > packages/cloud_firestore/cloud_firestore/pipeline_example/lib/firebase_options.dart - echo "$GOOGLE_SERVICES_JSON" > packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/google-services.json - echo "$GOOGLE_SERVICE_INFO_PLIST" > packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/GoogleService-Info.plist - - name: Bootstrap package - run: melos bootstrap --scope "cloud_firestore*" - - name: Run pipeline E2E tests (Chrome) - working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example - run: | - chromedriver --port=4444 --trace-buffer-size=100000 & - flutter drive --target=./integration_test/pipeline/pipeline_live_test.dart --driver=./test_driver/integration_test.dart -d chrome --dart-define=CI=true | tee output.log - output=$( packages/cloud_firestore/cloud_firestore/pipeline_example/lib/firebase_options.dart - echo "$GOOGLE_SERVICES_JSON" > packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/google-services.json - echo "$GOOGLE_SERVICE_INFO_PLIST" > packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/GoogleService-Info.plist - - name: Bootstrap package - run: melos bootstrap --scope "cloud_firestore*" - - name: Prepare iOS project for Swift Package Manager - working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example/ios - run: | - if [ -f Podfile ]; then pod deintegrate; fi - rm -f Podfile Podfile.lock - rm -rf Pods - - uses: futureware-tech/simulator-action@e89aa8f93d3aec35083ff49d2854d07f7186f7f5 - id: simulator - with: - model: "iPhone 16" - - name: Build iOS (simulator) - working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example - run: | - flutter build ios --no-codesign --simulator --debug --target=./integration_test/pipeline/pipeline_live_test.dart --dart-define=CI=true - - name: Run pipeline E2E tests (iOS) - working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example - env: - SIMULATOR: ${{ steps.simulator.outputs.udid }} - run: | - perl -e 'alarm 360; exec @ARGV' -- flutter test integration_test/pipeline/pipeline_live_test.dart -d "$SIMULATOR" --timeout 10x --dart-define=CI=true || { - echo "First attempt failed or timed out. Rebooting simulator and retrying..." - xcrun simctl shutdown "$SIMULATOR" || true - xcrun simctl boot "$SIMULATOR" - xcrun simctl bootstatus "$SIMULATOR" -b - flutter test integration_test/pipeline/pipeline_live_test.dart -d "$SIMULATOR" --timeout 10x --dart-define=CI=true - } + e2e-android: + uses: ./.github/workflows/android.yaml + secrets: inherit + e2e-ios: + uses: ./.github/workflows/ios.yaml + secrets: inherit + e2e-macos: + uses: ./.github/workflows/macos.yaml + secrets: inherit + e2e-web: + uses: ./.github/workflows/web.yaml + secrets: inherit + e2e-windows: + uses: ./.github/workflows/windows.yaml + secrets: inherit + e2e-fdc: + uses: ./.github/workflows/e2e_tests_fdc.yaml + secrets: inherit + e2e-pipeline: + uses: ./.github/workflows/e2e_tests_pipeline.yaml + secrets: inherit update-dashboard: runs-on: ubuntu-latest if: always() - needs: [pipeline-e2e-android, pipeline-e2e-web, pipeline-e2e-ios] + needs: [e2e-android, e2e-ios, e2e-macos, e2e-web, e2e-windows, e2e-fdc, e2e-pipeline] steps: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff @@ -218,9 +48,13 @@ jobs: - name: Update Dashboard Issue env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ANDROID_STATUS: ${{ needs.pipeline-e2e-android.result }} - WEB_STATUS: ${{ needs.pipeline-e2e-web.result }} - IOS_STATUS: ${{ needs.pipeline-e2e-ios.result }} + ANDROID_STATUS: ${{ needs.e2e-android.result }} + IOS_STATUS: ${{ needs.e2e-ios.result }} + MACOS_STATUS: ${{ needs.e2e-macos.result }} + WEB_STATUS: ${{ needs.e2e-web.result }} + WINDOWS_STATUS: ${{ needs.e2e-windows.result }} + FDC_STATUS: ${{ needs.e2e-fdc.result }} + PIPELINE_STATUS: ${{ needs.e2e-pipeline.result }} REPO: ${{ github.repository }} run: | dart .github/workflows/scripts/nightly_issue_dashboard.dart diff --git a/.github/workflows/scripts/nightly_issue_dashboard.dart b/.github/workflows/scripts/nightly_issue_dashboard.dart index 13dbf3cf535a..4996886f8808 100644 --- a/.github/workflows/scripts/nightly_issue_dashboard.dart +++ b/.github/workflows/scripts/nightly_issue_dashboard.dart @@ -22,6 +22,10 @@ void main() async { final androidStatus = env['ANDROID_STATUS'] ?? 'skipped'; final webStatus = env['WEB_STATUS'] ?? 'skipped'; final iosStatus = env['IOS_STATUS'] ?? 'skipped'; + final macosStatus = env['MACOS_STATUS'] ?? 'skipped'; + final windowsStatus = env['WINDOWS_STATUS'] ?? 'skipped'; + final fdcStatus = env['FDC_STATUS'] ?? 'skipped'; + final pipelineStatus = env['PIPELINE_STATUS'] ?? 'skipped'; final runId = env['GITHUB_RUN_ID']; final serverUrl = env['GITHUB_SERVER_URL'] ?? 'https://github.com'; @@ -37,8 +41,12 @@ void main() async { final androidIcon = _getIcon(androidStatus); final webIcon = _getIcon(webStatus); final iosIcon = _getIcon(iosStatus); + final macosIcon = _getIcon(macosStatus); + final windowsIcon = _getIcon(windowsStatus); + final fdcIcon = _getIcon(fdcStatus); + final pipelineIcon = _getIcon(pipelineStatus); - final newRow = '| $date | $androidIcon | $iosIcon | $webIcon | $notes |'; + final newRow = '| $date | $androidIcon | $iosIcon | $webIcon | $macosIcon | $windowsIcon | $fdcIcon | $pipelineIcon | $notes |'; print('New Row: $newRow'); @@ -107,8 +115,8 @@ Future _createIssue(HttpClient client, String token, String repo, String n 'body': ''' ## Testing History (last 30 days) -| Date | Android | iOS | Web | Notes | -| :--- | :--- | :--- | :--- | :--- | +| Date | Android | iOS | Web | MacOS | Windows | FDC | Pipeline | Notes | +| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | $newRow ''' }; @@ -193,8 +201,8 @@ String _appendRow(String currentBody, String newRow) { for (final line in lines) { if (line.startsWith('| Date |')) { if (!processedTable) { - newBodyLines.add('| Date | Android | iOS | Web | Notes |'); - newBodyLines.add('| :--- | :--- | :--- | :--- | :--- |'); + newBodyLines.add('| Date | Android | iOS | Web | MacOS | Windows | FDC | Pipeline | Notes |'); + newBodyLines.add('| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |'); newBodyLines.addAll(tableRows); processedTable = true; } @@ -212,8 +220,8 @@ String _appendRow(String currentBody, String newRow) { // Table not found, append it newBodyLines.add('## Testing History (last 30 days)'); newBodyLines.add(''); - newBodyLines.add('| Date | Android | iOS | Web | Notes |'); - newBodyLines.add('| :--- | :--- | :--- | :--- | :--- |'); + newBodyLines.add('| Date | Android | iOS | Web | MacOS | Windows | FDC | Pipeline | Notes |'); + newBodyLines.add('| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |'); newBodyLines.add(newRow); } diff --git a/.github/workflows/web.yaml b/.github/workflows/web.yaml index 30472fb9d097..eba42bc3fc37 100644 --- a/.github/workflows/web.yaml +++ b/.github/workflows/web.yaml @@ -23,6 +23,7 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: jobs: web: diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 2a56eee0ef39..16b8c8e24ecb 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -23,6 +23,7 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: jobs: windows: From 74d2c1fc5226210564a279801990035e8c363b6f Mon Sep 17 00:00:00 2001 From: Cynthia J Date: Thu, 23 Apr 2026 22:24:18 -0700 Subject: [PATCH 2/6] only update the dashboard once preset test is done --- .github/workflows/nightly.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 02db4f59fe58..1228dbd7d78e 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -38,7 +38,7 @@ jobs: update-dashboard: runs-on: ubuntu-latest - if: always() + if: ${{ !cancelled() }} needs: [e2e-android, e2e-ios, e2e-macos, e2e-web, e2e-windows, e2e-fdc, e2e-pipeline] steps: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 From 63fbda11e330af24a766f6719a73e7dbd03c4c1f Mon Sep 17 00:00:00 2001 From: Cynthia J Date: Thu, 23 Apr 2026 22:32:24 -0700 Subject: [PATCH 3/6] change the way how it runs --- .github/workflows/nightly.yaml | 23 ---- .../scripts/nightly_issue_dashboard.dart | 117 ++++++++++++------ .github/workflows/update_dashboard.yaml | 30 +++++ 3 files changed, 107 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/update_dashboard.yaml diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 1228dbd7d78e..cea5e5288142 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -35,26 +35,3 @@ jobs: e2e-pipeline: uses: ./.github/workflows/e2e_tests_pipeline.yaml secrets: inherit - - update-dashboard: - runs-on: ubuntu-latest - if: ${{ !cancelled() }} - needs: [e2e-android, e2e-ios, e2e-macos, e2e-web, e2e-windows, e2e-fdc, e2e-pipeline] - steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff - with: - channel: 'stable' - - name: Update Dashboard Issue - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ANDROID_STATUS: ${{ needs.e2e-android.result }} - IOS_STATUS: ${{ needs.e2e-ios.result }} - MACOS_STATUS: ${{ needs.e2e-macos.result }} - WEB_STATUS: ${{ needs.e2e-web.result }} - WINDOWS_STATUS: ${{ needs.e2e-windows.result }} - FDC_STATUS: ${{ needs.e2e-fdc.result }} - PIPELINE_STATUS: ${{ needs.e2e-pipeline.result }} - REPO: ${{ github.repository }} - run: | - dart .github/workflows/scripts/nightly_issue_dashboard.dart diff --git a/.github/workflows/scripts/nightly_issue_dashboard.dart b/.github/workflows/scripts/nightly_issue_dashboard.dart index 4996886f8808..a2cf278ec744 100644 --- a/.github/workflows/scripts/nightly_issue_dashboard.dart +++ b/.github/workflows/scripts/nightly_issue_dashboard.dart @@ -19,48 +19,30 @@ void main() async { final env = Platform.environment; final token = env['GITHUB_TOKEN']; final repo = env['REPO']; - final androidStatus = env['ANDROID_STATUS'] ?? 'skipped'; - final webStatus = env['WEB_STATUS'] ?? 'skipped'; - final iosStatus = env['IOS_STATUS'] ?? 'skipped'; - final macosStatus = env['MACOS_STATUS'] ?? 'skipped'; - final windowsStatus = env['WINDOWS_STATUS'] ?? 'skipped'; - final fdcStatus = env['FDC_STATUS'] ?? 'skipped'; - final pipelineStatus = env['PIPELINE_STATUS'] ?? 'skipped'; - final runId = env['GITHUB_RUN_ID']; + final workflowName = env['WORKFLOW_NAME']; + final status = env['STATUS']; + final runId = env['RUN_ID']; final serverUrl = env['GITHUB_SERVER_URL'] ?? 'https://github.com'; - if (token == null || repo == null) { - print('Error: GITHUB_TOKEN or REPO environment variables not set.'); + if (token == null || repo == null || workflowName == null || status == null) { + print('Error: Required environment variables not set.'); exit(1); } final date = DateTime.now().toUtc().toString().substring(0, 10); final runUrl = '$serverUrl/$repo/actions/runs/$runId'; - final notes = '[View Run]($runUrl)'; - - final androidIcon = _getIcon(androidStatus); - final webIcon = _getIcon(webStatus); - final iosIcon = _getIcon(iosStatus); - final macosIcon = _getIcon(macosStatus); - final windowsIcon = _getIcon(windowsStatus); - final fdcIcon = _getIcon(fdcStatus); - final pipelineIcon = _getIcon(pipelineStatus); - - final newRow = '| $date | $androidIcon | $iosIcon | $webIcon | $macosIcon | $windowsIcon | $fdcIcon | $pipelineIcon | $notes |'; - - print('New Row: $newRow'); + final statusIcon = _getIcon(status); final client = HttpClient(); try { - // 1. Find the issue final issueNumber = await _findIssue(client, token, repo); if (issueNumber == null) { print('Issue not found. Creating a new one.'); - await _createIssue(client, token, repo, newRow); + await _createIssue(client, token, repo, date, workflowName, statusIcon, runUrl); } else { print('Found issue #$issueNumber. Updating.'); - await _updateIssue(client, token, repo, issueNumber, newRow); + await _updateIssue(client, token, repo, issueNumber, date, workflowName, statusIcon, runUrl); } } finally { client.close(); @@ -82,6 +64,27 @@ String _getIcon(String status) { } } +int _getColumnIndex(String workflowName) { + switch (workflowName) { + case 'e2e-android': + return 1; + case 'e2e-iOS': + return 2; + case 'e2e-web': + return 3; + case 'e2e-macOS': + return 4; + case 'e2e-windows': + return 5; + case 'e2e-fdc': + return 6; + case 'e2e-pipeline': + return 7; + default: + return -1; + } +} + Future _findIssue(HttpClient client, String token, String repo) async { final url = Uri.parse('https://api.github.com/repos/$repo/issues?labels=nightly-testing&state=open'); final request = await client.getUrl(url); @@ -104,11 +107,21 @@ Future _findIssue(HttpClient client, String token, String repo) async { return null; } -Future _createIssue(HttpClient client, String token, String repo, String newRow) async { +Future _createIssue(HttpClient client, String token, String repo, String date, String workflowName, String statusIcon, String runUrl) async { final url = Uri.parse('https://api.github.com/repos/$repo/issues'); final request = await client.postUrl(url); _addHeaders(request, token); + final colIndex = _getColumnIndex(workflowName); + final rowData = List.filled(9, '➖ Skipped'); + rowData[0] = date; + if (colIndex != -1) { + rowData[colIndex] = statusIcon; + } + rowData[8] = '[View Run]($runUrl)'; + + final newRow = '| ${rowData.join(' | ')} |'; + final body = { 'title': '[FlutterFire] Nightly Integration Testing Report', 'labels': ['nightly-testing'], @@ -132,8 +145,7 @@ $newRow } } -Future _updateIssue(HttpClient client, String token, String repo, int issueNumber, String newRow) async { - // Fetch current issue body +Future _updateIssue(HttpClient client, String token, String repo, int issueNumber, String date, String workflowName, String statusIcon, String runUrl) async { final getUrl = Uri.parse('https://api.github.com/repos/$repo/issues/$issueNumber'); final getRequest = await client.getUrl(getUrl); _addHeaders(getRequest, token); @@ -148,10 +160,8 @@ Future _updateIssue(HttpClient client, String token, String repo, int issu final issueJson = jsonDecode(getBody); String currentBody = issueJson['body'] ?? ''; - // Parse and update table - final updatedBody = _appendRow(currentBody, newRow); + final updatedBody = _updateTable(currentBody, date, workflowName, statusIcon, runUrl); - // Update issue final patchUrl = Uri.parse('https://api.github.com/repos/$repo/issues/$issueNumber'); final patchRequest = await client.patchUrl(patchUrl); _addHeaders(patchRequest, token); @@ -169,12 +179,15 @@ Future _updateIssue(HttpClient client, String token, String repo, int issu } } -String _appendRow(String currentBody, String newRow) { +String _updateTable(String currentBody, String date, String workflowName, String statusIcon, String runUrl) { final lines = currentBody.split('\n'); final tableRows = []; var inTable = false; + var foundToday = false; + final colIndex = _getColumnIndex(workflowName); - for (final line in lines) { + for (var i = 0; i < lines.length; i++) { + final line = lines[i]; if (line.startsWith('| Date |')) { inTable = true; continue; @@ -183,18 +196,37 @@ String _appendRow(String currentBody, String newRow) { if (line.startsWith('| ---') || line.startsWith('| :---')) { continue; } - tableRows.add(line); + final cells = line.split('|').map((c) => c.trim()).toList(); + if (cells.length >= 10) { + final rowDate = cells[1]; + if (rowDate == date) { + foundToday = true; + if (colIndex != -1) { + cells[colIndex + 1] = statusIcon; + } + cells[9] = '[View Run]($runUrl)'; + tableRows.add('| ${cells.sublist(1, 10).join(' | ')} |'); + } else { + tableRows.add(line); + } + } } } - tableRows.add(newRow); + if (!foundToday) { + final rowData = List.filled(9, '➖ Skipped'); + rowData[0] = date; + if (colIndex != -1) { + rowData[colIndex] = statusIcon; + } + rowData[8] = '[View Run]($runUrl)'; + tableRows.add('| ${rowData.join(' | ')} |'); + } - // Keep only last 30 rows if (tableRows.length > 30) { tableRows.removeRange(0, tableRows.length - 30); } - // Rebuild body final newBodyLines = []; var processedTable = false; @@ -217,12 +249,17 @@ String _appendRow(String currentBody, String newRow) { } if (!processedTable) { - // Table not found, append it newBodyLines.add('## Testing History (last 30 days)'); newBodyLines.add(''); newBodyLines.add('| Date | Android | iOS | Web | MacOS | Windows | FDC | Pipeline | Notes |'); newBodyLines.add('| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |'); - newBodyLines.add(newRow); + final rowData = List.filled(9, '➖ Skipped'); + rowData[0] = date; + if (colIndex != -1) { + rowData[colIndex] = statusIcon; + } + rowData[8] = '[View Run]($runUrl)'; + newBodyLines.add('| ${rowData.join(' | ')} |'); } return newBodyLines.join('\n'); diff --git a/.github/workflows/update_dashboard.yaml b/.github/workflows/update_dashboard.yaml new file mode 100644 index 000000000000..b7b4fec8a529 --- /dev/null +++ b/.github/workflows/update_dashboard.yaml @@ -0,0 +1,30 @@ +name: update-dashboard + +on: + workflow_run: + workflows: ["e2e-android", "e2e-iOS", "e2e-web", "e2e-macOS", "e2e-windows", "e2e-fdc", "e2e-pipeline"] + types: + - completed + +permissions: + contents: read + issues: write + +jobs: + update: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.event == 'schedule' || github.event.workflow_run.event == 'workflow_dispatch' }} + steps: + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + with: + channel: 'stable' + - name: Update Dashboard Issue + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WORKFLOW_NAME: ${{ github.event.workflow_run.name }} + STATUS: ${{ github.event.workflow_run.conclusion }} + RUN_ID: ${{ github.event.workflow_run.id }} + REPO: ${{ github.repository }} + run: | + dart .github/workflows/scripts/nightly_issue_dashboard.dart From 8b5586ae456e50a6860a733c38eac63e08fa1403 Mon Sep 17 00:00:00 2001 From: Cynthia J Date: Thu, 23 Apr 2026 22:41:35 -0700 Subject: [PATCH 4/6] add timeout for writing issue --- .github/workflows/update_dashboard.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update_dashboard.yaml b/.github/workflows/update_dashboard.yaml index b7b4fec8a529..d0c14a4a6ce5 100644 --- a/.github/workflows/update_dashboard.yaml +++ b/.github/workflows/update_dashboard.yaml @@ -13,6 +13,7 @@ permissions: jobs: update: runs-on: ubuntu-latest + timeout-minutes: 5 if: ${{ github.event.workflow_run.event == 'schedule' || github.event.workflow_run.event == 'workflow_dispatch' }} steps: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 From 1119997bd047c94875df00d3f528f493ee4a5fea Mon Sep 17 00:00:00 2001 From: Cynthia J Date: Thu, 23 Apr 2026 22:43:41 -0700 Subject: [PATCH 5/6] comment out concurrency block for testing --- .github/workflows/nightly.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index cea5e5288142..ab82573ec3df 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -1,8 +1,8 @@ name: nightly-ci -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false +# concurrency: +# group: ${{ github.workflow }}-${{ github.ref }} +# cancel-in-progress: false on: # schedule: From 8810fb697167a51d622db6dbaaa8d23ccf4243ac Mon Sep 17 00:00:00 2001 From: Cynthia J Date: Fri, 24 Apr 2026 08:14:57 -0700 Subject: [PATCH 6/6] update concurrency group --- .github/workflows/android.yaml | 2 +- .github/workflows/e2e_tests_fdc.yaml | 2 +- .github/workflows/e2e_tests_pipeline.yaml | 2 +- .github/workflows/ios.yaml | 2 +- .github/workflows/macos.yaml | 2 +- .github/workflows/web.yaml | 2 +- .github/workflows/windows.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index f2b5b413274b..5e4f6d5f34b9 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -1,7 +1,7 @@ name: e2e-android concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-android cancel-in-progress: true on: diff --git a/.github/workflows/e2e_tests_fdc.yaml b/.github/workflows/e2e_tests_fdc.yaml index ff4036c29f0a..b260ad033243 100644 --- a/.github/workflows/e2e_tests_fdc.yaml +++ b/.github/workflows/e2e_tests_fdc.yaml @@ -1,7 +1,7 @@ name: e2e-fdc concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-fdc cancel-in-progress: true on: diff --git a/.github/workflows/e2e_tests_pipeline.yaml b/.github/workflows/e2e_tests_pipeline.yaml index fe9f71045124..f8b4732d65e9 100644 --- a/.github/workflows/e2e_tests_pipeline.yaml +++ b/.github/workflows/e2e_tests_pipeline.yaml @@ -1,7 +1,7 @@ name: e2e-pipeline concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-pipeline cancel-in-progress: true on: diff --git a/.github/workflows/ios.yaml b/.github/workflows/ios.yaml index bee1f26ef59e..90859a335e76 100644 --- a/.github/workflows/ios.yaml +++ b/.github/workflows/ios.yaml @@ -1,7 +1,7 @@ name: e2e-iOS concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-ios cancel-in-progress: true on: diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index fb1ce245bc21..0ec18a7b9d1c 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -1,7 +1,7 @@ name: e2e-macOS concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-macos cancel-in-progress: true on: diff --git a/.github/workflows/web.yaml b/.github/workflows/web.yaml index eba42bc3fc37..d77f622f367f 100644 --- a/.github/workflows/web.yaml +++ b/.github/workflows/web.yaml @@ -1,7 +1,7 @@ name: e2e-web concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-web cancel-in-progress: true on: diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 16b8c8e24ecb..52bae4e486cf 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -1,7 +1,7 @@ name: e2e-windows concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-windows cancel-in-progress: true on: