diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-02 11:09:19 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-02 11:09:19 +0100 |
| commit | 5cb29988d8ebb4c9625af6452f7931d0e68d1cb7 (patch) | |
| tree | de206444c7c6eca3627c0cd006619770ec253052 | |
| parent | 9eec620a4d95c4e3217f0868af0fc7f7bada5c91 (diff) | |
| download | HardenedPass-5cb29988d8ebb4c9625af6452f7931d0e68d1cb7.tar.gz HardenedPass-5cb29988d8ebb4c9625af6452f7931d0e68d1cb7.zip | |
build: update to Gradle 9.8.2, AGP 9.5.0, Kotlin 2.4.21, Hilt 2.60.1
AGP 9 migration:
- Configure the shared Android setup through the new DSL
(CommonExtension, ApplicationExtension) instead of the removed
TestedExtension and BaseAppModuleExtension; targetSdk moves to the
application extension.
- Stop applying the Kotlin Android plugin; AGP 9 compiles Kotlin itself.
- Replace kapt with KSP 2.3.13 for Hilt, which built-in Kotlin requires,
and carry the Dagger compiler options over as KSP arguments.
- AGP 9 now applies packaging excludes to unit-test resources, which
stripped the diceware word list (**/*.txt) from its tests; lift that
exclude in the diceware module.
- Use resources.directories instead of the deprecated srcDir.
Plugin updates required by Gradle 9 / AGP 9: Sentry Gradle plugin
6.25.0 (sentry-android 8.62.0), metalava plugin 0.5.1, maven-publish
0.37.0, spotless 8.10.4, version-catalog-update 1.1.2, download task
5.7.0, foojay resolver 1.0.0, with their API changes. Newer metalava
models Kotlin properties, so autofill-parser/api.txt is regenerated;
the diff is representation only, no API was added or removed.
Fix new Kotlin 2.4 and lint findings (explicit return type in a
function with an early return, UseKtx in autofill-parser), drop
deprecated Kotlin Gradle properties, and make the AGENTS.md pre-commit
command cover every task CI runs (both flavors, lint on all modules).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
23 files changed, 153 insertions, 208 deletions
@@ -33,13 +33,13 @@ Needs JDK 17 and the Android SDK. `local.properties` (gitignored) holds `sdk.dir ./gradlew assembleFreeDebug # debug APK ./gradlew test -PslimTests # unit tests, as CI runs them ./gradlew spotlessApply # formatting (ktfmt); CI runs spotlessCheck - ./gradlew lintFreeRelease + ./gradlew lint # all modules and variants, as CI runs it Prefix with `CI=1` to match CI: it turns Kotlin warnings into errors, so a deprecation warning that passes locally fails CI. Before committing, run: CI=1 ./gradlew spotlessCheck test -PslimTests assembleFreeDebug assembleFreeRelease \ - lintFreeRelease metalavaCheckCompatibilityRelease + assembleNonFreeDebug assembleNonFreeRelease lint metalavaCheckCompatibilityRelease Flavors: `free` (no proprietary dependencies, Sentry replaced by `sentry-stub`) and `nonFree` (adds Sentry and Play Services SMS OTP). @@ -63,6 +63,11 @@ itself, so it cannot be referenced with a local `./` path. and hash included. A dependency bump that moves a baselined jar needs the path updated. - `LaunchActivity` is exported and forwards `FILE_PATH` to `DecryptActivity` (not exported) only after `isFileInRepo` checks it canonically resolves inside the store. +- AGP 9 applies packaging excludes (`AndroidCommon`, e.g. `**/*.txt`) to unit-test java + resources as well. A module whose tests load such a file must remove the exclude locally + (see `passgen/diceware/build.gradle.kts`). +- Hilt runs through KSP (no kapt; AGP 9's built-in Kotlin does not support it). Dagger compiler + options are `ksp { arg(...) }` in `app/build.gradle.kts`. - JGit is pinned at 4.5.7 (2019). Newer JGit needs Java 11 APIs Android only partly desugars; upgrading it is a large, separate job. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 206a3972..bff7d73a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -7,11 +7,11 @@ plugins { id("com.github.android-password-store.android-application") id("com.github.android-password-store.kotlin-android") - id("com.github.android-password-store.kotlin-kapt") id("com.github.android-password-store.versioning-plugin") id("com.github.android-password-store.sentry") id("com.github.android-password-store.rename-artifacts") alias(libs.plugins.hilt) + alias(libs.plugins.ksp) alias(libs.plugins.kotlin.composeCompiler) } @@ -35,9 +35,17 @@ android { } } +ksp { + // https://dagger.dev/dev-guide/compiler-options + arg("dagger.fastInit", "enabled") + arg("dagger.experimentalDaggerErrorMessages", "enabled") + arg("dagger.hilt.shareTestComponents", "true") + arg("dagger.moduleBindingValidation", "ERROR") +} + dependencies { implementation(platform(libs.compose.bom)) - kapt(libs.dagger.hilt.compiler) + ksp(libs.dagger.hilt.compiler) implementation(libs.androidx.annotation) coreLibraryDesugaring(libs.android.desugarJdkLibs) implementation(projects.autofillParser) diff --git a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt index 2bd75aba..48804e7a 100644 --- a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt +++ b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt @@ -268,7 +268,7 @@ constructor( } .flowOn(dispatcherProvider.io()) - private fun shouldTake(file: File) = + private fun shouldTake(file: File): Boolean = with(file) { if (showHiddenContents) { return !file.name.startsWith(".git") diff --git a/autofill-parser/api.txt b/autofill-parser/api.txt index a999127f..89cfaa3c 100644 --- a/autofill-parser/api.txt +++ b/autofill-parser/api.txt @@ -2,8 +2,6 @@ package com.github.androidpasswordstore.autofillparser { public enum AutofillAction { - method public static com.github.androidpasswordstore.autofillparser.AutofillAction valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException; - method public static com.github.androidpasswordstore.autofillparser.AutofillAction[] values(); enum_constant public static final com.github.androidpasswordstore.autofillparser.AutofillAction FillOtpFromSms; enum_constant public static final com.github.androidpasswordstore.autofillparser.AutofillAction Generate; enum_constant public static final com.github.androidpasswordstore.autofillparser.AutofillAction Match; @@ -14,13 +12,13 @@ package com.github.androidpasswordstore.autofillparser { method public static String computeCertificatesHash(android.content.Context context, String appPackage); } - @RequiresApi(android.os.Build.VERSION_CODES.O) public abstract sealed class AutofillScenario<T> { - method public final java.util.List<T> getFieldsToSave(); - method public final boolean getHasFieldsToSave(); - method public final boolean getHasPasswordFieldsToSave(); - method public final boolean getHasUsername(); - method public abstract java.util.List<T> getPasswordFieldsToSave(); - method public abstract T? getUsername(); + @RequiresApi(android.os.Build.VERSION_CODES.O) public abstract sealed nonexhaustive class AutofillScenario<T> { + method @InaccessibleFromKotlin public final java.util.List<T> getFieldsToSave(); + method @InaccessibleFromKotlin public final boolean getHasFieldsToSave(); + method @InaccessibleFromKotlin public final boolean getHasPasswordFieldsToSave(); + method @InaccessibleFromKotlin public final boolean getHasUsername(); + method @InaccessibleFromKotlin public abstract java.util.List<T> getPasswordFieldsToSave(); + method @InaccessibleFromKotlin public abstract T? getUsername(); method public final boolean hasFieldsToFillOn(com.github.androidpasswordstore.autofillparser.AutofillAction action); property public final java.util.List<T> fieldsToSave; property public final boolean hasFieldsToSave; @@ -37,15 +35,16 @@ package com.github.androidpasswordstore.autofillparser { } public final class AutofillScenarioKt { - method @RequiresApi(android.os.Build.VERSION_CODES.O) public static void fillWithAutofillId(android.service.autofill.Dataset.Builder, com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario, com.github.androidpasswordstore.autofillparser.AutofillAction action, com.github.androidpasswordstore.autofillparser.Credentials? credentials); - method @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? getPasswordValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>); - method @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? getUsernameValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>); + method @KotlinOnly @RequiresApi(android.os.Build.VERSION_CODES.O) public static void fillWith(android.service.autofill.Dataset.Builder, com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario, com.github.androidpasswordstore.autofillparser.AutofillAction action, com.github.androidpasswordstore.autofillparser.Credentials? credentials); + method @InaccessibleFromKotlin @RequiresApi(android.os.Build.VERSION_CODES.O) public static void fillWithAutofillId(android.service.autofill.Dataset.Builder, com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario, com.github.androidpasswordstore.autofillparser.AutofillAction action, com.github.androidpasswordstore.autofillparser.Credentials? credentials); + method @InaccessibleFromKotlin @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? getPasswordValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>); + method @InaccessibleFromKotlin @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? getUsernameValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>); method @RequiresApi(android.os.Build.VERSION_CODES.O) public static com.github.androidpasswordstore.autofillparser.AutofillScenario<android.app.assist.AssistStructure.ViewNode>? recoverNodes(com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId>, android.app.assist.AssistStructure structure); + property @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? com.github.androidpasswordstore.autofillparser.AutofillScenario<android.app.assist.AssistStructure.ViewNode>.passwordValue; + property @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? com.github.androidpasswordstore.autofillparser.AutofillScenario<android.app.assist.AssistStructure.ViewNode>.usernameValue; } public enum BrowserAutofillSupportLevel { - method public static com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException; - method public static com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel[] values(); enum_constant public static final com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel FlakyFill; enum_constant public static final com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel GeneralFill; enum_constant public static final com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel GeneralFillAndSave; @@ -59,13 +58,13 @@ package com.github.androidpasswordstore.autofillparser { method public String? component1(); method public String? component2(); method public String? component3(); - method public com.github.androidpasswordstore.autofillparser.Credentials copy(String? username, String? password, String? otp); - method public String? getOtp(); - method public String? getPassword(); - method public String? getUsername(); - property public final String? otp; - property public final String? password; - property public final String? username; + method public com.github.androidpasswordstore.autofillparser.Credentials copy(optional String? username, optional String? password, optional String? otp); + method @InaccessibleFromKotlin public String? getOtp(); + method @InaccessibleFromKotlin public String? getPassword(); + method @InaccessibleFromKotlin public String? getUsername(); + property public String? otp; + property public String? password; + property public String? username; } public final class FeatureAndTrustDetectionKt { @@ -73,15 +72,15 @@ package com.github.androidpasswordstore.autofillparser { } @RequiresApi(android.os.Build.VERSION_CODES.O) public final class FillableForm { - method public com.github.androidpasswordstore.autofillparser.FormOrigin getFormOrigin(); - method public java.util.List<android.view.autofill.AutofillId> getIgnoredIds(); - method public Integer? getSaveFlags(); - method public com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> getScenario(); + method @InaccessibleFromKotlin public com.github.androidpasswordstore.autofillparser.FormOrigin getFormOrigin(); + method @InaccessibleFromKotlin public java.util.List<android.view.autofill.AutofillId> getIgnoredIds(); + method @InaccessibleFromKotlin public Integer? getSaveFlags(); + method @InaccessibleFromKotlin public com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> getScenario(); method public android.os.Bundle toClientState(); - property public final com.github.androidpasswordstore.autofillparser.FormOrigin formOrigin; - property public final java.util.List<android.view.autofill.AutofillId> ignoredIds; - property public final Integer? saveFlags; - property public final com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario; + property public com.github.androidpasswordstore.autofillparser.FormOrigin formOrigin; + property public java.util.List<android.view.autofill.AutofillId> ignoredIds; + property public Integer? saveFlags; + property public com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario; field public static final com.github.androidpasswordstore.autofillparser.FillableForm.Companion Companion; } @@ -95,8 +94,8 @@ package com.github.androidpasswordstore.autofillparser { method public void onSuccess(android.content.IntentSender intentSender); } - public abstract sealed class FormOrigin { - method public String getIdentifier(); + public abstract sealed exhaustive class FormOrigin { + method @InaccessibleFromKotlin public String getIdentifier(); method public final String getPrettyIdentifier(android.content.Context context, optional boolean untrusted); method public final android.os.Bundle toBundle(); property public String identifier; @@ -106,7 +105,7 @@ package com.github.androidpasswordstore.autofillparser { public static final class FormOrigin.App extends com.github.androidpasswordstore.autofillparser.FormOrigin { ctor public FormOrigin.App(String identifier); method public String component1(); - method public com.github.androidpasswordstore.autofillparser.FormOrigin.App copy(String identifier); + method public com.github.androidpasswordstore.autofillparser.FormOrigin.App copy(optional String identifier); property public String identifier; } @@ -117,7 +116,7 @@ package com.github.androidpasswordstore.autofillparser { public static final class FormOrigin.Web extends com.github.androidpasswordstore.autofillparser.FormOrigin { ctor public FormOrigin.Web(String identifier); method public String component1(); - method public com.github.androidpasswordstore.autofillparser.FormOrigin.Web copy(String identifier); + method public com.github.androidpasswordstore.autofillparser.FormOrigin.Web copy(optional String identifier); property public String identifier; } @@ -127,12 +126,3 @@ package com.github.androidpasswordstore.autofillparser { } -package mozilla.components.lib.publicsuffixlist { - - public final class PublicSuffixListLoaderTest { - ctor public PublicSuffixListLoaderTest(); - method public void testLoadingBundledPublicSuffixList(); - } - -} - diff --git a/autofill-parser/build.gradle.kts b/autofill-parser/build.gradle.kts index 472f78fb..39157b37 100644 --- a/autofill-parser/build.gradle.kts +++ b/autofill-parser/build.gradle.kts @@ -15,7 +15,7 @@ android { minSdk = 23 consumerProguardFiles("consumer-rules.pro") } - sourceSets { getByName("test") { resources.srcDir("src/main/assets") } } + sourceSets { getByName("test") { resources.directories.add("src/main/assets") } } namespace = "com.github.androidpasswordstore.autofillparser" } diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt index c273bdfc..3dc74f33 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt @@ -8,11 +8,11 @@ import android.app.assist.AssistStructure import android.content.Context import android.content.pm.PackageManager import android.content.pm.PackageManager.ApplicationInfoFlags -import android.net.Uri import android.os.Build import android.os.Bundle import android.view.autofill.AutofillId import androidx.annotation.RequiresApi +import androidx.core.net.toUri import logcat.logcat /** @@ -150,7 +150,7 @@ private class AutofillFormParser( } private fun webOriginToFormOrigin(context: Context, origin: String): FormOrigin? { - val uri = Uri.parse(origin) ?: return null + val uri = origin.toUri() val scheme = uri.scheme ?: return null if (scheme !in SUPPORTED_SCHEMES) return null val host = uri.host ?: return null diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt index 2da3a102..d531dd49 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt @@ -8,11 +8,11 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.PackageManager.ResolveInfoFlags -import android.net.Uri import android.os.Build import android.provider.Settings import android.service.autofill.SaveInfo import androidx.annotation.RequiresApi +import androidx.core.net.toUri /** * In order to add a new browser, do the following: @@ -244,7 +244,7 @@ private fun getBrowserAutofillSupportLevel( public fun getInstalledBrowsersWithAutofillSupportLevel( context: Context ): List<Pair<String, BrowserAutofillSupportLevel>> { - val testWebIntent = Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("https://example.org") } + val testWebIntent = Intent(Intent.ACTION_VIEW).apply { data = "https://example.org".toUri() } val installedBrowsers = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { context.packageManager.queryIntentActivities( diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 2bb5b6ea..dd9c0c8b 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -29,10 +29,6 @@ gradlePlugin { id = "com.github.android-password-store.kotlin-common" implementationClass = "app.passwordstore.gradle.KotlinCommonPlugin" } - register("kotlin-kapt") { - id = "com.github.android-password-store.kotlin-kapt" - implementationClass = "app.passwordstore.gradle.KotlinKaptPlugin" - } register("kotlin-jvm-library") { id = "com.github.android-password-store.kotlin-jvm-library" implementationClass = "app.passwordstore.gradle.KotlinJVMLibrary" diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index 1488630b..ea43de31 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -6,7 +6,7 @@ rootProject.name = "build-logic" -plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } +plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } dependencyResolutionManagement { repositories { diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt index a26faea9..94b9c112 100644 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt +++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt @@ -8,37 +8,29 @@ package app.passwordstore.gradle import app.passwordstore.gradle.LintConfig.configureLint import app.passwordstore.gradle.flavors.configureSlimTests import com.android.build.api.dsl.ApplicationExtension +import com.android.build.api.dsl.CommonExtension import com.android.build.api.dsl.LibraryExtension -import com.android.build.gradle.TestedExtension import org.gradle.accessors.dm.LibrariesForLibs import org.gradle.api.Project import org.gradle.api.tasks.testing.Test -import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.findByType import org.gradle.kotlin.dsl.withType object AndroidCommon { fun configure(project: Project) { - project.extensions.configure<TestedExtension> { - compileSdkVersion(35) - defaultConfig { - minSdk = 26 - targetSdk = 34 - } + project.extensions.getByType(CommonExtension::class.java).apply { + compileSdk = 35 + defaultConfig.minSdk = 26 - packagingOptions { - resources.excludes.add("**/*.version") - resources.excludes.add("**/*.txt") - resources.excludes.add("**/*.kotlin_module") - resources.excludes.add("**/plugin.properties") - resources.excludes.add("**/META-INF/AL2.0") - resources.excludes.add("**/META-INF/LGPL2.1") - } + packaging.resources.excludes.add("**/*.version") + packaging.resources.excludes.add("**/*.txt") + packaging.resources.excludes.add("**/*.kotlin_module") + packaging.resources.excludes.add("**/plugin.properties") + packaging.resources.excludes.add("**/META-INF/AL2.0") + packaging.resources.excludes.add("**/META-INF/LGPL2.1") - testOptions { - animationsDisabled = true - unitTests.isReturnDefaultValues = true - } + testOptions.animationsDisabled = true + testOptions.unitTests.isReturnDefaultValues = true project.tasks.withType<Test>().configureEach { jvmArgs( @@ -49,7 +41,10 @@ object AndroidCommon { project.configureSlimTests() } - project.extensions.findByType<ApplicationExtension>()?.run { lint.configureLint(project) } + project.extensions.findByType<ApplicationExtension>()?.run { + defaultConfig.targetSdk = 34 + lint.configureLint(project) + } project.extensions.findByType<LibraryExtension>()?.run { lint.configureLint(project) } val libs = project.extensions.getByName("libs") as LibrariesForLibs project.dependencies.apply { diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/DependencyUpdatesPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/DependencyUpdatesPlugin.kt index 8dbe586f..e35a1843 100644 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/DependencyUpdatesPlugin.kt +++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/DependencyUpdatesPlugin.kt @@ -19,7 +19,6 @@ class DependencyUpdatesPlugin : Plugin<Project> { override fun apply(project: Project) { project.pluginManager.apply(VersionCatalogUpdatePlugin::class) project.extensions.configure<VersionCatalogUpdateExtension> { - keep.keepUnusedLibraries.set(true) versionSelector { val currentVersion = Version.tryParse(it.currentVersion).getOrNull() val newVersion = Version.tryParse(it.candidate.version).getOrNull() diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinAndroidPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinAndroidPlugin.kt index 67fef255..d679abd3 100644 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinAndroidPlugin.kt +++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinAndroidPlugin.kt @@ -11,16 +11,13 @@ import org.gradle.api.Project import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.getByType import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension -import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper @Suppress("Unused") class KotlinAndroidPlugin : Plugin<Project> { override fun apply(project: Project) { - project.pluginManager.run { - apply(KotlinAndroidPluginWrapper::class) - apply(KotlinCommonPlugin::class) - } + // AGP 9 compiles Kotlin itself (built-in Kotlin); only the shared compiler options remain. + project.pluginManager.apply(KotlinCommonPlugin::class) project.extensions.getByType<KotlinProjectExtension>().jvmToolchain(JVM_TOOLCHAIN_ACTION) } } diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinKaptPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinKaptPlugin.kt deleted file mode 100644 index c4d6467a..00000000 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinKaptPlugin.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.gradle - -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.kotlin.dsl.apply -import org.gradle.kotlin.dsl.configure -import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin -import org.jetbrains.kotlin.gradle.plugin.KaptExtension - -@Suppress("Unused") -class KotlinKaptPlugin : Plugin<Project> { - - override fun apply(project: Project) { - project.pluginManager.apply(Kapt3GradleSubplugin::class) - project.afterEvaluate { - project.extensions.configure<KaptExtension> { - javacOptions { - if (hasDaggerCompilerDependency()) { - // https://dagger.dev/dev-guide/compiler-options#fastinit-mode - option("-Adagger.fastInit=enabled") - // Enable the better, experimental error messages - // https://github.com/google/dagger/commit/0d2505a727b54f47b8677f42dd4fc5c1924e37f5 - option("-Adagger.experimentalDaggerErrorMessages=enabled") - // Share test components for when we start leveraging Hilt for tests - // https://github.com/google/dagger/releases/tag/dagger-2.34 - option("-Adagger.hilt.shareTestComponents=true") - // KAPT nests errors causing real issues to be suppressed in CI logs - option("-Xmaxerrs", "500") - // Enables per-module validation for faster error detection - // https://github.com/google/dagger/commit/325b516ac6a53d3fc973d247b5231fafda9870a2 - option("-Adagger.moduleBindingValidation=ERROR") - } - } - } - } - project.tasks - .matching { it.name.startsWith("kapt") && it.name.endsWith("UnitTestKotlin") } - .configureEach { enabled = false } - } - - private fun Project.hasDaggerCompilerDependency(): Boolean { - return configurations.any { - it.dependencies.any { dependency -> dependency.name == "hilt-compiler" } - } - } -} diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/PublishedAndroidLibraryPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/PublishedAndroidLibraryPlugin.kt index 4e1ab741..055a921a 100644 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/PublishedAndroidLibraryPlugin.kt +++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/PublishedAndroidLibraryPlugin.kt @@ -10,8 +10,6 @@ package app.passwordstore.gradle import com.vanniktech.maven.publish.AndroidMultiVariantLibrary import com.vanniktech.maven.publish.MavenPublishBaseExtension import com.vanniktech.maven.publish.MavenPublishBasePlugin -import com.vanniktech.maven.publish.SonatypeHost -import me.tylerbwong.gradle.metalava.Documentation import me.tylerbwong.gradle.metalava.extension.MetalavaExtension import org.gradle.api.Plugin import org.gradle.api.Project @@ -28,7 +26,7 @@ class PublishedAndroidLibraryPlugin : Plugin<Project> { apply("me.tylerbwong.gradle.metalava") } project.extensions.configure<MavenPublishBaseExtension> { - publishToMavenCentral(SonatypeHost.DEFAULT, true) + publishToMavenCentral(automaticRelease = true) if (project.providers.environmentVariable("CI").isPresent) { signAllPublications() } @@ -36,10 +34,7 @@ class PublishedAndroidLibraryPlugin : Plugin<Project> { pomFromGradleProperties() } project.extensions.configure<MetalavaExtension> { - documentation.set(Documentation.PUBLIC) inputKotlinNulls.set(true) - outputKotlinNulls.set(true) - reportLintsAsErrors.set(true) reportWarningsAsErrors.set(true) } } diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/signing/AppSigning.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/signing/AppSigning.kt index f6a519c8..2a0825ed 100644 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/signing/AppSigning.kt +++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/signing/AppSigning.kt @@ -5,7 +5,7 @@ package app.passwordstore.gradle.signing -import com.android.build.gradle.internal.dsl.BaseAppModuleExtension +import com.android.build.api.dsl.ApplicationExtension import java.util.Properties import org.gradle.api.Project import org.gradle.kotlin.dsl.configure @@ -17,7 +17,7 @@ private const val KEYSTORE_CONFIG_PATH = "keystore.properties" internal fun Project.configureBuildSigning() { val keystoreConfigFile = rootProject.layout.projectDirectory.file(KEYSTORE_CONFIG_PATH) if (keystoreConfigFile.asFile.exists()) { - extensions.configure<BaseAppModuleExtension> { + extensions.configure<ApplicationExtension> { val contents = providers.fileContents(keystoreConfigFile).asText val keystoreProperties = Properties() keystoreProperties.load(contents.get().byteInputStream()) @@ -30,7 +30,7 @@ internal fun Project.configureBuildSigning() { } } val signingConfig = signingConfigs.getByName("release") - buildTypes.all { setSigningConfig(signingConfig) } + buildTypes.all { this.signingConfig = signingConfig } } } } diff --git a/gradle.properties b/gradle.properties index dde45722..b407f189 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,16 +18,7 @@ kotlin.incremental=true # Kotlin code style kotlin.code.style=official -# Enable incremental annotation processor for KAPT -kapt.incremental.apt=true - -# Turn off AP discovery in compile path to enable compile avoidance -kapt.include.compile.classpath=false - -# New incremental compilation for Kotlin -kotlin.compiler.preciseCompilationResultsBackup=true kotlin.build.report.output=file -kotlin.compiler.suppressExperimentalICOptimizationsWarning=true # Make all Kotlin DSL warnings fail build org.gradle.kotlin.dsl.allWarningsAsErrors=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8cc0acf2..fb831d85 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,11 +1,11 @@ [versions] -agp = "8.13.2" +agp = "9.5.0" androidxActivity = "1.9.2" bouncycastle = "1.86" coroutines = "1.9.0" flowbinding = "1.2.0" -hilt = "2.58" -kotlin = "2.1.21" +hilt = "2.60.1" +kotlin = "2.4.21" kotlinResult = "2.0.0" lifecycle = "2.8.6" moshi = "1.15.1" @@ -36,18 +36,18 @@ aps-sublimeFuzzy = "com.github.android-password-store:sublime-fuzzy:2.3.4" aps-zxingAndroidEmbedded = "com.github.android-password-store:zxing-android-embedded:4.2.1" build-agp = { module = "com.android.tools.build:gradle", version.ref = "agp" } build-diffutils = "io.github.java-diff-utils:java-diff-utils:4.12" -build-download = "de.undercouch:gradle-download-task:5.6.0" +build-download = "de.undercouch:gradle-download-task:5.7.0" build-javapoet = "com.squareup:javapoet:1.13.0" build-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } -build-mavenpublish = "com.vanniktech:gradle-maven-publish-plugin:0.29.0" -build-metalava = "me.tylerbwong.gradle.metalava:plugin:0.3.5" +build-mavenpublish = "com.vanniktech:gradle-maven-publish-plugin:0.37.0" +build-metalava = "me.tylerbwong.gradle.metalava:plugin:0.5.1" build-moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } build-moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } build-okhttp = "com.squareup.okhttp3:okhttp:5.0.0-alpha.14" build-semver = "com.github.zafarkhaja:java-semver:0.10.2" -build-sentry = "io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin:4.11.0" -build-spotless = "com.diffplug.spotless:spotless-plugin-gradle:7.0.0.BETA2" -build-vcu = "nl.littlerobots.version-catalog-update:nl.littlerobots.version-catalog-update.gradle.plugin:0.8.4" +build-sentry = "io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin:6.25.0" +build-spotless = "com.diffplug.spotless:spotless-plugin-gradle:8.10.4" +build-vcu = "nl.littlerobots.version-catalog-update:nl.littlerobots.version-catalog-update.gradle.plugin:1.1.2" compose-bom = "androidx.compose:compose-bom:2024.09.03" compose-foundation-core = { module = "androidx.compose.foundation:foundation" } compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout" } @@ -86,7 +86,7 @@ thirdparty-leakcanary-plumber = "com.squareup.leakcanary:plumber-android-startup thirdparty-logcat = "com.squareup.logcat:logcat:0.1" thirdparty-modernAndroidPrefs = "de.maxr1998:modernandroidpreferences:2.4.0-beta2" thirdparty-nonfree-googlePlayAuthApiPhone = "com.google.android.gms:play-services-auth-api-phone:18.1.0" -thirdparty-nonfree-sentry = "io.sentry:sentry-android:7.15.0" +thirdparty-nonfree-sentry = "io.sentry:sentry-android:8.62.0" thirdparty-pgpainless = "org.pgpainless:pgpainless-core:1.7.8" thirdparty-slack-lints = "com.slack.lint:slack-lint-checks:0.8.0" thirdparty-slf4j-api = { module = "org.slf4j:slf4j-api", version = { strictly = "[1.7, 1.8[", prefer = "1.7.36" } } @@ -106,4 +106,5 @@ testDependencies = [ [plugins] hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } +ksp = "com.google.devtools.ksp:2.3.13" kotlin-composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differindex 02f06ab2..9fa7f288 100644 --- a/gradle/wrapper/gradle-wrapper.jar +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f900c206..5907d39c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,8 +1,10 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=7988ed071b2a07900e2ec715fca15c6ab72bce6db433af301fa7fa7e9407bd24 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.6-bin.zip +distributionSha256Sum=21bd5da2de49b642904077f38c0efdc7778e92b3ed0889392dca61b97e389607 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.2-bin.zip networkTimeout=10000 +retries=0 +retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -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/HEAD/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/. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index 5eed7ee8..03754c4c 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -19,12 +19,39 @@ @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
+
+@rem Catch executions from older scripts and ensure they exit cleanly.
+@rem This can be removed once we can be reasonably confident that few people
+@rem will be migrating directly to this new wrapper.
+goto afterSafetyNet
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+goto exitWithErrorLevel
+:afterSafetyNet
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -45,13 +72,14 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-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
+1>&2 echo.
+1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+1>&2 echo.
+1>&2 echo Please set the JAVA_HOME variable in your environment to match the
+1>&2 echo location of your Java installation.
-goto fail
+"%COMSPEC%" /c exit 1
+goto exitWithErrorLevel
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -59,36 +87,26 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-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
+1>&2 echo.
+1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+1>&2 echo.
+1>&2 echo Please set the JAVA_HOME variable in your environment to match the
+1>&2 echo location of your Java installation.
-goto fail
+"%COMSPEC%" /c exit 1
+goto exitWithErrorLevel
:execute
@rem Setup the command line
-set CLASSPATH=
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -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 & goto 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
+@rem This label must not be changed. We rely on old scripts being able to jump to this point.
+:exitWithErrorLevel
+@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
+"%COMSPEC%" /c exit %ERRORLEVEL%
diff --git a/passgen/diceware/build.gradle.kts b/passgen/diceware/build.gradle.kts index 413d9a0d..5caaa366 100644 --- a/passgen/diceware/build.gradle.kts +++ b/passgen/diceware/build.gradle.kts @@ -10,7 +10,9 @@ plugins { android { namespace = "app.passwordstore.passgen.diceware" androidResources.enable = true - sourceSets { getByName("test") { resources.srcDir("src/main/res/raw") } } + sourceSets { getByName("test") { resources.directories.add("src/main/res/raw") } } + // AGP 9 applies packaging excludes to unit test resources too; keep the word list. + packaging.resources.excludes.remove("**/*.txt") } dependencies { diff --git a/settings.gradle.kts b/settings.gradle.kts index 04160f17..a27a0cbd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,7 +32,7 @@ pluginManagement { } } -plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } +plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) |
