diff --git a/app/build.gradle.kts b/app/build.gradle.kts index efbc7a7..13ae9a5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -98,7 +98,6 @@ android { kotlin { compilerOptions { jvmTarget = JvmTarget.JVM_21 - freeCompilerArgs.add("-Xcontext-receivers") } } @@ -121,6 +120,7 @@ dependencies { implementation(libs.firebase.analytics.ktx) implementation(libs.firebase.crashlytics) implementation(libs.javapoet) + implementation(libs.javax.inject) implementation(libs.kotlin.math) implementation(libs.metrox.viewmodel.compose) implementation(platform(libs.compose.bom)) @@ -130,6 +130,14 @@ dependencies { implementation(libs.datastore.preferences) implementation(libs.mixpanel) + constraints { + // androidx.test.ext:junit 1.3.0 requires concurrent-futures 1.2.0, but transitive + // deps like profileinstaller pull in 1.1.0 on the main classpath. AGP's consistent + // resolution then locks androidTest to 1.1.0, conflicting with junit's requirement. + // Bumping this here lets normal conflict resolution converge on 1.2.0 everywhere. + implementation(libs.androidx.concurrent.futures) + } + debugImplementation(libs.compose.ui.test.manifest) testImplementation(libs.coroutines.android) diff --git a/app/src/main/java/dev/simonas/quies/onboarding/OnboardingComponents.kt b/app/src/main/java/dev/simonas/quies/onboarding/OnboardingComponents.kt index f69a73a..9c5497a 100644 --- a/app/src/main/java/dev/simonas/quies/onboarding/OnboardingComponents.kt +++ b/app/src/main/java/dev/simonas/quies/onboarding/OnboardingComponents.kt @@ -22,7 +22,7 @@ import kotlin.math.sin interface CanvasComponent { - context(DrawScope) + context(drawScope: DrawScope) fun draw(progress: Float, isGlitch: Boolean): Vals data class Vals( @@ -66,7 +66,7 @@ class Splash( private val exit: ClosedRange, ) : CanvasComponent { - context(DrawScope) + context(drawScope: DrawScope) override fun draw(progress: Float, isGlitch: Boolean): Vals { val vals = Vals( glichyness = progress.rangeNorm( @@ -84,17 +84,19 @@ class Splash( .times(0.8f), ) - drawDialogLine( - textMeasurer, - isGlitch = isGlitch, - text = text, - vals = vals, - offset = textOffset, - textSizeMulti = 0.75f, - glitchPaleness = 0f, - offsetMultiplierX = 2f, - offsetMultiplierY = 2f, - ) + with(drawScope) { + drawDialogLine( + textMeasurer, + isGlitch = isGlitch, + text = text, + vals = vals, + offset = textOffset, + textSizeMulti = 0.75f, + glitchPaleness = 0f, + offsetMultiplierX = 2f, + offsetMultiplierY = 2f, + ) + } return vals.copy( glichyness = vals.glichyness, ) @@ -105,7 +107,7 @@ class OpenerDialogLine( private val textMeasurer: TextMeasurer, ) : CanvasComponent { - context(DrawScope) + context(drawScope: DrawScope) override fun draw(progress: Float, isGlitch: Boolean): Vals { val vals = Vals( glichyness = glitch(1, progress), @@ -113,12 +115,14 @@ class OpenerDialogLine( .rangeNorm(0.80f..1.95f) .hill(DIALOG_HILL_SIDE), ) - drawDialogLine( - textMeasurer, - isGlitch = isGlitch, - text = "This game is about creating meaningful dialogues", - vals = vals, - ) + with(drawScope) { + drawDialogLine( + textMeasurer, + isGlitch = isGlitch, + text = "This game is about creating meaningful dialogues", + vals = vals, + ) + } return vals } } @@ -127,7 +131,7 @@ class ChillOutDialogLine( private val textMeasurer: TextMeasurer, ) : CanvasComponent { - context(DrawScope) + context(drawScope: DrawScope) override fun draw(progress: Float, isGlitch: Boolean): Vals { val vals = Vals( glichyness = glitch(2, progress), @@ -135,12 +139,14 @@ class ChillOutDialogLine( .rangeNorm(2.00f..2.95f) .hill(DIALOG_HILL_SIDE), ) - drawDialogLine( - textMeasurer, - isGlitch = isGlitch, - text = "Slowly getting the conversation to the next level", - vals = vals, - ) + with(drawScope) { + drawDialogLine( + textMeasurer, + isGlitch = isGlitch, + text = "Slowly getting the conversation to the next level", + vals = vals, + ) + } return vals } } @@ -149,7 +155,7 @@ class ExcuseDialogLine( private val textMeasurer: TextMeasurer, ) : CanvasComponent { - context(DrawScope) + context(drawScope: DrawScope) override fun draw(progress: Float, isGlitch: Boolean): Vals { val vals = Vals( glichyness = glitch(3, progress), @@ -157,12 +163,14 @@ class ExcuseDialogLine( .rangeNorm(3.00f..3.95f) .hill(DIALOG_HILL_SIDE), ) - drawDialogLine( - textMeasurer, - isGlitch = isGlitch, - text = "An excuse to talk about things you’ve never had", - vals = vals, - ) + with(drawScope) { + drawDialogLine( + textMeasurer, + isGlitch = isGlitch, + text = "An excuse to talk about things you’ve never had", + vals = vals, + ) + } return vals } } @@ -171,7 +179,7 @@ class LetItOutDialogLine( private val textMeasurer: TextMeasurer, ) : CanvasComponent { - context(DrawScope) + context(drawScope: DrawScope) override fun draw(progress: Float, isGlitch: Boolean): Vals { val vals = Vals( glichyness = glitch(4, progress), @@ -179,12 +187,14 @@ class LetItOutDialogLine( .rangeNorm(4.00f..4.95f) .hill(DIALOG_HILL_SIDE), ) - drawDialogLine( - textMeasurer, - isGlitch = isGlitch, - text = "Share things we have kept to ourselves", - vals = vals, - ) + with(drawScope) { + drawDialogLine( + textMeasurer, + isGlitch = isGlitch, + text = "Share things we have kept to ourselves", + vals = vals, + ) + } return vals } } @@ -193,7 +203,7 @@ class ConnectionDialogLine( private val textMeasurer: TextMeasurer, ) : CanvasComponent { - context(DrawScope) + context(drawScope: DrawScope) override fun draw(progress: Float, isGlitch: Boolean): Vals { val vals = Vals( glichyness = glitch(5, progress), @@ -201,12 +211,14 @@ class ConnectionDialogLine( .rangeNorm(5.00f..5.95f) .hill(DIALOG_HILL_SIDE), ) - drawDialogLine( - textMeasurer, - isGlitch = isGlitch, - text = "Build a bridge between two or more inner worlds", - vals = vals, - ) + with(drawScope) { + drawDialogLine( + textMeasurer, + isGlitch = isGlitch, + text = "Build a bridge between two or more inner worlds", + vals = vals, + ) + } return vals } } @@ -215,7 +227,7 @@ class OutroDialogLine( private val textMeasurer: TextMeasurer, ) : CanvasComponent { - context(DrawScope) + context(drawScope: DrawScope) override fun draw(progress: Float, isGlitch: Boolean): Vals { val vals = Vals( glichyness = glitch( @@ -232,12 +244,14 @@ class OutroDialogLine( .rangeNorm(6.00f..6.95f) .hill(DIALOG_HILL_SIDE), ) - drawDialogLine( - textMeasurer, - isGlitch = isGlitch, - text = "Be honest\nShare your feelings\nThat’s the way to win", - vals = vals, - ) + with(drawScope) { + drawDialogLine( + textMeasurer, + isGlitch = isGlitch, + text = "Be honest\nShare your feelings\nThat’s the way to win", + vals = vals, + ) + } return vals } } diff --git a/app/src/test/java/dev/simonas/quies/utils/TurbineKtx.kt b/app/src/test/java/dev/simonas/quies/utils/TurbineKtx.kt index e619de4..528d7cb 100644 --- a/app/src/test/java/dev/simonas/quies/utils/TurbineKtx.kt +++ b/app/src/test/java/dev/simonas/quies/utils/TurbineKtx.kt @@ -6,12 +6,12 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle -context(TestScope) +context(testScope: TestScope) suspend inline fun Flow.testLast( crossinline validate: TurbineTestContext.(T) -> Unit, ) { test { - advanceUntilIdle() + testScope.advanceUntilIdle() validate(expectMostRecentItem()) } } diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index c216d47..09f75cf 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -715,6 +715,12 @@ formatting: active: true android: true autoCorrect: true + # detekt 1.23.8's bundled ktlint doesn't understand Kotlin 2.4's named + # context parameters (`context(name: Type)`) - it either crashes + # (Wrapping) or emits false positives (SpacingAroundColon, + # NoUnusedImports) on files that use them. Exclude those files from the + # whole formatting ruleset until detekt ships a newer ktlint. + excludes: ['**/onboarding/OnboardingComponents.kt', '**/utils/TurbineKtx.kt'] AnnotationOnSeparateLine: active: true autoCorrect: true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3395a81..9b6948f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,49 +1,51 @@ [versions] -agp = "9.2.1" +agp = "9.3.1" androidx_activity_compose = "1.13.0" androidx_test = "1.7.0" androidx_test_ext = "1.3.0" -appcompat = "1.7.1" -benmanesversion = "0.47.0" -colormath = "3.3.2" -colormath-ext-jetpack-compose = "3.3.2" +appcompat = "1.8.0" +benmanesversion = "0.61.0" +colormath = "3.6.1" +colormath-ext-jetpack-compose = "3.6.1" compile_sdk_version = "37" -compose-lifecycle = "2.10.0" +compose-lifecycle = "2.11.0" compose-navigation = "2.9.8" -compose_bom = "2026.05.00" -constraint_layout = "2.1.4" -core_ktx = "1.18.0" -coroutines = "1.10.2" -datastore = "1.1.1" -datastore-preferences = "1.1.1" +compose_bom = "2026.08.00" +concurrent_futures = "1.2.0" +constraint_layout = "2.2.2" +core_ktx = "1.19.0" +coroutines = "1.11.0" +datastore = "1.2.1" +datastore-preferences = "1.2.1" detekt = "1.23.8" espresso_core = "3.7.0" -firebase_analytics = "22.0.0" -firebase_bom = "33.0.0" -firebase_crashlytics = "19.0.0" -firebase_ktx = "22.0.0" -google_services = "4.4.1" -hilt = "2.56.2" -hilt-navigation-compose = "1.2.0" +firebase_analytics = "23.2.0" +firebase_bom = "34.18.0" +firebase_crashlytics = "20.1.0" +firebase_ktx = "22.5.0" +google_services = "4.5.0" +hilt = "2.60.1" +hilt-navigation-compose = "1.4.0" javapoet = "1.13.0" +javax_inject = "1" junit = "4.13.2" -junit-jupiter = "5.11.4" -kaml = "0.55.0" -kotlin = "2.3.0" -kotlin-math = "1.5.3" -ksp = "2.3.2" -ktlint_gradle = "11.5.0" +junit-jupiter = "6.1.3" +kaml = "0.104.0" +kotlin = "2.4.10" +kotlin-math = "1.8.0" +ksp = "2.3.11" +ktlint_gradle = "14.2.0" min_sdk_version = "28" metro = "1.4.2" -mixpanel-android = "8.2.1" -mockito-android = "5.4.0" -mockito-kotlin = "5.1.0" -play_publisher = "3.9.1" +mixpanel-android = "8.9.0" +mockito-android = "5.23.0" +mockito-kotlin = "6.3.0" +play_publisher = "4.1.1" simple_flank = "0.4.0" target_sdk_version = "37" -truth = "1.1.5" -turbine = "1.0.0" -material = "1.12.0" +truth = "1.4.5" +turbine = "1.2.1" +material = "1.14.0" [libraries] agp = { module = "com.android.tools.build:gradle", version.ref = "agp" } @@ -55,6 +57,7 @@ androidx_test_ext_junit = { module = "androidx.test.ext:junit", version.ref = "a androidx_test_ext_junit_ktx = { module = "androidx.test.ext:junit-ktx", version.ref = "androidx.test.ext" } androidx_test_rules = { module = "androidx.test:rules", version.ref = "androidx.test" } androidx_test_runner = { module = "androidx.test:runner", version.ref = "androidx.test" } +androidx_concurrent_futures = { module = "androidx.concurrent:concurrent-futures", version.ref = "concurrent_futures" } colormath = { module = "com.github.ajalt.colormath:colormath", version.ref = "colormath" } colormath-ext-jetpack-compose = { module = "com.github.ajalt.colormath:colormath-ext-jetpack-compose", version.ref = "colormath-ext-jetpack-compose" } compose-navigation = { module = "androidx.navigation:navigation-compose", version.ref = "compose-navigation" } @@ -83,6 +86,7 @@ hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hil hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hilt-navigation-compose" } hilt-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" } javapoet = { module = "com.squareup:javapoet", version.ref = "javapoet" } +javax_inject = { module = "javax.inject:javax.inject", version.ref = "javax_inject" } junit = { module = "junit:junit", version.ref = "junit" } junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } kaml = { module = "com.charleskorn.kaml:kaml", version.ref = "kaml" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d997cfc..eddabd2 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c61a118..ad7845b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,9 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip networkTimeout=10000 +retries=0 +retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 739907d..249efbb 100755 --- a/gradlew +++ b/gradlew @@ -20,7 +20,7 @@ ############################################################################## # -# Gradle start up script for POSIX generated by Gradle. +# gradlew start up script for POSIX generated by Gradle. # # Important for running: # @@ -29,7 +29,7 @@ # bash, then to run this script, type that shell name before the whole # command line, like: # -# ksh Gradle +# ksh gradlew # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/gradlew.bat b/gradlew.bat index e509b2d..8508ef6 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -19,12 +19,12 @@ @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem gradlew startup script for Windows @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @@ -51,7 +51,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,29 +65,18 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem Execute gradlew +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/maintenance/01-stale-dependencies.md b/maintenance/01-stale-dependencies.md deleted file mode 100644 index 48178f3..0000000 --- a/maintenance/01-stale-dependencies.md +++ /dev/null @@ -1,107 +0,0 @@ -# Section 1 — Stale Dependencies - -## 1. Goal - -Bring the version catalog at `gradle/libs.versions.toml` in line with current stable releases of AndroidX, Compose-adjacent, Kotlin coroutines, JUnit/test, detekt, and SDK libraries. Out-of-date pins predate the project's Kotlin 2.2.20 / AGP 8.13 / JDK 21 baseline and are blocking idiomatic APIs (typed Navigation, `collectAsStateWithLifecycle`, predictive back, Material3 1.4 components). Each batch is shippable independently and verifiable via existing gradle tasks. No Compose BOM rework, no CI/Gradle wrapper changes — those land in later sections. - -## 2. Inventory - -| Catalog key | Current pin | Latest stable (May 2026) | Risk / notes | -|---|---|---|---| -| `androidx_activity_compose` | 1.8.0 | **1.13.0** | Source-breaking: `ActivityResultLauncher.getContract` is now an abstract Kotlin property. `enableEdgeToEdge()` re-invoked on config changes (1.13). New `PredictiveBackHandler` composable. Pulls in `core:core-pip`. | -| `appcompat` | 1.6.1 | **1.7.1** | Patch-only risk. 1.7.0 required Activity 1.8 (already satisfied post-bump). 1.7.1 fixes `AppCompatActivity` × NavigationEvent interaction. | -| `core_ktx` | 1.10.1 | **1.18.0** | Multi-major jump. From 1.19.0-alpha onward `core-ktx` becomes an empty alias for `core`; staying on stable 1.18 sidesteps that. Watch for tightened nullability in `Bundle`/`Intent` extensions. | -| `compose-lifecycle` (lifecycle-runtime-compose) | 2.6.2 | **2.10.0** | minSdk floor moves to **API 23** (project is 28 — fine). 2.7+ promoted `collectAsStateWithLifecycle` to stable; 2.8 added KMP source set; 2.10 adds `rememberLifecycleOwner`. ViewModel `KClass` overloads added. | -| `compose-navigation` | 2.7.5 | **2.9.8** | Major: 2.8 introduced type-safe `@Serializable` routes (additive — string routes still work). 2.8.1 closed an implicit deeplink security hole — verify any deeplinks declare `` explicitly. 2.8.x **requires Compose runtime ≥ 1.7.2** (handled by Section 2's BOM). | -| `compose_material3` | 1.1.2 | **1.4.0** | Largest visual-risk bump. 1.2 typography line-height adjustments, 1.3 added new components, 1.4 has `MaterialTheme.LocalMaterialTheme.current` access path; checkbox sizing/colors realigned to spec (flag `isCheckboxStylingFixEnabled` available). Removed deprecated `DropdownMenuItem` overload, deprecated `FilterChip`/`AssistChip` `horizontalSpacing`. UI smoke required. | -| `coroutines` | 1.7.3 | **1.10.2** *(verify)* | 1.8 changed `Dispatchers.Main` initialisation (immediate-by-default) and removed several deprecated APIs; `runBlockingTest` deprecated path removed; new `kotlinx-coroutines-test` `runTest` semantics (timeout default 60s). 1.9+ requires Kotlin 2.0+. Inspect tests using `runTest` / `TestCoroutineScheduler`. | -| `detekt` | 1.23.1 | **1.23.8** *(verify)* | 1.23.x is the current stable stream; 2.x is in beta. Stay on 1.23.8 for Kotlin 2.2 compiler compat (1.23.1 predates Kotlin 2.0). **`detekt-formatting` plugin must move in lockstep** — already shares `detekt` version.ref. | -| `androidx_test` | 1.5.0 | **1.7.0** | minSdk floor 21 (fine). Internal Kotlin upgrade to 1.9. | -| `androidx_test_ext` | 1.1.5 | **1.3.0** | Aligns with `androidx_test` 1.7.0. | -| `espresso_core` | 3.5.1 | **3.7.0** | Robolectric+Espresso deadlock fix. Move with the test bundle. | -| `junit-jupiter` | 5.8.1 | **5.11.x** *(verify)* | 5.10 dropped the legacy vintage runner default; AGP 8.13 supports JUnit5 platform natively. Confirm test discovery still works (`testDebugUnitTest`). Currently only consumed in `app` test source set. | -| `mixpanel-android` | 7.5.2 | **8.x** *(verify)* | Major bump. 8.x changed init API (`MixpanelAPI.getInstance(...)` signature added trackAutomaticEvents bool), and dropped some legacy endpoints. Audit every callsite before bumping. | -| `org-jetbrains-kotlin-jvm` (version) | 1.8.0 | **DELETE** | Confirmed dead: no module applies `org.jetbrains.kotlin.jvm`. Remove both the `[versions]` entry (line 46) and the `[plugins]` alias (line 107). Leftover from kotlin-android-template. | -| `kotlinx-serialization` plugin | hardcoded 1.9.0 | track `kotlin` version | Used by `data/build.gradle.kts`. Plugin coordinate `org.jetbrains.kotlin.plugin.serialization` ships with the Kotlin compiler — must equal Kotlin version. Change line 109 to `version.ref = "kotlin"`. | - -Cross-reference (NOT in scope): `compose_foundation`, `compose_ui`, `compose_ui_tooling`, `compose_ui_test_junit4`, `compose_ui_test_manifest` all incorrectly point at `version.ref = "compose_compiler"` (1.5.4). Section 2 replaces these with the Compose BOM. - -## 3. Sequencing - -Six batches, intended to land as separate PRs. Each is independently revertible. - -**Batch A — Catalog hygiene (zero runtime change).** -- Delete `org-jetbrains-kotlin-jvm` version + plugin entries. -- Repoint `kotlinx-serialization` plugin to `version.ref = "kotlin"`. -- Justification: pure cleanup, no library bytecode shifts. Land first to de-noise diffs. - -**Batch B — Detekt toolchain.** -- `detekt` 1.23.1 → 1.23.8 (auto-propagates to `detekt-formatting`). -- Justification: build-tool only; no app code change. Validates the 2.2 Kotlin compiler is honoured by detekt's parser. - -**Batch C — Foundational AndroidX (no Compose API surface).** -- `appcompat` 1.6.1 → 1.7.1 -- `core_ktx` 1.10.1 → 1.18.0 -- `androidx_activity_compose` 1.8.0 → 1.13.0 -- Justification: `appcompat` 1.7+ pulls Activity ≥ 1.8 transitively; bumping all three at once avoids resolved-version surprises. None are Compose-runtime-pinned. - -**Batch D — Coroutines.** -- `coroutines` 1.7.3 → latest 1.10.x stable. -- Justification: behavioural changes around `Dispatchers.Main.immediate` and `runTest` warrant isolating from UI library bumps so any test failures are unambiguously attributable. - -**Batch E — Compose-adjacent (NOT Compose UI itself — that's Section 2).** -- `compose-lifecycle` 2.6.2 → 2.10.0 -- `compose-navigation` 2.7.5 → 2.9.8 -- `compose_material3` 1.1.2 → 1.4.0 -- Justification: these three change Compose-rendered surfaces. **Compat constraint**: navigation 2.8.x requires Compose runtime ≥ 1.7.2. The current `compose_compiler = 1.5.4` references resolve to Compose UI 1.5.4 — Section 2's BOM will fix this. **Hold this batch until Section 2 lands**, OR temporarily pin Compose libraries to ≥ 1.7.2 explicitly. Material3 1.4 also wants Compose ≥ 1.7. Note this dependency in the PR description. - -**Batch F — Test stack.** -- `androidx_test` 1.5.0 → 1.7.0 -- `androidx_test_ext` 1.1.5 → 1.3.0 -- `espresso_core` 3.5.1 → 3.7.0 -- `junit-jupiter` 5.8.1 → 5.11.x -- Justification: AndroidX test artifacts share a release train and must move together. JUnit 5 lumps in here because it only affects the same source sets. - -**Batch G — Mixpanel (independent vendor).** -- `mixpanel-android` 7.5.2 → 8.x. -- Justification: vendor SDK with non-trivial init API change. Land last and behind a manual verification build (analytics events visible in Mixpanel UI debug view). - -Ordering rationale: A → B → C → D → F → E → G. E waits on Section 2 unless explicit Compose pins are added; G is last because runtime regressions only show in release telemetry. - -## 4. Per-batch verification - -Common gates for every batch: -``` -./gradlew detekt lint testDebugUnitTest assembleDebug -``` -Plus `./gradlew dependencies --configuration releaseRuntimeClasspath | grep -i ` to confirm resolved version matches the catalog (no transitive override surprises). - -Per-batch additions: - -- **A (catalog hygiene)**: `./gradlew help` to confirm the catalog still parses; `./gradlew :data:tasks` confirms serialization plugin still resolves. -- **B (detekt)**: `./gradlew detekt` on every module; spot-check that `config/detekt/detekt.yml` rule IDs all still exist (run with `--build-upon-default-config` if needed). -- **C (foundational AndroidX)**: full `./gradlew lint` (lint-baseline diffs are the canary); manual smoke: launch debug build, exercise back gesture (predictive back from Activity 1.13), edge-to-edge insets on a Pixel emulator API 33+. -- **D (coroutines)**: `./gradlew testDebugUnitTest` with `-Pkotlinx.coroutines.test.default_timeout=60s` if any test hangs; audit any `Dispatchers.Main.immediate` assumptions; grep for `runBlockingTest` (deprecated) and `TestCoroutineDispatcher` (removed). -- **E (Compose-adjacent)**: full instrumented run via Flank: `./gradlew :app:runFlank`; UI smoke on every top-level screen for typography/spacing regressions (Material3 1.2 line-height, 1.4 checkbox); navigate every nav graph entry to confirm string routes still resolve. -- **F (test stack)**: `./gradlew testDebugUnitTest connectedDebugAndroidTest`; if connected tests are skipped locally, run Flank. -- **G (mixpanel)**: build debug, exercise tracked flows, verify event arrival in Mixpanel debug/live view; confirm `MixpanelAPI.getInstance` callsite signature matches 8.x. - -## 5. Risks & rollback - -Migration gotchas to anticipate: - -- **Material3 1.1 → 1.4**: typography line-height changes (1.2), checkbox/indicator size (1.4 — flagged via `isCheckboxStylingFixEnabled`), removed deprecated chip params. UI-test screenshot diffs likely; budget time for visual review. -- **Coroutines 1.8+**: `Dispatchers.Main` is now `Main.immediate` by default in some contexts; tests that asserted dispatcher ordering may flake. `runBlockingTest` removed — migrate to `runTest`. -- **Lifecycle 2.7+**: `collectAsStateWithLifecycle` is the canonical replacement for `collectAsState` for cold flows; current code likely still uses `collectAsState` — bump does not force a rewrite, but lint may warn. -- **Navigation 2.8.1 deeplink security fix**: any implicit deep links not declared via `` will stop matching. Audit `AndroidManifest.xml` and `composable(...)` deeplink params. -- **Activity 1.13**: source-breaking change to `ActivityResultLauncher.getContract` for any Kotlin code that overrides it. -- **Mixpanel 8.x**: init signature changed; missed callsite = silent telemetry loss in release builds. - -Rollback per batch: each batch is a single PR/commit touching only `gradle/libs.versions.toml` (plus, for Batch G/E, possibly a few callsites). Revert is `git revert `. Renovate's automerge is scoped to minor — if Renovate races a manual major bump, lock the major bump behind a `packageRules` entry in `renovate.json` for that batch's duration (note: editing `renovate.json` is a Section 4 concern; for this section, just revert the catalog entry). - -## 6. Out of scope - -- Compose BOM adoption and replacement of `version.ref = "compose_compiler"` on `compose_foundation`/`compose_ui`/`compose_ui_tooling`/`compose_ui_test_*` → **Section 2**. -- GitHub Actions runner versions, action SHAs, JDK setup → **Section 3**. -- `gradle-wrapper.properties` Gradle version, `renovate.json` rules, Ben-Manes `dependencyUpdates` config → **Section 4**. -- Firebase BOM, Hilt, KSP, AGP, Kotlin compiler, `kaml`, `colormath`, `datastore`, `kotlin-math`, `truth`, `turbine`, `mockito-*`, `simple_flank`, `play_publisher`, `ktlint_gradle`, `benmanesversion`, `material` (Google MDC) — these are either current or covered elsewhere. Material (`com.google.android.material:material` 1.12.0) is current stable; do not touch. diff --git a/maintenance/02-build-config.md b/maintenance/02-build-config.md deleted file mode 100644 index 3b71f3a..0000000 --- a/maintenance/02-build-config.md +++ /dev/null @@ -1,247 +0,0 @@ -# Section 2 — Build-config maintenance - -## Goal - -Clean up build-config drift in `gradle/libs.versions.toml`, `app/build.gradle.kts`, `data/build.gradle.kts`, `buildSrc/build.gradle.kts`, and `gradle.properties`. The work removes obsolete suppressions, dead version aliases, deprecated DSL, and incorrectly-pinned Compose library versions. Ordering favours behaviour-neutral cleanups first; the Compose BOM migration lands last because it actually changes the bytecode shipped to users. - ---- - -## 1. Compose libraries pinned to compiler version → Compose BOM - -**Problem.** `compose_foundation`, `compose_ui`, `compose_ui_tooling`, `compose_ui_test_junit4`, `compose_ui_test_manifest` all reference `version.ref = "compose_compiler"` (= `1.5.4`). The Kotlin Compose compiler plugin and Compose runtime/UI libraries are decoupled since Kotlin 2.0; the app is currently shipping Compose UI **1.5.4** (released Oct 2023) on Kotlin 2.2.20. - -**Files to change.** -- `gradle/libs.versions.toml:13` (drop `compose_compiler`), `:62`, `:65`, `:66`, `:67`, `:68` (rewrite library entries). -- `app/build.gradle.kts:108–160` (dependencies block; add `platform(libs.compose.bom)` and androidTest counterpart). - -**Concrete change.** - -`gradle/libs.versions.toml` — add a single BOM coordinate, keep `compose_material3` versioned only if you intentionally want a different track (BOM `2026.05.00` ships material3 `1.4.0`, so prefer dropping the explicit pin): - -```toml -# [versions] -compose_bom = "2026.05.00" -# remove: compose_compiler = "1.5.4" -# remove: compose_material3 = "1.1.2" (let BOM manage it) - -# [libraries] -compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } -compose_foundation = { module = "androidx.compose.foundation:foundation" } -compose_material3 = { module = "androidx.compose.material3:material3" } -compose_ui = { module = "androidx.compose.ui:ui" } -compose_ui_tooling = { module = "androidx.compose.ui:ui-tooling" } -compose_ui_test_junit4 = { module = "androidx.compose.ui:ui-test-junit4" } -compose_ui_test_manifest = { module = "androidx.compose.ui:ui-test-manifest" } -``` - -`app/build.gradle.kts` — add the BOM platform under `dependencies` next to the Firebase BOM (line 130): - -```kotlin -implementation(platform(libs.compose.bom)) -androidTestImplementation(platform(libs.compose.bom)) -``` - -No other dependency lines change — the unversioned coordinates in the catalog resolve through the BOM. - -**Verification.** -- `./gradlew :app:dependencies --configuration releaseRuntimeClasspath | grep "androidx.compose"` — every Compose artifact should show `1.11.1` (or whatever the BOM resolves to), not `1.5.4`. -- `./gradlew :app:assembleDebug` succeeds. -- `./gradlew :app:lintDebug` — Compose lint should not regress. -- Smoke-run the app; visually confirm onboarding screens, navigation, and material3 surfaces still render. - ---- - -## 2. Remove dead `composeOptions { kotlinCompilerExtensionVersion = … }` - -**Problem.** Once `org.jetbrains.kotlin.plugin.compose` is applied (it is — `gradle/libs.versions.toml:110`, `app/build.gradle.kts:12`), AGP ignores `composeOptions.kotlinCompilerExtensionVersion`. The Compose compiler version is governed by the Kotlin version. - -**Files to change.** `app/build.gradle.kts:41–43`. - -**Concrete change.** - -```kotlin -// before (lines 41-43) -composeOptions { - kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() -} -// after -// (delete the whole block) -``` - -The `compose_compiler` version entry is already removed in item 1. - -**Verification.** -- `./gradlew :app:assembleDebug` — build succeeds. -- `./gradlew :app:tasks` — no warnings about `kotlinCompilerExtensionVersion` being ignored. - ---- - -## 3. Disable Jetifier - -**Problem.** `android.enableJetifier=true` rewrites every transitive jar at build time to translate `android.support.*` → `androidx.*`. Every dependency in this project is AndroidX-native; Jetifier is dead weight. - -**Files to change.** `gradle.properties:11`. - -**Concrete change.** - -```properties -# before -android.enableJetifier=true -# after -# (delete line; android.useAndroidX=true on line 9 stays) -``` - -**Verification.** -- Add the `com.github.plnice.canidropjetifier` plugin to root `build.gradle.kts` temporarily (or run as a one-shot init script), then `./gradlew canIDropJetifier`. Expect zero hits. -- Alternative one-liner without adding a plugin: `./gradlew :app:dependencies | grep -i "support-"` — should return nothing referring to `com.android.support:*`. -- Full clean build: `./gradlew clean :app:assembleRelease`. - ---- - -## 4. `-Xcontext-receivers` → context parameters (or keep flag) - -**Problem.** `-Xcontext-receivers` is the experimental Kotlin 1.x flag. Kotlin 2.2 promotes the feature under a new flag, `-Xcontext-parameters`, with a slightly different declaration syntax. Specifying both flags is a compile error. - -**Files to change.** `app/build.gradle.kts:46`. - -**Usage audit (already run).** -- `app/src/main/.../onboarding/OnboardingComponents.kt` — 8 `context(DrawScope)` declarations, including one on the public `interface CanvasComponent.draw(...)`. -- `app/src/test/.../utils/TurbineKtx.kt:9` — `context(TestScope)` on `suspend inline fun Flow.testLast`. - -This is a real refactor (interface signature + every call site). **Recommend two-step:** - -**Step 4a (this pass — zero behavioural change).** Keep `-Xcontext-receivers` but stop putting it in `freeCompilerArgs` via the deprecated `kotlinOptions` block; move to the modern `kotlin { compilerOptions { … } }` DSL so the next refactor only edits one line. - -```kotlin -// before (app/build.gradle.kts:44-47, inside android { }) -kotlinOptions { - jvmTarget = JavaVersion.VERSION_21.toString() - freeCompilerArgs = listOf("-Xcontext-receivers") -} - -// after — delete the block above; add at module top-level: -kotlin { - compilerOptions { - jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) - freeCompilerArgs.add("-Xcontext-receivers") - } -} -``` - -**Step 4b (separate ticket, out of scope here).** Migrate the 9 declaration sites to context parameters (`context(scope: DrawScope)`), flip the flag to `-Xcontext-parameters`. IntelliJ has an assisted migration. Touches a public interface, so do it in isolation. - -**Verification.** -- `./gradlew :app:compileDebugKotlin` — no deprecation warnings about `kotlinOptions`. -- `./gradlew :app:compileReleaseKotlin --warning-mode all 2>&1 | grep -i context` — only the expected experimental-feature notice. - ---- - -## 5. Drop dead `target_sdk_version` catalog entry - -**Problem.** `app/build.gradle.kts:21` and `data/build.gradle.kts:10` (compileSdk) both read `compile.sdk.version`; nothing references `target.sdk.version`. - -**Files to change.** `gradle/libs.versions.toml:43`. - -**Concrete change.** - -```toml -# before -target_sdk_version = "35" -# after -# (delete) -``` - -**Verification.** -- `grep -rn "target.sdk.version\|target_sdk_version" .` returns nothing. -- `./gradlew help` — catalog resolves cleanly. - ---- - -## 6. Drop `@Suppress("DSL_SCOPE_VIOLATION")` and TODO - -**Problem.** KTIJ-19369 was resolved years ago; the suppression and TODO are noise. - -**Files to change.** `data/build.gradle.kts:1`. - -**Concrete change.** - -```kotlin -// before -@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed -plugins { -// after -plugins { -``` - -**Verification.** -- `./gradlew :data:assembleDebug` succeeds. -- IDE re-syncs without surfacing a DSL-scope warning. - ---- - -## 7. `buildSrc` — drop `kotlinOptions` block - -**Problem.** `buildSrc/build.gradle.kts:17–21` configures `tasks.withType { kotlinOptions { jvmTarget = … } }` but `kotlin { jvmToolchain(21) }` (lines 23–25) already pins both source/target levels for compile tasks. The `kotlinOptions` DSL is deprecated. - -**Files to change.** `buildSrc/build.gradle.kts:17–21`. - -**Concrete change.** - -```kotlin -// before -tasks.withType().configureEach { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_21.toString() - } -} - -kotlin { - jvmToolchain(21) -} -// after — delete the tasks.withType block; keep: -kotlin { - jvmToolchain(21) -} -``` - -**Verification.** -- `./gradlew help` — buildSrc still compiles, no deprecation warning. -- `./gradlew :app:assembleDebug` builds (proves Hilt/AGP plugins from buildSrc still resolve correctly). - ---- - -## Suggested order - -Land in two PRs. - -**PR 1 — risk-free cleanups (items 5, 6, 7, 2, 3 in that order).** -1. Item 5 — delete unused `target_sdk_version` catalog entry. -2. Item 6 — strip the `@Suppress` / TODO from `data/build.gradle.kts`. -3. Item 7 — remove the deprecated `kotlinOptions` block from `buildSrc/build.gradle.kts`. -4. Item 2 — delete the dead `composeOptions {}` block. -5. Item 3 — disable Jetifier (after running `canIDropJetifier` audit). - -**PR 2 — behaviour-affecting changes (items 4a, 1).** -6. Item 4a — migrate `kotlinOptions` block in `app/build.gradle.kts` to the `kotlin { compilerOptions {} }` DSL while keeping `-Xcontext-receivers`. -7. Item 1 — Compose BOM migration. Highest risk: bumps Compose UI from 1.5.4 to 1.11.x and material3 from 1.1.2 to 1.4.0; ship after manual smoke-test. - -(Item 4b — context-parameters migration — is a separate refactor, tracked elsewhere.) - ---- - -## Risks - -- **Compose 1.5.4 → 1.11.x (item 1) is a six-version jump.** Likely API surface deltas: `pullRefresh` graduations, `BasicTextField` rewrite, deprecated `NavHost` overloads, `LazyLayout` API shifts. Plan one cycle of manual smoke-testing onboarding, navigation, and any custom `DrawScope` work in `OnboardingComponents.kt`. -- **material3 1.1.2 → 1.4.0** changes default tonal/elevation palettes; visual diffs in surfaces, buttons, dialogs are likely. Take screenshots before/after. -- **Jetifier off (item 3)** — if any *runtime* (not compile-time) reflection-based dep references `android.support.*`, this can fail at runtime. Mitigate by running existing instrumentation tests post-change. -- **`-Xcontext-receivers` removal accidentally tripped** during DSL migration in item 4a will break `OnboardingComponents.kt` and `TurbineKtx.kt` compilation. Build before committing. -- **Compose compiler / Kotlin alignment** — once item 1 lands, Compose UI version is no longer cross-checked against the Kotlin Compose plugin in the catalog. Document in `README.md` or `TROUBLESHOOTING.md` that bumping Kotlin requires verifying Compose BOM compatibility. - ---- - -## Out of scope - -- Section 1: stale dependency bumps (covered in `01-stale-dependencies.md`). -- Section 3: CI/CD workflow changes (covered in `03-ci-workflows.md`). -- Section 4: Gradle wrapper upgrade, AGP/Kotlin version bumps, Renovate config (covered in `04-misc.md`). -- Item 4b: context-parameters source migration (separate refactor ticket). diff --git a/maintenance/03-ci-workflows.md b/maintenance/03-ci-workflows.md deleted file mode 100644 index 06a4384..0000000 --- a/maintenance/03-ci-workflows.md +++ /dev/null @@ -1,77 +0,0 @@ -# Plan: CI Workflows - -## 1. Goal - -Refresh the four GitHub Actions workflows under `.github/workflows/` to use currently-supported action versions, fix a copy-paste workflow name in `post-merge.yaml`, migrate off the deprecated `gradle/gradle-build-action` and standalone `gradle/wrapper-validation-action`, and clean up a no-op conditional. No behavioural changes to what gets built/tested/published. - -## 2. Per-file change list - -### 2.1 `.github/workflows/pre-merge.yaml` - -Diffs: -- L14, L29: `actions/checkout@v4.1.1` → `actions/checkout@v4` (float to major). Alternative: pin to a full SHA; see Open Questions. -- L16, L31: `actions/setup-java@v4.1.0` → `actions/setup-java@v4`. -- L21, L36: `gradle/gradle-build-action@v3.1.0` → `gradle/actions/setup-gradle@v4` (action was renamed/relocated; v3 of the old action is the last release). -- L11, L26: remove `if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}` — `head_commit` is null on `pull_request` events, so this never triggers. See Open Questions for the alternative (use PR title). - -Side effects: -- `gradle/actions/setup-gradle@v4` enables Gradle's built-in build cache reporting and uses a slightly different cache key scheme than `gradle-build-action@v3`. First run on the new action produces a cache miss; subsequent runs should hit. No config required — defaults are fine for this repo's two-step (`detekt lint`, then `compileX testDebugUnitTest flankRun`) usage. -- `setup-gradle@v4` adds a job summary by default with build scan / cache stats. Harmless; can be silenced with `add-job-summary: 'never'` if noisy. - -### 2.2 `.github/workflows/post-merge.yaml` - -Diffs: -- L1: `name: Pre Merge Checks` → `name: Post Merge Checks` (or `Validate Build`). User to pick — recommend `Post Merge Checks` for symmetry with `pre-merge.yaml`. -- L14: `actions/checkout@v4.1.1` → `actions/checkout@v4`. -- L16: `actions/setup-java@v4.1.0` → `actions/setup-java@v4`. -- L21: `gradle/gradle-build-action@v3.1.0` → `gradle/actions/setup-gradle@v4`. -- L11: the `head_commit` conditional is valid here (this workflow runs on `push`), so leave it. But note it skips the *whole job* — if intent was per-step skipping, that's fine; just be aware merge-commit messages on main rarely contain `ci skip` in practice. - -Side effects: same `setup-gradle@v4` notes as above. - -### 2.3 `.github/workflows/deploy.yml` - -Diffs: -- L14: `actions/checkout@v4.1.1` → `actions/checkout@v4`. -- L16: `actions/setup-java@v4.1.0` → `actions/setup-java@v4`. -- L21: `gradle/gradle-build-action@v3.1.0` → `gradle/actions/setup-gradle@v4`. -- L11: `head_commit` conditional — for tag pushes, `head_commit` is populated from the tagged commit, so the check works. Low value (you don't typically tag a release with `ci skip` in the message), but harmless. Suggest leaving it to keep the diff minimal. - -Side effects: none beyond the `setup-gradle@v4` cache-key change. `publishBundle` does not interact with the action upgrades. - -### 2.4 `.github/workflows/gradle-wrapper-validation.yml` - -Diffs: -- L16: `actions/checkout@v4.1.1` → `actions/checkout@v4`. -- L18: `gradle/wrapper-validation-action@v1` → `gradle/actions/wrapper-validation@v4`. The standalone action is in maintenance mode and has been merged into the umbrella `gradle/actions` repo. Behaviour is equivalent (verifies `gradle/wrapper/gradle-wrapper.jar` checksum against known-good list). -- L19: trailing whitespace cleanup (cosmetic). - -Side effects: none — same validation, same exit codes. - -## 3. Open questions (for the user to decide) - -1. **Pin actions to SHAs vs. float to major?** Floating to `@v4` (`actions/checkout`, `actions/setup-java`, `gradle/actions/*`) is simpler and gets security patches for free. Pinning to a full 40-char SHA (e.g. `actions/checkout@a5ac7e5...`) is the OpenSSF Scorecard recommendation and protects against tag-rewrite supply-chain attacks. For a personal/small project, floating to major is the pragmatic default. Decide once and apply uniformly. -2. **`pre-merge.yaml` trigger `branches: '*'`** — this fires on every PR regardless of base branch, including stacked feature-to-feature PRs. Most repos use `branches: [main]` to only run on PRs targeting main. Is the wildcard intentional (e.g. you stack PRs)? If not, narrow to `main`. -3. **`if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}` on `pre-merge.yaml`** — this is a no-op on `pull_request` events (`head_commit` is null, so `contains(null, 'ci skip')` is false, so the negation is true, so the job always runs — i.e. it works by accident). Three options: - - (a) Remove it from `pre-merge.yaml` entirely (cleanest). - - (b) Replace with `!contains(github.event.pull_request.title, 'ci skip')` so the skip mechanism actually works for PRs. - - (c) Leave as is. Recommend (a) unless you actively use `ci skip` on PRs. -4. **Workflow name for `post-merge.yaml`** — `Post Merge Checks` vs. `Validate Build`. No functional difference; pick one. - -## 4. Verification - -1. Open a throwaway branch (`chore/ci-bumps`) with the diffs and push a no-op commit (e.g. whitespace in `README.md`). Confirm: - - `Pre Merge Checks` runs both jobs to completion when the branch is opened as a PR. - - `Validate Gradle Wrapper` runs and succeeds on both `pull_request` and (after merge) `push` to main. -2. Inspect the `Setup Gradle` step's job summary — `gradle/actions/setup-gradle@v4` emits a summary with cache restore/save sizes. First run = miss, second run on the same branch = hit. -3. Merge to main and confirm `Post Merge Checks` (renamed) runs `testDebugUnitTest flankRun` end-to-end. -4. (Optional, when you cut the next release) tag `vX.Y.Z` on a throwaway and observe `Deploy` running through `publishBundle`. Skip if you don't want to burn a Play Store internal-track upload — the deploy workflow shares the same three action upgrades as the others, so success on `pre-merge` + `post-merge` is strong evidence it'll work. -5. Check the Actions tab for any deprecation warnings — should be zero after this pass. - -## 5. Out of scope - -- Bumping the Gradle wrapper version itself — covered in Section 4. -- Bumping the JDK version — already on Zulu 21, current LTS, no change needed. -- Migrating from Flank to GMD (Gradle Managed Devices) or another runner — orthogonal, larger discussion. -- Reworking the `data/downloadProdSource.sh` / `replaceSecrets.sh` flow used by `post-merge` and `deploy` — separate concern. -- Adding concurrency groups, build matrices, or caching of `~/.android` — possible follow-up, not part of this maintenance pass. diff --git a/maintenance/04-misc.md b/maintenance/04-misc.md deleted file mode 100644 index dc58d41..0000000 --- a/maintenance/04-misc.md +++ /dev/null @@ -1,120 +0,0 @@ -# Section 4 — Miscellaneous Maintenance Items - -## Goal - -Resolve a set of small, mostly-independent maintenance items that fall outside the dependency-bump and lint-cleanup work covered in Sections 1–3: Gradle wrapper refresh, Renovate config modernization, optional release version bump, lint baseline regeneration, type-safe project accessor cleanup, README/troubleshooting hygiene, and a `local.properties` gitignore confirmation. - ---- - -## 1. Gradle wrapper upgrade - -- **Problem:** `gradle-wrapper.properties` pins Gradle 8.13 (Mar 2024 era for the file timestamp). Latest stable 8.x is **8.14.5** (May 2026). AGP 8.13 (`gradle/libs.versions.toml:2`) requires minimum Gradle 8.13 and is forward-compatible within the 8.x line; Gradle 9.x is **not** an option without an AGP 9.x bump (out of scope here). -- **Files to change:** - - `/Users/simonas/Projects/quies/gradle/wrapper/gradle-wrapper.properties:4` -- **Concrete change:** Update `distributionUrl` to `https\://services.gradle.org/distributions/gradle-8.14.5-bin.zip`. Run `./gradlew wrapper --gradle-version 8.14.5 --distribution-type bin` so the wrapper jar / scripts get regenerated consistently. Re-run once (`./gradlew wrapper`) to lock the version. -- **Verification:** `./gradlew --version` reports 8.14.5; `./gradlew :app:assembleDebug` and `./gradlew :app:lintDebug` succeed. - ---- - -## 2. Renovate config modernization - -- **Problem:** `renovate.json` extends `config:base`, which Renovate has deprecated in favor of `config:recommended`. The preset `:automergeMinor` still works but warrants explicit re-confirmation since it was inherited from the `kotlin-android-template` upstream and auto-merging minors on a small Android codebase has nontrivial risk (e.g., AGP/Kotlin minors). -- **Files to change:** - - `/Users/simonas/Projects/quies/renovate.json` (entire file, 6 lines) -- **Concrete change:** Replace `"config:base"` with `"config:recommended"`. Decide whether to keep `:automergeMinor` (see Open Questions). If kept, consider scoping it away from `agp`, `kotlin`, and `gradle` packages via a `packageRules` block. -- **Verification:** Push a branch and open a Renovate PR or run the Renovate dry-run / config-validator (`npx --package renovate -c renovate-config-validator`). Confirm no config-deprecation warnings appear in the next Renovate dashboard issue. - ---- - -## 3. App version bump (decision-gated) - -- **Problem:** `Coordinates.kt` shows `APP_VERSION_NAME = "1.0.1"`, `APP_VERSION_CODE = 33`. If this maintenance pass is shipped to Play (internal track at minimum, given `play { track.set("internal") }` in `app/build.gradle.kts:182`), the version must bump. Not all maintenance passes ship. -- **Files to change:** - - `/Users/simonas/Projects/quies/buildSrc/src/main/java/Coordinates.kt:5-6` -- **Concrete change:** Bump to `APP_VERSION_NAME = "1.0.2"` and `APP_VERSION_CODE = 34`. Skip if not shipping. -- **Verification:** `./gradlew :app:assembleRelease` produces an AAB whose `versionCode` matches; confirm via `aapt dump badging` or Play console upload preflight. - ---- - -## 4. Lint baseline regeneration (depends on Sections 1 & 2) - -- **Problem:** `app/build.gradle.kts:98` references `app/lint-baseline.xml`. After the dependency bumps in Sections 1–2 land, new lint findings introduced by upgraded androidx/Compose/Hilt/etc. will be silently absorbed by the old baseline rather than surfacing for review. Regenerating ensures `warningsAsErrors = true` (line 96) actually catches new issues. -- **Files to change:** - - `/Users/simonas/Projects/quies/app/lint-baseline.xml` (delete, then regenerate) -- **Concrete change:** - 1. Delete `app/lint-baseline.xml`. - 2. Run `./gradlew :app:updateLintBaseline` (Gradle 8.x AGP task). If unavailable, temporarily comment out `baseline = ...` and run `./gradlew :app:lintDebug -Dlint.baselines.continue=true` to write a new baseline at `app/build/reports/lint-results-debug-baseline.xml`, then move it to `app/lint-baseline.xml`. - 3. Diff the new baseline against the old one. Triage any *newly-quieted* findings — fix in code rather than baselining when feasible. - 4. Commit the regenerated baseline. -- **Verification:** `./gradlew :app:lintRelease` passes with `warningsAsErrors = true`. New baseline diff committed alongside dep bumps. -- **Sequencing note:** Must run **after** Sections 1 (deps) and 2 (lint cleanup) merge, otherwise the baseline captures findings that those sections will eliminate. - ---- - -## 5. Type-safe project accessors - -- **Problem:** `settings.gradle.kts:18` enables `TYPESAFE_PROJECT_ACCESSORS`, but `app/build.gradle.kts:131` still uses the stringy form `implementation(project(":data"))`. Either use the preview or drop the flag. -- **Files to change:** - - `/Users/simonas/Projects/quies/app/build.gradle.kts:131` - - (or alternatively) `/Users/simonas/Projects/quies/settings.gradle.kts:18` -- **Concrete change:** **Preferred:** replace `implementation(project(":data"))` with `implementation(projects.data)`. Sweep other `build.gradle.kts` modules (`data/`, `buildSrc/`) for similar string accessors. **Alternative:** delete the `enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")` line if the team prefers stable-only features. -- **Verification:** `./gradlew :app:dependencies | grep -i ':data'` resolves the module dependency identically; `./gradlew :app:assembleDebug` succeeds. - ---- - -## 6. README expansion (optional) - -- **Problem:** `README.md` is 18 lines: a logo, two epigraphs, and a single acknowledgement line pointing at the upstream template. There's no build/setup, no description of what the app does, no badge/CI block. Acceptable for a personal project; suboptimal for onboarding or open-source visibility. -- **Files to change:** - - `/Users/simonas/Projects/quies/README.md` -- **Concrete change (if pursued):** Add sections — *What it does* (1 paragraph), *Build & run* (`./gradlew :app:installDebug`, JDK 21, Android SDK requirements per `compileSdk`), *Module layout* (`app/`, `data/`, `buildSrc/`), *CI/CD* (link to `.github/workflows`), preserve the epigraphs and template acknowledgement. -- **Verification:** `markdownlint README.md` clean (if used); manual readability review. -- **Optional flag:** Skip if the user prefers the current minimalist tone. - ---- - -## 7. `local.properties` gitignore confirmation - -- **Problem:** `local.properties` exists at repo root and would leak the local SDK path / arbitrary developer overrides if tracked. -- **Files to change:** None expected — confirmation only. -- **Concrete change:** Confirmed gitignored. `.gitignore:38` lists `local.properties`, and `.gitignore:254` lists `/*/local.properties` for module-level files. The file is **not** tracked by git (it appears in `ls -la` only because gitignore doesn't delete files; it just excludes them from indexing). -- **Verification:** Run `git ls-files | grep local.properties` — should return empty. Run `git check-ignore -v local.properties` — should print the matching `.gitignore` rule. No action needed unless either check fails. - ---- - -## 8. `TROUBLESHOOTING.md` review - -- **Problem:** The doc has two sections, both stale: - 1. *"agp incompatibility with IntelliJ IDEA"* references AGP 8.0.0 vs IDEA's max-supported 7.4.0 — irrelevant in 2026 with AGP 8.13. - 2. *"Use correct JVM version"* shows `jvmTarget = JavaVersion.VERSION_17` and a sample error listing JVM targets up to 18 — but `app/build.gradle.kts:38-45` now uses **JDK 21**. -- **Files to change:** - - `/Users/simonas/Projects/quies/TROUBLESHOOTING.md` -- **Concrete change:** Either (a) delete the file entirely (preferred — both items are obsolete and the IDE-version pain is a non-issue at this AGP/IDE pairing), or (b) rewrite: drop section 1 outright; in section 2 update the example to `VERSION_21` and update the error message snippet to a current-era one (or just say "your local JDK must be ≥ 21; check `java --version`"). -- **Verification:** Manual read; ensure no other doc references `TROUBLESHOOTING.md` (grep the repo). - ---- - -## Sequencing - -- **Item 4** (lint baseline regen) **must run after Sections 1 and 2** are merged so the regenerated baseline reflects the post-bump, post-cleanup state. -- **Items 1, 2, 3, 5, 6, 7, 8** are mutually independent and can ship in any order or as a single PR. -- Suggested grouping for one PR: 1 + 2 + 5 + 8 (all small mechanical edits). Items 3, 6 are decision-gated (see below). Item 7 needs no change. Item 4 lands in a follow-up. - ---- - -## Open questions for the user - -1. **Item 3 (release):** Is this maintenance pass shipping to Play? If yes, bump to `1.0.2` / `34`. If no, leave as-is. -2. **Item 2 (Renovate automerge):** Keep `:automergeMinor` for all packages, restrict it (exclude `agp`, `kotlin`, `gradle`, `compose-*`), or drop it? Recommendation: restrict — minor bumps to AGP/Kotlin can break Compose compiler compatibility. -3. **Item 5 (preview flag):** Adopt `projects.data` everywhere, or drop the preview flag? Recommendation: adopt; the feature is widely used and unlikely to be removed. -4. **Item 6 (README):** Expand with build/setup section, or keep the minimalist poetic tone? -5. **Item 8 (TROUBLESHOOTING):** Delete entirely, or keep a slimmed-down version? - ---- - -## Out of scope - -- Section 1 — dependency bumps in `gradle/libs.versions.toml`. -- Section 2 — build-config cleanup (Compose BOM, Jetifier, dead `composeOptions`, `kotlinOptions` migration). -- Section 3 — GitHub Actions / CI workflow upgrades. -- AGP 9.x / Gradle 9.x migration (would require coordinated AGP, JDK, and possibly Kotlin upgrades; not a maintenance task).