Răsfoiți Sursa

fix: zip qs testing workflows (#15358)

Nick Cooke 5 luni în urmă
părinte
comite
5349b87bb2

+ 194 - 137
.github/workflows/zip.yml

@@ -1,3 +1,12 @@
+# This workflow tests the zip distribution of the SDK.
+# There are three ways to configure the source of the zip file for testing:
+# 1. To iterate on a PR: Set the `PINNED_RUN_ID` environment variable in this
+#    file to a successful zip packaging run. This is useful for avoiding
+#    re-running the packaging jobs on every commit.
+# 2. For manual runs: Trigger the workflow via the GitHub UI (`workflow_dispatch`)
+#    and provide a "Run ID of a previous successful zip workflow" in the input.
+# 3. By default (for scheduled and other PR runs): The workflow will build the
+#    zip from the current commit (HEAD).
 name: zip
 
 # TODO(ncooke3): Add FirebaseAI test.
@@ -6,6 +15,11 @@ permissions:
   actions: read
   contents: read
 
+env:
+  # When a run_id is specified, build jobs will be skipped and the specified
+  # run's artifacts will be used for testing.
+  PINNED_RUN_ID: '17965877651'
+
 on:
   pull_request:
     paths:
@@ -39,12 +53,27 @@ concurrency:
     cancel-in-progress: true
 
 jobs:
+  should_package:
+    runs-on: ubuntu-latest
+    outputs:
+      should_package: ${{ steps.check.outputs.should_package }}
+    steps:
+    - name: Check if packaging should be skipped
+      id: check
+      run: |
+        if [[ -n "${{ env.PINNED_RUN_ID }}" || -n "${{ github.event.inputs.zip_run_id }}" ]]; then
+          echo "should_package=false" >> $GITHUB_OUTPUT
+        else
+          echo "should_package=true" >> $GITHUB_OUTPUT
+        fi
+
   package-release:
+    needs: should_package
     # Don't run on private repo.
     if: |
       github.repository == 'firebase/firebase-ios-sdk' &&
       contains(fromJSON('["schedule", "pull_request", "workflow_dispatch"]'), github.event_name) &&
-      github.event.inputs.zip_run_id == ''
+      needs.should_package.outputs.should_package == 'true'
     runs-on: macos-14
     steps:
     - uses: actions/checkout@v4
@@ -71,11 +100,12 @@ jobs:
         path: release_zip_dir
 
   build:
+    needs: should_package
     # Don't run on private repo unless it is a PR.
     if: |
       github.repository == 'firebase/firebase-ios-sdk' &&
       contains(fromJSON('["schedule", "pull_request", "workflow_dispatch"]'), github.event_name) &&
-      github.event.inputs.zip_run_id == ''
+      needs.should_package.outputs.should_package == 'true'
     runs-on: macos-14
     steps:
     - uses: actions/checkout@v4
@@ -87,7 +117,8 @@ jobs:
         swift build -v
 
   package-head:
-    needs: build
+    needs: [build, should_package]
+    if: needs.should_package.outputs.should_package == 'true'
     strategy:
       matrix:
         linking_type: [static, dynamic]
@@ -110,16 +141,71 @@ jobs:
            build-head \
            ${{ matrix.linking_type }}
     - uses: actions/upload-artifact@v4
-      if: ${{ always() }}
+      if: always()
       with:
         name: ${{ matrix.linking_type == 'static' && 'Firebase-actions-dir' || 'Firebase-actions-dir-dynamic' }}
         # Zip the entire output directory since the builder adds subdirectories we don't know the
         # name of.
         path: zip_output_dir
 
+  packaging_done:
+    runs-on: ubuntu-latest
+    needs: [package-head]
+    if: always()
+    outputs:
+      run_id: ${{ steps.get_run_id.outputs.run_id }}
+    steps:
+      - name: Check packaging result
+        if: ${{ needs.package-head.result == 'failure' }}
+        run: |
+          echo "Packaging failed. Aborting."
+          exit 1
+      - name: Get Run ID
+        id: get_run_id
+        run: |
+          if [[ -n "${{ github.event.inputs.zip_run_id }}" ]]; then
+            echo "run_id=${{ github.event.inputs.zip_run_id }}" >> $GITHUB_OUTPUT
+          elif [[ -n "${{ env.PINNED_RUN_ID }}" ]]; then
+            echo "run_id=${{ env.PINNED_RUN_ID }}" >> $GITHUB_OUTPUT
+          else
+            echo "run_id=${{ github.run_id }}" >> $GITHUB_OUTPUT
+          fi
+
+  check_framework_firestore_symbols:
+    needs: packaging_done
+    if: ${{ !cancelled() }}
+    env:
+      FIREBASECI_USE_LATEST_GOOGLEAPPMEASUREMENT: 1
+    runs-on: macos-14
+    steps:
+      - name: Xcode 16.2
+        run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
+      - uses: actions/checkout@v4
+      - name: Get framework dir
+        uses: actions/download-artifact@v4.1.7
+        with:
+          name: Firebase-actions-dir
+          run-id: ${{ needs.packaging_done.outputs.run_id }}
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+      - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
+      - name: Setup Bundler
+        run: ./scripts/setup_bundler.sh
+      - name: Install xcpretty
+        run: gem install xcpretty
+      - name: Move frameworks
+        run: |
+          mkdir -p "${HOME}"/ios_frameworks/
+          find "${GITHUB_WORKSPACE}" -name "Firebase*latest.zip" -exec unzip -d "${HOME}"/ios_frameworks/ {} +
+      - uses: actions/checkout@v4
+      - name: Check linked Firestore.xcframework for unlinked symbols.
+        run: |
+          scripts/check_firestore_symbols.sh \
+            $(pwd) \
+            "${HOME}"/ios_frameworks/Firebase/FirebaseFirestore/FirebaseFirestoreInternal.xcframework
+
   quickstart_framework_abtesting:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
+    needs: packaging_done
+    if: ${{ !cancelled() }}
     env:
       plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
       SDK: "ABTesting"
@@ -136,7 +222,7 @@ jobs:
       uses: actions/download-artifact@v4.1.7
       with:
         name: ${{ matrix.artifact }}
-        run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
+        run-id: ${{ needs.packaging_done.outputs.run_id }}
         github-token: ${{ secrets.GITHUB_TOKEN }}
     - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
     - name: Xcode
@@ -162,24 +248,30 @@ jobs:
     - name: Test Quickstart
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}")
     - name: Remove data before upload
