150 lines
5.1 KiB
Kotlin
150 lines
5.1 KiB
Kotlin
import java.util.Properties
|
|
import java.io.File
|
|
import java.io.FileInputStream
|
|
import java.security.MessageDigest
|
|
|
|
plugins {
|
|
id("com.android.application")
|
|
id("dev.flutter.flutter-gradle-plugin")
|
|
}
|
|
|
|
fun sha256Hex(file: File): String {
|
|
val digest = MessageDigest.getInstance("SHA-256").digest(file.readBytes())
|
|
return digest.joinToString("") { "%02x".format(it.toInt() and 0xff) }
|
|
}
|
|
|
|
android {
|
|
namespace = "com.paladinvpn.app"
|
|
compileSdk = flutter.compileSdkVersion
|
|
ndkVersion = flutter.ndkVersion
|
|
|
|
compileOptions {
|
|
isCoreLibraryDesugaringEnabled = true
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
}
|
|
|
|
val keystorePropertiesFile = rootProject.file("key.properties")
|
|
val keystoreProperties = Properties()
|
|
if (keystorePropertiesFile.exists()) {
|
|
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
|
}
|
|
|
|
val admobPropertiesFile = rootProject.file("admob.properties")
|
|
val admobProperties = Properties()
|
|
if (admobPropertiesFile.exists()) {
|
|
admobProperties.load(FileInputStream(admobPropertiesFile))
|
|
}
|
|
|
|
// Explicit opt-in used only by GitHub Actions to exercise the real
|
|
// release/R8 graph. Production release signing and config pinning remain
|
|
// fail-closed outside that CI smoke path.
|
|
val ciReleaseSmoke =
|
|
System.getenv("GITHUB_ACTIONS") == "true" &&
|
|
System.getenv("REVOLT_CI_RELEASE_SMOKE") == "true"
|
|
|
|
val appConfigFile = rootProject.file("../lib/logic/app_config.dart")
|
|
val verifyReleaseAppConfig = tasks.register("verifyReleaseAppConfig") {
|
|
group = "verification"
|
|
description = "Verify the production Dart app config against REVOLT_APP_CONFIG_SHA256."
|
|
doLast {
|
|
if (ciReleaseSmoke) {
|
|
logger.lifecycle("Skipping production app-config pin for CI release smoke build")
|
|
return@doLast
|
|
}
|
|
|
|
val expected = System.getenv("REVOLT_APP_CONFIG_SHA256")?.trim()?.lowercase()
|
|
if (expected.isNullOrEmpty()) {
|
|
throw org.gradle.api.GradleException(
|
|
"REVOLT_APP_CONFIG_SHA256 is required for production release builds"
|
|
)
|
|
}
|
|
if (!Regex("[0-9a-f]{64}").matches(expected)) {
|
|
throw org.gradle.api.GradleException(
|
|
"REVOLT_APP_CONFIG_SHA256 must be exactly 64 lowercase hexadecimal characters"
|
|
)
|
|
}
|
|
if (!appConfigFile.isFile) {
|
|
throw org.gradle.api.GradleException(
|
|
"Production app config is missing: ${appConfigFile.absolutePath}"
|
|
)
|
|
}
|
|
|
|
val configText = appConfigFile.readText()
|
|
val templateMarkers = listOf(
|
|
"'0.0.0.0'",
|
|
"YOUR_DOMAIN",
|
|
"YOUR_GITHUB_USERNAME",
|
|
"YOUR_REPO_NAME",
|
|
"ca-app-pub-0000000000000000/0000000000",
|
|
)
|
|
val remainingMarker = templateMarkers.firstOrNull(configText::contains)
|
|
if (remainingMarker != null) {
|
|
throw org.gradle.api.GradleException(
|
|
"Production app config still contains template marker: $remainingMarker"
|
|
)
|
|
}
|
|
|
|
val actual = sha256Hex(appConfigFile)
|
|
if (actual != expected) {
|
|
throw org.gradle.api.GradleException(
|
|
"Production app config SHA-256 mismatch: expected $expected, got $actual"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
tasks.matching { it.name == "preReleaseBuild" }.configureEach {
|
|
dependsOn(verifyReleaseAppConfig)
|
|
}
|
|
|
|
defaultConfig {
|
|
applicationId = "com.paladinvpn.app"
|
|
minSdk = flutter.minSdkVersion
|
|
targetSdk = flutter.targetSdkVersion
|
|
versionCode = flutter.versionCode
|
|
versionName = flutter.versionName
|
|
manifestPlaceholders["ADMOB_APP_ID"] = admobProperties.getProperty("ADMOB_APP_ID", "ca-app-pub-0000000000000000~0000000000")
|
|
}
|
|
|
|
signingConfigs {
|
|
create("release") {
|
|
keyAlias = keystoreProperties["keyAlias"] as String?
|
|
keyPassword = keystoreProperties["keyPassword"] as String?
|
|
storeFile = keystoreProperties["storeFile"]?.let { rootProject.file(it) }
|
|
storePassword = keystoreProperties["storePassword"] as String?
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
signingConfig = if (ciReleaseSmoke) {
|
|
signingConfigs.getByName("debug")
|
|
} else {
|
|
signingConfigs.getByName("release")
|
|
}
|
|
isMinifyEnabled = true
|
|
isShrinkResources = true
|
|
proguardFiles(
|
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
"proguard-rules.pro"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
kotlin {
|
|
compilerOptions {
|
|
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
|
|
}
|
|
}
|
|
|
|
flutter {
|
|
source = "../.."
|
|
}
|
|
|
|
dependencies {
|
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
|
implementation("androidx.core:core-ktx:1.13.1")
|
|
}
|