Skip to content

Commit d717536

Browse files
authored
fix: harden the Reply Drafter — foreground gate, model integrity, line endings (#2276)
Closes #2275
1 parent 3a195cd commit d717536

4 files changed

Lines changed: 226 additions & 205 deletions

File tree

app/build.gradle.kts

Lines changed: 196 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -1,196 +1,196 @@
1-
import java.util.Properties
2-
3-
plugins {
4-
alias(libs.plugins.compose.compiler)
5-
alias(libs.plugins.android.application)
6-
alias(libs.plugins.ksp)
7-
alias(libs.plugins.kotlin.serialization)
8-
9-
}
10-
11-
composeCompiler {
12-
}
13-
14-
android {
15-
namespace = "com.enaboapps.switchify"
16-
compileSdk = 36
17-
18-
defaultConfig {
19-
applicationId = "com.enaboapps.switchify"
20-
minSdk = 29
21-
targetSdk = 36
22-
versionCode = gitVersionCode()
23-
versionName = "2.25.0-beta.6"
24-
25-
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
26-
vectorDrawables {
27-
useSupportLibrary = true
28-
}
29-
30-
// Dual-channel config: env vars (used by CI from GitHub secrets) take
31-
// precedence, then local.properties (developer's local secrets). If
32-
// neither is present the build fails with the missing key name so
33-
// it's obvious what to set. local.properties is optional now; CI
34-
// builds don't generate one.
35-
val localProperties = Properties()
36-
val localPropertiesFile = rootProject.file("local.properties")
37-
if (localPropertiesFile.exists()) {
38-
localProperties.load(localPropertiesFile.inputStream())
39-
}
40-
fun configValue(envVar: String, propKey: String): String =
41-
System.getenv(envVar)?.takeIf { it.isNotEmpty() }
42-
?: localProperties.getProperty(propKey, "").takeIf { it.isNotEmpty() }
43-
?: throw GradleException(
44-
"Missing config: set $envVar env var or '$propKey' in local.properties"
45-
)
46-
47-
buildConfigField(
48-
"String",
49-
"REVENUECAT_PUBLIC_KEY",
50-
"\"${configValue("REVENUECAT_PUBLIC_KEY", "revenuecat.publicKey")}\""
51-
)
52-
buildConfigField(
53-
"String",
54-
"TIMBERLOGS_API_KEY",
55-
"\"${configValue("TIMBERLOGS_API_KEY", "timberlogs.apiKey")}\""
56-
)
57-
buildConfigField(
58-
"String",
59-
"SUPABASE_URL",
60-
"\"${configValue("SUPABASE_URL", "supabase.projectUrl")}\""
61-
)
62-
buildConfigField(
63-
"String",
64-
"SUPABASE_ANON_KEY",
65-
"\"${configValue("SUPABASE_ANON_KEY", "supabase.publishableKey")}\""
66-
)
67-
buildConfigField(
68-
"String",
69-
"GOOGLE_WEB_CLIENT_ID",
70-
"\"${configValue("GOOGLE_WEB_CLIENT_ID", "google.webClientId")}\""
71-
)
72-
buildConfigField(
73-
"String",
74-
"AI_MODEL_URL",
75-
"\"${configValue("AI_MODEL_URL", "aiModel.url")}\""
76-
)
77-
}
78-
79-
// CI-only release signing: activates when UPLOAD_KEYSTORE_PATH points at
80-
// a real file (the release workflow decodes the base64 secret into
81-
// RUNNER_TEMP before invoking Gradle). Android Studio's "Generate Signed
82-
// Bundle" flow continues to work locally — without the env var the
83-
// signing config is silently absent and the release build type is left
84-
// unsigned for Studio to handle.
85-
signingConfigs {
86-
create("release") {
87-
val ksPath = System.getenv("UPLOAD_KEYSTORE_PATH")
88-
if (!ksPath.isNullOrBlank() && file(ksPath).exists()) {
89-
storeFile = file(ksPath)
90-
storePassword = System.getenv("UPLOAD_KEYSTORE_PASSWORD")
91-
keyAlias = System.getenv("UPLOAD_KEY_ALIAS")
92-
keyPassword = System.getenv("UPLOAD_KEY_PASSWORD")
93-
}
94-
}
95-
}
96-
97-
buildTypes {
98-
debug {
99-
isDebuggable = true
100-
}
101-
release {
102-
isDebuggable = false
103-
isMinifyEnabled = false
104-
isShrinkResources = false
105-
proguardFiles(
106-
getDefaultProguardFile("proguard-android-optimize.txt"),
107-
"proguard-rules.pro"
108-
)
109-
signingConfigs.findByName("release")
110-
?.takeIf { it.storeFile?.exists() == true }
111-
?.let { signingConfig = it }
112-
}
113-
}
114-
compileOptions {
115-
sourceCompatibility = JavaVersion.VERSION_1_8
116-
targetCompatibility = JavaVersion.VERSION_1_8
117-
}
118-
buildFeatures {
119-
compose = true
120-
buildConfig = true
121-
}
122-
packaging {
123-
resources {
124-
excludes += "/META-INF/{AL2.0,LGPL2.1}"
125-
}
126-
}
127-
}
128-
129-
fun gitVersionCode(): Int {
130-
return runCatching {
131-
val process = ProcessBuilder("git", "rev-list", "--count", "HEAD")
132-
.redirectErrorStream(true)
133-
.start()
134-
val output = process.inputStream.bufferedReader().readText().trim()
135-
process.waitFor() // Optionally check the exit code if needed
136-
println("Git version code: $output")
137-
output.toInt()
138-
}.getOrElse { exception ->
139-
println("Warning: Failed to compute git version code: ${exception.message}")
140-
1 // Fallback value
141-
}
142-
}
143-
144-
dependencies {
145-
implementation(libs.core.ktx)
146-
implementation(platform(libs.compose.bom))
147-
implementation(libs.compose.ui)
148-
implementation(libs.compose.material)
149-
implementation(libs.compose.icons)
150-
implementation(libs.compose.ui.tooling.preview)
151-
implementation(libs.lifecycle.runtime.ktx)
152-
implementation(libs.compose.runtime.livedata)
153-
implementation(libs.activity.compose)
154-
implementation(libs.navigation.compose)
155-
implementation(platform(libs.supabase.bom))
156-
implementation(libs.supabase.postgrest)
157-
implementation(libs.supabase.auth)
158-
implementation(libs.supabase.realtime)
159-
implementation(libs.ktor.client.android)
160-
implementation(libs.gson)
161-
implementation(libs.androidx.material3.android)
162-
implementation(libs.app.update)
163-
implementation(libs.play.services.reviews)
164-
implementation(libs.play.services.reviews.ktx)
165-
implementation(libs.revenuecat)
166-
implementation(libs.revenuecat.ui)
167-
implementation(libs.androidx.camera.camera2)
168-
implementation(libs.androidx.camera.core)
169-
implementation(libs.androidx.camera.lifecycle)
170-
implementation(libs.androidx.camera.view)
171-
implementation(libs.accompanist.permissions)
172-
implementation(libs.mediapipe.tasks.vision)
173-
implementation(libs.mediapipe.tasks.genai)
174-
implementation(libs.mlkit.genai.prompt)
175-
implementation(libs.room.runtime)
176-
implementation(libs.room.ktx)
177-
ksp(libs.room.compiler)
178-
implementation(libs.reorderable)
179-
// New Google Identity Services with Credential Manager
180-
implementation(libs.credentials)
181-
implementation(libs.credentials.play.services.auth)
182-
implementation(libs.googleid)
183-
testImplementation(libs.junit)
184-
androidTestImplementation(libs.androidx.test.ext.junit)
185-
androidTestImplementation(libs.espresso.core)
186-
androidTestImplementation(platform(libs.compose.bom))
187-
androidTestImplementation(libs.compose.ui.test.junit4)
188-
debugImplementation(libs.compose.ui.tooling)
189-
debugImplementation(libs.compose.ui.test.manifest)
190-
}
191-
java {
192-
toolchain {
193-
languageVersion = JavaLanguageVersion.of(17)
194-
}
195-
}
196-
1+
import java.util.Properties
2+
3+
plugins {
4+
alias(libs.plugins.compose.compiler)
5+
alias(libs.plugins.android.application)
6+
alias(libs.plugins.ksp)
7+
alias(libs.plugins.kotlin.serialization)
8+
9+
}
10+
11+
composeCompiler {
12+
}
13+
14+
android {
15+
namespace = "com.enaboapps.switchify"
16+
compileSdk = 36
17+
18+
defaultConfig {
19+
applicationId = "com.enaboapps.switchify"
20+
minSdk = 29
21+
targetSdk = 36
22+
versionCode = gitVersionCode()
23+
versionName = "2.25.0-beta.6"
24+
25+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
26+
vectorDrawables {
27+
useSupportLibrary = true
28+
}
29+
30+
// Dual-channel config: env vars (used by CI from GitHub secrets) take
31+
// precedence, then local.properties (developer's local secrets). If
32+
// neither is present the build fails with the missing key name so
33+
// it's obvious what to set. local.properties is optional now; CI
34+
// builds don't generate one.
35+
val localProperties = Properties()
36+
val localPropertiesFile = rootProject.file("local.properties")
37+
if (localPropertiesFile.exists()) {
38+
localProperties.load(localPropertiesFile.inputStream())
39+
}
40+
fun configValue(envVar: String, propKey: String): String =
41+
System.getenv(envVar)?.takeIf { it.isNotEmpty() }
42+
?: localProperties.getProperty(propKey, "").takeIf { it.isNotEmpty() }
43+
?: throw GradleException(
44+
"Missing config: set $envVar env var or '$propKey' in local.properties"
45+
)
46+
47+
buildConfigField(
48+
"String",
49+
"REVENUECAT_PUBLIC_KEY",
50+
"\"${configValue("REVENUECAT_PUBLIC_KEY", "revenuecat.publicKey")}\""
51+
)
52+
buildConfigField(
53+
"String",
54+
"TIMBERLOGS_API_KEY",
55+
"\"${configValue("TIMBERLOGS_API_KEY", "timberlogs.apiKey")}\""
56+
)
57+
buildConfigField(
58+
"String",
59+
"SUPABASE_URL",
60+
"\"${configValue("SUPABASE_URL", "supabase.projectUrl")}\""
61+
)
62+
buildConfigField(
63+
"String",
64+
"SUPABASE_ANON_KEY",
65+
"\"${configValue("SUPABASE_ANON_KEY", "supabase.publishableKey")}\""
66+
)
67+
buildConfigField(
68+
"String",
69+
"GOOGLE_WEB_CLIENT_ID",
70+
"\"${configValue("GOOGLE_WEB_CLIENT_ID", "google.webClientId")}\""
71+
)
72+
buildConfigField(
73+
"String",
74+
"AI_MODEL_URL",
75+
"\"${configValue("AI_MODEL_URL", "aiModel.url")}\""
76+
)
77+
}
78+
79+
// CI-only release signing: activates when UPLOAD_KEYSTORE_PATH points at
80+
// a real file (the release workflow decodes the base64 secret into
81+
// RUNNER_TEMP before invoking Gradle). Android Studio's "Generate Signed
82+
// Bundle" flow continues to work locally — without the env var the
83+
// signing config is silently absent and the release build type is left
84+
// unsigned for Studio to handle.
85+
signingConfigs {
86+
create("release") {
87+
val ksPath = System.getenv("UPLOAD_KEYSTORE_PATH")
88+
if (!ksPath.isNullOrBlank() && file(ksPath).exists()) {
89+
storeFile = file(ksPath)
90+
storePassword = System.getenv("UPLOAD_KEYSTORE_PASSWORD")
91+
keyAlias = System.getenv("UPLOAD_KEY_ALIAS")
92+
keyPassword = System.getenv("UPLOAD_KEY_PASSWORD")
93+
}
94+
}
95+
}
96+
97+
buildTypes {
98+
debug {
99+
isDebuggable = true
100+
}
101+
release {
102+
isDebuggable = false
103+
isMinifyEnabled = false
104+
isShrinkResources = false
105+
proguardFiles(
106+
getDefaultProguardFile("proguard-android-optimize.txt"),
107+
"proguard-rules.pro"
108+
)
109+
signingConfigs.findByName("release")
110+
?.takeIf { it.storeFile?.exists() == true }
111+
?.let { signingConfig = it }
112+
}
113+
}
114+
compileOptions {
115+
sourceCompatibility = JavaVersion.VERSION_1_8
116+
targetCompatibility = JavaVersion.VERSION_1_8
117+
}
118+
buildFeatures {
119+
compose = true
120+
buildConfig = true
121+
}
122+
packaging {
123+
resources {
124+
excludes += "/META-INF/{AL2.0,LGPL2.1}"
125+
}
126+
}
127+
}
128+
129+
fun gitVersionCode(): Int {
130+
return runCatching {
131+
val process = ProcessBuilder("git", "rev-list", "--count", "HEAD")
132+
.redirectErrorStream(true)
133+
.start()
134+
val output = process.inputStream.bufferedReader().readText().trim()
135+
process.waitFor() // Optionally check the exit code if needed
136+
println("Git version code: $output")
137+
output.toInt()
138+
}.getOrElse { exception ->
139+
println("Warning: Failed to compute git version code: ${exception.message}")
140+
1 // Fallback value
141+
}
142+
}
143+
144+
dependencies {
145+
implementation(libs.core.ktx)
146+
implementation(platform(libs.compose.bom))
147+
implementation(libs.compose.ui)
148+
implementation(libs.compose.material)
149+
implementation(libs.compose.icons)
150+
implementation(libs.compose.ui.tooling.preview)
151+
implementation(libs.lifecycle.runtime.ktx)
152+
implementation(libs.compose.runtime.livedata)
153+
implementation(libs.activity.compose)
154+
implementation(libs.navigation.compose)
155+
implementation(platform(libs.supabase.bom))
156+
implementation(libs.supabase.postgrest)
157+
implementation(libs.supabase.auth)
158+
implementation(libs.supabase.realtime)
159+
implementation(libs.ktor.client.android)
160+
implementation(libs.gson)
161+
implementation(libs.androidx.material3.android)
162+
implementation(libs.app.update)
163+
implementation(libs.play.services.reviews)
164+
implementation(libs.play.services.reviews.ktx)
165+
implementation(libs.revenuecat)
166+
implementation(libs.revenuecat.ui)
167+
implementation(libs.androidx.camera.camera2)
168+
implementation(libs.androidx.camera.core)
169+
implementation(libs.androidx.camera.lifecycle)
170+
implementation(libs.androidx.camera.view)
171+
implementation(libs.accompanist.permissions)
172+
implementation(libs.mediapipe.tasks.vision)
173+
implementation(libs.mediapipe.tasks.genai)
174+
implementation(libs.mlkit.genai.prompt)
175+
implementation(libs.room.runtime)
176+
implementation(libs.room.ktx)
177+
ksp(libs.room.compiler)
178+
implementation(libs.reorderable)
179+
// New Google Identity Services with Credential Manager
180+
implementation(libs.credentials)
181+
implementation(libs.credentials.play.services.auth)
182+
implementation(libs.googleid)
183+
testImplementation(libs.junit)
184+
androidTestImplementation(libs.androidx.test.ext.junit)
185+
androidTestImplementation(libs.espresso.core)
186+
androidTestImplementation(platform(libs.compose.bom))
187+
androidTestImplementation(libs.compose.ui.test.junit4)
188+
debugImplementation(libs.compose.ui.tooling)
189+
debugImplementation(libs.compose.ui.test.manifest)
190+
}
191+
java {
192+
toolchain {
193+
languageVersion = JavaLanguageVersion.of(17)
194+
}
195+
}
196+

0 commit comments

Comments
 (0)