-      if: ${{ failure() }}
+      if: always()
       run: scripts/remove_data.sh abtesting
     # - uses: actions/upload-artifact@v4
-    #   if: ${{ failure() }}
+    #   if: failure()
     #   with:
     #     name: quickstart_artifacts_abtesting
-    #     path: quickstart-ios/
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: quickstart_artifacts_abtesting
+        path: |
+          quickstart-ios/
+          !quickstart-ios/**/GoogleService-Info.plist
 
   quickstart_framework_auth:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
+    needs: packaging_done
+    if: ${{ !cancelled() }}
     env:
       plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
       SDK:  "Authentication"
     strategy:
       matrix:
         os: [macos-15]
-        artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic]
+        artifact: [Firebase-actions-dir] , Firebase-actions-dir-dynamic]
         include:
           - os: macos-15
             xcode: Xcode_16.4
@@ -190,7 +282,7 @@ jobs:
       uses: actions/download-artifact@v4.1.7
       with:
         name: ${{ matrix.artifact }}
-        run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
+        run-id: ${{ needs.packaging_done.outputs.run_id }}
         github-token: ${{ secrets.GITHUB_TOKEN }}
     - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
     - name: Xcode
@@ -212,18 +304,17 @@ jobs:
         quickstart-ios/authentication/GoogleService-Info.plist "$plist_secret"
     - name: Test Swift Quickstart
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}")
-    - name: Remove data before upload
-      if: ${{ failure() }}
-      run: scripts/remove_data.sh authentiation
-    # - uses: actions/upload-artifact@v4
-    #   if: ${{ failure() }}
-    #   with:
-    #     name: quickstart_artifacts_auth
-    #     path: quickstart-ios/
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: quickstart_artifacts_auth
+        path: |
+          quickstart-ios/
+          !quickstart-ios/**/GoogleService-Info.plist
 
   quickstart_framework_config:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
