From 43e6c6140c45e8479ca1bcb06c73f7a333e81da8 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Fri, 4 Oct 2024 12:44:35 -0700 Subject: [PATCH] Merge pull request #50268 from Expensify/revert-50123-Rory-CallableIOSBuild Revert "[No QA] Callable buildIOS workflow" (cherry picked from commit 209d7a5aa7cc2852096d0edb685512ea7c94094c) (CP triggered by jasperhuangg) --- .github/workflows/buildAndroid.yml | 1 + .github/workflows/buildIOS.yml | 155 -------------------------- .github/workflows/deploy.yml | 170 ++++++++++++++++------------- .github/workflows/testBuild.yml | 114 +++++++++++++------ fastlane/Fastfile | 10 +- 5 files changed, 179 insertions(+), 271 deletions(-) delete mode 100644 .github/workflows/buildIOS.yml diff --git a/.github/workflows/buildAndroid.yml b/.github/workflows/buildAndroid.yml index d7ce7a33a31f..c91a9fbc91b8 100644 --- a/.github/workflows/buildAndroid.yml +++ b/.github/workflows/buildAndroid.yml @@ -41,6 +41,7 @@ on: description: Git ref to checkout and build required: true type: string + pull_request_number: description: The pull request number associated with this build, if relevant. type: number diff --git a/.github/workflows/buildIOS.yml b/.github/workflows/buildIOS.yml deleted file mode 100644 index 2386d01da793..000000000000 --- a/.github/workflows/buildIOS.yml +++ /dev/null @@ -1,155 +0,0 @@ -name: Build iOS app - -on: - workflow_call: - inputs: - type: - description: 'What type of build to run. Must be one of ["release", "adhoc"]' - type: string - required: true - ref: - description: Git ref to checkout and build - type: string - required: true - pull_request_number: - description: The pull request number associated with this build, if relevant. - type: string - required: false - outputs: - IPA_FILE_NAME: - value: ${{ jobs.build.outputs.IPA_FILE_NAME }} - DSYM_FILE_NAME: - value: ${{ jobs.build.outputs.DSYM_FILE_NAME }} - - workflow_dispatch: - inputs: - type: - description: What type of build do you want to run? - required: true - type: choice - options: - - release - - adhoc - ref: - description: Git ref to checkout and build - required: true - type: string - pull_request_number: - description: The pull request number associated with this build, if relevant. - type: number - required: false - -jobs: - build: - name: Build iOS app - runs-on: macos-13-xlarge - env: - DEVELOPER_DIR: /Applications/Xcode_15.2.0.app/Contents/Developer - outputs: - IPA_FILE_NAME: ${{ steps.build.outputs.IPA_FILE_NAME }} - DSYM_FILE_NAME: ${{ steps.build.outputs.DSYM_FILE_NAME }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ inputs.ref }} - - - name: Create .env.adhoc file based on staging and add PULL_REQUEST_NUMBER env to it - if: ${{ inputs.type == 'adhoc' }} - run: | - cp .env.staging .env.adhoc - sed -i '' 's/ENVIRONMENT=staging/ENVIRONMENT=adhoc/' .env.adhoc - echo "PULL_REQUEST_NUMBER=${{ inputs.pull_request_number }}" >> .env.adhoc - - - name: Configure MapBox SDK - run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} - - - name: Setup Node - id: setup-node - uses: ./.github/actions/composite/setupNode - - - name: Setup Ruby - uses: ruby/setup-ruby@v1.190.0 - with: - bundler-cache: true - - - name: Cache Pod dependencies - uses: actions/cache@v4 - id: pods-cache - with: - path: ios/Pods - key: ${{ runner.os }}-pods-cache-${{ hashFiles('ios/Podfile.lock', 'firebase.json') }} - - - name: Compare Podfile.lock and Manifest.lock - id: compare-podfile-and-manifest - run: echo "IS_PODFILE_SAME_AS_MANIFEST=${{ hashFiles('ios/Podfile.lock') == hashFiles('ios/Pods/Manifest.lock') }}" >> "$GITHUB_OUTPUT" - - - name: Install cocoapods - uses: nick-fields/retry@3f757583fb1b1f940bc8ef4bf4734c8dc02a5847 - if: steps.pods-cache.outputs.cache-hit != 'true' || steps.compare-podfile-and-manifest.outputs.IS_PODFILE_SAME_AS_MANIFEST != 'true' || steps.setup-node.outputs.cache-hit != 'true' - with: - timeout_minutes: 10 - max_attempts: 5 - command: scripts/pod-install.sh - - - name: Decrypt provisioning profiles - run: | - cd ios - provisioningProfile='' - if [ '${{ inputs.type }}' == 'release' ]; then - provisioningProfile='NewApp_AppStore' - else - provisioningProfile='NewApp_AdHoc' - fi - echo "Using provisioning profile: $provisioningProfile" - gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output "$provisioningProfile.mobileprovision" "$provisioningProfile.mobileprovision.gpg" - gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output "${provisioningProfile}_Notification_Service.mobileprovision" "${provisioningProfile}_Notification_Service.mobileprovision.gpg" - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - - name: Decrypt code signing certificate - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output Certificates.p12 Certificates.p12.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - - name: Build iOS ${{ inputs.type }} app - id: build - run: | - lane='' - if [ '${{ inputs.type }}' == 'release' ]; then - lane='build' - else - lane='build_adhoc' - fi - - bundle exec fastlane ios "$lane" - - # Reload environment variables from GITHUB_ENV - # shellcheck disable=SC1090 - source "$GITHUB_ENV" - - { - # ipaPath and dsymPath are environment variables set within the Fastfile - echo "IPA_PATH=$ipaPath" - echo "IPA_FILE_NAME=$(basename "$ipaPath")" - echo "DSYM_PATH=$dsymPath" - echo "DSYM_FILE_NAME=$(basename "$dsymPath")" - } >> "$GITHUB_OUTPUT" - - - name: Upload iOS build artifact - uses: actions/upload-artifact@v4 - with: - name: ios-artifact-ipa - path: ${{ steps.build.outputs.IPA_PATH }} - - - name: Upload iOS debug symbols artifact - uses: actions/upload-artifact@v4 - with: - name: ios-artifact-dsym - path: ${{ steps.build.outputs.DSYM_PATH }} - - - name: Upload iOS sourcemaps - uses: actions/upload-artifact@v4 - with: - name: ios-artifact-sourcemaps - path: ./main.jsbundle.map diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2053ce44aadb..555e7650193c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -203,73 +203,61 @@ jobs: name: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'desktop-build-artifact' || 'desktop-staging-build-artifact' }} path: ./desktop-build/NewExpensify.dmg - buildIOS: - name: Build iOS app - uses: ./.github/workflows/buildIOS.yml - if: ${{ github.ref == 'refs/heads/staging' }} + iOS: + name: Build and deploy iOS needs: prep - secrets: inherit - with: - type: release - ref: staging - - uploadIOS: - name: Upload iOS App to TestFlight - needs: buildIOS - runs-on: ubuntu-latest + env: + DEVELOPER_DIR: /Applications/Xcode_15.2.0.app/Contents/Developer + runs-on: macos-13-xlarge steps: - name: Checkout uses: actions/checkout@v4 + - name: Configure MapBox SDK + run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} + + - name: Setup Node + id: setup-node + uses: ./.github/actions/composite/setupNode + - name: Setup Ruby uses: ruby/setup-ruby@v1.190.0 with: bundler-cache: true - - name: Decrypt App Store Connect API key - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output ios-fastlane-json-key.json ios-fastlane-json-key.json.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - - name: Download iOS build artifacts - uses: actions/download-artifact@v4 + - name: Cache Pod dependencies + uses: actions/cache@v4 + id: pods-cache with: - path: /tmp/artifacts - pattern: ios-artifact-* - merge-multiple: true + path: ios/Pods + key: ${{ runner.os }}-pods-cache-${{ hashFiles('ios/Podfile.lock', 'firebase.json') }} - - name: Log downloaded artifact paths - run: ls -R /tmp/artifacts + - name: Compare Podfile.lock and Manifest.lock + id: compare-podfile-and-manifest + run: echo "IS_PODFILE_SAME_AS_MANIFEST=${{ hashFiles('ios/Podfile.lock') == hashFiles('ios/Pods/Manifest.lock') }}" >> "$GITHUB_OUTPUT" - - name: Upload iOS app to TestFlight - run: bundle exec fastlane ios upload_testflight - env: - APPLE_CONTACT_EMAIL: ${{ secrets.APPLE_CONTACT_EMAIL }} - APPLE_CONTACT_PHONE: ${{ secrets.APPLE_CONTACT_PHONE }} - APPLE_DEMO_EMAIL: ${{ secrets.APPLE_DEMO_EMAIL }} - APPLE_DEMO_PASSWORD: ${{ secrets.APPLE_DEMO_PASSWORD }} - ipaPath: /tmp/artifacts/${{ needs.buildIOS.outputs.IPA_FILE_NAME }} - dsymPath: /tmp/artifacts/${{ needs.buildIOS.outputs.DSYM_FILE_NAME }} + - name: Install cocoapods + uses: nick-fields/retry@3f757583fb1b1f940bc8ef4bf4734c8dc02a5847 + if: steps.pods-cache.outputs.cache-hit != 'true' || steps.compare-podfile-and-manifest.outputs.IS_PODFILE_SAME_AS_MANIFEST != 'true' || steps.setup-node.outputs.cache-hit != 'true' + with: + timeout_minutes: 10 + max_attempts: 5 + command: scripts/pod-install.sh - - name: Upload iOS build to Browser Stack - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} - run: curl -u "$BROWSERSTACK" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "file=@/tmp/artifacts/${{ needs.buildIOS.outputs.IPA_FILE_NAME }}" + - name: Decrypt AppStore profile + run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AppStore.mobileprovision NewApp_AppStore.mobileprovision.gpg env: - BROWSERSTACK: ${{ secrets.BROWSERSTACK }} + LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - submitIOS: - name: Submit iOS app for Apple review - needs: prep - if: ${{ github.ref == 'refs/heads/production' }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Decrypt AppStore Notification Service profile + run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AppStore_Notification_Service.mobileprovision NewApp_AppStore_Notification_Service.mobileprovision.gpg + env: + LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - name: Setup Ruby - uses: ruby/setup-ruby@v1.190.0 - with: - bundler-cache: true + - name: Decrypt certificate + run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output Certificates.p12 Certificates.p12.gpg + env: + LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - name: Decrypt App Store Connect API key run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output ios-fastlane-json-key.json ios-fastlane-json-key.json.gpg @@ -280,13 +268,47 @@ jobs: id: getIOSVersion run: echo "IOS_VERSION=$(echo '${{ needs.prep.outputs.APP_VERSION }}' | tr '-' '.')" >> "$GITHUB_OUTPUT" + - name: Build iOS release app + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + run: bundle exec fastlane ios build + + - name: Upload release build to TestFlight + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + run: bundle exec fastlane ios upload_testflight + env: + APPLE_CONTACT_EMAIL: ${{ secrets.APPLE_CONTACT_EMAIL }} + APPLE_CONTACT_PHONE: ${{ secrets.APPLE_CONTACT_PHONE }} + APPLE_DEMO_EMAIL: ${{ secrets.APPLE_DEMO_EMAIL }} + APPLE_DEMO_PASSWORD: ${{ secrets.APPLE_DEMO_PASSWORD }} + - name: Submit build for App Store review + if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} run: bundle exec fastlane ios submit_for_review env: VERSION: ${{ steps.getIOSVersion.outputs.IOS_VERSION }} + - name: Upload iOS build to Browser Stack + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + run: curl -u "$BROWSERSTACK" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "file=@/Users/runner/work/App/App/New Expensify.ipa" + env: + BROWSERSTACK: ${{ secrets.BROWSERSTACK }} + + - name: Upload iOS sourcemaps artifact + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + uses: actions/upload-artifact@v4 + with: + name: ios-sourcemaps-artifact + path: ./main.jsbundle.map + + - name: Upload iOS build artifact + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + uses: actions/upload-artifact@v4 + with: + name: ios-build-artifact + path: /Users/runner/work/App/App/New\ Expensify.ipa + - name: Warn deployers if iOS production deploy failed - if: ${{ failure() }} + if: ${{ failure() && fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} uses: 8398a7/action-slack@v3 with: status: custom @@ -389,7 +411,7 @@ jobs: name: Post a Slack message when any platform fails to build or deploy runs-on: ubuntu-latest if: ${{ failure() }} - needs: [buildAndroid, uploadAndroid, submitAndroid, desktop, buildIOS, uploadIOS, submitIOS, web] + needs: [buildAndroid, uploadAndroid, submitAndroid, desktop, iOS, web] steps: - name: Checkout uses: actions/checkout@v4 @@ -415,7 +437,7 @@ jobs: outputs: IS_AT_LEAST_ONE_PLATFORM_DEPLOYED: ${{ steps.checkDeploymentSuccessOnAtLeastOnePlatform.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED }} IS_ALL_PLATFORMS_DEPLOYED: ${{ steps.checkDeploymentSuccessOnAllPlatforms.outputs.IS_ALL_PLATFORMS_DEPLOYED }} - needs: [buildAndroid, uploadAndroid, submitAndroid, desktop, buildIOS, uploadIOS, submitIOS, web] + needs: [buildAndroid, uploadAndroid, submitAndroid, desktop, iOS, web] if: ${{ always() }} steps: - name: Check deployment success on at least one platform @@ -423,19 +445,18 @@ jobs: run: | isAtLeastOnePlatformDeployed="false" if [ ${{ github.ref }} == 'refs/heads/production' ]; then - if [ '${{ needs.submitAndroid.result }}' == 'success' ] || \ - [ '${{ needs.submitIOS.result }}' == 'success' ]; then + if [ "${{ needs.submitAndroid.result }}" == "success" ]; then isAtLeastOnePlatformDeployed="true" fi else - if [ '${{ needs.uploadAndroid.result }}' == 'success' ] || \ - [ '${{ needs.uploadIOS.result }}' == 'success' ]; then + if [ "${{ needs.uploadAndroid.result }}" == "success" ]; then isAtLeastOnePlatformDeployed="true" fi fi - - if [ '${{ needs.desktop.result }}' == 'success' ] || \ - [ '${{ needs.web.result }}' == 'success' ]; then + + if [ "${{ needs.iOS.result }}" == "success" ] || \ + [ "${{ needs.desktop.result }}" == "success" ] || \ + [ "${{ needs.web.result }}" == "success" ]; then isAtLeastOnePlatformDeployed="true" fi echo "IS_AT_LEAST_ONE_PLATFORM_DEPLOYED=$isAtLeastOnePlatformDeployed" >> "$GITHUB_OUTPUT" @@ -444,20 +465,19 @@ jobs: - name: Check deployment success on all platforms id: checkDeploymentSuccessOnAllPlatforms run: | - isAllPlatformsDeployed='false' - if [ '${{ needs.desktop.result }}' == 'success' ] && \ - [ '${{ needs.web.result }}' == 'success' ]; then + isAllPlatformsDeployed="false" + if [ "${{ needs.iOS.result }}" == "success" ] && \ + [ "${{ needs.desktop.result }}" == "success" ] && \ + [ "${{ needs.web.result }}" == "success" ]; then isAllPlatformsDeployed="true" fi if [ ${{ github.ref }} == 'refs/heads/production' ]; then - if [ '${{ needs.submitAndroid.result }}' != 'success' ] || \ - [ '${{ needs.submitIOS.result }}' != 'success' ]; then + if [ "${{ needs.submitAndroid.result }}" != "success" ]; then isAllPlatformsDeployed="false" fi else - if [ '${{ needs.uploadAndroid.result }}' != 'success' ] || \ - [ '${{ needs.uploadIOS.result }}' != 'success' ]; then + if [ "${{ needs.uploadAndroid.result }}" != "success" ]; then isAllPlatformsDeployed="false" fi fi @@ -468,7 +488,7 @@ jobs: createPrerelease: runs-on: ubuntu-latest if: ${{ always() && github.ref == 'refs/heads/staging' && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) }} - needs: [prep, checkDeploymentSuccess, buildAndroid, buildIOS] + needs: [prep, checkDeploymentSuccess] steps: - name: Download all workflow run artifacts uses: actions/download-artifact@v4 @@ -495,12 +515,12 @@ jobs: continue-on-error: true run: | gh release upload ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }} --clobber \ - ./android-artifact-sourcemaps/index.android.bundle.map#android-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ - ./android-artifact-aab/${{ needs.buildAndroid.outputs.AAB_FILE_NAME }} \ + ./android-sourcemaps-artifact/index.android.bundle.map#android-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ + ./android-build-artifact/app-production-release.aab \ ./desktop-staging-sourcemaps-artifact/desktop-staging-merged-source-map.js.map#desktop-staging-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ ./desktop-staging-build-artifact/NewExpensify.dmg#NewExpensifyStaging.dmg \ - ./ios-artifact-sourcemaps/main.jsbundle.map#ios-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ - ./ios-artifact-ipa/${{ needs.buildIOS.outputs.IPA_FILE_NAME }} \ + ./ios-sourcemaps-artifact/main.jsbundle.map#ios-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ + ./ios-build-artifact/New\ Expensify.ipa \ ./web-staging-sourcemaps-artifact/web-staging-merged-source-map.js.map#web-staging-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ ./web-staging-build-tar-gz-artifact/webBuild.tar.gz#stagingWebBuild.tar.gz \ ./web-staging-build-zip-artifact/webBuild.zip#stagingWebBuild.zip @@ -581,7 +601,7 @@ jobs: name: Post a Slack message when all platforms deploy successfully runs-on: ubuntu-latest if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_ALL_PLATFORMS_DEPLOYED) }} - needs: [prep, buildAndroid, uploadAndroid, submitAndroid, desktop, buildIOS, uploadIOS, submitIOS, web, checkDeploymentSuccess, createPrerelease, finalizeRelease] + needs: [prep, buildAndroid, uploadAndroid, submitAndroid, desktop, iOS, web, checkDeploymentSuccess, createPrerelease, finalizeRelease] steps: - name: 'Announces the deploy in the #announce Slack room' uses: 8398a7/action-slack@v3 @@ -635,11 +655,11 @@ jobs: postGithubComments: uses: ./.github/workflows/postDeployComments.yml if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) }} - needs: [prep, buildAndroid, uploadAndroid, submitAndroid, buildIOS, uploadIOS, submitIOS, desktop, web, checkDeploymentSuccess, createPrerelease, finalizeRelease] + needs: [prep, buildAndroid, uploadAndroid, submitAndroid, desktop, iOS, web, checkDeploymentSuccess, createPrerelease, finalizeRelease] with: version: ${{ needs.prep.outputs.APP_VERSION }} env: ${{ github.ref == 'refs/heads/production' && 'production' || 'staging' }} android: ${{ github.ref == 'refs/heads/production' && needs.submitAndroid.result || needs.uploadAndroid.result }} - ios: ${{ github.ref == 'refs/heads/production' && needs.submitIOS.result || needs.uploadIOS.result }} + ios: ${{ needs.iOS.result }} web: ${{ needs.web.result }} desktop: ${{ needs.desktop.result }} diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index ac20a8d09141..672d468ed3b1 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -120,41 +120,73 @@ jobs: # $s3APKPath is set from within the Fastfile, android upload_s3 lane echo "S3_APK_PATH=$s3APKPath" >> "$GITHUB_OUTPUT" - buildIOS: - name: Build iOS app for testing - uses: ./.github/workflows/buildIOS.yml - if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + iOS: + name: Build and deploy iOS for testing needs: [validateActor, getBranchRef] - secrets: inherit - with: - type: adhoc - ref: ${{ github.event.pull_request.head.sha || needs.getBranchRef.outputs.REF }} - pull_request_number: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }} - - uploadIOS: - name: Upload IOS app to S3 - needs: buildIOS - runs-on: ubuntu-latest - outputs: - S3_IPA_PATH: ${{ steps.exportS3Path.outputs.S3_IPA_PATH }} + if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + env: + DEVELOPER_DIR: /Applications/Xcode_15.2.0.app/Contents/Developer + runs-on: macos-13-xlarge steps: - name: Checkout uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || needs.getBranchRef.outputs.REF }} + + - name: Configure MapBox SDK + run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} + + - name: Create .env.adhoc file based on staging and add PULL_REQUEST_NUMBER env to it + run: | + cp .env.staging .env.adhoc + sed -i '' 's/ENVIRONMENT=staging/ENVIRONMENT=adhoc/' .env.adhoc + echo "PULL_REQUEST_NUMBER=$PULL_REQUEST_NUMBER" >> .env.adhoc + + - name: Setup Node + id: setup-node + uses: ./.github/actions/composite/setupNode + + - name: Setup XCode + run: sudo xcode-select -switch /Applications/Xcode_15.2.0.app - name: Setup Ruby uses: ruby/setup-ruby@v1.190.0 with: bundler-cache: true - - name: Download IOS build artifacts - uses: actions/download-artifact@v4 + - name: Cache Pod dependencies + uses: actions/cache@v4 + id: pods-cache with: - path: /tmp/artifacts - pattern: ios-artifact-* - merge-multiple: true + path: ios/Pods + key: ${{ runner.os }}-pods-cache-${{ hashFiles('ios/Podfile.lock', 'firebase.json') }} - - name: Log downloaded artifact paths - run: ls -R /tmp/artifacts + - name: Compare Podfile.lock and Manifest.lock + id: compare-podfile-and-manifest + run: echo "IS_PODFILE_SAME_AS_MANIFEST=${{ hashFiles('ios/Podfile.lock') == hashFiles('ios/Pods/Manifest.lock') }}" >> "$GITHUB_OUTPUT" + + - name: Install cocoapods + uses: nick-fields/retry@3f757583fb1b1f940bc8ef4bf4734c8dc02a5847 + if: steps.pods-cache.outputs.cache-hit != 'true' || steps.compare-podfile-and-manifest.outputs.IS_PODFILE_SAME_AS_MANIFEST != 'true' || steps.setup-node.outputs.cache-hit != 'true' + with: + timeout_minutes: 10 + max_attempts: 5 + command: scripts/pod-install.sh + + - name: Decrypt AdHoc profile + run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AdHoc.mobileprovision NewApp_AdHoc.mobileprovision.gpg + env: + LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + + - name: Decrypt AdHoc Notification Service profile + run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AdHoc_Notification_Service.mobileprovision NewApp_AdHoc_Notification_Service.mobileprovision.gpg + env: + LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + + - name: Decrypt certificate + run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output Certificates.p12 Certificates.p12.gpg + env: + LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 @@ -163,20 +195,22 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 + - name: Build AdHoc app + run: bundle exec fastlane ios build_adhoc + - name: Upload AdHoc build to S3 run: bundle exec fastlane ios upload_s3 env: - ipaPath: /tmp/artifacts/${{ needs.buildIOS.outputs.IPA_FILE_NAME }} S3_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }} S3_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} S3_BUCKET: ad-hoc-expensify-cash S3_REGION: us-east-1 - - name: Export S3 paths - id: exportS3Path - run: | - # $s3IpaPath is set from within the Fastfile, ios upload_s3 lane - echo "S3_IPA_PATH=$s3IpaPath" >> "$GITHUB_OUTPUT" + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: ios + path: ./ios_paths.json desktop: name: Build and deploy Desktop for testing @@ -257,27 +291,41 @@ jobs: postGithubComment: runs-on: ubuntu-latest name: Post a GitHub comment with app download links for testing - needs: [validateActor, getBranchRef, uploadAndroid, uploadIOS, desktop, web] - if: ${{ always() && fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + needs: [validateActor, getBranchRef, uploadAndroid, iOS, desktop, web] + if: ${{ always() }} steps: - name: Checkout uses: actions/checkout@v4 + if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} with: ref: ${{ github.event.pull_request.head.sha || needs.getBranchRef.outputs.REF }} - name: Download Artifact uses: actions/download-artifact@v4 + if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + + - name: Read JSONs with iOS paths + id: get_ios_path + if: ${{ needs.iOS.result == 'success' }} + run: | + content_ios="$(cat ./ios/ios_paths.json)" + content_ios="${content_ios//'%'/'%25'}" + content_ios="${content_ios//$'\n'/'%0A'}" + content_ios="${content_ios//$'\r'/'%0D'}" + ios_path=$(echo "$content_ios" | jq -r '.html_path') + echo "ios_path=$ios_path" >> "$GITHUB_OUTPUT" - name: Publish links to apps for download + if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} uses: ./.github/actions/javascript/postTestBuildComment with: PR_NUMBER: ${{ env.PULL_REQUEST_NUMBER }} GITHUB_TOKEN: ${{ github.token }} ANDROID: ${{ needs.uploadAndroid.result }} DESKTOP: ${{ needs.desktop.result }} - IOS: ${{ needs.uploadIOS.result }} + IOS: ${{ needs.iOS.result }} WEB: ${{ needs.web.result }} ANDROID_LINK: ${{ needs.uploadAndroid.outputs.S3_APK_PATH }} DESKTOP_LINK: https://ad-hoc-expensify-cash.s3.amazonaws.com/desktop/${{ env.PULL_REQUEST_NUMBER }}/NewExpensify.dmg - IOS_LINK: ${{ needs.uploadIOS.outputs.S3_IPA_PATH }} + IOS_LINK: ${{ steps.get_ios_path.outputs.ios_path }} WEB_LINK: https://${{ env.PULL_REQUEST_NUMBER }}.pr-testing.expensify.com diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 1a7499a2a2c3..eed84acdc916 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -19,7 +19,6 @@ KEY_GRADLE_APK_PATH = "apkPath" KEY_S3_APK_PATH = "s3APKPath" KEY_GRADLE_AAB_PATH = "aabPath" KEY_IPA_PATH = "ipaPath" -KEY_S3_IPA_PATH = "s3IpaPath" KEY_DSYM_PATH = "dsymPath" # Export environment variables to GITHUB_ENV @@ -216,7 +215,7 @@ platform :ios do build_app( workspace: "./ios/NewExpensify.xcworkspace", scheme: "New Expensify", - output_name: "NewExpensify.ipa", + output_name: "New Expensify.ipa", export_options: { provisioningProfiles: { "com.chat.expensify.chat" => "(NewApp) AppStore", @@ -257,7 +256,6 @@ platform :ios do workspace: "./ios/NewExpensify.xcworkspace", skip_profile_detection: true, scheme: "New Expensify AdHoc", - output_name: "NewExpensify_AdHoc.ipa", export_method: "ad-hoc", export_options: { method: "ad-hoc", @@ -282,16 +280,12 @@ platform :ios do ipa: ENV[KEY_IPA_PATH], app_directory: "ios/#{ENV['PULL_REQUEST_NUMBER']}", ) - puts "Saving S3 outputs in env..." - exportEnvVars({ - KEY_S3_IPA_PATH => lane_context[SharedValues::S3_HTML_OUTPUT_PATH], - }) + sh("echo '{\"ipa_path\": \"#{lane_context[SharedValues::S3_IPA_OUTPUT_PATH]}\",\"html_path\": \"#{lane_context[SharedValues::S3_HTML_OUTPUT_PATH]}\"}' > ../ios_paths.json") end desc "Upload app to TestFlight" lane :upload_testflight do upload_to_testflight( - ipa: ENV[KEY_IPA_PATH], api_key_path: "./ios/ios-fastlane-json-key.json", distribute_external: true, notify_external_testers: true,