Skip to content

Commit

Permalink
Implement release mode instrumentation with better proguard rules
Browse files Browse the repository at this point in the history
  • Loading branch information
voczi authored and mikehardy committed Jun 26, 2024
1 parent 9951dec commit a4f0d8f
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 27 deletions.
25 changes: 24 additions & 1 deletion .github/workflows/tests_emulator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ jobs:
timeout-minutes: 75
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
TEST_RELEASE_BUILD: true
# could be better, '/home/runner' should be '~/' but neither that nor '$HOME' worked
STOREFILEDIR: /home/runner/src
STOREFILE: android-keystore
STOREPASS: testpass
KEYPASS: testpass
KEYALIAS: nrkeystorealias
strategy:
fail-fast: false
matrix:
Expand All @@ -33,6 +40,15 @@ jobs:
# This is useful for benchmarking, do 0, 1, 2, etc (up to 256 max job-per-matrix limit) for averages
iteration: [0]
steps:
- name: Test Credential Prep
run: |
echo "KSTOREPWD=$STOREPASS" >> $GITHUB_ENV
echo "KEYPWD=$KEYPASS" >> $GITHUB_ENV
mkdir $STOREFILEDIR
cd $STOREFILEDIR
echo y | keytool -genkeypair -dname "cn=AnkiDroid, ou=ankidroid, o=AnkiDroid, c=US" -alias $KEYALIAS -keypass $KEYPASS -keystore "$STOREFILE" -storepass $STOREPASS -keyalg RSA -validity 20000
shell: bash

- uses: actions/checkout@v4
with:
fetch-depth: 50
Expand Down Expand Up @@ -80,7 +96,7 @@ jobs:
timeout_minutes: 15
retry_wait_seconds: 60
max_attempts: 3
command: ./gradlew packagePlayDebug packagePlayDebugAndroidTest --daemon
command: ./gradlew packagePlayRelease packagePlayReleaseAndroidTest --daemon

- name: AVD Boot and Snapshot Creation
# Only generate a snapshot for saving if we are on main branch with a cache miss
Expand Down Expand Up @@ -147,6 +163,13 @@ jobs:
sleep 5
./gradlew uninstallAll jacocoAndroidTestReport --daemon
- name: Upload Test Report
uses: actions/upload-artifact@v4
if: always()
with:
name: ${{ matrix.api-level }}-${{ matrix.arch }}-${{matrix.target}}-${{matrix.first-boot-delay}}-${{matrix.iteration}}-jacocoAndroidTestReport
path: ~/work/Anki-Android/Anki-Android/AnkiDroid/build/reports/jacoco/

- name: Upload Emulator Log
uses: actions/upload-artifact@v4
if: always()
Expand Down
25 changes: 23 additions & 2 deletions AnkiDroid/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.keeper)
id 'idea'
}

Expand All @@ -15,6 +16,12 @@ repositories {
maven { url "https://jitpack.io" }
}

keeper {
traceReferences {
// Silence missing definitions
arguments.set(["--map-diagnostics:MissingDefinitionsDiagnostic", "error", "none"])
}
}