+    needs: packaging_done
+    if: ${{ !cancelled() }}
     env:
       plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
       SDK: "Config"
@@ -240,7 +331,7 @@ jobs:
       uses: actions/download-artifact@v4.1.7
       with:
         name: ${{ matrix.artifact }}
-        run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
+        run-id: ${{ needs.packaging_done.outputs.run_id }}
         github-token: ${{ secrets.GITHUB_TOKEN }}
     - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
     - name: Xcode
@@ -262,17 +353,17 @@ jobs:
     - name: Test Swift Quickstart
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}")
     - name: Remove data before upload
-      if: ${{ failure() }}
+      if: always()
       run: scripts/remove_data.sh config
-    # - uses: actions/upload-artifact@v4
-    #   if: ${{ failure() }}
-    #   with:
-    #     name: quickstart_artifacts_config
-    #     path: quickstart-ios/
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: quickstart_artifacts_config
+        path: quickstart-ios/
 
   quickstart_framework_crashlytics:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
+    needs: packaging_done
+    if: ${{ !cancelled() }}
     env:
       plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
       SDK: "Crashlytics"
@@ -289,7 +380,7 @@ jobs:
       uses: actions/download-artifact@v4.1.7
       with:
         name: ${{ matrix.artifact }}
-        run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
+        run-id: ${{ needs.packaging_done.outputs.run_id }}
         github-token: ${{ secrets.GITHUB_TOKEN }}
     - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
     - name: Xcode
@@ -303,13 +394,18 @@ jobs:
     - uses: actions/checkout@v4
     - name: Setup quickstart
       run: |
-              SAMPLE="$SDK" TARGET="${SDK}Example" scripts/setup_quickstart_framework.sh \
-                                               "${HOME}"/ios_frameworks/Firebase/FirebaseCrashlytics/* \
-                                               "${HOME}"/ios_frameworks/Firebase/FirebaseAnalytics/*
-              cp quickstart-ios/crashlytics/LegacyCrashlyticsQuickstart/Firebase/run quickstart-ios/crashlytics/LegacyCrashlyticsQuickstart
-              cp quickstart-ios/crashlytics/LegacyCrashlyticsQuickstart/Firebase/upload-symbols quickstart-ios/crashlytics/LegacyCrashlyticsQuickstart
-              chmod +x quickstart-ios/crashlytics/LegacyCrashlyticsQuickstart/run
-              chmod +x quickstart-ios/crashlytics/LegacyCrashlyticsQuickstart/upload-symbols
+        rm -rf "${HOME}"/ios_frameworks/Firebase/FirebaseAnalytics/FirebaseAnalytics*
+        rm -rf "${HOME}"/ios_frameworks/Firebase/FirebaseAnalytics/GoogleAppMeasurement*
+        rm -rf "${HOME}"/ios_frameworks/Firebase/FirebaseAnalytics/GoogleAds*
+        SAMPLE="$SDK" TARGET="${SDK}Example" scripts/setup_quickstart_framework.sh \
+          "${HOME}"/ios_frameworks/Firebase/FirebaseCrashlytics/* \
+          "${HOME}"/ios_frameworks/Firebase/FirebaseAnalytics/*
+    - name: Add frameworks to Crashlytics watchOS target
+      run: |
+        cd quickstart-ios/crashlytics
+        "${GITHUB_WORKSPACE}"/quickstart-ios/scripts/add_framework_script.rb --sdk "Crashlytics" --target "CrashlyticsExample_(watchOS)_Extension" --framework_path Firebase/
+    - name: Patch Crashlytics Run Script Path
+      run: scripts/patch_crashlytics_run_path.rb
     # TODO(#8057): Restore Swift Quickstart
     # - name: Setup swift quickstart
     #   env:
@@ -327,18 +423,17 @@ jobs:
     #   env:
     #     LEGACY: true
     #   run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}" swift)
-    - name: Remove data before upload
-      if: ${{ failure() }}
-      run: scripts/remove_data.sh crashlytics
-    # - uses: actions/upload-artifact@v4
-    #   if: ${{ failure() }}
-    #   with:
-    #     name: quickstart_artifacts_crashlytics
-    #     path: quickstart-ios/
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: quickstart_artifacts_crashlytics
+        path: |
+          quickstart-ios/
+          !quickstart-ios/**/GoogleService-Info.plist
 
   quickstart_framework_database:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
