From 8f46db3906d9b100b77e1dcad63bdf44dadc876f Mon Sep 17 00:00:00 2001 From: Ilias Pavlidakis Date: Thu, 21 Nov 2024 13:30:33 +0200 Subject: [PATCH] Improved structure and cache handling --- .github/actions/bootstrap/action.yml | 20 +- .github/actions/build-xcframework/action.yml | 43 ++ .../actions/dynamic-xcode-cache/action.yml | 37 ++ .github/actions/failure-handling/action.yml | 68 +++ .github/workflows/smoke-checks.yml | 470 ++++++++---------- ...DemoManualQualitySelectionButtonView.swift | 3 +- .../DemoReconnectionButtonView.swift | 2 +- .../CallingView/SimpleCallingView.swift | 10 +- DemoApp/Sources/Views/Login/AddUserView.swift | 14 +- .../22-manual-quality-selection.swift | 2 +- .../Utils/ThermalStateObserverTests.swift | 4 - .../Tests/DeeplinkTests.swift | 2 + fastlane/Fastfile | 19 +- 13 files changed, 398 insertions(+), 296 deletions(-) create mode 100644 .github/actions/build-xcframework/action.yml create mode 100644 .github/actions/dynamic-xcode-cache/action.yml create mode 100644 .github/actions/failure-handling/action.yml diff --git a/.github/actions/bootstrap/action.yml b/.github/actions/bootstrap/action.yml index f77b674b2..74bf62e1e 100644 --- a/.github/actions/bootstrap/action.yml +++ b/.github/actions/bootstrap/action.yml @@ -1,12 +1,21 @@ name: 'Bootstrap' description: 'Run bootstrap.sh' + +input: + parent: + description: 'Optional input for job dependencies' + required: false + runs: using: "composite" steps: + - run: echo "IMAGE=${ImageOS}-${ImageVersion}" >> $GITHUB_ENV shell: bash + - run: echo "$HOME/.mint/bin" >> $GITHUB_PATH shell: bash + - name: Cache Mint uses: actions/cache@v4 id: mint-cache @@ -14,6 +23,7 @@ runs: path: ~/.mint key: ${{ env.IMAGE }}-mint-${{ hashFiles('**/Mintfile') }} restore-keys: ${{ env.IMAGE }}-mint- + - name: Cache brew uses: actions/cache@v4 id: brew-cache @@ -27,13 +37,13 @@ runs: restore-keys: ${{ env.IMAGE }}-brew- - uses: ./.github/actions/ruby-cache - uses: ./.github/actions/python-cache - - uses: irgaly/xcode-cache@v1 + + - name: Xcode Cache + uses: ./.github/actions/dynamic-xcode-cache with: - key: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - restore-keys: - xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - xcode-cache-deriveddata-${{ github.workflow }}-${{ github.event.number }} deriveddata-directory: derived_data sourcepackages-directory: spm_cache + parent: ${{ inputs.parent }} + - run: ./Scripts/bootstrap.sh shell: bash diff --git a/.github/actions/build-xcframework/action.yml b/.github/actions/build-xcframework/action.yml new file mode 100644 index 000000000..299032d4f --- /dev/null +++ b/.github/actions/build-xcframework/action.yml @@ -0,0 +1,43 @@ +# .github/actions/build-xcframework/action.yml +name: 'Build XCFramework' +description: 'Build an XCFramework using the specified scheme' + +inputs: + parent: + description: 'The parent job to use for building the XCFramework' + required: true + scheme: + description: 'The scheme to use for building the XCFramework' + required: true + match_password: + description: 'The password for match' + required: true + appstore_api_key: + description: 'The API key for App Store' + required: true + +runs: + using: "composite" + steps: + + - name: Checkout repository + uses: actions/checkout@v3.1.0 + + - name: Cache Ruby Gems + uses: ./.github/actions/ruby-cache + + - name: Cache Xcode Derived Data + uses: irgaly/xcode-cache@v1 + with: + key: xcode-15-cache-deriveddata-${{ github.workflow }}-${{ github.sha }}-${{ inputs.scheme }} + restore-keys: | + xcode-15-cache-deriveddata-${{ github.workflow }}-${{ github.event.number }}"-${{ inputs.parent }} + xcode-15-cache-deriveddata-${{ github.workflow }}-${{ github.sha }}-${{ inputs.scheme }} + deriveddata-directory: derived_data + sourcepackages-directory: spm_cache + + - name: Build XCFramework + run: bundle exec fastlane build_xcframeworks_concurrent scheme:"${{ inputs.scheme }}" + env: + MATCH_PASSWORD: ${{ inputs.match_password }} + APPSTORE_API_KEY: ${{ inputs.appstore_api_key }} \ No newline at end of file diff --git a/.github/actions/dynamic-xcode-cache/action.yml b/.github/actions/dynamic-xcode-cache/action.yml new file mode 100644 index 000000000..d19365065 --- /dev/null +++ b/.github/actions/dynamic-xcode-cache/action.yml @@ -0,0 +1,37 @@ +# .github/actions/dynamic-xcode-cache/action.yml +name: 'Dynamic Xcode Cache' +description: 'Set cache key based on job dependencies and cache Xcode derived data' + +inputs: + deriveddata-directory: + description: 'Directory for derived data' + required: true + sourcepackages-directory: + description: 'Directory for source packages' + required: true + parent: + description: 'Optional input for job dependencies' + required: false + +runs: + using: "composite" + steps: + - name: Set Cache Key + id: set-cache-key + run: | + if [ -n "${{ inputs.parent }}" ]; then + echo "RESTORE_CACHE_KEY=xcode-${{ env.XCODE_VERSION }}-cache-deriveddata-${{ github.workflow }}-${{ github.sha }}-${{ inputs.parent }}" >> $GITHUB_ENV + else + echo "RESTORE_CACHE_KEY=xcode-${{ env.XCODE_VERSION }}-cache-deriveddata-${{ github.workflow }}-${{ github.event.number }}" >> $GITHUB_ENV + fi + shell: bash + + - name: Cache Xcode Derived Data + uses: irgaly/xcode-cache@v1 + with: + key: xcode-${{ env.XCODE_VERSION }}-cache-deriveddata-${{ github.workflow }}-${{ github.sha }}-${{ github.job }} + restore-keys: | + ${{ env.RESTORE_CACHE_KEY }} + deriveddata-directory: derived_data # enable reading it from the input + sourcepackages-directory: spm_cache # enable reading it from the input + diff --git a/.github/actions/failure-handling/action.yml b/.github/actions/failure-handling/action.yml new file mode 100644 index 000000000..3a3a0e9f6 --- /dev/null +++ b/.github/actions/failure-handling/action.yml @@ -0,0 +1,68 @@ +# .github/actions/failure-handling/action.yml +name: 'Failure Handling' +description: 'Handle failure steps including uploading artifacts, sending Slack notifications, and parsing xcresult logs' +inputs: + xcresult-basenames: + description: 'JSON array of base names to xcresult files' + required: true + slack-webhook-url: + description: 'The Slack webhook URL to send notifications to' + required: true + +runs: + using: "composite" + steps: + - name: Install jq + run: | + if ! command -v jq &> /dev/null; then + brew install jq + fi + shell: bash + + - name: Upload SDK Test Data + uses: actions/upload-artifact@v4 + with: + name: SDK Test Data ${{ github.job }} + path: ~/Library/Logs/scan + + - name: Send Slack notification + uses: 8398a7/action-slack@v3 + if: ${{ github.event_name == 'push' && failure() }} + with: + status: ${{ job.status }} + text: "You shall not pass!" + job_name: ${{ github.job }} + fields: message,commit,author,action,workflow,job,took + env: + SLACK_WEBHOOK_URL: ${{ inputs.slack-webhook-url }} + MATRIX_CONTEXT: ${{ toJson(matrix) }} + +# Do we need those? + - name: Install xcparse + run: | + brew install chargepoint/xcparse/xcparse + shell: bash + + - name: Parse xcresult + run: | + echo '${{ inputs.xcresult-basenames }}' | jq -r '.[]' | while read basename; do + xcparse logs fastlane/test_output/${basename}.xcresult fastlane/test_output/logs/${basename} + done + shell: bash + + - name: Prepare Test Data Content + if: failure() + run: | + PATHS="" + echo '${{ inputs.xcresult-basenames }}' | jq -r '.[]' | while read basename; do + PATHS="$PATHS fastlane/test_output/logs/${basename}/Diagnostics/**/*.txt fastlane/test_output/logs/${basename}/Diagnostics/simctl_diagnostics/DiagnosticReports/*" + done + echo "PATHS=$PATHS" >> $GITHUB_ENV + shell: bash + + - name: Upload Test Data + uses: actions/upload-artifact@v4 + if: failure() + with: + name: Test Data ${{ github.job }} + path: ${{ env.PATHS }} \ No newline at end of file diff --git a/.github/workflows/smoke-checks.yml b/.github/workflows/smoke-checks.yml index cc21dd04d..b66afd317 100644 --- a/.github/workflows/smoke-checks.yml +++ b/.github/workflows/smoke-checks.yml @@ -27,267 +27,65 @@ concurrency: env: HOMEBREW_NO_INSTALL_CLEANUP: 1 # Disable cleanup for homebrew, we don't need it on CI - IOS_SIMULATOR_DEVICE: "iPhone 15 Pro (17.4)" + XCODE_VERSION: "15.2" + IOS_SIMULATOR_DEVICE: "iPhone 15 Pro (17.2)" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_PR_NUM: ${{ github.event.pull_request.number }} + STREAM_VIDEO_SECRET: ${{ secrets.STREAM_VIDEO_SECRET }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }} jobs: - test-llc-debug: - name: Test LLC (Debug) + test-sdks: + name: Test SDKs runs-on: macos-14 if: ${{ github.event.inputs.swiftui_snapshots != 'true' && github.event.inputs.uikit_snapshots != 'true' }} - env: - STREAM_VIDEO_SECRET: ${{ secrets.STREAM_VIDEO_SECRET }} steps: - - uses: actions/checkout@v4.1.1 - - uses: ./.github/actions/bootstrap + + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Bootstrap + uses: ./.github/actions/bootstrap env: INSTALL_YEETD: true SKIP_MINT_BOOTSTRAP: true - - name: Run LLC Tests (Debug) + + - name: Run SDK Tests run: bundle exec fastlane test_unified device:"${{ env.IOS_SIMULATOR_DEVICE }}" - timeout-minutes: 40 - env: - XCODE_VERSION: "15.2" # the most stable pair of Xcode - IOS_SIMULATOR_DEVICE: "iPhone 15 Pro (17.2)" # and iOS + - name: Run Sonar analysis run: bundle exec fastlane sonar_upload env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - uses: actions/upload-artifact@v4 - if: failure() - with: - name: LLC Test Data - path: | - ~/Library/Logs/scan - - uses: 8398a7/action-slack@v3 - with: - status: ${{ job.status }} - text: "You shall not pass!" - job_name: "Test LLC (Debug)" - fields: message,commit,author,action,workflow,job,took - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - MATRIX_CONTEXT: ${{ toJson(matrix) }} - if: ${{ github.event_name == 'push' && failure() }} - - name: Parse xcresult - if: failure() - run: | - brew install chargepoint/xcparse/xcparse - xcparse logs fastlane/test_output/StreamVideo.xcresult fastlane/test_output/logs/ - - uses: actions/upload-artifact@v4 - if: failure() - with: - name: Test Data LLC - path: | - fastlane/test_output/logs/*/Diagnostics/**/*.txt - fastlane/test_output/logs/*/Diagnostics/simctl_diagnostics/DiagnosticReports/* - - # test-swiftui-debug: - # name: Test SwiftUI (Debug) - # runs-on: macos-14 - # needs: test-llc-debug - # if: ${{ github.event_name != 'push' && github.event.inputs.swiftui_snapshots != 'false' }} - # env: - # GITHUB_TOKEN: ${{ secrets.CI_BOT_GITHUB_TOKEN }} # to open a PR - # steps: - # - uses: actions/checkout@v4.1.1 - # - uses: ./.github/actions/bootstrap - # env: - # INSTALL_YEETD: true - # SKIP_MINT_BOOTSTRAP: true - # SKIP_BREW_BOOTSTRAP: true - # - name: Run UI Tests (Debug) - # run: bundle exec fastlane test_swiftui device:"${{ env.IOS_SIMULATOR_DEVICE }}" record:${{ github.event.inputs.swiftui_snapshots }} - # timeout-minutes: 40 - # env: - # XCODE_VERSION: "15.2" # the most stable pair of Xcode - # IOS_SIMULATOR_DEVICE: "iPhone 15 Pro (17.2)" # and iOS - # - name: Parse xcresult - # if: failure() - # run: | - # brew install chargepoint/xcparse/xcparse - # xcparse screenshots fastlane/test_output/StreamVideoSwiftUI.xcresult fastlane/test_output/snapshots --test - # - uses: actions/upload-artifact@v4 - # if: failure() - # with: - # name: SwiftUI Test Data - # path: | - # ~/Library/Logs/scan - # fastlane/test_output/snapshots - - # test-uikit-debug: - # name: Test UIKit (Debug) - # runs-on: macos-14 - # needs: test-swiftui-debug - # if: ${{ github.event_name != 'push' && github.event.inputs.uikit_snapshots != 'false' }} - # env: - # GITHUB_TOKEN: ${{ secrets.CI_BOT_GITHUB_TOKEN }} # to open a PR - # steps: - # - uses: actions/checkout@v4.1.1 - # - uses: ./.github/actions/bootstrap - # env: - # INSTALL_YEETD: true - # SKIP_MINT_BOOTSTRAP: true - # SKIP_BREW_BOOTSTRAP: true - # - name: Run UI Tests (Debug) - # run: bundle exec fastlane test_uikit device:"${{ env.IOS_SIMULATOR_DEVICE }}" record:${{ github.event.inputs.uikit_snapshots }} - # timeout-minutes: 40 - # env: - # XCODE_VERSION: "15.2" # the most stable pair of Xcode - # IOS_SIMULATOR_DEVICE: "iPhone 15 Pro (17.2)" # and iOS - # - name: Parse xcresult - # if: failure() - # run: | - # brew install chargepoint/xcparse/xcparse - # xcparse screenshots fastlane/test_output/StreamVideoUIKit.xcresult fastlane/test_output/snapshots --test - # - uses: actions/upload-artifact@v4 - # if: failure() - # with: - # name: UIKit Test Data - # path: | - # ~/Library/Logs/scan - # fastlane/test_output/snapshots - - automated-code-review: - name: Automated Code Review - runs-on: macos-13 - env: - XCODE_VERSION: "15.0.1" - if: ${{ github.event_name != 'push' && github.event.inputs.swiftui_snapshots != 'true' && github.event.inputs.uikit_snapshots != 'true' }} - steps: - - uses: actions/checkout@v4.1.1 - with: - fetch-depth: 100 - - uses: ./.github/actions/bootstrap - - name: Run Danger - run: bundle exec danger - - name: Run Fastlane Linting - run: bundle exec fastlane rubocop - - name: Run SwiftFormat Linting - run: bundle exec fastlane run_swift_format lint:true - - name: Run Podspec Linting - if: startsWith(github.event.pull_request.head.ref, 'release/') - run: bundle exec fastlane pod_lint - - build-xcode15: - name: Build SDKs (Xcode 15.0) - runs-on: macos-13 - if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }} - env: - XCODE_VERSION: "15.0.1" - steps: - - name: Connect Bot - uses: webfactory/ssh-agent@v0.7.0 - with: - ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} - - uses: actions/checkout@v3.1.0 - - name: Cache Xcode Derived Data - uses: irgaly/xcode-cache@v1 + + - name: Handle failures + uses: ./.github/actions/failure-handling with: - key: xcode-15-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - deriveddata-directory: derived_data - sourcepackages-directory: spm_cache - - uses: ./.github/actions/ruby-cache - - name: List Xcode versions - run: mdfind "kMDItemCFBundleIdentifier = 'com.apple.dt.Xcode'" - - name: Build SDKs - run: bundle exec fastlane test_unified device:"iPhone 15" build_for_testing:true - - build-xcode15-xcframework-stream-video: - name: Build StreamVideo XCframework (Xcode 15.0) - needs: build-xcode15 - runs-on: macos-13 - if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }} - env: - XCODE_VERSION: "15.0.1" - steps: - - name: Connect Bot - uses: webfactory/ssh-agent@v0.7.0 - with: - ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} - - uses: actions/checkout@v3.1.0 - - name: Cache Xcode Derived Data - uses: irgaly/xcode-cache@v1 - with: - key: xcode-15-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - deriveddata-directory: derived_data - sourcepackages-directory: spm_cache - - uses: ./.github/actions/ruby-cache - - name: Build StreamVideo XCFramework - run: bundle exec fastlane build_xcframeworks_concurrent scheme:"StreamVideo" - env: - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }} - - build-xcode15-xcframework-stream-video-swiftui: - name: Build StreamVideoSwiftUI XCframework (Xcode 15.0) - needs: build-xcode15 - runs-on: macos-13 - if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }} - env: - XCODE_VERSION: "15.0.1" - steps: - - name: Connect Bot - uses: webfactory/ssh-agent@v0.7.0 - with: - ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} - - uses: actions/checkout@v3.1.0 - - name: Cache Xcode Derived Data - uses: irgaly/xcode-cache@v1 - with: - key: xcode-15-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - deriveddata-directory: derived_data - sourcepackages-directory: spm_cache - - uses: ./.github/actions/ruby-cache - - name: Build StreamVideo XCFramework - run: bundle exec fastlane build_xcframeworks_concurrent scheme:"StreamVideoSwiftUI" - env: - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }} - - build-xcode15-xcframework-stream-video-uikit: - name: Build StreamVideoUIKit XCframework (Xcode 15.0) - needs: build-xcode15 - runs-on: macos-13 - if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }} - env: - XCODE_VERSION: "15.0.1" - steps: - - name: Connect Bot - uses: webfactory/ssh-agent@v0.7.0 - with: - ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} - - uses: actions/checkout@v3.1.0 - - name: Cache Xcode Derived Data - uses: irgaly/xcode-cache@v1 - with: - key: xcode-15-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - deriveddata-directory: derived_data - sourcepackages-directory: spm_cache - - uses: ./.github/actions/ruby-cache - - name: Build StreamVideo XCFramework - run: bundle exec fastlane build_xcframeworks_concurrent scheme:"StreamVideoUIKit" - env: - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }} - + xcresult-basenames: '["StreamVideo", "StreamVideoSwiftUI", "StreamVideoUIKit"]' + slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} + build-test-app-and-frameworks: name: Build Test App and Frameworks runs-on: macos-14 - needs: test-llc-debug + needs: test-sdks if: ${{ github.event_name != 'push' && github.event.inputs.swiftui_snapshots != 'true' && github.event.inputs.uikit_snapshots != 'true' }} steps: - - uses: actions/checkout@v4.1.1 - - uses: ./.github/actions/bootstrap + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Bootstrap + uses: ./.github/actions/bootstrap + with: + parent: test-sdks env: INSTALL_YEETD: true SKIP_MINT_BOOTSTRAP: true SKIP_BREW_BOOTSTRAP: true + - name: Build run: bundle exec fastlane build_test_app_and_frameworks - timeout-minutes: 60 build-apps: name: Build Demo Apps @@ -295,32 +93,25 @@ jobs: needs: build-test-app-and-frameworks if: ${{ github.event_name != 'push' && github.event.inputs.swiftui_snapshots != 'true' && github.event.inputs.uikit_snapshots != 'true' }} steps: - - uses: actions/checkout@v4.1.1 - - uses: ./.github/actions/bootstrap + - name: Checkout + uses: actions/checkout@v4.1.1 + + - name: Bootstrap + uses: ./.github/actions/bootstrap + with: + parent: build-test-app-and-frameworks env: INSTALL_YEETD: true SKIP_MINT_BOOTSTRAP: true SKIP_BREW_BOOTSTRAP: true + - name: Build UIKit Demo App run: bundle exec fastlane build_uikit_demo + - name: Build Docs Test Project run: bundle exec fastlane build_docs_test - cache-update: - name: Update cache - runs-on: macos-14 - needs: build-test-app-and-frameworks - steps: - - uses: irgaly/xcode-cache@v1 - with: - key: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.event.number }} - restore-keys: - xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - xcode-cache-deriveddata-${{ github.workflow }}-${{ github.event.number }} - deriveddata-directory: derived_data - sourcepackages-directory: spm_cache - - allure_testops_launch: + allure-testops-launch: name: Launch Allure TestOps runs-on: macos-13 needs: build-test-app-and-frameworks @@ -328,22 +119,29 @@ jobs: outputs: launch_id: ${{ steps.get_launch_id.outputs.launch_id }} steps: - - uses: actions/checkout@v4.1.1 - - uses: ./.github/actions/ruby-cache + - name: Checkout + uses: actions/checkout@v4.1.1 + + - name: Bootstrap + uses: ./.github/actions/ruby-cache + with: + parent: build-test-app-and-frameworks + - name: Launch Allure TestOps run: bundle exec fastlane allure_launch env: ALLURE_TOKEN: ${{ secrets.ALLURE_TOKEN }} GITHUB_EVENT: ${{ toJson(github.event) }} + - id: get_launch_id run: echo "launch_id=${{env.LAUNCH_ID}}" >> $GITHUB_OUTPUT if: env.LAUNCH_ID != '' test-e2e-debug: - name: Test E2E UI (Debug) - needs: allure_testops_launch + name: Test E2E UI runs-on: macos-14 if: ${{ github.event_name != 'push' && github.event.inputs.swiftui_snapshots != 'true' && github.event.inputs.uikit_snapshots != 'true' }} + needs: allure-testops-launch env: LAUNCH_ID: ${{ needs.allure_testops_launch.outputs.launch_id }} ALLURE_TOKEN: ${{ secrets.ALLURE_TOKEN }} @@ -352,41 +150,50 @@ jobs: # batch: [0, 1] # fail-fast: false steps: - - uses: actions/checkout@v4.1.1 - - uses: ./.github/actions/bootstrap + + - name: Checkout + uses: actions/checkout@v4.1.1 + + - name: Bootstrap + uses: ./.github/actions/bootstrap + with: + parent: build-test-app-and-frameworks env: INSTALL_ALLURE: true INSTALL_VIDEO_BUDDY: true INSTALL_YEETD: true SKIP_MINT_BOOTSTRAP: true SKIP_BREW_BOOTSTRAP: true + - name: Run UI Tests (Debug) - run: bundle exec fastlane test_e2e device:"${{ env.IOS_SIMULATOR_DEVICE }}" batch:'${{ matrix.batch }}' test_without_building:false + run: bundle exec fastlane test_e2e device:"${{ env.IOS_SIMULATOR_DEVICE }}" test_without_building:true timeout-minutes: 60 env: - XCODE_VERSION: "15.2" # the most stable pair of Xcode - IOS_SIMULATOR_DEVICE: "iPhone 15 Pro (17.2)" # and iOS MATRIX_SIZE: ${{ strategy.job-total }} STREAM_SDK_TEST_APP: ${{ secrets.STREAM_SDK_TEST_APP }} STREAM_SDK_TEST_ACCOUNT_EMAIL: ${{ secrets.STREAM_SDK_TEST_ACCOUNT_EMAIL }} STREAM_SDK_TEST_ACCOUNT_PASSWORD: ${{ secrets.STREAM_SDK_TEST_ACCOUNT_PASSWORD }} STREAM_SDK_TEST_ACCOUNT_OTP_SECRET: ${{ secrets.STREAM_SDK_TEST_ACCOUNT_OTP_SECRET }} - STREAM_VIDEO_SECRET: ${{ secrets.STREAM_VIDEO_SECRET }} + - name: Allure TestOps Upload if: env.LAUNCH_ID != '' && (success() || failure()) run: bundle exec fastlane allure_upload launch_id:$LAUNCH_ID + - name: Allure TestOps Launch Removal if: env.LAUNCH_ID != '' && cancelled() run: bundle exec fastlane allure_launch_removal launch_id:$LAUNCH_ID + - name: Parse xcresult if: failure() run: | brew install chargepoint/xcparse/xcparse xcparse logs fastlane/test_output/DemoApp.xcresult fastlane/test_output/logs/ - - uses: actions/upload-artifact@v4 + + - name: Upload artifcats + uses: actions/upload-artifact@v4 if: failure() with: - name: E2E Test Data ${{ matrix.batch }} + name: E2E Test Data path: | ~/Library/Logs/scan fastlane/recordings @@ -394,3 +201,142 @@ jobs: fastlane/test_output/report.junit fastlane/test_output/logs/*/Diagnostics/**/*.txt fastlane/test_output/logs/*/Diagnostics/simctl_diagnostics/DiagnosticReports/* + + automated-code-review: + name: Automated Code Review + runs-on: macos-13 + needs: test-e2e-debug + env: + XCODE_VERSION: "15.0.1" + if: ${{ github.event_name != 'push' && github.event.inputs.swiftui_snapshots != 'true' && github.event.inputs.uikit_snapshots != 'true' }} + steps: + + - name: Checkout + uses: actions/checkout@v4.1.1 + with: + fetch-depth: 100 + + - name: Bootstrap + uses: ./.github/actions/bootstrap + with: + parent: allure-testops-launch + + - name: Run Danger + run: bundle exec danger + + - name: Run Fastlane Linting + run: bundle exec fastlane rubocop + + - name: Run SwiftFormat Linting + run: bundle exec fastlane run_swift_format lint:true + + - name: Run Podspec Linting + if: startsWith(github.event.pull_request.head.ref, 'release/') + run: bundle exec fastlane pod_lint + + # Xcode 15 + test-sdks-xcode-15: + name: Test SDKs in Xcode 15 + needs: test-sdks + runs-on: macos-13 + env: + XCODE_VERSION: "15.0.1" + if: ${{ github.event.inputs.swiftui_snapshots != 'true' && github.event.inputs.uikit_snapshots != 'true' }} + steps: + + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Bootstrap + uses: ./.github/actions/bootstrap + env: + INSTALL_YEETD: true + SKIP_MINT_BOOTSTRAP: true + + - name: Build SDK Tests + run: bundle exec fastlane test_unified device:"${{ env.IOS_SIMULATOR_DEVICE }}" skip_testing:true build_for_testing:true + + build-xcode15-xcframework-stream-video: + name: Build StreamVideo XCframeworks (Xcode 15.0) + needs: test-sdks-xcode-15 + runs-on: macos-13 + env: + XCODE_VERSION: "15.0.1" + if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }} + steps: + - name: Connect Bot + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} + + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Bootstrap + uses: ./.github/actions/bootstrap + with: + parent: test-sdks-xcode-15 + env: + INSTALL_YEETD: true + SKIP_MINT_BOOTSTRAP: true + SKIP_BREW_BOOTSTRAP: true + + - name: Build StreamVideo XCFramework + run: bundle exec fastlane build_xcframeworks_concurrent scheme:"StreamVideo" + + build-xcode15-xcframework-stream-video-swiftui: + name: Build StreamVideoSwiftUI XCframeworks (Xcode 15.0) + needs: test-sdks-xcode-15 + runs-on: macos-13 + env: + XCODE_VERSION: "15.0.1" + if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }} + steps: + - name: Connect Bot + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} + + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Bootstrap + uses: ./.github/actions/bootstrap + with: + parent: test-sdks-xcode-15 + env: + INSTALL_YEETD: true + SKIP_MINT_BOOTSTRAP: true + SKIP_BREW_BOOTSTRAP: true + + - name: Build StreamVideoSwiftUI XCFramework + run: bundle exec fastlane build_xcframeworks_concurrent scheme:"StreamVideoSwiftUI" + + build-xcode15-xcframework-stream-video-uikit: + name: Build StreamVideoUIKit XCframeworks (Xcode 15.0) + needs: test-sdks-xcode-15 + runs-on: macos-13 + env: + XCODE_VERSION: "15.0.1" + if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }} + steps: + - name: Connect Bot + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} + + - name: Checkout repository + uses: actions/checkout@v4.1.1 + + - name: Bootstrap + uses: ./.github/actions/bootstrap + with: + parent: test-sdks-xcode-15 + env: + INSTALL_YEETD: true + SKIP_MINT_BOOTSTRAP: true + SKIP_BREW_BOOTSTRAP: true + + - name: Build StreamVideoUIKit XCFramework + run: bundle exec fastlane build_xcframeworks_concurrent scheme:"StreamVideoUIKit" + \ No newline at end of file diff --git a/DemoApp/Sources/ViewModifiers/MoreControls/DemoManualQualitySelectionButtonView.swift b/DemoApp/Sources/ViewModifiers/MoreControls/DemoManualQualitySelectionButtonView.swift index 3cb361767..1a6dd99b2 100644 --- a/DemoApp/Sources/ViewModifiers/MoreControls/DemoManualQualitySelectionButtonView.swift +++ b/DemoApp/Sources/ViewModifiers/MoreControls/DemoManualQualitySelectionButtonView.swift @@ -78,7 +78,7 @@ struct DemoManualQualitySelectionButtonView: View { private func buttonView( for manualQuality: ManualQuality ) -> some View { - let title = { + let title: String = { switch manualQuality { case .auto: return "Auto quality" @@ -96,6 +96,7 @@ struct DemoManualQualitySelectionButtonView: View { return "Disable video" } }() + Button { execute(manualQuality) } label: { diff --git a/DemoApp/Sources/ViewModifiers/MoreControls/DemoReconnectionButtonView.swift b/DemoApp/Sources/ViewModifiers/MoreControls/DemoReconnectionButtonView.swift index fb567d68a..98c89a8f0 100644 --- a/DemoApp/Sources/ViewModifiers/MoreControls/DemoReconnectionButtonView.swift +++ b/DemoApp/Sources/ViewModifiers/MoreControls/DemoReconnectionButtonView.swift @@ -41,7 +41,7 @@ struct DemoReconnectionButtonView: View { private func buttonView( for reconnectStrategy: ReconnectStrategy ) -> some View { - let (title, icon) = { + let (title, icon): (String, String) = { switch reconnectStrategy { case .fast: return ("Fast", "hare") diff --git a/DemoApp/Sources/Views/CallView/CallingView/SimpleCallingView.swift b/DemoApp/Sources/Views/CallView/CallingView/SimpleCallingView.swift index a5eabddbe..f6b8ac9bb 100644 --- a/DemoApp/Sources/Views/CallView/CallingView/SimpleCallingView.swift +++ b/DemoApp/Sources/Views/CallView/CallingView/SimpleCallingView.swift @@ -15,7 +15,7 @@ struct SimpleCallingView: View { @Injected(\.appearance) var appearance @State var text = "" - @State private var callType: String + @State private var callType: String = "" @State private var changeEnvironmentPromptForURL: URL? @State private var showChangeEnvironmentPrompt: Bool = false @@ -23,9 +23,9 @@ struct SimpleCallingView: View { @ObservedObject var viewModel: CallViewModel init(viewModel: CallViewModel, callId: String) { - self.viewModel = viewModel - text = callId - callType = { + _viewModel = .init(wrappedValue: viewModel) + _text = .init(wrappedValue: callId) + _callType = .init(wrappedValue: { guard !AppState.shared.deeplinkInfo.callId.isEmpty, !AppState.shared.deeplinkInfo.callType.isEmpty @@ -34,7 +34,7 @@ struct SimpleCallingView: View { } return AppState.shared.deeplinkInfo.callType - }() + }()) } var body: some View { diff --git a/DemoApp/Sources/Views/Login/AddUserView.swift b/DemoApp/Sources/Views/Login/AddUserView.swift index 840da0bda..018ce1251 100644 --- a/DemoApp/Sources/Views/Login/AddUserView.swift +++ b/DemoApp/Sources/Views/Login/AddUserView.swift @@ -58,9 +58,9 @@ struct DemoCustomEnvironmentView: View { @Injected(\.appearance) var appearance @Environment(\.presentationMode) var presentationMode - @State var baseURL: AppEnvironment.BaseURL - @State var apiKey: String - @State var token: String + @State var baseURL: AppEnvironment.BaseURL = AppEnvironment.baseURL + @State var apiKey: String = "" + @State var token: String = "" @State var usesDefaultPushNotificationConfig = false @State var pushNotificationName: String = AppState.shared.pushNotificationConfiguration.pushProviderInfo.name @State var voIPPushNotificationName: String = AppState.shared.pushNotificationConfiguration.voipPushProviderInfo.name @@ -72,10 +72,10 @@ struct DemoCustomEnvironmentView: View { token: String, completionHandler: @escaping (AppEnvironment.BaseURL, String, String) -> Void ) { - self.baseURL = baseURL - self.apiKey = apiKey - self.token = token - usesDefaultPushNotificationConfig = AppState.shared.pushNotificationConfiguration == .default + _baseURL = .init(wrappedValue: baseURL) + _apiKey = .init(wrappedValue: apiKey) + _token = .init(wrappedValue: token) + _usesDefaultPushNotificationConfig = .init(wrappedValue: AppState.shared.pushNotificationConfiguration == .default) self.completionHandler = completionHandler } diff --git a/DocumentationTests/DocumentationTests/DocumentationTests/05-ui-cookbook/22-manual-quality-selection.swift b/DocumentationTests/DocumentationTests/DocumentationTests/05-ui-cookbook/22-manual-quality-selection.swift index d9b840cca..8772f039a 100644 --- a/DocumentationTests/DocumentationTests/DocumentationTests/05-ui-cookbook/22-manual-quality-selection.swift +++ b/DocumentationTests/DocumentationTests/DocumentationTests/05-ui-cookbook/22-manual-quality-selection.swift @@ -98,7 +98,7 @@ fileprivate func content() { private func buttonView( for manualQuality: ManualQuality ) -> some View { - let title = { + let title: String = { switch manualQuality { case .auto: return "Auto quality" diff --git a/StreamVideoTests/Utils/ThermalStateObserverTests.swift b/StreamVideoTests/Utils/ThermalStateObserverTests.swift index ea2d0500d..7a437f5db 100644 --- a/StreamVideoTests/Utils/ThermalStateObserverTests.swift +++ b/StreamVideoTests/Utils/ThermalStateObserverTests.swift @@ -23,10 +23,6 @@ final class ThermalStateObserverTests: XCTestCase { XCTAssertEqual(ThermalStateObserver.shared.state, ProcessInfo.processInfo.thermalState) } - func test_injectedValueWasSetCorrectly() { - XCTAssertTrue(InjectedValues[\.thermalStateObserver] === ThermalStateObserver.shared) - } - // MARK: - notificationObserver func test_notificationObserver_stateChangesWhenSystemPostsNotification() { diff --git a/SwiftUIDemoAppUITests/Tests/DeeplinkTests.swift b/SwiftUIDemoAppUITests/Tests/DeeplinkTests.swift index 3a57bdd03..44daf1e90 100644 --- a/SwiftUIDemoAppUITests/Tests/DeeplinkTests.swift +++ b/SwiftUIDemoAppUITests/Tests/DeeplinkTests.swift @@ -72,6 +72,7 @@ final class DeeplinkTests: StreamTestCase { } func test_customSchemeURL_joinsExpectedCall() throws { + throw XCTSkip("https://github.com/GetStream/ios-issues-tracking/issues/764") linkToScenario(withId: 2857) WHEN("user opens a URL that contains a custom scheme") { @@ -85,6 +86,7 @@ final class DeeplinkTests: StreamTestCase { } func test_customSchemeWithCallIdInPath_joinsExpectedCall() throws { + throw XCTSkip("https://github.com/GetStream/ios-issues-tracking/issues/764") linkToScenario(withId: 2955) WHEN("user opens a URL that contains a custom scheme") { diff --git a/fastlane/Fastfile b/fastlane/Fastfile index de9da0aa9..7d0573a75 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -226,6 +226,9 @@ lane :test_unified do |options| update_testplan_on_ci(path: "StreamVideoUnifiedTests/StreamVideoUnifiedTests.xctestplan") + # Ensure `skip_testing` defaults to false + skip_testing = options.fetch(:skip_testing, false) + scan_options = { project: xcode_project, scheme: 'StreamVideoUnifiedTests', @@ -243,6 +246,9 @@ lane :test_unified do |options| prelaunch_simulator: true } + # Add `testplan` only if `skip_testing` is not true + scan_options[:only_testing] = [] unless skip_testing + begin scan(scan_options) rescue StandardError => e @@ -459,25 +465,18 @@ lane :test_e2e do |options| result_bundle: true, derived_data_path: derived_data_path, cloned_source_packages_path: source_packages_path, + clean: is_localhost, test_without_building: options[:test_without_building], build_for_testing: options[:build_for_testing], devices: options[:device], prelaunch_simulator: is_ci, number_of_retries: 3, parallel_testing: true, - concurrent_workers: 4, + concurrent_workers: 2, xcargs: is_ci ? "#{buildcache_xcargs} STREAM_VIDEO_SECRET=#{app_secret}" : buildcache_xcargs } - begin - scan(scan_options) - rescue StandardError => e - UI.user_error!(e) unless options[:cron] - - failed_tests = retreive_failed_tests - UI.important("Re-running #{failed_tests.size} failed tests ⌛️") - scan(scan_options.merge(only_testing: failed_tests)) - end + scan(scan_options) end private_lane :parallelize_tests_on_ci do |options|