idea {
module {
Expand Down Expand Up @@ -42,6 +49,12 @@ android {
aidl = true
}

if (rootProject.testReleaseBuild) {
testBuildType "release"
} else {
testBuildType "debug"
}

defaultConfig {
applicationId "com.ichi2.anki"
buildConfigField "Boolean", "CI", (System.getenv("CI") == "true").toString()
Expand Down Expand Up @@ -77,6 +90,12 @@ android {
versionCode=21900107
versionName="2.19alpha7"
minSdk libs.versions.minSdk.get().toInteger()

// Stays until this is in a release: https://github.com/google/desugar_jdk_libs/commit/c01a5446ca13586b801dbba4d83c6821337b3cc2
if (testReleaseBuild && minSdk < 24) {
minSdk 24
}

// After #13695: change .tests_emulator.yml
targetSdk libs.versions.targetSdk.get().toInteger()
testApplicationId "com.ichi2.anki.tests"
Expand Down Expand Up @@ -129,9 +148,11 @@ android {
resValue "string", "applicationId", "${defaultConfig.applicationId}${applicationIdSuffix}"
}
named('release') {
testCoverageEnabled = testReleaseBuild
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
testProguardFile 'proguard-test-rules.pro'
splits.abi.universalApk = universalApkEnabled // Build universal APK for release with `-Duniversal-apk=true`
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release

// syntax: assembleRelease -PcustomSuffix="suffix" -PcustomName="New name"
Expand Down Expand Up @@ -293,7 +314,7 @@ tasks.register('assertNonzeroAndroidTests') {
}
}
afterEvaluate {
tasks.named('connectedPlayDebugAndroidTest').configure { finalizedBy('assertNonzeroAndroidTests') }
tasks.named(androidTestName).configure { finalizedBy('assertNonzeroAndroidTests') }
}

apply from: "./robolectricDownloader.gradle"
Expand Down
17 changes: 12 additions & 5 deletions AnkiDroid/jacoco.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ def fileFilter = [
'**/*$Result$*.*'
]

def classDir = 'tmp/kotlin-classes/play'
if (rootProject.testReleaseBuild)
classDir += "Release"
else
classDir += "Debug"

// Our merge report task
def testReport = tasks.register('jacocoTestReport', JacocoReport) {
def htmlOutDir = project.layout.buildDirectory.dir("reports/jacoco/$name/html").get().asFile
Expand All @@ -105,7 +111,7 @@ def testReport = tasks.register('jacocoTestReport', JacocoReport) {
html.destination htmlOutDir
}

def kotlinClasses = fileTree(dir: project.layout.buildDirectory.dir('tmp/kotlin-classes/playDebug'), excludes: fileFilter)
def kotlinClasses = fileTree(dir: project.layout.buildDirectory.dir(classDir), excludes: fileFilter)
def mainSrc = "$project.projectDir/src/main/java"

sourceDirectories.from = files([mainSrc])
Expand All @@ -117,7 +123,7 @@ def testReport = tasks.register('jacocoTestReport', JacocoReport) {
}
testReport.configure {
dependsOn('testPlayDebugUnitTest')
dependsOn('connectedPlayDebugAndroidTest')
dependsOn(rootProject.androidTestName)
}

// A unit-test only report task
Expand All @@ -133,7 +139,7 @@ def unitTestReport = tasks.register('jacocoUnitTestReport', JacocoReport) {
html.destination htmlOutDir
}

def kotlinClasses = fileTree(dir: project.layout.buildDirectory.dir('tmp/kotlin-classes/playDebug'), excludes: fileFilter)
def kotlinClasses = fileTree(dir: project.layout.buildDirectory.dir(classDir), excludes: fileFilter)
def mainSrc = "$project.projectDir/src/main/java"

sourceDirectories.from = files([mainSrc])
Expand All @@ -158,7 +164,7 @@ def androidTestReport = tasks.register('jacocoAndroidTestReport', JacocoReport)
html.destination htmlOutDir
}

def kotlinClasses = fileTree(dir: project.layout.buildDirectory.dir('tmp/kotlin-classes/playDebug'), excludes: fileFilter)
def kotlinClasses = fileTree(dir: project.layout.buildDirectory.dir(classDir), excludes: fileFilter)
def mainSrc = "$project.projectDir/src/main/java"

sourceDirectories.from = files([mainSrc])
Expand All @@ -167,7 +173,8 @@ def androidTestReport = tasks.register('jacocoAndroidTestReport', JacocoReport)
'**/*.ec'
])
}
androidTestReport.configure { dependsOn('connectedPlayDebugAndroidTest') }

androidTestReport.configure { dependsOn(rootProject.androidTestName) }

// Issue 16640 - some emulators run, but register zero coverage
tasks.register('assertNonzeroAndroidTestCoverage') {
Expand Down
34 changes: 15 additions & 19 deletions AnkiDroid/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Program Files (x86)\Android\android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.kts.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# FIXME remove this entire Android 4.2 workaround from 12/3/15 timrae commit for 2.15.x+
# Samsung Android 4.2 bug workaround
# https://code.google.com/p/android/issues/detail?id=78377
-keepattributes **
-keep class !android.support.v7.view.menu.**,!android.support.design.internal.NavigationMenu,!android.support.design.internal.NavigationMenuPresenter,!android.support.design.internal.NavigationSubMenu,** {*;}
#5806 - Class: ActionBarOverflow
-keep public class android.support.v7.internal.view.menu.** { *; }
-keep public class androidx.appcompat.view.menu.** { *; }
-dontpreverify
-dontoptimize
-dontshrink
-dontwarn **
-dontnote **
# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable

# Used through Reflection
-keep class com.ichi2.anki.**.*Fragment { *; }
-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
-keep class androidx.core.app.ActivityCompat$* { *; }
-keep class androidx.concurrent.futures.** { *; }

# Ignore unused packages
-dontwarn javax.naming.**
-dontwarn org.ietf.jgss.**
11 changes: 11 additions & 0 deletions AnkiDroid/proguard-test-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# These proguard rules are only needed when building
# for the combination of testing and release mode
# Certain androidx frameworks that are test-only have
# issues with proguard / minimization in release mode

# Used for testing
-keep class kotlin.test.** { *; }
-keep class **.R$layout { <init> (...); <fields>; }

# Ignore unused packages
-dontwarn com.google.protobuf.GeneratedMessageLite$*
22 changes: 22 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.extension.impl.AndroidComponentsExtensionImpl
import com.slack.keeper.optInToKeeper
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.internal.jvm.Jvm
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
Expand All @@ -18,6 +20,11 @@ buildscript {
}
}
}