+    needs: packaging_done
+    if: ${{ !cancelled() }}
     env:
       plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
       SDK: "Database"
@@ -354,7 +449,7 @@ jobs:
       uses: actions/download-artifact@v4.1.7
       with:
         name: ${{ matrix.artifact }}
-        run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
+        run-id: ${{ needs.packaging_done.outputs.run_id }}
         github-token: ${{ secrets.GITHUB_TOKEN }}
     - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
     - name: Xcode
@@ -379,18 +474,17 @@ jobs:
         quickstart-ios/database/GoogleService-Info.plist "$plist_secret"
     - name: Test Quickstart
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}")
-    - name: Remove data before upload
-      if: ${{ failure() }}
-      run: scripts/remove_data.sh database
-    # - uses: actions/upload-artifact@v4
-    #   if: ${{ failure() }}
-    #   with:
-    #     name: quickstart_artifacts database
-    #     path: quickstart-ios/
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: quickstart_artifacts_database
+        path: |
+          quickstart-ios/
+          !quickstart-ios/**/GoogleService-Info.plist
 
   quickstart_framework_firestore:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
+    needs: packaging_done
+    if: ${{ !cancelled() }}
     env:
       plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
       SDK: "Firestore"
@@ -407,7 +501,7 @@ jobs:
       uses: actions/download-artifact@v4.1.7
       with:
         name: ${{ matrix.artifact }}
-        run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
+        run-id: ${{ needs.packaging_done.outputs.run_id }}
         github-token: ${{ secrets.GITHUB_TOKEN }}
     - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
     - name: Xcode
