From 42cab0855330e80b3469859841fa6052c4435edb Mon Sep 17 00:00:00 2001 From: Siddarth Kumar Date: Wed, 14 Feb 2024 19:58:45 +0530 Subject: [PATCH] chore: Improve DX for building the app locally (#18784) After upgrading `react-native` to `0.72.5` we frequently started seeing the _red screen of death_ on both `Android` and `iOS` simulators right after the app was built and installed. This used to happen because our workflow required us to do the following : - `make run-clojure` - `make run-metro` - `make run-ios` OR `make run-android` The problem with this approach was after `metro` was started the `iOS`, `Android` build step would change the files that `metro` couldn't handle and hence metro would go out of sync. The quick fix back then was to restart `metro` terminal and to open the app again from the simulator. This was however not a good DX. This commit fixes that. We no longer rely on `react-native` cli to generate and deploy debug builds on simulators. We take control of the process via our own script. The new workflow introduced in this commit will first build the app, then install the app on the simulators and then start metro terminal. When `metro` is successfully running the script will then open the app. The new workflow now is : - `make run-clojure` - `make run-ios` OR `make run-android` --- .gitignore | 3 ++ Makefile | 15 ++----- doc/ide-setup.md | 3 +- doc/starting-guide.md | 3 +- ios/Podfile.lock | 2 +- nix/mobile/android/release.nix | 12 ++++-- scripts/run-android.sh | 53 +++++++++++++++++++++++++ scripts/run-ios.sh | 72 ++++++++++++++++++++++++++++++++++ scripts/run-metro.sh | 5 +++ scripts/start-react-native.sh | 17 -------- 10 files changed, 148 insertions(+), 37 deletions(-) create mode 100755 scripts/run-android.sh create mode 100755 scripts/run-ios.sh create mode 100755 scripts/run-metro.sh delete mode 100755 scripts/start-react-native.sh diff --git a/.gitignore b/.gitignore index ad4dd9f005..e4c7e47f89 100644 --- a/.gitignore +++ b/.gitignore @@ -194,3 +194,6 @@ test/appium/tests/users.py ## git hooks lefthook.yml + +## metro server logs +metro-server-logs.log diff --git a/Makefile b/Makefile index 03654c49c2..4fe32474e0 100644 --- a/Makefile +++ b/Makefile @@ -267,7 +267,7 @@ run-clojure: ##@run Watch for and build Clojure changes for mobile run-metro: export TARGET := clojure run-metro: ##@run Start Metro to build React Native changes - @scripts/start-react-native.sh + @scripts/run-metro.sh run-re-frisk: export TARGET := clojure run-re-frisk: ##@run Start re-frisk server @@ -278,22 +278,15 @@ run-android: export TARGET := android # Disabled for debug builds to avoid 'maximum call stack exceeded' errors. # https://github.com/status-im/status-mobile/issues/18493 run-android: export ORG_GRADLE_PROJECT_hermesEnabled := false -# INFO: If it's empty (no devices attached, parsing issues, script error) - for Nix it's the same as not set. -run-android: export ANDROID_ABI_INCLUDE ?= $(shell ./scripts/adb_devices_abis.sh) run-android: ##@run Build Android APK and start it on the device - npx react-native run-android --appIdSuffix debug + @scripts/run-android.sh SIMULATOR=iPhone 13 # TODO: fix IOS_STATUS_GO_TARGETS to be either amd64 or arm64 when RN is upgraded run-ios: export TARGET := ios run-ios: export IOS_STATUS_GO_TARGETS := ios/arm64;iossimulator/amd64 -run-ios: export XCBeautify=$(shell which xcbeautify) # for react-native-cli to pick this up and to auto format output -run-ios: ##@run Build iOS app and start it in a simulator/device -ifneq ("$(SIMULATOR)", "") - npx react-native run-ios --simulator="$(SIMULATOR)" -else - npx react-native run-ios -endif +run-ios: ##@run Build iOS app and start it in on the simulator + @scripts/run-ios.sh "${SIMULATOR}" show-ios-devices: ##@other shows connected ios device and its name xcrun xctrace list devices diff --git a/doc/ide-setup.md b/doc/ide-setup.md index d1085018b5..aa400509d7 100644 --- a/doc/ide-setup.md +++ b/doc/ide-setup.md @@ -122,10 +122,9 @@ See below: Do the following: -Ensure you have 3 terminals running the following +Ensure you have 2 terminals running the following - `make run-clojure` -- `make run-metro` - `make run-ios` / `make run-android` [See the STARTING GUIDE for details](STARTING_GUIDE.md#development) diff --git a/doc/starting-guide.md b/doc/starting-guide.md index 9f8e55721b..0d1fff2514 100644 --- a/doc/starting-guide.md +++ b/doc/starting-guide.md @@ -15,8 +15,7 @@ This step will take a while the first time as it will download all dependencies. There are three steps necessary to start development, in this case for Android: 1. `make run-clojure` - Compiles Clojure into JavaScript, watches for changes on cljs files, and hot-reloads code in the app. -2. `make run-metro` - Starts metro bundler and watches JavaScript code. -3. `make run-android` or `make run-ios` - Builds the Android/iOS app and starts it on the device. +2. `make run-android` or `make run-ios` - Builds the Android/iOS app, starts it on the device and starts metro bundler. The first two will continue watching for changes and keep re-building the app. They need to be ready first. The last one will exit once the app is up and ready. diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 486b75e855..f539dec0ed 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -793,7 +793,7 @@ CHECKOUT OPTIONS: :submodules: true SPEC CHECKSUMS: - boost: 57d2868c099736d80fcd648bf211b4431e51a558 + boost: 64032b9e9b938fda23325e68a3771f0fabf414dc BVLinearGradient: 612a04ff38e8480291f3379ee5b5a2c571f03fe0 CryptoSwift: c4f2debceb38bf44c80659afe009f71e23e4a082 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 diff --git a/nix/mobile/android/release.nix b/nix/mobile/android/release.nix index 160e548562..fcd60d3d8a 100644 --- a/nix/mobile/android/release.nix +++ b/nix/mobile/android/release.nix @@ -40,8 +40,10 @@ let else if (elem buildType ["pr" "manual"]) then ".env.jenkins" else ".env"; - # There are only two types of Gradle build targets: pr and release - gradleBuildType = if buildType == "pr" then "Pr" else "Release"; + gradleBuildType = + if buildType == "pr" then "Pr" + else if buildType == "debug" then "Debug" + else "Release"; apksPath = "./android/app/build/outputs/apk/${toLower gradleBuildType}"; @@ -171,12 +173,14 @@ in stdenv.mkDerivation rec { ${adhocEnvVars} ${gradleCommand} popd > /dev/null ''; - doCheck = true; - checkPhase = '' + + doCheck = buildType != "debug"; + checkPhase = '' ls ${apksPath}/*.apk \ | xargs -n1 ${pkgs.unzip}/bin/unzip -qql \ | grep 'index.android.bundle' ''; + installPhase = '' mkdir -p $out cp ${apksPath}/*.apk $out/ diff --git a/scripts/run-android.sh b/scripts/run-android.sh new file mode 100755 index 0000000000..8f4c11aa84 --- /dev/null +++ b/scripts/run-android.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail +set -m # needed to access jobs + +GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel) + +# We run Metro in background while calling adb. +cleanupMetro() { + pkill -f run-metro.sh + rm -f metro-server-logs.log +} + +# Using function gives a neater jobspec name. +runMetro() { + nohup "${GIT_ROOT}/scripts/run-metro.sh" 2>&1 \ + | tee metro-server-logs.log +} + +waitForMetro() { + set +e # Allow grep command to fail in the loop. + TIMEOUT=5 + echo "Waiting for Metro server..." >&2 + while ! grep -q "Welcome to Metro" metro-server-logs.log; do + echo -n "." >&2 + sleep 1 + if ((TIMEOUT == 0)); then + echo -e "\nMetro server timed out, exiting" >&2 + set -e # Restore errexit for rest of script. + return 1 + fi + ((TIMEOUT--)) + done + set -e # Restore errexit for rest of script. +} + +# Generate android debug build. +export ANDROID_ABI_INCLUDE=$("${GIT_ROOT}/scripts/adb_devices_abis.sh") +export BUILD_ENV=debug +export BUILD_TYPE=debug +"${GIT_ROOT}/scripts/build-android.sh" + +# Install the APK on running emulator or android device. +adb install ./result/app-debug.apk + +trap cleanupMetro EXIT ERR INT QUIT +runMetro & +waitForMetro + +# Start the installed app. +adb shell monkey -p im.status.ethereum.debug 1 + +# bring metro job to foreground +fg 'runMetro' diff --git a/scripts/run-ios.sh b/scripts/run-ios.sh new file mode 100755 index 0000000000..c913d4895f --- /dev/null +++ b/scripts/run-ios.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +set -euo pipefail +set -m # needed to access jobs + +GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel) + +# We run Metro in background while calling adb. +cleanupMetro() { + pkill -f run-metro.sh + rm -f metro-server-logs.log +} + +# Using function gives a neater jobspec name. +runMetro() { + nohup "${GIT_ROOT}/scripts/run-metro.sh" 2>&1 \ + | tee metro-server-logs.log +} + +waitForMetro() { + set +e # Allow grep command to fail in the loop. + TIMEOUT=5 + echo "Waiting for Metro server..." >&2 + while ! grep -q "Welcome to Metro" metro-server-logs.log; do + echo -n "." >&2 + sleep 1 + if ((TIMEOUT == 0)); then + echo -e "\nMetro server timed out, exiting" >&2 + set -e # Restore errexit for rest of script. + return 1 + fi + ((TIMEOUT--)) + done + set -e # Restore errexit for rest of script. +} + +# Check if the first argument is provided +if [ -z "${1-}" ]; then + echo "Error: No simulator name provided." >&2 + exit 1 +fi + +SIMULATOR=${1} + +# get our desired UUID +UUID=$(xcrun simctl list devices | grep -E "$SIMULATOR \(" | head -n 1 | awk -F '[()]' '{print $2}') + +# get simulator status +SIMULATOR_STATE=$(xcrun simctl list devices | grep -E "$SIMULATOR \(" | head -n 1 | awk '{print $NF}') + +# sometimes a simulator is already running, shut it down to avoid errors +if [ "$SIMULATOR_STATE" != "(Shutdown)" ]; then + xcrun simctl shutdown "$UUID" +fi + +# boot up iOS for simulator +xcrun simctl boot "$UUID" + +# start the simulator +open -a Simulator --args -CurrentDeviceUDID "$UUID" + +#iOS build of debug scheme +xcodebuild -workspace "ios/StatusIm.xcworkspace" -configuration Debug -scheme StatusIm -destination id="$UUID" | xcbeautify + +trap cleanupMetro EXIT ERR INT QUIT +runMetro & +waitForMetro + +# launch the app when metro is ready +xcrun simctl launch "$UUID" im.status.ethereum.debug + +# bring metro job to foreground +fg 'runMetro' diff --git a/scripts/run-metro.sh b/scripts/run-metro.sh new file mode 100755 index 0000000000..37741881c6 --- /dev/null +++ b/scripts/run-metro.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +pkill -f 'react-native start' + +react-native start --reset-cache diff --git a/scripts/start-react-native.sh b/scripts/start-react-native.sh deleted file mode 100755 index f137844bbb..0000000000 --- a/scripts/start-react-native.sh +++ /dev/null @@ -1,17 +0,0 @@ - -#!/usr/bin/env bash - -GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel) -source "${GIT_ROOT}/scripts/colors.sh" - -METRO_PORT=8081 -METRO_PID="$(lsof -i :${METRO_PORT} | awk 'NR!=1 {print $2}' | sort -u | tr '\r\n' ' ')" -if [ ! -z "$METRO_PID" ]; then - echo -e "${YLW}TCP port ${METRO_PORT} is required by the Metro packager.\nThe following processes currently have the port open, preventing Metro from starting:${RST}" - ps -fp $METRO_PID - echo -e "${YLW}Do you want to terminate them (y/n)?${RST}" - read -n 1 term - [[ $term == 'y' ]] && kill $METRO_PID -fi - -react-native start --reset-cache