Refactor: Convert build configuration to Gradle plugin pattern

- Migrate ProjectConfig from static object to proper Gradle plugin
- Add version type support (beta/rc) to version.properties
- Clean up legacy fastlane changelogs
- Update release script to support version types
- Remove automatic fastlane changelog generation from release script
- Add BuildConfig field injection for version info
This commit is contained in:
darken
2025-10-07 13:54:24 +02:00
committed by Matthias Urhahn
parent 12f0662146
commit 221f3ccbed
52 changed files with 179 additions and 241 deletions
+10
View File
@@ -3,10 +3,20 @@ plugins {
`java-library`
}
gradlePlugin {
plugins {
create("projectConfigPlugin") {
id = "projectConfig"
implementationClass = "ProjectConfigPlugin"
}
}
}
repositories {
google()
mavenCentral()
}
dependencies {
implementation("com.android.tools.build:gradle:8.13.0")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.10")
+43 -51
View File
@@ -1,64 +1,56 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.io.File
import java.io.FileInputStream
import java.util.Properties
object ProjectConfig {
const val packageName = "eu.darken.capod"
open class ProjectConfig {
val packageName = "eu.darken.capod"
val minSdk = 26
const val minSdk = 26
const val compileSdk = 36
const val targetSdk = 36
val compileSdk = 36
val targetSdk = 36
object Version {
lateinit var version: Version
override fun toString(): String {
return "ProjectConfig($packageName, min=$minSdk, compile=$compileSdk, target=$targetSdk, version=$version)"
}
fun init(project: Project) {
val versionProperties = Properties().apply {
load(FileInputStream(File("version.properties")))
val propsPath = File(project.rootDir, "version.properties")
println("Version: From $propsPath:")
load(FileInputStream(propsPath))
println("$this")
}
val major = versionProperties.getProperty("project.versioning.major").toInt()
val minor = versionProperties.getProperty("project.versioning.minor").toInt()
val patch = versionProperties.getProperty("project.versioning.patch").toInt()
val build = versionProperties.getProperty("project.versioning.build").toInt()
version = Version(
major = versionProperties.getProperty("project.versioning.major").toInt(),
minor = versionProperties.getProperty("project.versioning.minor").toInt(),
patch = versionProperties.getProperty("project.versioning.patch").toInt(),
build = versionProperties.getProperty("project.versioning.build").toInt(),
type = versionProperties.getProperty("project.versioning.type"),
)
}
val name = "${major}.${minor}.${patch}-rc${build}"
val code = major * 10000000 + minor * 100000 + patch * 1000 + build * 10
data class Version(
val major: Int,
val minor: Int,
val patch: Int,
val build: Int,
val type: String,
) {
val name: String
get() = "${major}.${minor}.${patch}-$type${build}"
val code: Long
get() = major * 10000000 + minor * 100000 + patch * 1000 + build * 10L
}
}
fun lastCommitHash(): String = Runtime.getRuntime().exec("git rev-parse --short HEAD").let { process ->
process.waitFor()
val output = process.inputStream.use { input ->
input.bufferedReader().use {
it.readText()
}
class ProjectConfigPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create("projectConfig", ProjectConfig::class.java)
extension.init(project)
project.afterEvaluate { println("ProjectConfigPlugin loaded: $extension") }
}
process.destroy()
output.trim()
}
fun com.android.build.api.dsl.SigningConfig.setupCredentials(
signingPropsPath: File? = null
) {
val keyStoreFromEnv = System.getenv("STORE_PATH")?.let { File(it) }
if (keyStoreFromEnv?.exists() == true) {
println("Using signing data from environment variables.")
storeFile = keyStoreFromEnv
storePassword = System.getenv("STORE_PASSWORD")
keyAlias = System.getenv("KEY_ALIAS")
keyPassword = System.getenv("KEY_PASSWORD")
} else {
println("Using signing data from properties file.")
val props = Properties().apply {
signingPropsPath?.takeIf { it.canRead() }?.let { load(FileInputStream(it)) }
}
val keyStorePath = props.getProperty("release.storePath")?.let { File(it) }
if (keyStorePath?.exists() == true) {
storeFile = keyStorePath
storePassword = props.getProperty("release.storePassword")
keyAlias = props.getProperty("release.keyAlias")
keyPassword = props.getProperty("release.keyPassword")
}
}
}
}
@@ -0,0 +1,75 @@
import com.android.build.api.dsl.SigningConfig
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.TestDescriptor
import org.gradle.api.tasks.testing.TestListener
import org.gradle.api.tasks.testing.TestResult
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import java.io.File
import java.io.FileInputStream
import java.util.Properties
val Project.projectConfig: ProjectConfig
get() = extensions.findByType(ProjectConfig::class.java)!!
fun SigningConfig.setupCredentials(
signingPropsPath: File? = null
) {
val keyStoreFromEnv = System.getenv("STORE_PATH")?.let { File(it) }
if (keyStoreFromEnv?.exists() == true) {
println("Using signing data from environment variables.")
storeFile = keyStoreFromEnv
storePassword = System.getenv("STORE_PASSWORD")
keyAlias = System.getenv("KEY_ALIAS")
keyPassword = System.getenv("KEY_PASSWORD")
} else {
println("Using signing data from properties file.")
val props = Properties().apply {
signingPropsPath?.takeIf { it.canRead() }?.let { load(FileInputStream(it)) }
}
val keyStorePath = props.getProperty("release.storePath")?.let { File(it) }
if (keyStorePath?.exists() == true) {
storeFile = keyStorePath
storePassword = props.getProperty("release.storePassword")
keyAlias = props.getProperty("release.keyAlias")
keyPassword = props.getProperty("release.keyPassword")
}
}
}
fun Test.setupTestLogging() {
testLogging {
events(
TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED,
// TestLogEvent.STANDARD_OUT,
)
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
addTestListener(object : TestListener {
override fun beforeSuite(suite: TestDescriptor) {}
override fun beforeTest(testDescriptor: TestDescriptor) {}
override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {}
override fun afterSuite(suite: TestDescriptor, result: TestResult) {
if (suite.parent != null) {
val messages = """
------------------------------------------------------------------------------------------------
| ${result.resultType} ${result.testCount} tests: ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)
------------------------------------------------------------------------------------------------
""".trimIndent()
println(messages)
}
}
})
}
}