@@ -426,7 +520,7 @@ jobs:
                                                "${HOME}"/ios_frameworks/Firebase/FirebaseAuth/* \
                                                "${HOME}"/ios_frameworks/Firebase/FirebaseAnalytics/*
     - name: Upload build logs on failure
-      if: ${{ failure() }}
+      if: failure()
       uses: actions/upload-artifact@v4
       with:
         name: build_logs_firestore_${{ matrix.artifact }}_${{ matrix.build-env.os }}
@@ -437,49 +531,17 @@ jobs:
     - name: Test Quickstart
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}")
     - name: Remove data before upload and zip directory to reduce upload size.
-      if: ${{ failure() }}
+      if: always()
       run: scripts/remove_data.sh firestore; zip -r --symlinks quickstart_artifacts_firestore.zip quickstart-ios/
-    # - uses: actions/upload-artifact@v4
-    #   if: ${{ failure() }}
-    #   with:
-    #     name: quickstart_artifacts_firestore_${{ matrix.artifact }}_${{ matrix.build-env.os }}
-    #     path: quickstart_artifacts_firestore.zip
-
-  check_framework_firestore_symbols:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
-    env:
-      FIREBASECI_USE_LATEST_GOOGLEAPPMEASUREMENT: 1
-    runs-on: macos-14
-    steps:
-      - name: Xcode 16.2
-        run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
-      - uses: actions/checkout@v4
-      - name: Get framework dir
-        uses: actions/download-artifact@v4.1.7
-        with:
-          name: Firebase-actions-dir
-          run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
-          github-token: ${{ secrets.GITHUB_TOKEN }}
-      - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
-      - name: Setup Bundler
-        run: ./scripts/setup_bundler.sh
-      - name: Install xcpretty
-        run: gem install xcpretty
-      - name: Move frameworks
-        run: |
-          mkdir -p "${HOME}"/ios_frameworks/
-          find "${GITHUB_WORKSPACE}" -name "Firebase*latest.zip" -exec unzip -d "${HOME}"/ios_frameworks/ {} +
-      - uses: actions/checkout@v4
-      - name: Check linked Firestore.xcframework for unlinked symbols.
-        run: |
-          scripts/check_firestore_symbols.sh \
-            $(pwd) \
-            "${HOME}"/ios_frameworks/Firebase/FirebaseFirestore/FirebaseFirestoreInternal.xcframework
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: quickstart_artifacts_firestore_${{ matrix.artifact }}_${{ matrix.build-env.os }}
+        path: quickstart_artifacts_firestore.zip
 
   quickstart_framework_inappmessaging:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
+    needs: packaging_done
+    if: ${{ !cancelled() }}
     env:
       plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
       SDK: "InAppMessaging"
@@ -496,7 +558,7 @@ jobs:
       uses: actions/download-artifact@v4.1.7
       with:
         name: ${{ matrix.artifact }}
-        run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
+        run-id: ${{ needs.packaging_done.outputs.run_id }}
         github-token: ${{ secrets.GITHUB_TOKEN }}
     - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
     - name: Xcode
@@ -521,18 +583,17 @@ jobs:
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}")
     - name: Test Swift Quickstart
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}" swift)
-    - name: Remove data before upload
-      if: ${{ failure() }}
-      run: scripts/remove_data.sh inappmessaging
-    # - uses: actions/upload-artifact@v4
-    #   if: ${{ failure() }}
-    #   with:
-    #     name: quickstart_artifacts_inappmessaging
-    #     path: quickstart-ios/
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: quickstart_artifacts_inappmessaging
+        path: |
+          quickstart-ios/
+          !quickstart-ios/**/GoogleService-Info.plist
 
   quickstart_framework_messaging:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
+    needs: packaging_done
+    if: ${{ !cancelled() }}
     env:
       plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
       SDK: "Messaging"
@@ -549,7 +610,7 @@ jobs:
       uses: actions/download-artifact@v4.1.7
       with:
         name: ${{ matrix.artifact }}
-        run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
+        run-id: ${{ needs.packaging_done.outputs.run_id }}
         github-token: ${{ secrets.GITHUB_TOKEN }}
     - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
     - name: Xcode
@@ -574,18 +635,17 @@ jobs:
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}")
     - name: Test Swift Quickstart
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}" swift)
-    - name: Remove data before upload
-      if: ${{ failure() }}
-      run: scripts/remove_data.sh messaging
-    # - uses: actions/upload-artifact@v4
-    #   if: ${{ failure() }}
-    #   with:
-    #     name: quickstart_artifacts_messaging
-    #     path: quickstart-ios/
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: quickstart_artifacts_messaging
+        path: |
+          quickstart-ios/
+          !quickstart-ios/**/GoogleService-Info.plist
 
   quickstart_framework_storage:
-    needs: package-head
-    if: ${{ !cancelled() && (success() || github.event.inputs.zip_run_id != '') }}
+    needs: packaging_done
+    if: ${{ !cancelled() }}
     env:
       plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
       SDK: "Storage"
@@ -602,7 +662,7 @@ jobs:
       uses: actions/download-artifact@v4.1.7
       with:
         name: ${{ matrix.artifact }}
-        run-id: ${{ github.event.inputs.zip_run_id || github.run_id }}
+        run-id: ${{ needs.packaging_done.outputs.run_id }}
         github-token: ${{ secrets.GITHUB_TOKEN }}
     - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
     - name: Xcode
