diff options
-rw-r--r-- | app/build.gradle.kts | 91 | ||||
-rw-r--r-- | autofill-parser/build.gradle.kts | 10 | ||||
-rw-r--r-- | build.gradle.kts | 22 | ||||
-rw-r--r-- | buildSrc/build.gradle.kts | 20 | ||||
-rw-r--r-- | buildSrc/buildSrc/build.gradle.kts | 17 | ||||
-rw-r--r-- | buildSrc/settings.gradle.kts | 7 | ||||
-rw-r--r-- | buildSrc/src/main/java/BaseProjectConfig.kt | 6 | ||||
-rw-r--r-- | buildSrc/src/main/java/Dependencies.kt | 94 | ||||
-rw-r--r-- | gradle/libs.versions.toml | 74 | ||||
-rw-r--r-- | gradle/wrapper/gradle-wrapper.properties | 9 | ||||
-rw-r--r-- | openpgp-ktx/build.gradle.kts | 2 | ||||
-rw-r--r-- | settings.gradle.kts | 3 |
12 files changed, 147 insertions, 208 deletions
diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 401b29a5..5901b7f3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -42,64 +42,57 @@ android { } dependencies { - compileOnly(Dependencies.AndroidX.annotation) - implementation(project(":autofill-parser")) - implementation(project(":openpgp-ktx")) - implementation(Dependencies.AndroidX.activity_ktx) - implementation(Dependencies.AndroidX.appcompat) - implementation(Dependencies.AndroidX.autofill) - implementation(Dependencies.AndroidX.biometric_ktx) - implementation(Dependencies.AndroidX.constraint_layout) - implementation(Dependencies.AndroidX.core_ktx) - implementation(Dependencies.AndroidX.documentfile) - implementation(Dependencies.AndroidX.fragment_ktx) - implementation(Dependencies.AndroidX.lifecycle_common) - implementation(Dependencies.AndroidX.lifecycle_livedata_ktx) - implementation(Dependencies.AndroidX.lifecycle_viewmodel_ktx) - implementation(Dependencies.AndroidX.material) - implementation(Dependencies.AndroidX.preference) - implementation(Dependencies.AndroidX.recycler_view) - implementation(Dependencies.AndroidX.recycler_view_selection) - implementation(Dependencies.AndroidX.security) - implementation(Dependencies.AndroidX.swiperefreshlayout) + compileOnly(libs.androidx.annotation) + implementation(projects.autofillParser) + implementation(projects.openpgpKtx) + implementation(libs.androidx.activityKtx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.autofill) + implementation(libs.androidx.biometricKtx) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.coreKtx) + implementation(libs.androidx.documentfile) + implementation(libs.androidx.fragmentKtx) + implementation(libs.bundles.androidxLifecycle) + implementation(libs.androidx.material) + implementation(libs.androidx.preference) + implementation(libs.androidx.recyclerview) + implementation(libs.androidx.recyclerviewSelection) + implementation(libs.androidx.security) + implementation(libs.androidx.swiperefreshlayout) - implementation(Dependencies.Kotlin.Coroutines.android) - implementation(Dependencies.Kotlin.Coroutines.core) + implementation(libs.kotlin.coroutines.android) + implementation(libs.kotlin.coroutines.core) - implementation(Dependencies.FirstParty.sublime_fuzzy) - implementation(Dependencies.FirstParty.zxing_android_embedded) + implementation(libs.aps.sublimeFuzzy) + implementation(libs.aps.zxingAndroidEmbedded) - implementation(Dependencies.ThirdParty.bouncycastle) - implementation(Dependencies.ThirdParty.commons_codec) - implementation(Dependencies.ThirdParty.eddsa) - implementation(Dependencies.ThirdParty.fastscroll) - implementation(Dependencies.ThirdParty.jgit) { + implementation(libs.thirdparty.bouncycastle) + implementation(libs.thirdparty.commons.codec) + implementation(libs.thirdparty.eddsa) + implementation(libs.thirdparty.fastscroll) + implementation(libs.thirdparty.jgit) { exclude(group = "org.apache.httpcomponents", module = "httpclient") } - implementation(Dependencies.ThirdParty.kotlin_result) - implementation(Dependencies.ThirdParty.modern_android_prefs) - implementation(Dependencies.ThirdParty.plumber) - implementation(Dependencies.ThirdParty.ssh_auth) - implementation(Dependencies.ThirdParty.sshj) - implementation(Dependencies.ThirdParty.timber) - implementation(Dependencies.ThirdParty.timberkt) + implementation(libs.thirdparty.kotlinResult) + implementation(libs.thirdparty.modernAndroidPrefs) + implementation(libs.thirdparty.plumber) + implementation(libs.thirdparty.sshauth) + implementation(libs.thirdparty.sshj) + implementation(libs.thirdparty.timber) + implementation(libs.thirdparty.timberkt) if (isSnapshot()) { - implementation(Dependencies.ThirdParty.leakcanary) - implementation(Dependencies.ThirdParty.whatthestack) + implementation(libs.thirdparty.leakcanary) + implementation(libs.thirdparty.whatthestack) } else { - debugImplementation(Dependencies.ThirdParty.leakcanary) - debugImplementation(Dependencies.ThirdParty.whatthestack) + debugImplementation(libs.thirdparty.leakcanary) + debugImplementation(libs.thirdparty.whatthestack) } - "nonFreeImplementation"(Dependencies.NonFree.google_play_auth_api_phone) + "nonFreeImplementation"(libs.thirdparty.nonfree.googlePlayAuthApiPhone) - // Testing-only dependencies - androidTestImplementation(Dependencies.Testing.junit) - androidTestImplementation(Dependencies.Testing.kotlin_test_junit) - androidTestImplementation(Dependencies.Testing.AndroidX.runner) - androidTestImplementation(Dependencies.Testing.AndroidX.rules) - - testImplementation(Dependencies.Testing.junit) - testImplementation(Dependencies.Testing.kotlin_test_junit) + androidTestImplementation(libs.bundles.testDependencies) + androidTestImplementation(libs.bundles.androidTestDependencies) + testImplementation(libs.bundles.testDependencies) } diff --git a/autofill-parser/build.gradle.kts b/autofill-parser/build.gradle.kts index da6dcc4d..928e3828 100644 --- a/autofill-parser/build.gradle.kts +++ b/autofill-parser/build.gradle.kts @@ -23,9 +23,9 @@ android { } dependencies { - compileOnly(Dependencies.AndroidX.annotation) - implementation(Dependencies.AndroidX.autofill) - implementation(Dependencies.Kotlin.Coroutines.android) - implementation(Dependencies.Kotlin.Coroutines.core) - implementation(Dependencies.ThirdParty.timberkt) + compileOnly(libs.androidx.annotation) + implementation(libs.androidx.autofill) + implementation(libs.kotlin.coroutines.android) + implementation(libs.kotlin.coroutines.core) + implementation(libs.thirdparty.timberkt) } diff --git a/build.gradle.kts b/build.gradle.kts index 300a1256..0718d817 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,26 +7,4 @@ plugins { `aps-plugin` } -buildscript { dependencies { classpath(Plugins.ktfmtGradlePlugin) } } - allprojects { apply(plugin = "com.ncorti.ktfmt.gradle") } - -subprojects { - // Gradle's automatic resolution fails to handle AndroidX annotation for - // some reason so here we simply hack it up to use the correct version manually. - val annotationParts = Dependencies.AndroidX.annotation.split(":") - val annotationGroup = annotationParts[0] - val annotationModule = annotationParts[1] - val annotationVersion = annotationParts[2] - configurations.all { - resolutionStrategy.dependencySubstitution { - substitute(module("org.jetbrains.trove4j:trove4j:20160824")) - .using(module("org.jetbrains.intellij.deps:trove4j:1.0.20200330")) - } - resolutionStrategy.eachDependency { - if (requested.group == annotationGroup && requested.name == annotationModule) { - useVersion(annotationVersion) - } - } - } -} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 685ab88b..05fc2284 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -5,7 +5,7 @@ plugins { `kotlin-dsl` - id("com.ncorti.ktfmt.gradle") version "0.4.0" + id("com.ncorti.ktfmt.gradle") version "0.5.0" } repositories { @@ -21,8 +21,6 @@ ktfmt { maxWidth.set(120) } -kotlinDslPluginOptions { experimentalWarning.set(false) } - gradlePlugin { plugins { register("aps") { @@ -41,12 +39,12 @@ gradlePlugin { } dependencies { - implementation(Plugins.androidGradlePlugin) - implementation(Plugins.binaryCompatibilityValidator) - implementation(Plugins.dokkaPlugin) - implementation(Plugins.downloadTaskPlugin) - implementation(Plugins.kotlinGradlePlugin) - implementation(Plugins.ktfmtGradlePlugin) - implementation(Plugins.mavenPublishPlugin) - implementation(Plugins.semver4j) + implementation(libs.androidGradlePlugin) + implementation(libs.binaryCompatibilityValidator) + implementation(libs.dokkaPlugin) + implementation(libs.downloadTaskPlugin) + implementation(libs.kotlinGradlePlugin) + implementation(libs.ktfmtGradlePlugin) + implementation(libs.mavenPublishPlugin) + implementation(libs.semver4j) } diff --git a/buildSrc/buildSrc/build.gradle.kts b/buildSrc/buildSrc/build.gradle.kts deleted file mode 100644 index f4bc8a7e..00000000 --- a/buildSrc/buildSrc/build.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -plugins { `kotlin-dsl` } - -repositories { - mavenCentral() - google() - gradlePluginPortal() -} - -kotlinDslPluginOptions { experimentalWarning.set(false) } - -// force compilation of Dependencies.kt so it can be referenced in buildSrc/build.gradle.kts -sourceSets.main { - java { - setSrcDirs(setOf(projectDir.parentFile.resolve("src/main/java"))) - include("Dependencies.kt") - } -} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 00000000..215a5d58 --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,7 @@ +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/buildSrc/src/main/java/BaseProjectConfig.kt b/buildSrc/src/main/java/BaseProjectConfig.kt index 45465c3d..f212bc61 100644 --- a/buildSrc/src/main/java/BaseProjectConfig.kt +++ b/buildSrc/src/main/java/BaseProjectConfig.kt @@ -24,9 +24,9 @@ internal fun Project.configureForRootProject() { // register task for cleaning the build directory in the root project tasks.register("clean", Delete::class.java) { delete(rootProject.buildDir) } tasks.withType<Wrapper> { - gradleVersion = "6.8.3" + gradleVersion = "7.0" distributionType = Wrapper.DistributionType.ALL - distributionSha256Sum = "9af5c8e7e2cd1a3b0f694a4ac262b9f38c75262e74a9e8b5101af302a6beadd7" + distributionSha256Sum = "81003f83b0056d20eedf48cddd4f52a9813163d4ba185bcf8abd34b8eeea4cbd" } configureBinaryCompatibilityValidator() } @@ -40,6 +40,8 @@ internal fun Project.configureForAllProjects() { content { // https://github.com/zhanghai/AndroidFastScroll/issues/35 includeModule("me.zhanghai.android.fastscroll", "library") + + includeModule("org.jetbrains.kotlinx", "kotlinx-html-jvm") } } maven("https://jitpack.io") { diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt deleted file mode 100644 index b20653a0..00000000 --- a/buildSrc/src/main/java/Dependencies.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -private const val KOTLIN_VERSION = "1.4.32" - -object Plugins { - const val androidGradlePlugin = "com.android.tools.build:gradle:4.1.3" - const val binaryCompatibilityValidator = "org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4" - const val dokkaPlugin = "org.jetbrains.dokka:dokka-gradle-plugin:1.4.30" - const val downloadTaskPlugin = "de.undercouch:gradle-download-task:4.1.1" - const val kotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION" - const val ktfmtGradlePlugin = "com.ncorti.ktfmt.gradle:plugin:0.5.0" - const val mavenPublishPlugin = "com.vanniktech:gradle-maven-publish-plugin:0.13.0" - const val semver4j = "com.vdurmont:semver4j:3.1.0" -} - -object Dependencies { - object Kotlin { - object Coroutines { - - private const val version = "1.4.3" - const val android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version" - const val core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version" - } - } - - object AndroidX { - - private const val lifecycleVersion = "2.4.0-alpha01" - - const val activity_ktx = "androidx.activity:activity-ktx:1.3.0-alpha06" - const val annotation = "androidx.annotation:annotation:1.2.0" - const val autofill = "androidx.autofill:autofill:1.2.0-alpha01" - const val appcompat = "androidx.appcompat:appcompat:1.3.0-rc01" - const val biometric_ktx = "androidx.biometric:biometric-ktx:1.2.0-alpha03" - const val constraint_layout = "androidx.constraintlayout:constraintlayout:2.1.0-beta01" - const val core_ktx = "androidx.core:core-ktx:1.6.0-alpha01" - const val documentfile = "androidx.documentfile:documentfile:1.0.1" - const val fragment_ktx = "androidx.fragment:fragment-ktx:1.3.2" - const val lifecycle_common = "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion" - const val lifecycle_livedata_ktx = "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion" - const val lifecycle_viewmodel_ktx = "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion" - const val material = "com.google.android.material:material:1.4.0-alpha02" - const val preference = "androidx.preference:preference:1.1.1" - const val recycler_view = "androidx.recyclerview:recyclerview:1.2.0" - const val recycler_view_selection = "androidx.recyclerview:recyclerview-selection:1.1.0" - const val security = "androidx.security:security-crypto:1.1.0-alpha03" - const val swiperefreshlayout = "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01" - } - - object FirstParty { - - const val sublime_fuzzy = "com.github.android-password-store:sublime-fuzzy:1.0.0" - const val zxing_android_embedded = "com.github.android-password-store:zxing-android-embedded:4.1.0-aps" - } - - object ThirdParty { - - const val bouncycastle = "org.bouncycastle:bcprov-jdk15on:1.68" - const val commons_codec = "commons-codec:commons-codec:1.14" - const val eddsa = "net.i2p.crypto:eddsa:0.3.0" - const val fastscroll = "me.zhanghai.android.fastscroll:library:1.1.5" - const val jgit = "org.eclipse.jgit:org.eclipse.jgit:3.7.1.201504261725-r" - const val kotlin_result = "com.michael-bull.kotlin-result:kotlin-result:1.1.11" - const val leakcanary = "com.squareup.leakcanary:leakcanary-android:2.7" - const val modern_android_prefs = "de.maxr1998:modernandroidpreferences:2.0" - const val plumber = "com.squareup.leakcanary:plumber-android:2.7" - const val sshj = "com.hierynomus:sshj:0.31.0" - const val ssh_auth = "com.github.open-keychain.open-keychain:sshauthentication-api:v5.7.1" - const val timber = "com.jakewharton.timber:timber:4.7.1" - const val timberkt = "com.github.ajalt:timberkt:1.5.1" - const val whatthestack = "com.github.haroldadmin:WhatTheStack:0.3.0" - } - - object NonFree { - - const val google_play_auth_api_phone = "com.google.android.gms:play-services-auth-api-phone:17.5.0" - } - - object Testing { - - const val junit = "junit:junit:4.13.2" - const val kotlin_test_junit = "org.jetbrains.kotlin:kotlin-test-junit:$KOTLIN_VERSION" - - object AndroidX { - - private const val version = "1.4.0-alpha05" - const val runner = "androidx.test:runner:$version" - const val rules = "androidx.test:rules:$version" - } - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..28198ae7 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,74 @@ +# Centralized versions for dependencies that share versions +[versions] +androidx_test = "1.4.0-alpha05" +coroutines = "1.4.3" +kotlin = "1.4.32" +lifecycle = "2.4.0-alpha01" + +[libraries] +# buildSrc dependencies +androidGradlePlugin = "com.android.tools.build:gradle:4.1.3" +binaryCompatibilityValidator = "org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4" +dokkaPlugin = "org.jetbrains.dokka:dokka-gradle-plugin:1.4.30" +downloadTaskPlugin = "de.undercouch:gradle-download-task:4.1.1" +kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +ktfmtGradlePlugin = "com.ncorti.ktfmt.gradle:plugin:0.5.0" +mavenPublishPlugin = "com.vanniktech:gradle-maven-publish-plugin:0.13.0" +semver4j = "com.vdurmont:semver4j:3.1.0" + +# Kotlin dependencies +kotlin-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" } +kotlin-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } + +# AndroidX dependencies +androidx-activityKtx = "androidx.activity:activity-ktx:1.3.0-alpha06" +androidx-annotation = "androidx.annotation:annotation:1.1.0" +androidx-autofill = "androidx.autofill:autofill:1.2.0-alpha01" +androidx-appcompat = "androidx.appcompat:appcompat:1.3.0-rc01" +androidx-biometricKtx = "androidx.biometric:biometric-ktx:1.2.0-alpha03" +androidx-constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.0-beta01" +androidx-coreKtx = "androidx.core:core-ktx:1.6.0-alpha01" +androidx-documentfile = "androidx.documentfile:documentfile:1.0.1" +androidx-fragmentKtx = "androidx.fragment:fragment-ktx:1.3.2" +androidx-lifecycle_common = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref="lifecycle" } +androidx-lifecycle_livedataKtx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref="lifecycle" } +androidx-lifecycle_viewmodelKtx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref="lifecycle" } +androidx-material = "com.google.android.material:material:1.4.0-alpha02" +androidx-preference = "androidx.preference:preference:1.1.1" +androidx-recyclerview = "androidx.recyclerview:recyclerview:1.2.0" +androidx-recyclerviewSelection = "androidx.recyclerview:recyclerview-selection:1.1.0" +androidx-security = "androidx.security:security-crypto:1.1.0-alpha03" +androidx-swiperefreshlayout = "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01" + +# First-party libraries +aps-sublimeFuzzy = "com.github.android-password-store:sublime-fuzzy:1.0.0" +aps-zxingAndroidEmbedded = "com.github.android-password-store:zxing-android-embedded:4.1.0-aps" + +# Third-party dependencies +thirdparty-bouncycastle = "org.bouncycastle:bcprov-jdk15on:1.68" +thirdparty-commons_codec = "commons-codec:commons-codec:1.14" +thirdparty-eddsa = "net.i2p.crypto:eddsa:0.3.0" +thirdparty-fastscroll = "me.zhanghai.android.fastscroll:library:1.1.5" +thirdparty-jgit = "org.eclipse.jgit:org.eclipse.jgit:3.7.1.201504261725-r" +thirdparty-kotlinResult = "com.michael-bull.kotlin-result:kotlin-result:1.1.11" +thirdparty-leakcanary = "com.squareup.leakcanary:leakcanary-android:2.7" +thirdparty-modernAndroidPrefs = "de.maxr1998:modernandroidpreferences:2.0" +thirdparty-plumber = "com.squareup.leakcanary:plumber-android:2.7" +thirdparty-sshj = "com.hierynomus:sshj:0.31.0" +thirdparty-sshauth = "com.github.open-keychain.open-keychain:sshauthentication-api:v5.7.1" +thirdparty-timber = "com.jakewharton.timber:timber:4.7.1" +thirdparty-timberkt = "com.github.ajalt:timberkt:1.5.1" +thirdparty-whatthestack = "com.github.haroldadmin:WhatTheStack:0.3.0" +thirdparty-nonfree-googlePlayAuthApiPhone = "com.google.android.gms:play-services-auth-api-phone:17.5.0" + +# Testing dependencies +testing-junit = "junit:junit:4.13.2" +testing-kotlintest-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } +androidx-testing-rules = { module = "androidx.test:rules", version.ref="androidx_test" } +androidx-testing-runner = { module = "androidx.test:runner", version.ref="androidx_test" } + +# Bundles of dependencies usually used together so they can be included in one go +[bundles] +androidxLifecycle = ["androidx-lifecycle_common", "androidx-lifecycle_livedataKtx", "androidx-lifecycle_viewmodelKtx"] +testDependencies = ["testing-junit", "testing-kotlintest-junit"] +androidTestDependencies = ["androidx-testing-rules", "androidx-testing-runner"] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7858495e..9d174797 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,11 +1,6 @@ -# -# Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. -# SPDX-License-Identifier: GPL-3.0-only -# - distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=9af5c8e7e2cd1a3b0f694a4ac262b9f38c75262e74a9e8b5101af302a6beadd7 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip +distributionSha256Sum=81003f83b0056d20eedf48cddd4f52a9813163d4ba185bcf8abd34b8eeea4cbd +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/openpgp-ktx/build.gradle.kts b/openpgp-ktx/build.gradle.kts index 6f90e6c8..90a89501 100644 --- a/openpgp-ktx/build.gradle.kts +++ b/openpgp-ktx/build.gradle.kts @@ -20,4 +20,4 @@ android { kotlinOptions { freeCompilerArgs = freeCompilerArgs + listOf("-Xexplicit-api=strict") } } -dependencies { implementation(Dependencies.Kotlin.Coroutines.core) } +dependencies { implementation(libs.kotlin.coroutines.core) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 87a9e75f..9fde0bb1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,6 +8,9 @@ include(":app") include(":openpgp-ktx") +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +enableFeaturePreview("VERSION_CATALOGS") + pluginManagement { repositories { gradlePluginPortal() |