Pin AGP 'debug.keystore' in the repo.
This change adds shared 'debug.keystore' into the repository and sets all the tests to use it. Fixes issue when keystore was created by AGP 7+, but then consumed in the test using lower versions of AGP. ^KT-45745 In Progress
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
## Description
|
||||
|
||||
Contains a plugin for Gradle integration tests applying different fixes
|
||||
for test that are using Android Gradle plugin.
|
||||
@@ -0,0 +1,56 @@
|
||||
import plugins.KotlinBuildPublishingPlugin
|
||||
|
||||
plugins {
|
||||
id("java-gradle-plugin")
|
||||
id("gradle-plugin-common-configuration")
|
||||
id("com.gradle.plugin-publish")
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(gradleKotlinDsl())
|
||||
compileOnly("com.android.tools.build:gradle:3.4.0")
|
||||
compileOnly("com.android.tools.build:gradle-api:3.4.0")
|
||||
compileOnly("com.android.tools.build:builder:3.4.0")
|
||||
compileOnly("com.android.tools.build:builder-model:3.4.0")
|
||||
}
|
||||
|
||||
configure<GradlePluginDevelopmentExtension> {
|
||||
isAutomatedPublishing = false
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
(plugins) {
|
||||
create("android-test-fixes") {
|
||||
id = "org.jetbrains.kotlin.test.fixes.android"
|
||||
implementationClass = "org.jetbrains.kotlin.gradle.test.fixes.android.AndroidTestFixesPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pluginBundle {
|
||||
(plugins) {
|
||||
named("android-test-fixes") {
|
||||
id = "org.jetbrains.kotlin.test.fixes.android"
|
||||
displayName = "AndroidTestFixes"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishPluginMarkers()
|
||||
|
||||
// Disable releasing for this plugin
|
||||
// It is not intended to be released publicly
|
||||
tasks.withType<PublishToMavenRepository>()
|
||||
.configureEach {
|
||||
if (name.endsWith("PublicationTo${KotlinBuildPublishingPlugin.REPOSITORY_NAME}Repository")) {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("publishPlugins") {
|
||||
enabled = false
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.test.fixes.android
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.test.fixes.android.fixes.applyDebugKeystoreFix
|
||||
|
||||
class AndroidTestFixesPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
val testFixesProperties = TestFixesProperties(target)
|
||||
target.applyDebugKeystoreFix(testFixesProperties)
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.test.fixes.android
|
||||
|
||||
import org.gradle.api.Project
|
||||
|
||||
internal class TestFixesProperties(
|
||||
private val project: Project
|
||||
) {
|
||||
val androidDebugKeystoreLocation: String
|
||||
get() = project.findProperty(ANDROID_DEBUG_KEYSTORE_LOCATION) as String? ?: throw IllegalArgumentException(
|
||||
"$ANDROID_DEBUG_KEYSTORE_LOCATION property was not found in 'gradle.properties'."
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val PROP_PREFIX = "test.fixes."
|
||||
private const val ANDROID_DEBUG_KEYSTORE_LOCATION = "${PROP_PREFIX}android.debugKeystore"
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.test.fixes.android.fixes
|
||||
|
||||
import com.android.build.gradle.AppExtension
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import com.android.build.gradle.TestExtension
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.jetbrains.kotlin.gradle.test.fixes.android.TestFixesProperties
|
||||
|
||||
/**
|
||||
* AGP 7+ creates a keystore that is not compatible with lover versions of AGP,
|
||||
* but could consume keystores created by them.
|
||||
*
|
||||
* With this fix 'debug.keystore' could be checked in into the repo and shared
|
||||
* between test executions.
|
||||
*/
|
||||
internal fun Project.applyDebugKeystoreFix(
|
||||
testFixesProperties: TestFixesProperties
|
||||
) {
|
||||
plugins.withId("com.android.application", fix<AppExtension>(testFixesProperties))
|
||||
plugins.withId("com.android.test", fix<TestExtension>(testFixesProperties))
|
||||
}
|
||||
|
||||
private inline fun <reified AndroidExtension : BaseExtension> Project.fix(
|
||||
testFixesProperties: TestFixesProperties
|
||||
): Action<Plugin<*>> = Action {
|
||||
extensions.configure<AndroidExtension> {
|
||||
logger.info("Reconfiguring Android debug keystore")
|
||||
buildTypes.named("debug") {
|
||||
it.signingConfig?.storeFile = file(testFixesProperties.androidDebugKeystoreLocation)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,43 @@ fun someTest(
|
||||
}
|
||||
```
|
||||
|
||||
##### Common test fixes
|
||||
|
||||
Test infrastructure adds following common fixes to all test projects:
|
||||
- applies 'org.jetbrains.kotlin.test.fixes.android' [plugin](../gradle/android-test-fixes/Readme.md). If you are using custom `settings.gradle`
|
||||
or `settings.gradle.kts` content in the test project, you need to add this plugin into `pluginManagement`:
|
||||
<details open>
|
||||
<summary>Kotlin script</summary>
|
||||
|
||||
```kotlin
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
val test_fixes_version: String by settings
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.test.fixes.android") version test_fixes_version
|
||||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
<details>
|
||||
<summary>Groovy</summary>
|
||||
|
||||
```groovy
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version $test_fixes_version
|
||||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
##### Deprecated tests setup
|
||||
|
||||
When you create a new test, figure out which Gradle versions it is supposed to run on. Then, when you instantiate a test project, specify one of:
|
||||
|
||||
@@ -288,6 +288,7 @@ tasks.withType<Test> {
|
||||
|
||||
dependsOn(":kotlin-gradle-plugin:validatePlugins")
|
||||
dependsOnKotlinGradlePluginInstall()
|
||||
dependsOn(":gradle:android-test-fixes:install")
|
||||
dependsOn(":examples:annotation-processor-example:install")
|
||||
|
||||
systemProperty("kotlinVersion", rootProject.extra["kotlinVersion"] as String)
|
||||
|
||||
+4
-1
@@ -14,6 +14,7 @@ import org.intellij.lang.annotations.Language
|
||||
import org.jetbrains.kotlin.gradle.model.ModelContainer
|
||||
import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
|
||||
import org.jetbrains.kotlin.gradle.testbase.applyAndroidTestFixes
|
||||
import org.jetbrains.kotlin.gradle.testbase.enableCacheRedirector
|
||||
import org.jetbrains.kotlin.gradle.testbase.extractJavaCompiledSources
|
||||
import org.jetbrains.kotlin.gradle.testbase.extractKotlinCompiledSources
|
||||
@@ -302,6 +303,7 @@ abstract class BaseGradleIT {
|
||||
projectDir.toPath().apply {
|
||||
addPluginManagementToSettings()
|
||||
if (enableCacheRedirector) enableCacheRedirector()
|
||||
applyAndroidTestFixes()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,7 +447,7 @@ abstract class BaseGradleIT {
|
||||
}
|
||||
|
||||
val options = defaultBuildOptions()
|
||||
val arguments = mutableListOf("-Pkotlin_version=${options.kotlinVersion}")
|
||||
val arguments = mutableListOf("-Pkotlin_version=${options.kotlinVersion}", "-Ptest_fixes_version=$KOTLIN_VERSION")
|
||||
options.androidGradlePluginVersion?.let { arguments.add("-Pandroid_tools_version=$it") }
|
||||
val env = createEnvironmentVariablesMap(options)
|
||||
val wrapperVersion = chooseWrapperVersionOrFinishTest()
|
||||
@@ -874,6 +876,7 @@ Finished executing task ':$taskName'|
|
||||
}
|
||||
|
||||
add("-Pkotlin_version=" + options.kotlinVersion)
|
||||
add("-Ptest_fixes_version=$KOTLIN_VERSION")
|
||||
options.incremental?.let {
|
||||
add("-Pkotlin.incremental=$it")
|
||||
}
|
||||
|
||||
+1
@@ -1551,6 +1551,7 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
.replace(": ", " = ")
|
||||
.replace("def ", " val ")
|
||||
.replace("new File(cacheRedirectorFile)", "File(cacheRedirectorFile)")
|
||||
.replace("id \"org.jetbrains.kotlin.test.fixes.android\"", "id(\"org.jetbrains.kotlin.test.fixes.android\")")
|
||||
}
|
||||
renameTo(projectDir.resolve("app/build.gradle.kts"))
|
||||
}
|
||||
|
||||
+1
@@ -109,6 +109,7 @@ data class BuildOptions(
|
||||
if (androidVersion != null) {
|
||||
arguments.add("-Pandroid_tools_version=${androidVersion}")
|
||||
}
|
||||
arguments.add("-Ptest_fixes_version=${TestVersions.Kotlin.CURRENT}")
|
||||
|
||||
return arguments.toList()
|
||||
}
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.testbase
|
||||
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.exists
|
||||
|
||||
internal fun Path.applyAndroidTestFixes() {
|
||||
// Path relative to the current gradle module project dir
|
||||
val keystoreFile = Paths.get("src/test/resources/common/debug.keystore")
|
||||
assert(keystoreFile.exists()) {
|
||||
"Common 'debug.keystore' file does not exists in ${keystoreFile.toAbsolutePath()} location!"
|
||||
}
|
||||
resolve("gradle.properties").append(
|
||||
"""
|
||||
|test.fixes.android.debugKeystore=${keystoreFile.toAbsolutePath().toString().normalizePath()}
|
||||
|
|
||||
""".trimMargin()
|
||||
)
|
||||
|
||||
val pathFile = toFile()
|
||||
|
||||
pathFile.walkTopDown()
|
||||
.filter { it.name == "build.gradle" || it.name == "build.gradle.kts" }
|
||||
.forEach { file ->
|
||||
when (file.name) {
|
||||
"build.gradle" -> file.updateBuildGradle()
|
||||
"build.gradle.kts" -> file.updateBuildGradleKts()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.updateBuildGradle() {
|
||||
modify {
|
||||
if (it.contains("plugins {")) {
|
||||
"""
|
||||
|${it.substringBefore("plugins {")}
|
||||
|plugins {
|
||||
| id "org.jetbrains.kotlin.test.fixes.android"
|
||||
|${it.substringAfter("plugins {")}
|
||||
""".trimMargin()
|
||||
} else if (it.contains("apply plugin:")) {
|
||||
it.modifyBuildScript().run {
|
||||
"""
|
||||
|${substringBefore("apply plugin:")}
|
||||
|apply plugin: 'org.jetbrains.kotlin.test.fixes.android'
|
||||
|apply plugin:${substringAfter("apply plugin:")}
|
||||
""".trimMargin()
|
||||
}
|
||||
} else {
|
||||
it.modifyBuildScript()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.modifyBuildScript(isKts: Boolean = false): String =
|
||||
if (contains("buildscript {") &&
|
||||
contains("classpath")
|
||||
) {
|
||||
val kotlinVersionStr = if (isKts) "${'$'}{property(\"test_fixes_version\")}" else "${'$'}test_fixes_version"
|
||||
"""
|
||||
|${substringBefore("classpath")}
|
||||
|classpath("org.jetbrains.kotlin:android-test-fixes:$kotlinVersionStr")
|
||||
|classpath${substringAfter("classpath")}
|
||||
""".trimMargin()
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
|
||||
private fun File.updateBuildGradleKts() {
|
||||
modify {
|
||||
if (it.contains("plugins {")) {
|
||||
"""
|
||||
|${it.substringBefore("plugins {")}
|
||||
|plugins {
|
||||
| id("org.jetbrains.kotlin.test.fixes.android")
|
||||
|${it.substringAfter("plugins {")}
|
||||
""".trimMargin()
|
||||
} else if (it.contains("apply(plugin")) {
|
||||
it.modifyBuildScript(true).run {
|
||||
"""
|
||||
|${substringBefore("apply(plugin")}
|
||||
|apply(plugin = "org.jetbrains.kotlin.test.fixes.android")
|
||||
|apply(plugin${substringAfter("apply(plugin")}
|
||||
""".trimMargin()
|
||||
}
|
||||
} else {
|
||||
it.modifyBuildScript(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -26,7 +26,7 @@ internal val DEFAULT_GROOVY_SETTINGS_FILE =
|
||||
id "org.jetbrains.kotlin.multiplatform" version "${'$'}kotlin_version"
|
||||
id "org.jetbrains.kotlin.multiplatform.pm20" version "${'$'}kotlin_version"
|
||||
id "org.jetbrains.kotlin.plugin.allopen" version "${'$'}kotlin_version"
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version "${'$'}kotlin_version"
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version "${'$'}test_fixes_version"
|
||||
}
|
||||
|
||||
resolutionStrategy {
|
||||
@@ -67,6 +67,7 @@ internal val DEFAULT_KOTLIN_SETTINGS_FILE =
|
||||
|
||||
val kotlin_version: String by settings
|
||||
val android_tools_version: String by settings
|
||||
val test_fixes_version: String by settings
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.jvm") version kotlin_version
|
||||
id("org.jetbrains.kotlin.kapt") version kotlin_version
|
||||
@@ -75,7 +76,7 @@ internal val DEFAULT_KOTLIN_SETTINGS_FILE =
|
||||
id("org.jetbrains.kotlin.multiplatform") version kotlin_version
|
||||
id("org.jetbrains.kotlin.multiplatform.pm20") version kotlin_version
|
||||
id("org.jetbrains.kotlin.plugin.allopen") version kotlin_version
|
||||
id("org.jetbrains.kotlin.test.fixes.android") version kotlin_version
|
||||
id("org.jetbrains.kotlin.test.fixes.android") version test_fixes_version
|
||||
}
|
||||
|
||||
resolutionStrategy {
|
||||
|
||||
+5
@@ -318,6 +318,10 @@ internal fun Path.addPluginManagementToSettings() {
|
||||
}
|
||||
else -> settingsGradle.toFile().writeText(DEFAULT_GROOVY_SETTINGS_FILE)
|
||||
}
|
||||
|
||||
if (Files.exists(resolve("buildSrc"))) {
|
||||
resolve("buildSrc").addPluginManagementToSettings()
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestProject.agreeToBuildScanService() {
|
||||
@@ -365,6 +369,7 @@ internal fun Path.enableAndroidSdk() {
|
||||
""".trimIndent()
|
||||
)
|
||||
acceptAndroidSdkLicenses(androidSdk)
|
||||
applyAndroidTestFixes()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPathApi::class)
|
||||
|
||||
BIN
Binary file not shown.
+1
@@ -7,5 +7,6 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm" version "$kotlin_version"
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version "$test_fixes_version"
|
||||
}
|
||||
}
|
||||
+7
-3
@@ -1,10 +1,14 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
|
||||
mavenLocal()
|
||||
|
||||
mavenCentral()
|
||||
google()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.js" version "$kotlin_version"
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version "$test_fixes_version"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -1,6 +1,11 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
val test_fixes_version: String by settings
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.test.fixes.android") version test_fixes_version
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
apply plugin: 'kotlin'
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
|
||||
+4
-2
@@ -1,10 +1,12 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
|
||||
dependencies {
|
||||
runtimeOnly "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version "$test_fixes_version"
|
||||
}
|
||||
}
|
||||
+2
@@ -5,6 +5,8 @@ pluginManagement {
|
||||
}
|
||||
plugins {
|
||||
val kotlin_version: String by settings
|
||||
val test_fixes_version: String by settings
|
||||
kotlin("multiplatform").version(kotlin_version)
|
||||
id("org.jetbrains.kotlin.test.fixes.android") version test_fixes_version
|
||||
}
|
||||
}
|
||||
+13
@@ -1 +1,14 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version "$test_fixes_version"
|
||||
}
|
||||
}
|
||||
|
||||
include 'projA', 'projB'
|
||||
|
||||
+4
@@ -3,4 +3,8 @@ pluginManagement {
|
||||
maven { url '<mavenLocalUrl>' }
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version "$test_fixes_version"
|
||||
}
|
||||
}
|
||||
+4
@@ -3,4 +3,8 @@ pluginManagement {
|
||||
maven { url '<mavenLocalUrl>' }
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version "$test_fixes_version"
|
||||
}
|
||||
}
|
||||
+4
@@ -3,6 +3,10 @@ pluginManagement {
|
||||
maven { url '<mavenLocalUrl>' }
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.test.fixes.android" version "$test_fixes_version"
|
||||
}
|
||||
}
|
||||
|
||||
include 'subproject'
|
||||
@@ -234,6 +234,7 @@ include ":benchmarks",
|
||||
":kotlin-gradle-plugin-model",
|
||||
":kotlin-gradle-plugin-test-utils-embeddable",
|
||||
":kotlin-gradle-plugin-integration-tests",
|
||||
":gradle:android-test-fixes",
|
||||
":kotlin-tooling-metadata",
|
||||
":kotlin-allopen",
|
||||
":kotlin-noarg",
|
||||
@@ -616,6 +617,7 @@ project(':kotlin-gradle-plugin').projectDir = "$rootDir/libraries/tools/kotlin-g
|
||||
project(':kotlin-gradle-plugin-model').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-model" as File
|
||||
project(':kotlin-gradle-plugin-test-utils-embeddable').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-test-utils-embeddable" as File
|
||||
project(':kotlin-gradle-plugin-integration-tests').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-integration-tests" as File
|
||||
project(':gradle:android-test-fixes').projectDir = "$rootDir/libraries/tools/gradle/android-test-fixes" as File
|
||||
project(':kotlin-tooling-metadata').projectDir = "$rootDir/libraries/tools/kotlin-tooling-metadata" as File
|
||||
project(':kotlin-allopen').projectDir = "$rootDir/libraries/tools/kotlin-allopen" as File
|
||||
project(':kotlin-noarg').projectDir = "$rootDir/libraries/tools/kotlin-noarg" as File
|
||||
|
||||
Reference in New Issue
Block a user