@@ -626,13 +686,10 @@ jobs:
         quickstart-ios/storage/GoogleService-Info.plist "$plist_secret"
     - name: Test Quickstart
       run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}")
-    - name: Test Swift Quickstart
-      run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/test_quickstart_framework.sh "${SDK}" swift)
-    - name: Remove data before upload
-      if: ${{ failure() }}
-      run: scripts/remove_data.sh storage
-    # - uses: actions/upload-artifact@v4
-    #   if: ${{ failure() }}
-    #   with:
-    #     name: quickstart_artifacts_storage
-    #     path: quickstart-ios/
+    - uses: actions/upload-artifact@v4
+      if: failure()
+      with:
+        name: quickstart_artifacts_storage
+        path: |
+          quickstart-ios/
+          !quickstart-ios/**/GoogleService-Info.plist

+ 41 - 0
scripts/patch_crashlytics_run_path.rb

@@ -0,0 +1,41 @@
+#!/usr/bin/env ruby
+
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'xcodeproj'
+
+# This script patches the Crashlytics Quickstart's Xcode project to fix the
+# path to the `run` script for XCFramework-based builds.
+#
+# The default project assumes an SPM dependency. This script changes the path
+# to point to the location where the `run` script is placed in the zip
+# distribution test environment.
+
+project_path = 'quickstart-ios/crashlytics/CrashlyticsExample.xcodeproj'
+project = Xcodeproj::Project.open(project_path)
+new_path = '"${SRCROOT}/Firebase/run"'
+
+project.targets.each do |target|
+  target.build_phases.each do |phase|
+    if phase.is_a?(Xcodeproj::Project::Object::PBXShellScriptBuildPhase) && phase.name == 'Run Script'
+      if phase.shell_script.include?('SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run')
+        puts "Patching Run Script phase in target '#{target.name}' to: #{new_path}"
+        phase.shell_script = new_path
+      end
+    end
+  end
+end
+
+project.save

+ 112 - 0
scripts/remove_spm_dependencies.rb

@@ -0,0 +1,112 @@
+#!/usr/bin/env ruby
+
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may
+# obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'xcodeproj'
+require 'set'
+
+# This script removes all Swift Package Manager dependencies from an Xcode project.
+# It's designed to be used in CI to prepare a project for framework-based testing.
+
+# --- Argument Parsing ---
+unless ARGV.length == 1
+  puts "Usage: #{$0} <path_to.xcodeproj>"
+  exit 1
+end
+
+project_path = ARGV[0]
+
+# --- Main Logic ---
+begin
+  project = Xcodeproj::Project.open(project_path)
+rescue => e
+  puts "Error opening project at #{project_path}: #{e.message}"
+  exit 1
+end
+
+puts "Opened project: #{project.path}"
+
+# --- Step 1: Find all SPM product dependencies ---
+package_product_dependencies = project.objects.select do |obj|
+  obj.is_a?(Xcodeproj::Project::Object::XCSwiftPackageProductDependency)
+end
+
+if package_product_dependencies.empty?
+  puts "No SPM product dependencies found to remove."
+else
+  puts "Found #{package_product_dependencies.count} SPM product dependencies. Removing all references..."
+  package_product_dep_uuids = package_product_dependencies.map(&:uuid).to_set
+
+  # --- Step 2: Find all BuildFile objects that reference these SPM products ---
+  build_files_to_remove = project.objects.select do |obj|
+    obj.is_a?(Xcodeproj::Project::Object::PBXBuildFile) &&
+    obj.product_ref &&
+    package_product_dep_uuids.include?(obj.product_ref.uuid)
+  end
+  build_file_uuids_to_remove = build_files_to_remove.map(&:uuid).to_set
+
+  # --- Step 3: Remove references from all targets ---
+  project.targets.each do |target|
+    puts "Cleaning target '#{target.name}'..."
+
+    # Remove from target dependencies list
+    removed_deps = target.dependencies.reject! do |dep|
+      package_product_dep_uuids.include?(dep.uuid)
+    end
+    if removed_deps
+      puts "  - Removed #{removed_deps.count} SPM target dependencies."
+    end
+
+    # Remove from build phases (e.g., "Link Binary With Libraries")
+    target.build_phases.each do |phase|
+      next unless phase.respond_to?(:files)
+
+      original_file_count = phase.files.count
+      phase.files.reject! do |build_file|
+        build_file_uuids_to_remove.include?(build_file.uuid)
+      end
+      removed_count = original_file_count - phase.files.count
+      if removed_count > 0
+        puts "  - Removed #{removed_count} SPM build file references from '#{phase.display_name}'."
+      end
+    end
+  end
+
+  # --- Step 4: Delete the now-orphaned BuildFile and dependency objects ---
+  puts "Deleting #{build_files_to_remove.count} SPM BuildFile object(s)..."
+  build_files_to_remove.each(&:remove_from_project)
+
+  puts "Deleting #{package_product_dependencies.count} SPM product dependency object(s)..."
+  package_product_dependencies.each(&:remove_from_project)
+end
+
+# --- Step 5: Remove package references from the project root ---
+unless project.root_object.package_references.empty?
+  puts "Removing #{project.root_object.package_references.count} package reference(s)..."
+  project.root_object.package_references.clear
+  puts "All package references removed from the project."
+else
+  puts "No package references found in the project."
+end
+
+# --- Step 6: Save the modified project ---
+begin
+  project.save
+  puts "Project saved successfully."
+rescue => e
+  puts "Error saving project: #{e.message}"
+  exit 1
+end

