diff --git a/build-logic/src/main/kotlin/gwenedeg.secrets.gradle.kts b/build-logic/src/main/kotlin/gwenedeg.secrets.gradle.kts new file mode 100644 index 0000000..5454b8a --- /dev/null +++ b/build-logic/src/main/kotlin/gwenedeg.secrets.gradle.kts @@ -0,0 +1,84 @@ +import java.util.Properties + +/** + * Injects build-time secrets into `commonMain` so they stay out of version control. + * + * Each secret is read from the environment first (CI), then from `local.properties` + * (local development), and defaults to an empty string when neither provides it — + * consumers are expected to degrade gracefully rather than fail. + * + * The values are emitted as a generated `BuildSecrets` object, available to every + * Kotlin target of the module. + */ + +plugins { + id("org.jetbrains.kotlin.multiplatform") +} + +val secretsPackage = "bzh.ajaury.chombev" + +private val localProperties: Provider = + providers + .fileContents(layout.settingsDirectory.file("local.properties")) + .asText + .map { text -> Properties().apply { load(text.reader()) } } + +/** Resolves a secret from [environmentVariable], falling back to [localPropertyKey], then to an empty string. */ +fun secret( + environmentVariable: String, + localPropertyKey: String, +): Provider = + providers + .environmentVariable(environmentVariable) + .orElse(localProperties.map { properties -> properties.getProperty(localPropertyKey).orEmpty() }) + .orElse("") + +// GlitchTip DSN, consumed by initializeSentry() in the shared module. +val glitchtipDsn: Provider = secret(environmentVariable = "GLITCHTIP_DSN", localPropertyKey = "glitchtip.dsn") + +val generateBuildSecrets = + tasks.register("generateBuildSecrets") { + description = "Generates the BuildSecrets object from local.properties and environment variables." + + // Escaped here so the value can be inlined as-is in a Kotlin string literal. + val dsn = + glitchtipDsn.map { value -> + value + .replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("$", "\${'$'}") + } + val outputDir = layout.buildDirectory.dir("generated/secrets/commonMain/kotlin") + // Kept in locals so the execution-time action never captures the script instance. + val packageName = secretsPackage + val packagePath = packageName.replace('.', '/') + + inputs.property("glitchtipDsn", dsn) + outputs.dir(outputDir) + + doLast { + val file = + outputDir + .get() + .asFile + .resolve("$packagePath/BuildSecrets.kt") + file.parentFile.mkdirs() + file.writeText( + """ + package $packageName + + // Generated by the `gwenedeg.secrets` convention plugin. Do not edit. + internal object BuildSecrets { + const val GLITCHTIP_DSN: String = "${dsn.get()}" + } + + """.trimIndent(), + ) + } + } + +kotlin { + sourceSets.commonMain { + kotlin.srcDir(generateBuildSecrets) + } +}