feat(config): shared app.properties for app-wide constants

Add a single source of truth (root app.properties) for public app-wide
constants, injected at build time on both platforms instead of hardcoding.

- Apple: generate-appconfig.sh -> Generated/AppConfig.swift (wired into
  `make apple-app-config`), consumed as AppConfig.privacyPolicyURL.
- KMP: generateAppConfig task -> AppConfig.kt (mirrors DiagnosticsBuildConfig),
  consumed as AppConfig.PRIVACY_POLICY_URL.

Replaces the stale hardcoded privacy-policy URL on both sides with
https://vnidrop.sudosy.fr/privacy/.

Also fix the Apple release core build: disable release LTO in build-core.sh
(Cargo forbids lto in a build-override) to avoid the proc-macro
"mis-aligned LINKEDIT string pool" corruption, so release archives are
compact instead of shipping the debug core.

Update the app icon.

Tests: shell test for the generator (escaping, missing/duplicate key),
plus XCTest and jvmTest asserting the generated value matches app.properties.
This commit is contained in:
2026-08-01 19:54:37 +02:00
parent d52ac52cea
commit 232fb125d3
11 changed files with 308 additions and 66 deletions

View File

@@ -52,7 +52,7 @@ import vnidrop.shared.generated.resources.os_version_title
import vnidrop.shared.generated.resources.value_unavailable
import vnidrop.shared.generated.resources.version_title
private const val PrivacyPolicyUrl = "https://github.com/vnidrop/vnidrop"
private val PrivacyPolicyUrl = com.vnidrop.app.AppConfig.PRIVACY_POLICY_URL
@Composable
internal fun AboutSettings(

View File

@@ -0,0 +1,40 @@
package com.vnidrop.app
import java.io.File
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
/**
* Verifies the build-time [AppConfig] (generated from the shared `app.properties`)
* exposes the expected, well-formed values to the shared UI.
*/
class AppConfigTest {
@Test
fun privacyPolicyUrlIsTheExpectedHttpsEndpoint() {
assertTrue(AppConfig.PRIVACY_POLICY_URL.startsWith("https://"), "must be https")
assertEquals("https://vnidrop.sudosy.fr/privacy/", AppConfig.PRIVACY_POLICY_URL)
}
@Test
fun privacyPolicyUrlMatchesTheSharedConfigFile() {
// Cross-check the generated constant against the single source of truth so a
// broken codegen (or drift) is caught, not just a hardcoded copy.
assertEquals(privacyUrlFromAppProperties(), AppConfig.PRIVACY_POLICY_URL)
}
private fun privacyUrlFromAppProperties(): String {
var dir: File? = File(System.getProperty("user.dir")).absoluteFile
repeat(8) {
val candidate = File(dir, "app.properties")
if (candidate.isFile) {
return candidate.readLines()
.firstOrNull { it.startsWith("PRIVACY_POLICY_URL=") }
?.substringAfter("PRIVACY_POLICY_URL=")
?: error("PRIVACY_POLICY_URL missing in ${candidate.path}")
}
dir = dir?.parentFile
}
error("app.properties not found from ${System.getProperty("user.dir")}")
}
}