+ 5 - 0
scripts/setup_quickstart_framework.sh

@@ -20,6 +20,11 @@ if [ ! -d "quickstart-ios" ]; then
 fi
 QS_SCRIPTS="${REPO}"/quickstart-ios/scripts
 cd quickstart-ios/"${SAMPLE}"
+git checkout nc/quickstarts
+
+# Remove all SPM dependencies from the project. This is necessary to prepare
+# the project for framework-based testing.
+"${REPO}"/scripts/remove_spm_dependencies.rb "${SAMPLE}Example.xcodeproj"
 
 if [[ ! -z "$LEGACY" ]]; then
   cd "Legacy${SAMPLE}Quickstart"

+ 15 - 2
scripts/zip_quickstart_test.sh

@@ -42,10 +42,23 @@ else
   device_name="iPhone 16"
 fi
 
+# Define project and scheme names
+PROJECT_NAME="${SAMPLE}Example.xcodeproj"
+SCHEME_NAME="${SAMPLE}Example${SWIFT_SUFFIX}"
+
+# Check if the scheme exists before attempting to build.
+# The `awk` command prints all lines from "Schemes:" to the end of the output.
+if ! xcodebuild -list -project "${PROJECT_NAME}" | awk '/Schemes:/,0' | grep -q "^\s*${SCHEME_NAME}"$; then
+    echo "Error: Scheme '${SCHEME_NAME}' not found in project '${PROJECT_NAME}'."
+    echo "Available schemes from '${PROJECT_NAME}':"
+    xcodebuild -list -project "${PROJECT_NAME}"
+    exit 65
+fi
+
 (
 xcodebuild \
--project ${SAMPLE}Example.xcodeproj \
--scheme  ${SAMPLE}Example${SWIFT_SUFFIX} \
+-project ${PROJECT_NAME} \
+-scheme  ${SCHEME_NAME} \
 -destination "platform=iOS Simulator,name=$device_name" "SWIFT_VERSION=5.3" "OTHER_LDFLAGS=\$(OTHER_LDFLAGS) -ObjC" "FRAMEWORK_SEARCH_PATHS= \$(PROJECT_DIR)/Firebase/" HEADER_SEARCH_PATHS='$(PROJECT_DIR)/Firebase' \
 build \
 ) || EXIT_STATUS=$?