versionName 1.0.0, versionCode 10000. Merging this to main triggers .gitea/workflows/release.yaml, which builds it, publishes to the F-Droid repo, and mints the v1.0.0 tag — and this is the release where `prerelease` flips to false on its own, since the pipeline derives it from MAJOR. CHANGELOG's 1.0.0 section covers the own store, list management, recurrence, iCalendar export, the storage picker and the copy path, then the fixes to the external-provider mode that 0.4.0 and earlier used for everything — the reverting due times, the all-day drift, the silent save failure on tasks with a duration, recurrence detection on tasks.org, the inert reminder field, and the provider-failure recovery. Nothing about the dropped authority or the custom permissions: they never reached a release, so there is nothing for a user to have lost. `changelogs/10000.txt` is `scripts/sync_changelog_to_fastlane.sh` output, for the official F-Droid listing, at 471 of the 500 characters F-Droid will show — the section is written to that budget rather than trimmed to fit it, since the same text is the Gitea release notes. The detail behind each line is in the commit messages, CHANGELOG's older entries stay as they were. Still open before this merges, per docs/RELEASING.md step 4 and ROADMAP: `./gradlew :app:connectedDebugAndroidTest` against the tip, and `scripts/verify-release.sh` on a real device.
219 lines
8.4 KiB
Kotlin
219 lines
8.4 KiB
Kotlin
import java.util.Properties
|
|
import java.io.FileInputStream
|
|
|
|
plugins {
|
|
alias(libs.plugins.android.application)
|
|
alias(libs.plugins.kotlin.compose)
|
|
alias(libs.plugins.ksp)
|
|
alias(libs.plugins.hilt)
|
|
}
|
|
|
|
val keystorePropertiesFile = rootProject.file("key.properties")
|
|
val keystoreProperties = Properties().apply {
|
|
if (keystorePropertiesFile.exists()) {
|
|
load(FileInputStream(keystorePropertiesFile))
|
|
}
|
|
}
|
|
|
|
android {
|
|
namespace = "de.jeanlucmakiola.agendula"
|
|
compileSdk = 37
|
|
|
|
defaultConfig {
|
|
applicationId = "de.jeanlucmakiola.agendula"
|
|
minSdk = 29
|
|
targetSdk = 36
|
|
// These committed values ARE the source of truth for a release: merging
|
|
// a bumped versionName into main triggers .gitea/workflows/release.yaml,
|
|
// which builds this version and then creates the matching vX.Y.Z tag +
|
|
// release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 +
|
|
// PATCH from versionName, e.g. 1.0.0 -> 10000). Releases were flagged as
|
|
// pre-releases while MAJOR was 0; 1.0.0 is the first stable one, and the
|
|
// pipeline graduates it on its own. See docs/RELEASING.md.
|
|
versionCode = 10000
|
|
versionName = "1.0.0"
|
|
|
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
}
|
|
|
|
signingConfigs {
|
|
if (keystorePropertiesFile.exists()) {
|
|
create("release") {
|
|
keyAlias = keystoreProperties["keyAlias"] as String
|
|
keyPassword = keystoreProperties["keyPassword"] as String
|
|
storeFile = file(keystoreProperties["storeFile"] as String)
|
|
storePassword = keystoreProperties["storePassword"] as String
|
|
}
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
// Keep release builds reproducible for F-Droid: don't let AGP embed
|
|
// build-environment git metadata (META-INF/version-control-info.textproto),
|
|
// whose `revision`/path content varies by build machine and is the only
|
|
// thing that otherwise differs from a clean from-source rebuild.
|
|
vcsInfo { include = false }
|
|
isMinifyEnabled = true
|
|
isShrinkResources = true
|
|
proguardFiles(
|
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
"proguard-rules.pro"
|
|
)
|
|
if (keystorePropertiesFile.exists()) {
|
|
signingConfig = signingConfigs.getByName("release")
|
|
}
|
|
}
|
|
debug {
|
|
applicationIdSuffix = ".debug"
|
|
isMinifyEnabled = false
|
|
}
|
|
// A locally-installable twin of `release`: same R8 shrinking + obfuscation
|
|
// and resource shrinking, but debug-signed and given its own applicationId
|
|
// suffix so it installs alongside both the production app (signed with the
|
|
// real key) and the debug build. Used to smoke-test a release candidate on
|
|
// a real device before merging to main — R8-only breakage and first-run/
|
|
// permission states don't surface in the unminified debug build, nor on a
|
|
// device that already holds the permission. Never published. See
|
|
// docs/RELEASING.md.
|
|
create("releaseTest") {
|
|
initWith(getByName("release"))
|
|
applicationIdSuffix = ".releasetest"
|
|
signingConfig = signingConfigs.getByName("debug")
|
|
isMinifyEnabled = true
|
|
isShrinkResources = true
|
|
matchingFallbacks += "release"
|
|
}
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
}
|
|
|
|
buildFeatures {
|
|
compose = true
|
|
buildConfig = true
|
|
}
|
|
|
|
// Don't embed AGP's dependency-metadata block in the APK signing block. It's
|
|
// a Play-oriented blob, and F-Droid's reproducible-build scanner rejects any
|
|
// "extra signing block" — so leaving it in blocks publishing to the official
|
|
// repo. It lives in the signing block, not the zip entries, so disabling it
|
|
// doesn't change the build output (reproducibility is unaffected).
|
|
dependenciesInfo {
|
|
includeInApk = false
|
|
includeInBundle = false
|
|
}
|
|
|
|
packaging {
|
|
resources {
|
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
|
}
|
|
}
|
|
|
|
lint {
|
|
// Community translations are expected to be partial — a missing string
|
|
// falls back to the English base at runtime — so don't fail the build on
|
|
// it. Likewise a translated <plurals> may not fill every CLDR quantity
|
|
// form its locale defines (e.g. Arabic needs "zero"); the missing form
|
|
// falls back to "other" at runtime, so MissingQuantity is informational
|
|
// too. Stale/extra keys (ExtraTranslation) stay fatal; scripts/
|
|
// check_translations.py guards the same invariants with clearer,
|
|
// translator-facing messages.
|
|
informational += listOf("MissingTranslation", "MissingQuantity")
|
|
}
|
|
|
|
testOptions {
|
|
unitTests {
|
|
all { it.useJUnitPlatform() }
|
|
isReturnDefaultValues = true
|
|
}
|
|
}
|
|
|
|
// MigrationTestHelper reads the exported schemas out of the test APK's
|
|
// assets, so app/schemas/ has to ship with the instrumented tests.
|
|
sourceSets.getByName("androidTest").assets.srcDir("$projectDir/schemas")
|
|
}
|
|
|
|
kotlin {
|
|
compilerOptions {
|
|
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
|
|
}
|
|
}
|
|
|
|
// Export each Room schema version to app/schemas/ and commit it. That JSON is
|
|
// what MigrationTestHelper reads to build an old database and migrate it, so
|
|
// without it a migration can only be tested by hand.
|
|
ksp {
|
|
arg("room.schemaLocation", "$projectDir/schemas")
|
|
}
|
|
|
|
dependencies {
|
|
// Not a dependency we use directly — lifecycle already drags it in at 1.7.3.
|
|
// AGP's consistent resolution then pins androidTest to the app classpath, and
|
|
// room-testing's MigrationTestHelper needs 1.8+ to deserialize the exported
|
|
// schema; on 1.7.3 it dies with an AbstractMethodError. Raise it in one place.
|
|
constraints {
|
|
implementation(libs.kotlinx.serialization.json)
|
|
}
|
|
|
|
implementation(libs.androidx.core.ktx)
|
|
implementation(libs.androidx.appcompat)
|
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
|
implementation(libs.androidx.lifecycle.process)
|
|
implementation(libs.androidx.activity.compose)
|
|
|
|
implementation(platform(libs.androidx.compose.bom))
|
|
implementation(libs.androidx.ui)
|
|
implementation(libs.androidx.ui.graphics)
|
|
implementation(libs.androidx.ui.tooling.preview)
|
|
implementation(libs.androidx.material3)
|
|
implementation(libs.androidx.compose.material.icons.core)
|
|
implementation(libs.androidx.compose.material.icons.extended)
|
|
|
|
implementation(libs.hilt.android)
|
|
implementation(libs.androidx.hilt.navigation.compose)
|
|
implementation(libs.androidx.hilt.lifecycle.viewmodel.compose)
|
|
implementation(libs.androidx.navigation.compose)
|
|
ksp(libs.hilt.compiler)
|
|
|
|
// RFC 5545 recurrence expansion, in-process; see the catalog for the pin.
|
|
implementation(libs.dmfs.lib.recur)
|
|
|
|
implementation(libs.androidx.room.runtime)
|
|
implementation(libs.androidx.room.ktx)
|
|
ksp(libs.androidx.room.compiler)
|
|
|
|
implementation(libs.androidx.datastore.preferences)
|
|
implementation(libs.androidx.documentfile)
|
|
|
|
implementation(libs.kotlinx.datetime)
|
|
implementation(libs.kotlinx.coroutines.core)
|
|
implementation(libs.floret.core.time)
|
|
implementation(libs.floret.core.reminders)
|
|
implementation(libs.floret.core.locale)
|
|
implementation(libs.floret.core.crash)
|
|
implementation(libs.floret.identity)
|
|
implementation(libs.floret.components)
|
|
|
|
debugImplementation(libs.androidx.ui.tooling)
|
|
debugImplementation(libs.androidx.ui.test.manifest)
|
|
|
|
testImplementation(libs.junit.jupiter.api)
|
|
testRuntimeOnly(libs.junit.jupiter.engine)
|
|
testRuntimeOnly(libs.junit.platform.launcher)
|
|
testImplementation(libs.truth)
|
|
testImplementation(libs.turbine)
|
|
testImplementation(libs.kotlinx.coroutines.test)
|
|
|
|
androidTestImplementation(libs.androidx.junit)
|
|
androidTestImplementation(libs.androidx.espresso.core)
|
|
androidTestImplementation(libs.androidx.test.rules)
|
|
androidTestImplementation(libs.truth)
|
|
androidTestImplementation(libs.androidx.room.testing)
|
|
androidTestImplementation(platform(libs.androidx.compose.bom))
|
|
androidTestImplementation(libs.androidx.ui.test.junit4)
|
|
}
|