diff --git a/plugins/devkit/devkit-core/intellij.devkit.iml b/plugins/devkit/devkit-core/intellij.devkit.iml index 2de433272b5d..a2961da857dd 100644 --- a/plugins/devkit/devkit-core/intellij.devkit.iml +++ b/plugins/devkit/devkit-core/intellij.devkit.iml @@ -6,6 +6,7 @@ + @@ -63,5 +64,6 @@ + \ No newline at end of file diff --git a/plugins/devkit/devkit-core/src/commit/KotlinPluginCommitMessageHandlerFactory.kt b/plugins/devkit/devkit-core/src/commit/KotlinPluginCommitMessageHandlerFactory.kt index 20ba6012b89b..7d9a2ede1e8a 100644 --- a/plugins/devkit/devkit-core/src/commit/KotlinPluginCommitMessageHandlerFactory.kt +++ b/plugins/devkit/devkit-core/src/commit/KotlinPluginCommitMessageHandlerFactory.kt @@ -13,26 +13,36 @@ import com.intellij.util.PairConsumer import com.intellij.util.io.systemIndependentPath import org.jetbrains.idea.devkit.DevKitBundle import org.jetbrains.idea.devkit.util.PsiUtil +import java.io.File import kotlin.io.path.extension -import kotlin.io.path.isRegularFile class KotlinPluginCommitMessageHandlerFactory : CheckinHandlerFactory() { - companion object { - private const val kotlinPluginPath = "plugins/kotlin/" - private val pathsToIgnore = setOf("/test/", "/testData/") - private val fileExtensionsNotToTrack = setOf("iml", "md") - private val commitMessageRegex = Regex(".*KTIJ-\\d+.*", RegexOption.DOT_MATCHES_ALL /* line breaks matter */) - } - override fun createHandler(panel: CheckinProjectPanel, commitContext: CommitContext): CheckinHandler = YouTrackIssueCommitMessageHandler(panel) - private class YouTrackIssueCommitMessageHandler(private val checkinPanel: CheckinProjectPanel) : CheckinHandler() { + internal class YouTrackIssueCommitMessageHandler(private val checkinPanel: CheckinProjectPanel) : CheckinHandler() { companion object { - const val HANDLER_ENABLED_KEY = "kotlin.commit.message.validation.enabled" + private const val HANDLER_ENABLED_KEY = "kotlin.commit.message.validation.enabled" + private const val KOTLIN_PLUGIN_PATH = "plugins/kotlin/" + private val pathsToIgnore = setOf("/test/", "/testData/", "/fleet/plugins/kotlin/") + private val fileExtensionsNotToTrack = setOf("iml", "md") + private val commitMessageRegex = Regex(".*KTIJ-\\d+.*", RegexOption.DOT_MATCHES_ALL /* line breaks matter */) + + internal fun selectedFilesBelongToKotlinIdePlugin(files: Collection): Boolean { + return files.asSequence() + .map { file -> file.toPath() } + .any { path -> + val siPath = path.systemIndependentPath + path.extension !in fileExtensionsNotToTrack + && siPath.contains(KOTLIN_PLUGIN_PATH) + && pathsToIgnore.none { siPath.contains(it) } + } + } + + internal fun commitMessageIsCorrect(message: String): Boolean = message.matches(commitMessageRegex) } private val project = checkinPanel.project @@ -52,7 +62,7 @@ class KotlinPluginCommitMessageHandlerFactory : CheckinHandlerFactory() { if (!messageHandlerEnabled || !commitMessageShouldBeChecked() - || checkinPanel.commitMessage.matches(commitMessageRegex) + || commitMessageIsCorrect(checkinPanel.commitMessage) ) { return ReturnResult.COMMIT // as is } @@ -73,18 +83,6 @@ class KotlinPluginCommitMessageHandlerFactory : CheckinHandlerFactory() { } private fun commitMessageShouldBeChecked(): Boolean = - PsiUtil.isIdeaProject(project) && selectedFilesBelongToKotlinIdePlugin() - - private fun selectedFilesBelongToKotlinIdePlugin(): Boolean { - return checkinPanel.files.asSequence() - .map { file -> file.toPath() } - .any { path -> - val siPath = path.systemIndependentPath - path.isRegularFile() - && path.extension !in fileExtensionsNotToTrack - && siPath.contains(kotlinPluginPath) - && pathsToIgnore.none { siPath.contains(it) } - } - } + PsiUtil.isIdeaProject(project) && selectedFilesBelongToKotlinIdePlugin(checkinPanel.files) } } \ No newline at end of file diff --git a/plugins/devkit/devkit-core/testSrc/org/jetbrains/idea/devkit/commit/KotlinYouTrackIssueCommitMessageHandlerTest.kt b/plugins/devkit/devkit-core/testSrc/org/jetbrains/idea/devkit/commit/KotlinYouTrackIssueCommitMessageHandlerTest.kt new file mode 100644 index 000000000000..aa214e49eb24 --- /dev/null +++ b/plugins/devkit/devkit-core/testSrc/org/jetbrains/idea/devkit/commit/KotlinYouTrackIssueCommitMessageHandlerTest.kt @@ -0,0 +1,276 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.idea.devkit.commit + +import org.jetbrains.idea.devkit.commit.KotlinPluginCommitMessageHandlerFactory.YouTrackIssueCommitMessageHandler +import org.junit.Test +import org.junit.experimental.runners.Enclosed +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameter +import java.io.File +import java.nio.file.Paths + +@RunWith(Enclosed::class) +class KotlinYouTrackIssueCommitMessageHandlerTest { + + @RunWith(Parameterized::class) + class FilesBelongToPlugin { + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun provideTestParameters(): Iterable>> { + return listOf( + listOf(ULTIMATE_KOTLIN_PLUGIN to "File.kt"), + listOf(COMMUNITY_KOTLIN_PLUGIN to "File.kt"), + + listOf(ULTIMATE_KOTLIN_PLUGIN to "File.kts"), + listOf(COMMUNITY_KOTLIN_PLUGIN to "File.kts"), + + listOf(ULTIMATE_KOTLIN_PLUGIN to "File.java"), + listOf(COMMUNITY_KOTLIN_PLUGIN to "File.java"), + + listOf(ULTIMATE_KOTLIN_PLUGIN to "File.properties"), + listOf(COMMUNITY_KOTLIN_PLUGIN to "File.properties"), + + listOf(ULTIMATE_KOTLIN_PLUGIN to "File.html"), + listOf(COMMUNITY_KOTLIN_PLUGIN to "File.html"), + + listOf(ULTIMATE_KOTLIN_PLUGIN to "File.xml"), + listOf(COMMUNITY_KOTLIN_PLUGIN to "File.xml"), + + listOf( + COMMUNITY_KOTLIN_PLUGIN to "File.kt", + "not-important-dir-A" to "whatever-file", + "not-important-dir-B" to "whatever-file" + ), + + listOf( + "not-important-dir-A" to "whatever-file", + COMMUNITY_KOTLIN_PLUGIN to "File.kt", + "not-important-dir-B" to "whatever-file" + ), + + listOf( + "not-important-dir-A" to "whatever-file", + "not-important-dir-B" to "whatever-file", + COMMUNITY_KOTLIN_PLUGIN to "File.kt" + ) + ) + } + } + + @Parameter + lateinit var files: List> + + @Test + fun testThatChecksForFileSet() { + val filesSet = files.map { fileAt(it.first, it.second) } + assert(YouTrackIssueCommitMessageHandler.selectedFilesBelongToKotlinIdePlugin(filesSet)) { + "The following set of files doesn't trigger the check: $filesSet" + } + } + } + + + @RunWith(Parameterized::class) + class FilesDoNotBelongToPlugin { + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun provideTestParameters(): Iterable>> { + return listOf( + // file path has no kotlin-plugin subdirectory + listOf("not-important-dir" to "File.kt"), + listOf(FLEET_KOTLIN_PLUGIN to "File.kt"), + + // kotlin-plugin path contains subdirectories to exclude + listOf("$ULTIMATE_KOTLIN_PLUGIN/test/" to "File.kt"), + listOf("$ULTIMATE_KOTLIN_PLUGIN/testData/" to "File.kt"), + + // file extensions to exclude + listOf(COMMUNITY_KOTLIN_PLUGIN to "File.iml"), + listOf(COMMUNITY_KOTLIN_PLUGIN to "File.md"), + + // multiple files, none matches + listOf( + "not-important-dir" to "File.kt", + FLEET_KOTLIN_PLUGIN to "File.kt", + "$ULTIMATE_KOTLIN_PLUGIN/test/" to "File.kt", + "$ULTIMATE_KOTLIN_PLUGIN/testData/" to "File.kt", + COMMUNITY_KOTLIN_PLUGIN to "File.iml", + COMMUNITY_KOTLIN_PLUGIN to "File.md" + ) + ) + } + } + + @Parameter + lateinit var files: List> + + @Test + fun testThatIgnoresFileSet() { + val filesSet = files.map { fileAt(it.first, it.second) } + assert(!YouTrackIssueCommitMessageHandler.selectedFilesBelongToKotlinIdePlugin(filesSet)) { + "The following set of files triggered the check: $filesSet" + } + } + } + + @RunWith(Parameterized::class) + class ValidCommitMessages { + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun provideTestParameters(): Iterable { + return listOf( + "KTIJ-123", + "(KTIJ-123)", + "[KTIJ-123]", + "{KTIJ-123}", + "'KTIJ-123'", + "`KTIJ-123`", + "\"KTIJ-123\"", + + "KTIJ-123 ", + " KTIJ-123", + " KTIJ-123 ", + + "KTIJ-123 header", + "Header KTIJ-123", + "Header KTIJ-123 header", + + """ + Header KTIJ-123 header + + Body-line-1 + Body-line-N + """.trimIndent(), + + """ + Header KTIJ-123 header + + Body-line-1 + Body-line-N + + ^KTIJ-123 fixed + """.trimIndent(), + + """ + Header + + Body-line-1 + Body-line-N + + ^KTIJ-123 fixed + """.trimIndent(), + + """ + Header + + Body-line-1 + Body-line-N + + #KTIJ-123 fixed + """.trimIndent(), + + """ + Header + + Body-line-1 + Body-line-N + + Relates to #KTIJ-123 + """.trimIndent() + ) + } + } + + @Parameter + lateinit var commitMessage: String + + @Test + fun testThatCommitMessageIsValid() { + assert(YouTrackIssueCommitMessageHandler.commitMessageIsCorrect(commitMessage)) { + "The following commit message was considered invalid: $commitMessage" + } + } + } + + @RunWith(Parameterized::class) + class InvalidCommitMessages { + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun provideTestParameters(): Iterable { + return listOf( + "", + "KTIJ", + "KTIJ-", + "KTIJ-one", + "KT-123", + "IDEA-123", + "WHATEVER-123", + "Header KTIJ", + "Header KTIJ header", + + """ + Header + + Body-line-1 + Body-line-N + """.trimIndent(), + + """ + Header + + Body-line-1 + Body-line-N + + ^IDEA-123 fixed + """.trimIndent(), + + """ + Header + + Body-line-1 + Body-line-N + + #IDEA-123 fixed + """.trimIndent(), + + """ + Header + + Body-line-1 + Body-line-N + + Relates to #IDEA-123 + """.trimIndent(), + ) + } + } + + @Parameter + lateinit var commitMessage: String + + @Test + fun testThatCommitMessageIsValid() { + assert(!YouTrackIssueCommitMessageHandler.commitMessageIsCorrect(commitMessage)) { + "The following commit message was considered as valid: $commitMessage" + } + } + } +} + + +val tempDir: String = System.getProperty("java.io.tmpdir") +const val ULTIMATE_KOTLIN_PLUGIN = "plugins/kotlin/package/" +const val COMMUNITY_KOTLIN_PLUGIN = "community/plugins/kotlin/package/" +const val FLEET_KOTLIN_PLUGIN = "fleet/plugins/kotlin/package/" + + +private fun fileAt(dirPath: String, fileName: String): File { + return Paths.get(tempDir, *dirPath.split("/").toTypedArray(), fileName).toFile() +} \ No newline at end of file