diff --git a/.github/workflows/scripts-android.yml b/.github/workflows/scripts-android.yml index 1537fd0807..cc3dfec3cc 100644 --- a/.github/workflows/scripts-android.yml +++ b/.github/workflows/scripts-android.yml @@ -141,7 +141,7 @@ jobs: - name: Run Android instrumentation tests uses: reactivecircus/android-emulator-runner@v2 with: - api-level: 31 + api-level: 36 arch: x86_64 target: google_apis disk-size: 2048M @@ -155,6 +155,24 @@ jobs: if-no-files-found: warn retention-days: 14 compression-level: 6 + - name: Upload Android Gradle logs + if: always() && matrix.id == 'default' + uses: actions/upload-artifact@v4 + with: + name: android-gradle-logs + path: artifacts/*gradle*.log + if-no-files-found: warn + retention-days: 14 + compression-level: 6 + - name: Upload Android test report + if: always() && matrix.id == 'default' + uses: actions/upload-artifact@v4 + with: + name: android-test-report + path: artifacts/android-test-report + if-no-files-found: warn + retention-days: 14 + compression-level: 6 - name: Upload Android Jacoco coverage report if: always() && matrix.id == 'default' uses: actions/upload-artifact@v4 diff --git a/scripts/android/lib/PatchGradleFiles.java b/scripts/android/lib/PatchGradleFiles.java index 3bf2ef2643..fc562f3905 100644 --- a/scripts/android/lib/PatchGradleFiles.java +++ b/scripts/android/lib/PatchGradleFiles.java @@ -48,6 +48,7 @@ public static void main(String[] args) throws Exception { private static boolean patchRootBuildGradle(Path path) throws IOException { String content = Files.readString(path, StandardCharsets.UTF_8); Matcher matcher = REPOSITORIES_PATTERN.matcher(content); + boolean changed = false; if (!matcher.find()) { if (!content.endsWith("\n")) { content += "\n"; @@ -57,31 +58,37 @@ private static boolean patchRootBuildGradle(Path path) throws IOException { return true; } - String block = matcher.group(); - boolean changed = false; - if (!block.contains("google()") || !block.contains("mavenCentral()")) { - String[] lines = block.split("\n"); - java.util.LinkedHashSet body = new java.util.LinkedHashSet<>(); - for (int i = 1; i < lines.length - 1; i++) { - String line = lines[i].trim(); - if (!line.isEmpty()) { - body.add(" " + line.trim()); + matcher.reset(); + StringBuffer updated = new StringBuffer(); + while (matcher.find()) { + String block = matcher.group(); + if (!block.contains("google()") || !block.contains("mavenCentral()")) { + String[] lines = block.split("\n"); + java.util.LinkedHashSet body = new java.util.LinkedHashSet<>(); + for (int i = 1; i < lines.length - 1; i++) { + String line = lines[i].trim(); + if (!line.isEmpty()) { + body.add(" " + line.trim()); + } } + body.add(" google()"); + body.add(" mavenCentral()"); + StringBuilder newBlock = new StringBuilder(); + newBlock.append(lines[0]).append('\n'); + for (String line : body) { + newBlock.append(line).append('\n'); + } + newBlock.append(lines[lines.length - 1]); + matcher.appendReplacement(updated, Matcher.quoteReplacement(newBlock.toString())); + changed = true; + } else { + matcher.appendReplacement(updated, Matcher.quoteReplacement(block)); } - body.add(" google()"); - body.add(" mavenCentral()"); - StringBuilder newBlock = new StringBuilder(); - newBlock.append(lines[0]).append('\n'); - for (String line : body) { - newBlock.append(line).append('\n'); - } - newBlock.append(lines[lines.length - 1]); - content = content.substring(0, matcher.start()) + newBlock + content.substring(matcher.end()); - changed = true; } + matcher.appendTail(updated); if (changed) { - Files.writeString(path, ensureTrailingNewline(content), StandardCharsets.UTF_8); + Files.writeString(path, ensureTrailingNewline(updated.toString()), StandardCharsets.UTF_8); } return changed; } @@ -304,7 +311,7 @@ private static Result ensureJacocoConfiguration(String content) { def kotlinClasses = fileTree(dir: "$buildDir/tmp/kotlin-classes/debug", exclude: excludes) def aarMainJar = file("$buildDir/intermediates/aar_main_jar/debug/classes.jar") def aarTrees = aarMainJar.exists() ? [zipTree(aarMainJar)] : [] - def runtimeJars = configurations.debugRuntimeClasspath.filter { it.name.endsWith('.jar') }.collect { zipTree(it) } + def runtimeJars = project.files({ configurations.debugRuntimeClasspath.filter { it.name.endsWith('.jar') }.collect { zipTree(it) } }) classDirectories.setFrom(files(javaClasses, kotlinClasses, aarTrees, runtimeJars).asFileTree.matching { include 'com/codename1/impl/android/**' @@ -356,8 +363,8 @@ private static class Arguments { static Arguments parse(String[] args) { Path root = null; Path app = null; - int compileSdk = 33; - int targetSdk = 33; + int compileSdk = 36; + int targetSdk = 36; for (int i = 0; i < args.length; i++) { String arg = args[i]; switch (arg) { diff --git a/scripts/build-android-app.sh b/scripts/build-android-app.sh index 6d90a33c53..b67bdddc9b 100755 --- a/scripts/build-android-app.sh +++ b/scripts/build-android-app.sh @@ -120,6 +120,7 @@ ba_log "Installed Android instrumentation tests in $ANDROID_TEST_JAVA_DIR" GRADLE_PROPS="$GRADLE_PROJECT_DIR/gradle.properties" grep -q '^android.useAndroidX=' "$GRADLE_PROPS" 2>/dev/null || echo 'android.useAndroidX=true' >> "$GRADLE_PROPS" grep -q '^android.enableJetifier=' "$GRADLE_PROPS" 2>/dev/null || echo 'android.enableJetifier=true' >> "$GRADLE_PROPS" +grep -q '^android.suppressUnsupportedCompileSdk=' "$GRADLE_PROPS" 2>/dev/null || echo 'android.suppressUnsupportedCompileSdk=36' >> "$GRADLE_PROPS" APP_BUILD_GRADLE="$GRADLE_PROJECT_DIR/app/build.gradle" ROOT_BUILD_GRADLE="$GRADLE_PROJECT_DIR/build.gradle" @@ -140,8 +141,8 @@ fi "$PATCH_GRADLE_JAVA" "$PATCH_GRADLE_SOURCE_PATH/$PATCH_GRADLE_MAIN_CLASS.java" \ --root "$ROOT_BUILD_GRADLE" \ --app "$APP_BUILD_GRADLE" \ - --compile-sdk 33 \ - --target-sdk 33 + --compile-sdk 36 \ + --target-sdk 36 # --- END: robust Gradle patch --- echo "----- app/build.gradle tail -----" @@ -155,8 +156,12 @@ export JAVA_HOME="${JDK_HOME:-$JAVA17_HOME}" ( cd "$GRADLE_PROJECT_DIR" if command -v sdkmanager >/dev/null 2>&1; then + ba_log "Ensuring Android SDK platform/build-tools 36 are installed" + yes | sdkmanager "platforms;android-36" "build-tools;36.0.0" >/dev/null 2>&1 || ba_log "Warning: unable to install Android SDK 36 components" yes | sdkmanager --licenses >/dev/null 2>&1 || true elif [ -x "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" ]; then + ba_log "Ensuring Android SDK platform/build-tools 36 are installed" + yes | "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" "platforms;android-36" "build-tools;36.0.0" >/dev/null 2>&1 || ba_log "Warning: unable to install Android SDK 36 components" yes | "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" --licenses >/dev/null 2>&1 || true fi ./gradlew --no-daemon assembleDebug diff --git a/scripts/run-android-instrumentation-tests.sh b/scripts/run-android-instrumentation-tests.sh index 06c6381d2e..fe6c59fa73 100755 --- a/scripts/run-android-instrumentation-tests.sh +++ b/scripts/run-android-instrumentation-tests.sh @@ -69,6 +69,34 @@ cn1ss_setup "$TARGET_JAVA_BIN" "$CN1SS_HELPER_SOURCE_DIR" [ -d "$GRADLE_PROJECT_DIR" ] || { ra_log "Gradle project directory not found: $GRADLE_PROJECT_DIR"; exit 4; } [ -x "$GRADLE_PROJECT_DIR/gradlew" ] || chmod +x "$GRADLE_PROJECT_DIR/gradlew" +ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-}}" +if [ -z "$ANDROID_SDK_ROOT" ]; then + if [ -d "/usr/local/lib/android/sdk" ]; then ANDROID_SDK_ROOT="/usr/local/lib/android/sdk" + elif [ -d "$HOME/Android/Sdk" ]; then ANDROID_SDK_ROOT="$HOME/Android/Sdk"; fi +fi +if [ -n "$ANDROID_SDK_ROOT" ] && [ -d "$ANDROID_SDK_ROOT" ]; then + export ANDROID_SDK_ROOT ANDROID_HOME="$ANDROID_SDK_ROOT" + SDKMANAGER_BIN="" + if [ -x "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" ]; then + SDKMANAGER_BIN="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" + elif command -v sdkmanager >/dev/null 2>&1; then + SDKMANAGER_BIN="$(command -v sdkmanager)" + fi + if [ -n "$SDKMANAGER_BIN" ]; then + ra_log "Ensuring Android SDK platform/build-tools 36 are installed" + SDK_INSTALL_LOG="$ARTIFACTS_DIR/sdkmanager-android-36.log" + if yes | "$SDKMANAGER_BIN" "platforms;android-36" "build-tools;36.0.0" >"$SDK_INSTALL_LOG" 2>&1; then + ra_log "Android SDK 36 components installed" + else + ra_log "Warning: unable to install Android SDK 36 components (see $SDK_INSTALL_LOG)" + fi + else + ra_log "Warning: sdkmanager not found; cannot install API 36 components" + fi +else + ra_log "Warning: Android SDK root not found; cannot install API 36 components" +fi + # ---- Prepare app + emulator state ----------------------------------------- MANIFEST="$GRADLE_PROJECT_DIR/app/src/main/AndroidManifest.xml" if [ ! -f "$MANIFEST" ]; then @@ -113,13 +141,23 @@ sleep 2 GRADLEW="./gradlew" GRADLE_CMD=("$GRADLEW" --stacktrace --info --no-daemon connectedDebugAndroidTest) +GRADLE_LOG="$ARTIFACTS_DIR/connectedDebugAndroidTest-gradle.log" +ANDROID_TEST_REPORT_DIR="scripts/hellocodenameone/android/target/hellocodenameone-android-1.0-SNAPSHOT-android-source/app/build/reports/androidTests/connected" +ANDROID_TEST_REPORT_DEST="$ARTIFACTS_DIR/android-test-report" ra_log "Executing connectedDebugAndroidTest via Gradle" if ! ( cd "scripts/hellocodenameone/android/target/hellocodenameone-android-1.0-SNAPSHOT-android-source" - JAVA_HOME="${JDK_HOME:-$JAVA17_HOME}" "${GRADLE_CMD[@]}" + JAVA_HOME="${JDK_HOME:-$JAVA17_HOME}" "${GRADLE_CMD[@]}" 2>&1 | tee "$GRADLE_LOG" ); then - ra_log "FATAL: connectedDebugAndroidTest failed" + if [ -d "$ANDROID_TEST_REPORT_DIR" ]; then + rm -rf "$ANDROID_TEST_REPORT_DEST" + cp -R "$ANDROID_TEST_REPORT_DIR" "$ANDROID_TEST_REPORT_DEST" + ra_log "Saved Android test report to $ANDROID_TEST_REPORT_DEST" + else + ra_log "Android test report directory not found at $ANDROID_TEST_REPORT_DIR" + fi + ra_log "FATAL: connectedDebugAndroidTest failed (see $GRADLE_LOG)" exit 10 fi