build.gradle

10.2 KB · raw

1plugins {2    id "com.android.application"3    id "org.jetbrains.kotlin.android"4    id "org.jetbrains.kotlin.plugin.compose"5    id "org.jetbrains.kotlin.plugin.parcelize"6    id "org.jetbrains.kotlin.plugin.serialization"7    id "kotlin-kapt"8    id "com.google.dagger.hilt.android"9}1011android {12    namespace "com.peachpasswords"13    compileSdk 351415    // VERSION is the single semantic-version authority. versionCode remains16    // Android/Gradle metadata because Kepr's manifest does not carry it; the17    // updater enforces exact VERSION plus a strictly newer code.18    def versionAuthorityFile = rootProject.file("VERSION")19    if (!versionAuthorityFile.isFile()) {20        throw new GradleException("VERSION is missing; it is the semantic-version authority")21    }22    def semanticVersion = versionAuthorityFile.getText("UTF-8").trim()23    if (!(semanticVersion ==~ /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/)) {24        throw new GradleException("VERSION must contain exactly one strict major.minor.patch value")25    }26    def versionParts = semanticVersion.split("\\.")27    def vMajor = versionParts[0].toInteger()28    def vMinor = versionParts[1].toInteger()29    def vPatch = versionParts[2].toInteger()30    if (vMajor > 99 || vMinor > 99 || vPatch > 999) {31        throw new GradleException("VERSION components exceed versionCode encoding range (major/minor <= 99, patch <= 999)")32    }33    // Derive a strictly-monotonic versionCode from semver so it can never34    // accidentally stay flat across releases (which breaks the updater's35    // versionCode > currentVersionCode identity gate).36    def derivedVersionCode = vMajor * 1000000 + vMinor * 10000 + vPatch * 103738    defaultConfig {39        applicationId "com.peachpasswords"40        minSdk 2641        targetSdk 3542        versionCode derivedVersionCode43        versionName semanticVersion4445        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"46        vectorDrawables {47            useSupportLibrary true48        }49    }5051    signingConfigs {52        release {53            // Release authority comes only from process-scoped protected54            // injection. local.properties is limited to non-secret local SDK55            // configuration and is never a signing-secret store.56            def signingEnvironment = [57                storeFile: System.getenv("PEACH_RELEASE_STORE_FILE"),58                storePassword: System.getenv("PEACH_RELEASE_STORE_PASSWORD"),59                keyAlias: System.getenv("PEACH_RELEASE_KEY_ALIAS"),60                keyPassword: System.getenv("PEACH_RELEASE_KEY_PASSWORD"),61            ]62            def configuredValues = signingEnvironment.values().count { it != null && !it.isEmpty() }63            if (configuredValues != 0 && configuredValues != signingEnvironment.size()) {64                throw new GradleException("incomplete protected release signing environment")65            }66            if (configuredValues == signingEnvironment.size()) {67                storeFile file(signingEnvironment.storeFile)68                storePassword signingEnvironment.storePassword69                keyAlias signingEnvironment.keyAlias70                keyPassword signingEnvironment.keyPassword71            }72            // v3 is the only escape hatch from "this key is forever": it carries a73            // rotation lineage, so a future key can be proven to descend from this74            // one instead of forcing every tester to uninstall and lose their75            // vault. AGP leaves it off unless asked.76            enableV2Signing true77            enableV3Signing true78        }79    }8081    splits {82        abi {83            enable true84            reset()85            include "arm64-v8a", "x86_64"86            universalApk false87        }88    }8990    buildTypes {91        debug {92            applicationIdSuffix ".debug"93            versionNameSuffix "-debug"94            debuggable true95        }96        release {97            signingConfig signingConfigs.release98            minifyEnabled true99            shrinkResources true100            proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"101        }102    }103104    // Never let a release task silently emit an unsigned artifact. Debug tasks105    // remain usable in fresh workspaces that do not hold release authority.106    gradle.taskGraph.whenReady { graph ->107        if (graph.allTasks.any { it.project == project && it.name.toLowerCase().contains("release") } &&108            !signingConfigs.release.isSigningReady()) {109            throw new GradleException(110                "release signing requires PEACH_RELEASE_STORE_FILE, " +111                    "PEACH_RELEASE_STORE_PASSWORD, PEACH_RELEASE_KEY_ALIAS, and " +112                    "PEACH_RELEASE_KEY_PASSWORD",113            )114        }115    }116117    compileOptions {118        sourceCompatibility JavaVersion.VERSION_17119        targetCompatibility JavaVersion.VERSION_17120    }121122    kotlinOptions {123        jvmTarget = "17"124    }125126    buildFeatures {127        compose true128        buildConfig true129    }130131    composeOptions {132        kotlinCompilerExtensionVersion "1.5.15"133    }134135    packaging {136        resources {137            excludes += [138                "/META-INF/{AL2.0,LGPL2.1}",139                "META-INF/DEPENDENCIES",140                "META-INF/LICENSE*",141                "META-INF/NOTICE*",142            ]143        }144    }145146    testOptions {147        unitTests.returnDefaultValues = true148        unitTests.all {149            def coreDir = rootProject.file("../peach-browser/crypto-core")150            def nativeLibrary = new File(151                coreDir,152                "target/release/libpeach_noise_core.dylib",153            )154            if (coreDir.exists()) {155                dependsOn rootProject.tasks.named(name.contains("Test") ? "buildRustNoiseHostTest" : "buildRustNoiseHost")156                systemProperty "peach.noise.library.path", nativeLibrary.absolutePath157            }158        }159    }160}161162dependencies {163    implementation platform("androidx.compose:compose-bom:2024.06.00")164    androidTestImplementation platform("androidx.compose:compose-bom:2024.06.00")165166    implementation "androidx.activity:activity-compose:1.9.2"167    implementation "androidx.autofill:autofill:1.1.0"168    implementation "androidx.biometric:biometric:1.1.0"169    implementation "androidx.core:core-splashscreen:1.0.1"170    implementation "androidx.appcompat:appcompat:1.7.0"171    implementation "androidx.credentials:credentials:1.5.0"172    implementation "androidx.credentials:credentials-play-services-auth:1.5.0"173    implementation "androidx.glance:glance-appwidget:1.1.0"174    implementation "androidx.glance:glance-material3:1.1.0"175    implementation "androidx.hilt:hilt-navigation-compose:1.2.0"176    // In-app updates: a periodic release check that survives Doze without a177    // service. hilt-work lets the worker take UpdateRepository by injection178    // rather than reaching for a global.179    implementation "androidx.hilt:hilt-work:1.2.0"180    kapt "androidx.hilt:hilt-compiler:1.2.0"181    implementation "androidx.work:work-runtime-ktx:2.9.1"182    implementation "androidx.lifecycle:lifecycle-runtime-compose:2.8.4"183    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4"184    implementation "androidx.lifecycle:lifecycle-process:2.8.4"185    implementation "androidx.navigation:navigation-compose:2.8.0"186    // Deliberately pinned to 1.1.0-alpha06 (audit M-12). 1.1.0 stable exists187    // now and GradleDependency lint flags this coordinate; a bump is a policy188    // choice (Tink runtime vs. baseline AES, master-key rotation semantics),189    // not an oversight, and is tracked separately. Exact alpha (never a floating190    // range) so the resolved artifact is reproducible. Used only for the191    // duress/license EncryptedSharedPreferences; the vault slots are sealed by192    // VaultCrypto, not this.193    implementation "androidx.security:security-crypto:1.1.0-alpha06"194    implementation "androidx.compose.material3:material3"195    implementation "androidx.compose.ui:ui"196    implementation "androidx.compose.ui:ui-tooling-preview"197    implementation "androidx.compose.foundation:foundation"198    implementation "androidx.compose.material:material-icons-extended"199    implementation "androidx.core:core-ktx:1.13.1"200    implementation "com.google.android.material:material:1.12.0"201202    implementation "com.google.dagger:hilt-android:2.51.1"203    kapt "com.google.dagger:hilt-compiler:2.51.1"204    implementation "com.google.zxing:core:3.5.3"205    implementation "androidx.camera:camera-core:1.3.4"206    implementation "androidx.camera:camera-camera2:1.3.4"207    implementation "androidx.camera:camera-lifecycle:1.3.4"208    implementation "androidx.camera:camera-view:1.3.4"209    implementation "com.squareup.okhttp3:okhttp:4.12.0"210    implementation "org.bouncycastle:bcprov-jdk18on:1.84"211    // zstd-jni (reference libzstd) — per-section compression for the PeachScript212    // PCHS binary format (MF-2). Required for cross-platform interop with the213    // browser's zstd sections. The @aar variant ships the Android jniLibs214    // (lib/<abi>/libzstd-jni.so); the plain jar bundles only desktop natives and215    // would fail with UnsatisfiedLinkError on-device.216    implementation "com.github.luben:zstd-jni:1.5.6-3@aar"217    // The @aar ships only Android .so; the plain jar carries the desktop natives218    // the host JVM unit tests need.219    testImplementation "com.github.luben:zstd-jni:1.5.6-3"220    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"221    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1"222223    debugImplementation "androidx.compose.ui:ui-tooling"224    debugImplementation "androidx.compose.ui:ui-test-manifest"225226    androidTestImplementation "androidx.test.ext:junit:1.2.1"227    androidTestImplementation "androidx.test:core:1.6.1"228    androidTestImplementation "androidx.test.espresso:espresso-core:3.6.1"229    androidTestImplementation "androidx.test.uiautomator:uiautomator:2.3.0"230    androidTestImplementation "androidx.compose.ui:ui-test-junit4"231232    testImplementation "junit:junit:4.13.2"233    // Real org.json on the unit-test classpath; android.jar's is a throwing stub,234    // so the release-manifest parser would otherwise be untestable off-device.235    testImplementation "org.json:json:20240303"236    testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1"237}