From 8b4f4076188cf894c53912f3524a3101011e0779 Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 9 Oct 2025 14:15:11 -0500 Subject: [PATCH 01/10] Add script for decrypting secrets --- scripts/tests/ai/decrypt_secrets.sh | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100755 scripts/tests/ai/decrypt_secrets.sh diff --git a/scripts/tests/ai/decrypt_secrets.sh b/scripts/tests/ai/decrypt_secrets.sh new file mode 100755 index 00000000000..16719ef7964 --- /dev/null +++ b/scripts/tests/ai/decrypt_secrets.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# Copyright 2025 Google +# +# 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. + +# USAGE: ./decrypt_secrests.sh +# +# Decrypts the secret files used for integration tests with the +# FirebaseAI sample app. +# +# Expects the environment variable "secrets_passphrase" to be set. +# This should be set to gpg password for encrypting/decrypting the files. + +if [[ ! "$secrets_passphrase" ]]; then + echo "Missing environment variable (secrets_passphrase) to decrypt the files with." + exit 1 +fi + +decrypt () { + local source=$1 + local dest=$2 + + scripts/decrypt_gha_secret.sh $1 $2 "$secrets_passphrase" + echo "$source => $dest" +} + +echo "Decrypting files" + +decrypt scripts/gha-encrypted/FirebaseAI/TestApp-GoogleService-Info.plist.gpg \ + FirebaseAI/Tests/TestApp/Resources/GoogleService-Info.plist + +decrypt scripts/gha-encrypted/FirebaseAI/TestApp-GoogleService-Info-Spark.plist.gpg \ + FirebaseAI/Tests/TestApp/Resources/GoogleService-Info-Spark.plist + +decrypt scripts/gha-encrypted/FirebaseAI/TestApp-Credentials.swift.gpg \ + FirebaseAI/Tests/TestApp/Tests/Integration/Credentials.swift + +echo "Files decrypted" From f42243eae5763a1885f36063920870ef88a726d9 Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 9 Oct 2025 14:15:18 -0500 Subject: [PATCH 02/10] Add script for running tests --- scripts/tests/ai/run.sh | 134 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100755 scripts/tests/ai/run.sh diff --git a/scripts/tests/ai/run.sh b/scripts/tests/ai/run.sh new file mode 100755 index 00000000000..24e72c79f1f --- /dev/null +++ b/scripts/tests/ai/run.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash + +# Copyright 2025 Google +# +# 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. + +# USAGE: ./run.sh [Xcode] [target] +# +# EXAMPLE: ./run.sh Xcode_16.4 iOS +# +# Runs the integration tests for the FirebaseAI sample app. +# +# ARGUMENTS: +# - [Xcode]: The version of Xcode to use. Defaults to searching for Xcode under `/Applications`. +# - [target]: The platform target to test for (eg; iOS or macOS). Defaults to "iOS". +# +# ENVIRONMENT VARIABLES: +# - : The app check debug token to use. This is required. +# - : The gpg secret that the secret files were encrypted with. +# This is semi-required. If this is not present, then the decrypted secret files +# must already be present (ie; you manually added them). +# +# ADDITIONAL NOTES: +# If you provide a value for "secrets_passphrase", then the secret files will be decrypted +# on the fly, and will be deleted after the tests run. Even if the tests fail, the +# secret files will be deleted before the error propogates up. If you do NOT pass +# a value for "secrets_passphrase", and instead manually added the decrypted secret +# files, then the decrypted files will NOT be deleted after the script runs. +# +# If you don't specify an Xcode version, the script will attempt to search for an install +# under `/Applications`. If no installs or found, an error will be thrown. If multiple +# installs are found, they will be listed, and the script will prompt you to rerun it, +# while manually specifying the Xcode version to use. + +shopt -s nullglob + +xcode=$1 + +# Look for Xcode installations if a version wasn't provided explicitly +if [[ ! "$xcode" ]]; then + apps=(/Applications/Xcode*.app) + names=() + for p in "${apps[@]}"; do + names+=("$(basename "${p%.app}")") + done + + case ${#names[@]} in + 0) + echo "No Xcode installs found in /Applications" >&2 + exit 1 + ;; + 1) + xcode="${names[0]}" + echo "Using Xcode version: $xcode" + ;; + *) + echo "Multiple Xcode installs found:" + printf ' %s\n' "${names[@]}" + echo "Manually specify an Xcode version instead" + echo "USAGE: $0 [Xcode] [target]" + exit 1 + ;; + esac +fi + +target="iOS" +if [[ $# -gt 1 ]]; then + target="$2" +fi + +if [[ ! "$TEST_RUNNER_FIRAAppCheckDebugToken" ]]; then + echo "Missing required environment variable for app check debug token (TEST_RUNNER_FIRAAppCheckDebugToken)" + exit 1 +fi + +# Files used in integration tests. These are usually encrypted under /scripts/gha-encrypted/FirebaseAI +secret_files=( + "FirebaseAI/Tests/TestApp/Resources/GoogleService-Info.plist" + "FirebaseAI/Tests/TestApp/Resources/GoogleService-Info-Spark.plist" + "FirebaseAI/Tests/TestApp/Tests/Integration/Credentials.swift" +) + +# Checks if any of the secret files are absent, throwing an error if so. +check_for_secret_files () { + for file in "${secret_files[@]}"; do + if [[ ! -f "$file" ]]; then + echo "Missing required decrypted secret file: $file" + exit 1 + fi + done +} + +delete_secrets=true + +if [[ ! "$secrets_passphrase" ]]; then + echo "Environment variable 'secrets_passphrase' wasn't set. Checking if files are already present" + check_for_secret_files + echo "Files are present, moving forward" + + unset delete_secrets +else + scripts/tests/ai/decrypt_secrets.sh +fi + +( + echo "Selecting Xcode version: $xcode" + sudo xcode-select -s /Applications/$xcode.app/Contents/Developer + + echo "Running integration tests for target: $target" + scripts/build.sh FirebaseAIIntegration $target +) +# We run the tests in a subshell so we can delete the secret files regardless of the exit code. +exit_code=$? + +# We only delete the decrypted secret files if we were the ones to decrypt them. +if [[ "$delete_secrets" ]]; then + echo "Removing secret files" + for file in "${secret_files[@]}"; do + rm $file + done + echo "Secret files removed" +fi + +exit $exit_code From e5d1da0deb8630c53ac327e4c6f3d727f9244adb Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 9 Oct 2025 15:20:31 -0500 Subject: [PATCH 03/10] Use set -eo --- scripts/tests/ai/run.sh | 43 +++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/scripts/tests/ai/run.sh b/scripts/tests/ai/run.sh index 24e72c79f1f..b905c281cad 100755 --- a/scripts/tests/ai/run.sh +++ b/scripts/tests/ai/run.sh @@ -43,6 +43,7 @@ # while manually specifying the Xcode version to use. shopt -s nullglob +set -eo pipefail xcode=$1 @@ -100,35 +101,31 @@ check_for_secret_files () { done } -delete_secrets=true +cleanup () { + # We only delete the decrypted secret files if we were the ones to decrypt them. + if [[ "$delete_secrets" ]]; then + echo "Removing secret files" + for file in "${secret_files[@]}"; do + rm -f $file + done + echo "Secret files removed" + fi +} + +# always run cleanup last, even on errors +trap 'exit_code=$?; cleanup; exit "$exit_code"' ERR +trap 'cleanup' EXIT if [[ ! "$secrets_passphrase" ]]; then echo "Environment variable 'secrets_passphrase' wasn't set. Checking if files are already present" check_for_secret_files echo "Files are present, moving forward" - - unset delete_secrets + delete_secrets=true else scripts/tests/ai/decrypt_secrets.sh fi -( - echo "Selecting Xcode version: $xcode" - sudo xcode-select -s /Applications/$xcode.app/Contents/Developer - - echo "Running integration tests for target: $target" - scripts/build.sh FirebaseAIIntegration $target -) -# We run the tests in a subshell so we can delete the secret files regardless of the exit code. -exit_code=$? - -# We only delete the decrypted secret files if we were the ones to decrypt them. -if [[ "$delete_secrets" ]]; then - echo "Removing secret files" - for file in "${secret_files[@]}"; do - rm $file - done - echo "Secret files removed" -fi - -exit $exit_code +echo "Selecting Xcode version: $xcode" +sudo xcode-select -s /Applications/$xcode.app/Contents/Developer +echo "Running integration tests for target: $target" +scripts/build.sh FirebaseAIIntegration $target From 86c1f89b1cafb1f11896efe732ccad695c02b8a8 Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 9 Oct 2025 15:24:14 -0500 Subject: [PATCH 04/10] Use brackets and double quotes for variables --- scripts/tests/ai/decrypt_secrets.sh | 6 +++--- scripts/tests/ai/run.sh | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/tests/ai/decrypt_secrets.sh b/scripts/tests/ai/decrypt_secrets.sh index 16719ef7964..1a97b63140b 100755 --- a/scripts/tests/ai/decrypt_secrets.sh +++ b/scripts/tests/ai/decrypt_secrets.sh @@ -22,7 +22,7 @@ # Expects the environment variable "secrets_passphrase" to be set. # This should be set to gpg password for encrypting/decrypting the files. -if [[ ! "$secrets_passphrase" ]]; then +if [[ ! "${secrets_passphrase}" ]]; then echo "Missing environment variable (secrets_passphrase) to decrypt the files with." exit 1 fi @@ -31,8 +31,8 @@ decrypt () { local source=$1 local dest=$2 - scripts/decrypt_gha_secret.sh $1 $2 "$secrets_passphrase" - echo "$source => $dest" + scripts/decrypt_gha_secret.sh "$1" "$2" "${secrets_passphrase}" + echo "${source} => ${dest}" } echo "Decrypting files" diff --git a/scripts/tests/ai/run.sh b/scripts/tests/ai/run.sh index b905c281cad..1562def3761 100755 --- a/scripts/tests/ai/run.sh +++ b/scripts/tests/ai/run.sh @@ -48,7 +48,7 @@ set -eo pipefail xcode=$1 # Look for Xcode installations if a version wasn't provided explicitly -if [[ ! "$xcode" ]]; then +if [[ ! "${xcode}" ]]; then apps=(/Applications/Xcode*.app) names=() for p in "${apps[@]}"; do @@ -62,7 +62,7 @@ if [[ ! "$xcode" ]]; then ;; 1) xcode="${names[0]}" - echo "Using Xcode version: $xcode" + echo "Using Xcode version: ${xcode}" ;; *) echo "Multiple Xcode installs found:" @@ -79,7 +79,7 @@ if [[ $# -gt 1 ]]; then target="$2" fi -if [[ ! "$TEST_RUNNER_FIRAAppCheckDebugToken" ]]; then +if [[ ! "${TEST_RUNNER_FIRAAppCheckDebugToken}" ]]; then echo "Missing required environment variable for app check debug token (TEST_RUNNER_FIRAAppCheckDebugToken)" exit 1 fi @@ -94,8 +94,8 @@ secret_files=( # Checks if any of the secret files are absent, throwing an error if so. check_for_secret_files () { for file in "${secret_files[@]}"; do - if [[ ! -f "$file" ]]; then - echo "Missing required decrypted secret file: $file" + if [[ ! -f "${file}" ]]; then + echo "Missing required decrypted secret file: ${file}" exit 1 fi done @@ -103,10 +103,10 @@ check_for_secret_files () { cleanup () { # We only delete the decrypted secret files if we were the ones to decrypt them. - if [[ "$delete_secrets" ]]; then + if [[ "${delete_secrets}" ]]; then echo "Removing secret files" for file in "${secret_files[@]}"; do - rm -f $file + rm -f "${file}" done echo "Secret files removed" fi @@ -116,7 +116,7 @@ cleanup () { trap 'exit_code=$?; cleanup; exit "$exit_code"' ERR trap 'cleanup' EXIT -if [[ ! "$secrets_passphrase" ]]; then +if [[ ! "${secrets_passphrase}" ]]; then echo "Environment variable 'secrets_passphrase' wasn't set. Checking if files are already present" check_for_secret_files echo "Files are present, moving forward" @@ -125,7 +125,7 @@ else scripts/tests/ai/decrypt_secrets.sh fi -echo "Selecting Xcode version: $xcode" -sudo xcode-select -s /Applications/$xcode.app/Contents/Developer -echo "Running integration tests for target: $target" -scripts/build.sh FirebaseAIIntegration $target +echo "Selecting Xcode version: ${xcode}" +sudo xcode-select -s /Applications/"${xcode}".app/Contents/Developer +echo "Running integration tests for target: ${target}" +scripts/build.sh FirebaseAIIntegration "${target}" From a10c676595cb7feade305240aae769db4c576f06 Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 9 Oct 2025 15:25:53 -0500 Subject: [PATCH 05/10] Use explicit -n --- scripts/tests/ai/decrypt_secrets.sh | 2 +- scripts/tests/ai/run.sh | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/tests/ai/decrypt_secrets.sh b/scripts/tests/ai/decrypt_secrets.sh index 1a97b63140b..6b3dd8d6d03 100755 --- a/scripts/tests/ai/decrypt_secrets.sh +++ b/scripts/tests/ai/decrypt_secrets.sh @@ -22,7 +22,7 @@ # Expects the environment variable "secrets_passphrase" to be set. # This should be set to gpg password for encrypting/decrypting the files. -if [[ ! "${secrets_passphrase}" ]]; then +if [[ -n "${secrets_passphrase}" ]]; then echo "Missing environment variable (secrets_passphrase) to decrypt the files with." exit 1 fi diff --git a/scripts/tests/ai/run.sh b/scripts/tests/ai/run.sh index 1562def3761..cfdf5b26ab8 100755 --- a/scripts/tests/ai/run.sh +++ b/scripts/tests/ai/run.sh @@ -48,7 +48,7 @@ set -eo pipefail xcode=$1 # Look for Xcode installations if a version wasn't provided explicitly -if [[ ! "${xcode}" ]]; then +if [[ -n "${xcode}" ]]; then apps=(/Applications/Xcode*.app) names=() for p in "${apps[@]}"; do @@ -79,7 +79,7 @@ if [[ $# -gt 1 ]]; then target="$2" fi -if [[ ! "${TEST_RUNNER_FIRAAppCheckDebugToken}" ]]; then +if [[ -n "${TEST_RUNNER_FIRAAppCheckDebugToken}" ]]; then echo "Missing required environment variable for app check debug token (TEST_RUNNER_FIRAAppCheckDebugToken)" exit 1 fi @@ -103,7 +103,7 @@ check_for_secret_files () { cleanup () { # We only delete the decrypted secret files if we were the ones to decrypt them. - if [[ "${delete_secrets}" ]]; then + if [[ -n "${delete_secrets}" ]]; then echo "Removing secret files" for file in "${secret_files[@]}"; do rm -f "${file}" @@ -116,7 +116,7 @@ cleanup () { trap 'exit_code=$?; cleanup; exit "$exit_code"' ERR trap 'cleanup' EXIT -if [[ ! "${secrets_passphrase}" ]]; then +if [[ -n "${secrets_passphrase}" ]]; then echo "Environment variable 'secrets_passphrase' wasn't set. Checking if files are already present" check_for_secret_files echo "Files are present, moving forward" From e151c43258f733358b7e069d23c84bcdf7c25d9c Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 9 Oct 2025 15:26:29 -0500 Subject: [PATCH 06/10] Fix typo --- scripts/tests/ai/decrypt_secrets.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tests/ai/decrypt_secrets.sh b/scripts/tests/ai/decrypt_secrets.sh index 6b3dd8d6d03..1852d8e1c46 100755 --- a/scripts/tests/ai/decrypt_secrets.sh +++ b/scripts/tests/ai/decrypt_secrets.sh @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# USAGE: ./decrypt_secrests.sh +# USAGE: ./decrypt_secrets.sh # # Decrypts the secret files used for integration tests with the # FirebaseAI sample app. From 92ed20305a4d70920d8ce1622f97cdcd25b0c1e6 Mon Sep 17 00:00:00 2001 From: Daymon Date: Fri, 10 Oct 2025 17:12:20 -0500 Subject: [PATCH 07/10] Add future todo re paul's suggestion --- scripts/tests/ai/run.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/tests/ai/run.sh b/scripts/tests/ai/run.sh index cfdf5b26ab8..aae1adbdcdd 100755 --- a/scripts/tests/ai/run.sh +++ b/scripts/tests/ai/run.sh @@ -42,6 +42,8 @@ # installs are found, they will be listed, and the script will prompt you to rerun it, # while manually specifying the Xcode version to use. +# TODO: (b/450976183) Implement a generic alternative for all SDKs + shopt -s nullglob set -eo pipefail From 9ed63cd0c435e3f1e16b11122813930679a8bf25 Mon Sep 17 00:00:00 2001 From: Daymon Date: Fri, 10 Oct 2025 17:12:51 -0500 Subject: [PATCH 08/10] Add missing LLC suffix --- scripts/tests/ai/decrypt_secrets.sh | 2 +- scripts/tests/ai/run.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/tests/ai/decrypt_secrets.sh b/scripts/tests/ai/decrypt_secrets.sh index 1852d8e1c46..7d5cdd1f14f 100755 --- a/scripts/tests/ai/decrypt_secrets.sh +++ b/scripts/tests/ai/decrypt_secrets.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2025 Google +# 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. diff --git a/scripts/tests/ai/run.sh b/scripts/tests/ai/run.sh index aae1adbdcdd..a10041e1c09 100755 --- a/scripts/tests/ai/run.sh +++ b/scripts/tests/ai/run.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2025 Google +# 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. From 4f4cb2cd62bd0ec1a27f2ac6c0dbecc40996bf3b Mon Sep 17 00:00:00 2001 From: Daymon Date: Fri, 10 Oct 2025 17:13:26 -0500 Subject: [PATCH 09/10] Revert "Use explicit -n" This reverts commit a10c676595cb7feade305240aae769db4c576f06. --- scripts/tests/ai/decrypt_secrets.sh | 2 +- scripts/tests/ai/run.sh | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/tests/ai/decrypt_secrets.sh b/scripts/tests/ai/decrypt_secrets.sh index 7d5cdd1f14f..1991e9c9471 100755 --- a/scripts/tests/ai/decrypt_secrets.sh +++ b/scripts/tests/ai/decrypt_secrets.sh @@ -22,7 +22,7 @@ # Expects the environment variable "secrets_passphrase" to be set. # This should be set to gpg password for encrypting/decrypting the files. -if [[ -n "${secrets_passphrase}" ]]; then +if [[ ! "${secrets_passphrase}" ]]; then echo "Missing environment variable (secrets_passphrase) to decrypt the files with." exit 1 fi diff --git a/scripts/tests/ai/run.sh b/scripts/tests/ai/run.sh index a10041e1c09..813619240e0 100755 --- a/scripts/tests/ai/run.sh +++ b/scripts/tests/ai/run.sh @@ -50,7 +50,7 @@ set -eo pipefail xcode=$1 # Look for Xcode installations if a version wasn't provided explicitly -if [[ -n "${xcode}" ]]; then +if [[ ! "${xcode}" ]]; then apps=(/Applications/Xcode*.app) names=() for p in "${apps[@]}"; do @@ -81,7 +81,7 @@ if [[ $# -gt 1 ]]; then target="$2" fi -if [[ -n "${TEST_RUNNER_FIRAAppCheckDebugToken}" ]]; then +if [[ ! "${TEST_RUNNER_FIRAAppCheckDebugToken}" ]]; then echo "Missing required environment variable for app check debug token (TEST_RUNNER_FIRAAppCheckDebugToken)" exit 1 fi @@ -105,7 +105,7 @@ check_for_secret_files () { cleanup () { # We only delete the decrypted secret files if we were the ones to decrypt them. - if [[ -n "${delete_secrets}" ]]; then + if [[ "${delete_secrets}" ]]; then echo "Removing secret files" for file in "${secret_files[@]}"; do rm -f "${file}" @@ -118,7 +118,7 @@ cleanup () { trap 'exit_code=$?; cleanup; exit "$exit_code"' ERR trap 'cleanup' EXIT -if [[ -n "${secrets_passphrase}" ]]; then +if [[ ! "${secrets_passphrase}" ]]; then echo "Environment variable 'secrets_passphrase' wasn't set. Checking if files are already present" check_for_secret_files echo "Files are present, moving forward" From dc66f08b115ca869f2ea9125d243acd8f4aa58c1 Mon Sep 17 00:00:00 2001 From: Daymon Date: Fri, 10 Oct 2025 17:17:13 -0500 Subject: [PATCH 10/10] Make workflow use script --- .github/workflows/firebaseai.yml | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/.github/workflows/firebaseai.yml b/.github/workflows/firebaseai.yml index 610c3c7d07f..3800d3b89a1 100644 --- a/.github/workflows/firebaseai.yml +++ b/.github/workflows/firebaseai.yml @@ -55,19 +55,8 @@ jobs: with: path: .build key: ${{ needs.spm.outputs.cache_key }} - - name: Install Secret GoogleService-Info.plist - run: scripts/decrypt_gha_secret.sh scripts/gha-encrypted/FirebaseAI/TestApp-GoogleService-Info.plist.gpg \ - FirebaseAI/Tests/TestApp/Resources/GoogleService-Info.plist "$secrets_passphrase" - - name: Install Secret GoogleService-Info-Spark.plist - run: scripts/decrypt_gha_secret.sh scripts/gha-encrypted/FirebaseAI/TestApp-GoogleService-Info-Spark.plist.gpg \ - FirebaseAI/Tests/TestApp/Resources/GoogleService-Info-Spark.plist "$secrets_passphrase" - - name: Install Secret Credentials.swift - run: scripts/decrypt_gha_secret.sh scripts/gha-encrypted/FirebaseAI/TestApp-Credentials.swift.gpg \ - FirebaseAI/Tests/TestApp/Tests/Integration/Credentials.swift "$secrets_passphrase" - - name: Xcode - run: sudo xcode-select -s /Applications/${{ matrix.xcode }}.app/Contents/Developer - - name: Run IntegrationTests - run: scripts/build.sh FirebaseAIIntegration ${{ matrix.target }} + - name: Run integration tests + run: scripts/tests/ai/run.sh ${{ matrix.xcode }} ${{ matrix.target }} - name: Upload xcodebuild logs if: failure() uses: actions/upload-artifact@v4