dependencies {
// Stays until this is merged: https://github.com/slackhq/keeper/pull/147
classpath(files("${rootDir.path}/tools/keeper-gradle-plugin.jar"))
}
}

plugins {
Expand All @@ -29,6 +36,7 @@ plugins {
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.ktlint) apply false
alias(libs.plugins.dokka) apply false
alias(libs.plugins.keeper) apply false
}

val localProperties = java.util.Properties()
Expand Down Expand Up @@ -64,6 +72,15 @@ subprojects {
it.systemProperties["junit.jupiter.execution.parallel.enabled"] = true
it.systemProperties["junit.jupiter.execution.parallel.mode.default"] = "concurrent"
}

val androidComponentsExtension =
extensions.findByName("androidComponents") as AndroidComponentsExtensionImpl<*, *, *>
androidComponentsExtension.beforeVariants { builder ->
if (testReleaseBuild && builder.name == "playRelease")
{
builder.optInToKeeper()
}
}
}

/**
Expand Down Expand Up @@ -118,6 +135,11 @@ val preDexEnabled by extra("true" == System.getProperty("pre-dex", "true"))
// allows for universal APKs to be generated
val universalApkEnabled by extra("true" == System.getProperty("universal-apk", "false"))

val testReleaseBuild by extra(System.getenv("TEST_RELEASE_BUILD") == "true")
var androidTestName by extra(
if (testReleaseBuild) "connectedPlayReleaseAndroidTest" else "connectedPlayDebugAndroidTest"
)

val gradleTestMaxParallelForks by extra(
if (System.getProperty("os.name") == "Mac OS X") {
// macOS reports hardware cores. This is accurate for CI, Intel (halved due to SMT) and Apple Silicon
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ robolectric = "4.12.2"
searchpreference = "2.5.1"
seismic = "1.0.3"
sharedPreferencesMock = "1.2.4"
slackKeeper = "0.16.0"
slf4jTimber = "3.1"
timber = "5.0.1"
triplet = "3.10.0"
Expand Down Expand Up @@ -177,3 +178,4 @@ dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }


keeper = { id = "com.slack.keeper", version.ref = "slackKeeper" }
Binary file added tools/keeper-gradle-plugin.jar
Binary file not shown.

0 comments on commit a4f0d8f

Please sign in to comment.