diff --git a/.github/actions/composite/setupNode/action.yml b/.github/actions/composite/setupNode/action.yml index 95e187ff4d3a9..d475acf5380ff 100644 --- a/.github/actions/composite/setupNode/action.yml +++ b/.github/actions/composite/setupNode/action.yml @@ -4,10 +4,6 @@ description: Set up Node runs: using: composite steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - with: - fetch-depth: 0 - - uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/e2ePerformanceRegressionTests.yml b/.github/workflows/e2ePerformanceRegressionTests.yml index a59eb8c0dbfff..ad6425d4ec90b 100644 --- a/.github/workflows/e2ePerformanceRegressionTests.yml +++ b/.github/workflows/e2ePerformanceRegressionTests.yml @@ -7,10 +7,15 @@ on: jobs: e2e-tests: if: ${{ github.event.label.name == 'e2e' }} - name: "Run e2e performance regression tests" + name: Run e2e performance regression tests # Although the tests will run on an android emulator, using macOS as its more performant runs-on: macos-12 steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main - uses: ruby/setup-ruby@eae47962baca661befdfd24e4d6c34ade04858f7 @@ -43,7 +48,7 @@ jobs: disable-animations: false script: echo "Generated AVD snapshot for caching." -# Note: if the android build fails the logs can be incomplete. It can help to run the build once manually to get a full log + # Note: if the android build fails the logs can be incomplete. It can help to run the build once manually to get a full log - name: Preheat build system env: JAVA_HOME: ${{ env.JAVA_HOME_11_X64 }} @@ -76,4 +81,3 @@ jobs: name: test-failure-logs path: e2e/.results retention-days: 5 - diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0af8907b085d5..f0bcf63b69555 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,6 +11,11 @@ jobs: if: ${{ github.actor != 'OSBotify' || github.event_name == 'workflow_call' }} runs-on: ubuntu-latest steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main - run: npm run lint diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml index 6aa397758ea23..5b61a294ca5b2 100644 --- a/.github/workflows/platformDeploy.yml +++ b/.github/workflows/platformDeploy.yml @@ -31,6 +31,11 @@ jobs: if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }} runs-on: ubuntu-latest steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main - uses: ruby/setup-ruby@eae47962baca661befdfd24e4d6c34ade04858f7 @@ -100,6 +105,11 @@ jobs: if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }} runs-on: macos-12 steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main - name: Decrypt Developer ID Certificate @@ -135,6 +145,11 @@ jobs: if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }} runs-on: macos-12 steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main - uses: ruby/setup-ruby@eae47962baca661befdfd24e4d6c34ade04858f7 @@ -219,6 +234,11 @@ jobs: if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }} runs-on: ubuntu-latest steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main - name: Setup Cloudflare CLI @@ -340,6 +360,11 @@ jobs: if: ${{ always() }} needs: [android, desktop, iOS, web] steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main - name: Set version diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 90e6e06e75d28..72dde5fe38cf6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,6 +39,11 @@ jobs: chunk: ${{fromJson(needs.config.outputs.MATRIX)}} steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main # If automatic signing is enabled, iOS builds will fail, so ensure we always have the proper profile specified diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml new file mode 100644 index 0000000000000..79503b8f4b099 --- /dev/null +++ b/.github/workflows/testBuild.yml @@ -0,0 +1,183 @@ +name: Build and deploy apps for testing + +on: + workflow_dispatch: + pull_request_target: + types: [opened, synchronize] + +env: + DEVELOPER_DIR: /Applications/Xcode_14.0.1.app/Contents/Developer + +jobs: + validateActor: + runs-on: ubuntu-latest + outputs: + IS_TEAM_MEMBER: ${{ fromJSON(steps.isUserDeployer.outputs.isTeamMember) }} + steps: + - id: isUserDeployer + uses: tspascoal/get-user-teams-membership@baf2e6adf4c3b897bd65a7e3184305c165aec872 + with: + GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} + username: ${{ github.actor }} + team: mobile-deployers + + android: + name: Build and deploy Android + needs: validateActor + if: ${{ fromJSON(needs.validateActor.outputs.IS_TEAM_MEMBER) }} + runs-on: ubuntu-latest + steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - uses: Expensify/App/.github/actions/composite/setupNode@main + + - uses: ruby/setup-ruby@eae47962baca661befdfd24e4d6c34ade04858f7 + with: + ruby-version: '2.7' + bundler-cache: true + + - name: Decrypt keystore + run: cd android/app && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output my-upload-key.keystore my-upload-key.keystore.gpg + env: + LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + + - name: Decrypt json key + run: cd android/app && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output android-fastlane-json-key.json android-fastlane-json-key.json.gpg + env: + LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + + - name: Run Fastlane beta test + id: runFastlaneBetaTest + run: bundle exec fastlane android build_test + env: + 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 + PULL_REQUEST_NUMBER: ${{ github.event.number }} + + - uses: actions/upload-artifact@v3 + with: + name: android + path: ./android_paths.json + + iOS: + name: Build and deploy iOS + needs: validateActor + if: ${{ fromJSON(needs.validateActor.outputs.IS_TEAM_MEMBER) }} + runs-on: macos-12 + steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - uses: Expensify/App/.github/actions/composite/setupNode@main + + - uses: ruby/setup-ruby@eae47962baca661befdfd24e4d6c34ade04858f7 + with: + ruby-version: '2.7' + bundler-cache: true + + - name: Install cocoapods + uses: nick-invision/retry@0711ba3d7808574133d713a0d92d2941be03a350 + with: + timeout_minutes: 10 + max_attempts: 5 + command: cd ios && pod install + + - name: Decrypt profile + run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output chat_expensify_adhoc.mobileprovision.gpg chat_expensify_adhoc.mobileprovision.gpg.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: Run Fastlane + run: bundle exec fastlane ios build_test + env: + 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 + PULL_REQUEST_NUMBER: ${{ github.event.number }} + + - uses: actions/upload-artifact@v3 + with: + name: ios + path: ./ios_paths.json + + # web: + # name: Build and deploy Web + # needs: validateActor + # if: ${{ fromJSON(needs.validateActor.outputs.IS_TEAM_MEMBER) }} + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + # with: + # fetch-depth: 0 + # ref: ${{ github.event.pull_request.head.sha }} + + # - uses: Expensify/App/.github/actions/composite/setupNode@main + + # - name: Configure AWS Credentials + # # Version: 1.5.5 + # uses: aws-actions/configure-aws-credentials@e97d7fbc8e0e5af69631c13daa0f4b5a8d88165b + # with: + # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws-region: us-east-1 + + # - name: Build web for staging + # run: npm run build-staging + + # - name: Build docs + # run: npm run storybook-build + # continue-on-error: true + + # - name: Deploy to S3 for internal testing + # run: aws s3 cp --recursive --acl public-read "$GITHUB_WORKSPACE"/dist s3://ad-hoc-expensify-cash/web/"$PULL_REQUEST_NUMBER" + # env: + # PULL_REQUEST_NUMBER: ${{ github.event.number }} + + postGithubComment: + runs-on: ubuntu-latest + name: Post a GitHub comment with app download links for testing + needs: [android, ios] + steps: + - name: Checkout + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + ref: ${{ github.event.pull_request.head.ref }} + + - uses: actions/download-artifact@v3 + + - name: Read JSONs with paths + id: set_var + run: | + content_android="$(cat ./android/android_paths.json)" + content_android="${content_android//'%'/'%25'}" + content_android="${content_android//$'\n'/'%0A'}" + content_android="${content_android//$'\r'/'%0D'}" + echo "android_paths=$content_android" >> "$GITHUB_OUTPUT" + content_ios="$(cat ./ios/ios_paths.json)" + content_ios="${content_ios//'%'/'%25'}" + content_ios="${content_ios//$'\n'/'%0A'}" + content_ios="${content_ios//$'\r'/'%0D'}" + echo "ios_paths=$content_ios" >> "$GITHUB_OUTPUT" + + - name: Publish links to apps for download + run: | + gh pr comment --body \ + "Use the links below to test this build in android and iOS. Happy testing! + | android :robot: | iOS :apple: | + | ------------- | ------------- | + | ${{fromJson(steps.set_var.outputs.android_paths).html_path}} | ${{fromJson(steps.set_var.outputs.ios_paths).html_path}} |" + env: + GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} diff --git a/.github/workflows/validateGithubActions.yml b/.github/workflows/validateGithubActions.yml index 4e51d785d6b05..b583ba31f2bce 100644 --- a/.github/workflows/validateGithubActions.yml +++ b/.github/workflows/validateGithubActions.yml @@ -10,6 +10,11 @@ jobs: if: github.actor != 'OSBotify' runs-on: ubuntu-latest steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main # Rebuild all the actions on this branch and check for a diff. Fail if there is one, diff --git a/.github/workflows/verifyPodfile.yml b/.github/workflows/verifyPodfile.yml index 17d80ca7e055d..84f52d09997a0 100644 --- a/.github/workflows/verifyPodfile.yml +++ b/.github/workflows/verifyPodfile.yml @@ -10,6 +10,11 @@ jobs: if: github.actor != 'OSBotify' runs-on: ubuntu-latest steps: + # This action checks-out the repository, so the workflow can access it. + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + with: + fetch-depth: 0 + - uses: Expensify/App/.github/actions/composite/setupNode@main - run: ./.github/scripts/verifyPodfile.sh diff --git a/Gemfile b/Gemfile index c41a21c6604f4..d8b9c135680dc 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,7 @@ source "https://rubygems.org" gem "cocoapods", "~> 1.11.3" gem "fastlane", "~> 2" gem "xcpretty", "~> 0" + + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock index 5ac7631891875..95a2611d953c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,6 +14,8 @@ GEM algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) + apktools (0.7.4) + rubyzip (~> 2.0) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) @@ -155,6 +157,10 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) + fastlane-plugin-aws_s3 (2.1.0) + apktools (~> 0.7) + aws-sdk-s3 (~> 1) + mime-types (~> 3.3) ffi (1.15.5) fourflusher (2.3.1) fuzzy_match (2.0.4) @@ -207,6 +213,9 @@ GEM json (2.6.2) jwt (2.5.0) memoist (0.16.2) + mime-types (3.4.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2022.0105) mini_magick (4.11.0) mini_mime (1.1.2) minitest (5.16.2) @@ -283,6 +292,7 @@ PLATFORMS DEPENDENCIES cocoapods (~> 1.11.3) fastlane (~> 2) + fastlane-plugin-aws_s3 xcpretty (~> 0) BUNDLED WITH diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 74060ccb501cd..4188e62db7be4 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -36,6 +36,29 @@ platform :android do ) end + desc "Build app for testing" + lane :build_test do + ENV["ENVFILE"]=".env.staging" + + gradle( + project_dir: './android', + task: 'assemble', + build_type: 'Release', + ) + + aws_s3( + access_key: ENV['S3_ACCESS_KEY'], + secret_access_key: ENV['S3_SECRET_ACCESS_KEY'], + bucket: ENV['S3_BUCKET'], + region: ENV['S3_REGION'], + + apk: lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], + app_directory: "android/#{ENV['PULL_REQUEST_NUMBER']}", + ) + + sh("echo '{\"apk_path\": \"#{lane_context[SharedValues::S3_APK_OUTPUT_PATH]}\",\"html_path\": \"#{lane_context[SharedValues::S3_HTML_OUTPUT_PATH]}\"}' > ../android_paths.json") + end + desc "Build and upload app to Google Play" lane :beta do ENV["ENVFILE"]=".env.production" @@ -91,6 +114,53 @@ platform :ios do ) end + desc "Build app for testing" + lane :build_test do + require 'securerandom' + ENV["ENVFILE"]=".env.staging" + + keychain_password = SecureRandom.uuid + + create_keychain( + name: "ios-build.keychain", + password: keychain_password, + default_keychain: "true", + unlock: "true", + timeout: "3600", + add_to_search_list: "true" + ) + + import_certificate( + certificate_path: "./ios/Certificates.p12", + keychain_name: "ios-build.keychain", + keychain_password: keychain_password + ) + + install_provisioning_profile( + path: "./ios/chat_expensify_adhoc.mobileprovision.gpg" + ) + + build_app( + workspace: "./ios/NewExpensify.xcworkspace", + scheme: "NewExpensify", + export_options: { + manageAppVersionAndBuildNumber: false + } + ) + + aws_s3( + access_key: ENV['S3_ACCESS_KEY'], + secret_access_key: ENV['S3_SECRET_ACCESS_KEY'], + bucket: ENV['S3_BUCKET'], + region: ENV['S3_REGION'], + + ipa: lane_context[SharedValues::IPA_OUTPUT_PATH], + app_directory: "android/#{ENV['PULL_REQUEST_NUMBER']}", + ) + + 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 "Build and upload app to TestFlight" lane :beta do require 'securerandom' diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile new file mode 100644 index 0000000000000..c6ab0dfb46a4d --- /dev/null +++ b/fastlane/Pluginfile @@ -0,0 +1,5 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-aws_s3'