From 0467ff1f68c68e1408acc11b09b94d9f0779e62b Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Sat, 11 Apr 2026 09:53:10 -0500 Subject: [PATCH] Improve build.sh logging, validation, and option parsing --- build.sh | 160 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 53 deletions(-) diff --git a/build.sh b/build.sh index da32aa5b..d7c8296b 100755 --- a/build.sh +++ b/build.sh @@ -29,21 +29,6 @@ SDKMANAGER="${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" VERSION=$(grep versionName uhabits-android/build.gradle.kts | sed -e 's/.*"\([^"]*\)".*/\1/g') BOOT_TIMEOUT=360 -if [ -z $VERSION ]; then - echo "Could not parse app version from: uhabits-android/build.gradle.kts" - exit 1 -fi - -if [ ! -f "${ANDROID_HOME}/platform-tools/adb" ]; then - echo "Error: ANDROID_HOME is not set correctly; ${ANDROID_HOME}/platform-tools/adb not found" - exit 1 -fi - -if [ ! -f "$EMULATOR" ]; then - echo "Error: Not found: $EMULATOR" - exit 1 -fi - # Logging # ----------------------------------------------------------------------------- @@ -59,12 +44,73 @@ log_info() { echo -e "$COLOR* $1 $NC" } +log_debug() { + local COLOR='\033[0;90m' + local NC='\033[0m' + echo -e "${COLOR}$1 $NC" +} + +run() { + log_debug "$*" + "$@" +} + fail() { log_error "BUILD FAILED" exit 1 } +# Validation +# ----------------------------------------------------------------------------- + +if [ -z $VERSION ]; then + log_error "Could not parse app version from: uhabits-android/build.gradle.kts" + exit 1 +fi + +if [ ! -f "${ANDROID_HOME}/platform-tools/adb" ]; then + log_error "ANDROID_HOME is not set correctly; ${ANDROID_HOME}/platform-tools/adb not found" + exit 1 +fi + +if [ ! -f "$EMULATOR" ]; then + log_error "Not found: $EMULATOR" + exit 1 +fi + +MISSING_DEPS=0 +IS_MACOS=0 +if [[ "$(uname)" == "Darwin" ]]; then + IS_MACOS=1 +fi + +check_cmd() { + local cmd=$1 + local brew_pkg=$2 + if ! command -v "$cmd" &>/dev/null; then + if [ $IS_MACOS -eq 1 ] && [ -n "$brew_pkg" ]; then + log_error "Required command not found: $cmd (try: brew install $brew_pkg)" + else + log_error "Required command not found: $cmd" + fi + MISSING_DEPS=1 + fi +} + +check_cmd flock flock +check_cmd timeout coreutils +check_cmd ts moreutils +check_cmd rsync rsync +check_cmd pgrep "" +check_cmd pkill "" + +if [ $MISSING_DEPS -ne 0 ]; then + exit 1 +fi + + gradle_run() { + log_debug "./gradlew $*" mkdir -p build if ! $GRADLE "$@" > "$GRADLE_LOG" 2>&1; then log_error "Gradle command failed: $*" @@ -80,15 +126,19 @@ gradle_run() { core_build() { log_info "Formatting code..." gradle_run ktlintFormat || fail - log_info "Upgrading yarn lock..." - gradle_run kotlinUpgradeYarnLock || fail log_info "Building uhabits-core..." + gradle_run kotlinUpgradeYarnLock || fail gradle_run :uhabits-core:build || fail } # Android # ----------------------------------------------------------------------------- +android_accept_licenses() { + log_info "Accepting Android SDK licenses..." + yes | run $SDKMANAGER --licenses +} + android_setup() { API=$1 AVDNAME=${AVD_PREFIX}${API} @@ -101,11 +151,11 @@ android_setup() { done log_info "Removing existing Android virtual device..." - $AVDMANAGER delete avd --name $AVDNAME + run $AVDMANAGER delete avd --name $AVDNAME log_info "Creating new Android virtual device (API $API)..." - (echo "y" | $SDKMANAGER --install "system-images;android-$API;google_apis;x86_64") || return 1 - $AVDMANAGER create avd \ + run $SDKMANAGER --install "system-images;android-$API;google_apis;x86_64" || return 1 + run $AVDMANAGER create avd \ --name $AVDNAME \ --package "system-images;android-$API;google_apis;x86_64" \ --device "Nexus 4" || return 1 @@ -114,21 +164,22 @@ android_setup() { ) 10>/tmp/uhabitsTest.lock log_info "Launching emulator..." + EMULATOR_LOG="build/emulator-${API}.log" $EMULATOR \ -avd $AVDNAME \ -port 6${API}0 \ - 1>/dev/null 2>&1 & + 1>"$EMULATOR_LOG" 2>&1 & log_info "Waiting for emulator to boot..." export ADB="$ADB -s emulator-6${API}0" - timeout $BOOT_TIMEOUT $ADB wait-for-device shell 'while [[ -z "$(getprop sys.boot_completed)" ]]; do echo Waiting...; sleep 1; done; input keyevent 82' + run timeout $BOOT_TIMEOUT $ADB wait-for-device shell 'while [[ -z "$(getprop sys.boot_completed)" ]]; do echo Waiting...; sleep 1; done; input keyevent 82' if [ $? -ne 0 ]; then log_error "Emulator failed to boot after $BOOT_TIMEOUT seconds." return 1 fi log_info "Saving snapshot..." - $ADB emu avd snapshot save fresh-install + run $ADB emu avd snapshot save fresh-install } android_boot_attempt() { @@ -141,32 +192,33 @@ android_boot_attempt() { done log_info "Launching emulator..." + EMULATOR_LOG="build/emulator-${API}.log" $EMULATOR \ -avd $AVDNAME \ -port 6${API}0 \ -snapshot fresh-install \ -no-snapshot-save \ -wipe-data \ - 1>/dev/null 2>&1 & + 1>"$EMULATOR_LOG" 2>&1 & log_info "Waiting for emulator to boot..." export ADB="$ADB -s emulator-6${API}0" sleep 5 - timeout $BOOT_TIMEOUT $ADB wait-for-device shell 'while [[ -z "$(getprop sys.boot_completed)" ]]; do echo Waiting...; sleep 1; done; input keyevent 82' + run timeout $BOOT_TIMEOUT $ADB wait-for-device shell 'while [[ -z "$(getprop sys.boot_completed)" ]]; do echo Waiting...; sleep 1; done; input keyevent 82' if [ $? -ne 0 ]; then log_error "Emulator failed to boot after $BOOT_TIMEOUT seconds." return 1 fi log_info "Disabling animations..." - $ADB root || return 1 + run $ADB root || return 1 sleep 5 - $ADB shell settings put global window_animation_scale 0 || return 1 - $ADB shell settings put global transition_animation_scale 0 || return 1 - $ADB shell settings put global animator_duration_scale 0 || return 1 + run $ADB shell settings put global window_animation_scale 0 || return 1 + run $ADB shell settings put global transition_animation_scale 0 || return 1 + run $ADB shell settings put global animator_duration_scale 0 || return 1 log_info "Acquiring wake lock..." - $ADB shell 'echo android-test > /sys/power/wake_lock' || return 1 + run $ADB shell 'echo android-test > /sys/power/wake_lock' || return 1 } @@ -188,13 +240,13 @@ android_test() { if [ -n "$RELEASE" ]; then log_info "Installing release APK..." - $ADB install -r ${ANDROID_OUTPUTS_DIR}/apk/release/uhabits-android-release.apk || return 1 + run $ADB install -r ${ANDROID_OUTPUTS_DIR}/apk/release/uhabits-android-release.apk || return 1 else log_info "Installing debug APK..." - $ADB install -t -r ${ANDROID_OUTPUTS_DIR}/apk/debug/uhabits-android-debug.apk || return 1 + run $ADB install -t -r ${ANDROID_OUTPUTS_DIR}/apk/debug/uhabits-android-debug.apk || return 1 fi log_info "Installing test APK..." - $ADB install -r ${ANDROID_OUTPUTS_DIR}/apk/androidTest/debug/uhabits-android-debug-androidTest.apk || return 1 + run $ADB install -r ${ANDROID_OUTPUTS_DIR}/apk/androidTest/debug/uhabits-android-debug-androidTest.apk || return 1 for size in medium large; do OUT_INSTRUMENT=${ANDROID_OUTPUTS_DIR}/instrument-${API}.txt @@ -220,8 +272,8 @@ android_test() { log_error "Saving logcat: $OUT_LOGCAT..." $ADB logcat -d > $OUT_LOGCAT log_error "Fetching test screenshots..." - $ADB pull /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots ${ANDROID_OUTPUTS_DIR}/ - $ADB shell rm -r /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/ + run $ADB pull /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots ${ANDROID_OUTPUTS_DIR}/ + run $ADB shell rm -r /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/ return 1 fi done @@ -258,7 +310,6 @@ android_test_parallel() { if [ $ret_code != 0 ]; then success=1 fi - echo pid=$pid ret_code=$ret_code success=$success done # Print all logs @@ -287,15 +338,15 @@ android_build() { log_info "Building release APK..." gradle_run updateTranslators gradle_run :uhabits-android:assembleRelease - cp -v \ - uhabits-android/build/outputs/apk/release/uhabits-android-release.apk \ + log_info "Copying release APK..." + cp uhabits-android/build/outputs/apk/release/uhabits-android-release.apk \ uhabits-android/build/loop-"$VERSION"-release.apk fi log_info "Building debug APK..." gradle_run :uhabits-android:assembleDebug || fail - cp -v \ - uhabits-android/build/outputs/apk/debug/uhabits-android-debug.apk \ + log_info "Copying debug APK..." + cp uhabits-android/build/outputs/apk/debug/uhabits-android-debug.apk \ uhabits-android/build/loop-"$VERSION"-debug.apk log_info "Building instrumentation APK..." @@ -313,23 +364,20 @@ android_build() { } android_accept_images() { - find ${ANDROID_OUTPUTS_DIR}/test-screenshots -name '*.expected*' -delete - rsync -av ${ANDROID_OUTPUTS_DIR}/test-screenshots/ uhabits-android/src/androidTest/assets/ + log_info "Accepting test screenshots..." + run find ${ANDROID_OUTPUTS_DIR}/test-screenshots -name '*.expected*' -delete + run rsync -av ${ANDROID_OUTPUTS_DIR}/test-screenshots/ uhabits-android/src/androidTest/assets/ } # General # ----------------------------------------------------------------------------- _parse_opts() { - if ! OPTS="$(getopt -o r --long release -n 'build.sh' -- "$@")" ; then - exit 1; - fi - eval set -- "$OPTS" - - while true; do + while [ $# -gt 0 ]; do case "$1" in - -r | --release ) RELEASE=1; shift ;; - * ) break ;; + -r ) RELEASE=1; shift ;; + -c ) CLEAN=1; shift ;; + * ) shift ;; esac done } @@ -340,6 +388,7 @@ CI/CD script for Loop Habit Tracker. Usage: build.sh build [options] + build.sh android-accept-licenses build.sh android-setup build.sh android-tests [options] build.sh android-tests-parallel ... [options] @@ -347,17 +396,20 @@ Usage: Commands: build Build the app and run small tests + android-accept-licenses Accept all Android SDK licenses android-setup Create Android virtual machine android-tests Run medium and large Android tests on an emulator android-tests-parallel Tests multiple API levels simultaneously android-accept-images Copy fetched images to corresponding assets folder Options: - -r --release Build and test release version, instead of debug + -c Remove build folders before building + -r Build and test release version, instead of debug END } clean() { + log_info "Cleaning build folders..." rm -rf uhabits-android/.gradle rm -rf uhabits-android/android-pickers/build rm -rf uhabits-android/build @@ -379,11 +431,13 @@ main() { case "$1" in build) shift; _parse_opts "$@" - clean - log_info "Formatting code..." + if [ -n "$CLEAN" ]; then clean; fi core_build android_build ;; + android-accept-licenses) + android_accept_licenses + ;; android-setup) shift; _parse_opts "$@" android_setup $1