diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1a68bc2..bd82e1a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -117,6 +117,10 @@ jobs:
with:
gradle-home-cache-cleanup: true
+ # Set git config variables
+ - name: Set Git Config Variables
+ run: git config --global user.name 'Nikita Velikiy'
+
# Run tests
- name: Run Tests
run: ./gradlew check
diff --git a/.gitignore b/.gitignore
index e2e5d94..0fc1d59 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
.idea
.qodana
build
+src/test/testData/hooks
+out
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0cc4182..cd3d9c2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,5 +3,7 @@
# GitGlobalHooksLocator Changelog
## [Unreleased]
-### Added
-- Initial scaffold created from [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template)
+
+## [0.0.1]
+
+- Initial release
diff --git a/README.md b/README.md
index 70615bc..7cafeaf 100644
--- a/README.md
+++ b/README.md
@@ -4,24 +4,11 @@
[](https://plugins.jetbrains.com/plugin/PLUGIN_ID)
[](https://plugins.jetbrains.com/plugin/PLUGIN_ID)
-## Template ToDo list
-- [x] Create a new [IntelliJ Platform Plugin Template][template] project.
-- [ ] Get familiar with the [template documentation][template].
-- [ ] Adjust the [pluginGroup](./gradle.properties), [plugin ID](./src/main/resources/META-INF/plugin.xml) and [sources package](./src/main/kotlin).
-- [ ] Adjust the plugin description in `README` (see [Tips][docs:plugin-description])
-- [ ] Review the [Legal Agreements](https://plugins.jetbrains.com/docs/marketplace/legal-agreements.html?from=IJPluginTemplate).
-- [ ] [Publish a plugin manually](https://plugins.jetbrains.com/docs/intellij/publishing-plugin.html?from=IJPluginTemplate) for the first time.
-- [ ] Set the `PLUGIN_ID` in the above README badges.
-- [ ] Set the [Plugin Signing](https://plugins.jetbrains.com/docs/intellij/plugin-signing.html?from=IJPluginTemplate) related [secrets](https://github.com/JetBrains/intellij-platform-plugin-template#environment-variables).
-- [ ] Set the [Deployment Token](https://plugins.jetbrains.com/docs/marketplace/plugin-upload.html?from=IJPluginTemplate).
-- [ ] Click the Watch button on the top of the [IntelliJ Platform Plugin Template][template] to be notified about releases containing new features and fixes.
+## Description
-This Fancy IntelliJ Platform Plugin is going to be your implementation of the brilliant ideas that you have.
-
-This specific section is a source for the [plugin.xml](/src/main/resources/META-INF/plugin.xml) file which will be extracted by the [Gradle](/build.gradle.kts) during the build process.
-
-To keep everything working, do not remove `` sections.
+Intellij plugin to enable or disable global Git hooks
+
## Installation
@@ -34,11 +21,4 @@ To keep everything working, do not remove `` sections.
- Manually:
Download the [latest release](https://github.com/y0ung3r/GitGlobalHooksLocator/releases/latest) and install it manually using
- Settings/Preferences > Plugins > ⚙️ > Install plugin from disk...
-
-
----
-Plugin based on the [IntelliJ Platform Plugin Template][template].
-
-[template]: https://github.com/JetBrains/intellij-platform-plugin-template
-[docs:plugin-description]: https://plugins.jetbrains.com/docs/intellij/plugin-user-experience.html#plugin-description-and-presentation
+ Settings/Preferences > Plugins > ⚙️ > Install plugin from disk...
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 2fdbdf8..9782186 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -8,7 +8,6 @@ pluginVersion = 0.0.1
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 223
-pluginUntilBuild = 233.*
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
platformType = IC
@@ -19,7 +18,7 @@ platformVersion = 2022.3.3
platformPlugins =
# Gradle Releases -> https://github.com/gradle/gradle/releases
-gradleVersion = 8.4
+gradleVersion = 9.0
# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib
kotlin.stdlib.default.dependency = false
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/MyBundle.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/LocatorBundle.kt
similarity index 83%
rename from src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/MyBundle.kt
rename to src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/LocatorBundle.kt
index b69708e..e1acca7 100644
--- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/MyBundle.kt
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/LocatorBundle.kt
@@ -5,9 +5,9 @@ import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.PropertyKey
@NonNls
-private const val BUNDLE = "messages.MyBundle"
+private const val BUNDLE = "messages.LocatorBundle"
-object MyBundle : DynamicBundle(BUNDLE) {
+object LocatorBundle : DynamicBundle(BUNDLE) {
@JvmStatic
fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/Git.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/Git.kt
new file mode 100644
index 0000000..24f80c9
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/Git.kt
@@ -0,0 +1,57 @@
+package com.github.y0ung3r.gitglobalhookslocator.git
+
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.DefaultCliCommandExecutor
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.NotFoundCliResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.GitCommandNotFoundException
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.GitIsNotInstalledException
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.GitVersionIsNotSupportedException
+import com.github.y0ung3r.gitglobalhookslocator.git.extensions.toGitResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.interfaces.CliCommandExecutor
+
+class Git(private val commandExecutor: CliCommandExecutor) {
+ companion object {
+ private const val GIT_COMMAND = "git"
+ private const val GIT_VERSION_COMMAND = "version"
+ const val GIT_CONFIG_COMMAND = "config"
+ const val GIT_GLOBAL_COMMAND = "--global"
+ const val GIT_CONFIG_GET_COMMAND = "--get"
+ const val GIT_HOOKS_PATH_SECTION = "core.hooksPath"
+
+ @JvmStatic
+ val minRequiredVersion = SemanticVersion(2, 9, 0)
+
+ @JvmStatic
+ val instance = Git(DefaultCliCommandExecutor())
+ }
+
+ init {
+ if (getInstalledVersion() < minRequiredVersion) {
+ throw GitVersionIsNotSupportedException()
+ }
+ }
+
+ fun executeCommand(vararg params: String): GitResponse {
+ val processBuilder = ProcessBuilder(
+ ArrayList().apply {
+ add(GIT_COMMAND)
+ addAll(params)
+ }
+ )
+
+ processBuilder.redirectErrorStream(true)
+
+ return when (val response = commandExecutor.execute(processBuilder)) {
+ is NotFoundCliResponse -> throw GitCommandNotFoundException(*params)
+ else -> response.toGitResponse()
+ }
+ }
+
+ fun getInstalledVersion() : SemanticVersion {
+ val installedVersion = executeCommand(GIT_VERSION_COMMAND)
+
+ return when {
+ !installedVersion.isEmpty() -> SemanticVersion.parse(installedVersion.value)
+ else -> throw GitIsNotInstalledException()
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/GitResponse.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/GitResponse.kt
new file mode 100644
index 0000000..b782651
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/GitResponse.kt
@@ -0,0 +1,9 @@
+package com.github.y0ung3r.gitglobalhookslocator.git
+
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.EmptyCliResponse
+
+class GitResponse(private val cliResponse: CliResponse) {
+ val value = cliResponse.value
+ fun isEmpty() = cliResponse is EmptyCliResponse
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEntry.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEntry.kt
new file mode 100644
index 0000000..9c93052
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEntry.kt
@@ -0,0 +1,56 @@
+package com.github.y0ung3r.gitglobalhookslocator.git
+
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.HookNotFoundException
+import java.io.File
+import java.nio.file.Path
+
+class HookEntry(private var file: File) {
+ companion object {
+ @JvmStatic
+ fun load(filePath: Path): HookEntry {
+ val file = filePath.toFile()
+
+ if (!file.exists()) {
+ throw HookNotFoundException(file.name.toString())
+ }
+
+ return HookEntry(file)
+ }
+ }
+
+ val name: String
+ = HooksFolder
+ .availableHooks
+ .first { file.nameWithoutExtension.contains(it) }
+
+ fun isDisabled()
+ = HooksFolder
+ .availableHooks
+ .all { it != file.nameWithoutExtension }
+
+ fun enable() {
+ if (!isDisabled()) {
+ return
+ }
+
+ renameFile(name)
+ }
+
+ fun disable() {
+ if (isDisabled()) {
+ return
+ }
+
+ renameFile("_${file.name}")
+ }
+
+ private fun renameFile(newName: String) {
+ file = Path
+ .of(file.parent, newName)
+ .toFile()
+ .let {
+ file.renameTo(it)
+ it
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolder.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolder.kt
new file mode 100644
index 0000000..ac2a431
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolder.kt
@@ -0,0 +1,68 @@
+package com.github.y0ung3r.gitglobalhookslocator.git
+
+import com.github.y0ung3r.gitglobalhookslocator.git.extensions.getGlobalHooksPath
+import com.intellij.openapi.diagnostic.thisLogger
+import java.nio.file.Files
+import java.nio.file.Path
+import kotlin.io.path.nameWithoutExtension
+import java.nio.file.NoSuchFileException
+
+class HooksFolder(git: Git) {
+ companion object {
+ @JvmStatic
+ val availableHooks = arrayOf(
+ "pre-commit",
+ "prepare-commit-msg",
+ "commit-msg",
+ "post-commit",
+ "applypatch-msg",
+ "pre-applypatch",
+ "post-applypatch",
+ "pre-rebase",
+ "post-rewrite",
+ "post-checkout",
+ "post-merge",
+ "pre-push",
+ "pre-auto-gc"
+ )
+ }
+
+ val hooks: List
+ val path: Path
+
+ fun isEmpty(): Boolean
+ = hooks.isEmpty()
+
+ init {
+ path = Path.of(git.getGlobalHooksPath())
+
+ val files = try {
+ Files.list(path)
+ }
+ catch (exception: NoSuchFileException) {
+ thisLogger()
+ .info("Provided hooks path doesn't exists", exception)
+
+ emptyList()
+ .stream()
+ }
+
+ hooks = files
+ .filter {
+ availableHooks
+ .any { hookName ->
+ it.fileName.nameWithoutExtension.contains(hookName) }
+ }
+ .map { HookEntry.load(it) }
+ .toList()
+ }
+
+ fun isAllDisabled()
+ = hooks.all { it.isDisabled() }
+
+ fun enableAll()
+ = hooks.forEach { it.enable() }
+
+ fun disableAll()
+ = hooks.forEach { it.disable() }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/SemanticVersion.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/SemanticVersion.kt
new file mode 100644
index 0000000..f954283
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/SemanticVersion.kt
@@ -0,0 +1,47 @@
+package com.github.y0ung3r.gitglobalhookslocator.git
+
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.ProvidedSemanticVersionIsInvalidException
+
+class SemanticVersion(val major: Int, val minor: Int, val patch: Int) : Comparable {
+ companion object {
+ private const val COMPONENTS_DELIMITER = '.'
+ private const val VERSION_PATTERN = "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)?"
+
+ @JvmStatic
+ fun parse(version: String): SemanticVersion {
+ val match = Regex(VERSION_PATTERN).findAll(version).firstOrNull()
+ ?: throw ProvidedSemanticVersionIsInvalidException(version)
+
+ return match.value.split(COMPONENTS_DELIMITER).let {
+ SemanticVersion(it[0].toInt(), it[1].toInt(), it[2].toInt())
+ }
+ }
+ }
+
+ override fun compareTo(other: SemanticVersion): Int =
+ when {
+ major > other.major -> 1
+ major < other.major -> -1
+ minor > other.minor -> 1
+ minor < other.minor -> -1
+ patch > other.patch -> 1
+ patch < other.patch -> -1
+ else -> 0
+ }
+
+ override fun equals(other: Any?): Boolean {
+ val version = other as? SemanticVersion
+
+ return when {
+ version == null -> false
+ compareTo(version) == 0 -> true
+ else -> false
+ }
+ }
+
+ override fun hashCode(): Int
+ = major.hashCode() * 31 + minor.hashCode() * 31 + patch.hashCode()
+
+ override fun toString(): String
+ = "$major.$minor.$patch"
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/CliResponse.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/CliResponse.kt
new file mode 100644
index 0000000..7a77c41
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/CliResponse.kt
@@ -0,0 +1,6 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.cli
+
+open class CliResponse(value: String) {
+ val value: String = value.trim()
+}
+
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/DefaultCliCommandExecutor.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/DefaultCliCommandExecutor.kt
new file mode 100644
index 0000000..82aa735
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/DefaultCliCommandExecutor.kt
@@ -0,0 +1,32 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.cli
+
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.interfaces.CliCommandExecutor
+import java.io.BufferedReader
+import java.io.InputStreamReader
+import java.nio.charset.StandardCharsets
+
+class DefaultCliCommandExecutor : CliCommandExecutor {
+ private companion object {
+ const val TERMINATION_EXIT_CODE = 0
+ }
+
+ override fun execute(processBuilder: ProcessBuilder) : CliResponse {
+ val process = processBuilder.start()
+ val streamReader = InputStreamReader(process.inputStream, StandardCharsets.UTF_8)
+ val bufferedReader = BufferedReader(streamReader)
+
+ val response = StringBuilder()
+ var line: String? = bufferedReader.readLine()
+
+ while (line != null) {
+ response.append(line)
+ response.append(System.lineSeparator())
+ line = bufferedReader.readLine()
+ }
+
+ return when (process.waitFor()) {
+ TERMINATION_EXIT_CODE -> CliResponse(response.toString())
+ else -> NotFoundCliResponse(response.toString())
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/EmptyCliResponse.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/EmptyCliResponse.kt
new file mode 100644
index 0000000..8e2d846
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/EmptyCliResponse.kt
@@ -0,0 +1,4 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.cli
+
+class EmptyCliResponse
+ : CliResponse("")
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/NotFoundCliResponse.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/NotFoundCliResponse.kt
new file mode 100644
index 0000000..536fed5
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/NotFoundCliResponse.kt
@@ -0,0 +1,4 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.cli
+
+class NotFoundCliResponse(details: String)
+ : CliResponse(details)
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/interfaces/CliCommandExecutor.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/interfaces/CliCommandExecutor.kt
new file mode 100644
index 0000000..82f409e
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/cli/interfaces/CliCommandExecutor.kt
@@ -0,0 +1,7 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.cli.interfaces
+
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
+
+interface CliCommandExecutor {
+ fun execute(processBuilder: ProcessBuilder): CliResponse
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/GitCommandNotFoundException.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/GitCommandNotFoundException.kt
new file mode 100644
index 0000000..184eb93
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/GitCommandNotFoundException.kt
@@ -0,0 +1,4 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.exceptions
+
+class GitCommandNotFoundException(vararg params: String)
+ : Exception("Native Git command \"git ${params.joinToString(" ")}\" not found")
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/GitIsNotInstalledException.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/GitIsNotInstalledException.kt
new file mode 100644
index 0000000..7f1de64
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/GitIsNotInstalledException.kt
@@ -0,0 +1,4 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.exceptions
+
+class GitIsNotInstalledException
+ : Exception("Git is not installed")
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/GitVersionIsNotSupportedException.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/GitVersionIsNotSupportedException.kt
new file mode 100644
index 0000000..2000dbf
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/GitVersionIsNotSupportedException.kt
@@ -0,0 +1,6 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.exceptions
+
+import com.github.y0ung3r.gitglobalhookslocator.git.Git
+
+class GitVersionIsNotSupportedException
+ : Exception("The plugin requires Git version above ${Git.minRequiredVersion}")
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/HookNotFoundException.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/HookNotFoundException.kt
new file mode 100644
index 0000000..aacc032
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/HookNotFoundException.kt
@@ -0,0 +1,4 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.exceptions
+
+class HookNotFoundException(fileName: String)
+ : Exception("Global hook \"$fileName\" not found")
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/ProvidedSemanticVersionIsInvalidException.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/ProvidedSemanticVersionIsInvalidException.kt
new file mode 100644
index 0000000..19bc7d7
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/ProvidedSemanticVersionIsInvalidException.kt
@@ -0,0 +1,4 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.exceptions
+
+class ProvidedSemanticVersionIsInvalidException(version: String)
+ : Exception("The provided version does not conform to semantic versioning: $version")
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/CliResponseExtensions.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/CliResponseExtensions.kt
new file mode 100644
index 0000000..1c03e85
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/CliResponseExtensions.kt
@@ -0,0 +1,7 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.extensions
+
+import com.github.y0ung3r.gitglobalhookslocator.git.GitResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
+
+fun CliResponse.toGitResponse(): GitResponse
+ = GitResponse(this)
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/GitExtensions.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/GitExtensions.kt
new file mode 100644
index 0000000..aaafdf8
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/GitExtensions.kt
@@ -0,0 +1,14 @@
+package com.github.y0ung3r.gitglobalhookslocator.git.extensions
+
+import com.github.y0ung3r.gitglobalhookslocator.git.Git
+
+fun Git.getGlobalHooksPath(): String {
+ val path = executeCommand(
+ Git.GIT_CONFIG_COMMAND,
+ Git.GIT_GLOBAL_COMMAND,
+ Git.GIT_CONFIG_GET_COMMAND,
+ Git.GIT_HOOKS_PATH_SECTION
+ )
+
+ return path.value
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/listeners/MyApplicationActivationListener.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/listeners/MyApplicationActivationListener.kt
deleted file mode 100644
index 4a73cf2..0000000
--- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/listeners/MyApplicationActivationListener.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.github.y0ung3r.gitglobalhookslocator.listeners
-
-import com.intellij.openapi.application.ApplicationActivationListener
-import com.intellij.openapi.diagnostic.thisLogger
-import com.intellij.openapi.wm.IdeFrame
-
-internal class MyApplicationActivationListener : ApplicationActivationListener {
-
- override fun applicationActivated(ideFrame: IdeFrame) {
- thisLogger().warn("Don't forget to remove all non-needed sample code files with their corresponding registration entries in `plugin.xml`.")
- }
-}
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/services/MyProjectService.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/services/MyProjectService.kt
deleted file mode 100644
index 16a90e2..0000000
--- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/services/MyProjectService.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.github.y0ung3r.gitglobalhookslocator.services
-
-import com.intellij.openapi.components.Service
-import com.intellij.openapi.diagnostic.thisLogger
-import com.intellij.openapi.project.Project
-import com.github.y0ung3r.gitglobalhookslocator.MyBundle
-
-@Service(Service.Level.PROJECT)
-class MyProjectService(project: Project) {
-
- init {
- thisLogger().info(MyBundle.message("projectService", project.name))
- thisLogger().warn("Don't forget to remove all non-needed sample code files with their corresponding registration entries in `plugin.xml`.")
- }
-
- fun getRandomNumber() = (1..100).random()
-}
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/toolWindow/MyToolWindowFactory.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/toolWindow/MyToolWindowFactory.kt
deleted file mode 100644
index 42929f8..0000000
--- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/toolWindow/MyToolWindowFactory.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.github.y0ung3r.gitglobalhookslocator.toolWindow
-
-import com.intellij.openapi.components.service
-import com.intellij.openapi.diagnostic.thisLogger
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.wm.ToolWindow
-import com.intellij.openapi.wm.ToolWindowFactory
-import com.intellij.ui.components.JBLabel
-import com.intellij.ui.components.JBPanel
-import com.intellij.ui.content.ContentFactory
-import com.github.y0ung3r.gitglobalhookslocator.MyBundle
-import com.github.y0ung3r.gitglobalhookslocator.services.MyProjectService
-import javax.swing.JButton
-
-
-class MyToolWindowFactory : ToolWindowFactory {
-
- init {
- thisLogger().warn("Don't forget to remove all non-needed sample code files with their corresponding registration entries in `plugin.xml`.")
- }
-
- override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
- val myToolWindow = MyToolWindow(toolWindow)
- val content = ContentFactory.getInstance().createContent(myToolWindow.getContent(), null, false)
- toolWindow.contentManager.addContent(content)
- }
-
- override fun shouldBeAvailable(project: Project) = true
-
- class MyToolWindow(toolWindow: ToolWindow) {
-
- private val service = toolWindow.project.service()
-
- fun getContent() = JBPanel>().apply {
- val label = JBLabel(MyBundle.message("randomLabel", "?"))
-
- add(label)
- add(JButton(MyBundle.message("shuffle")).apply {
- addActionListener {
- label.text = MyBundle.message("randomLabel", service.getRandomNumber())
- }
- })
- }
- }
-}
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindow.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindow.kt
new file mode 100644
index 0000000..731fbff
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindow.kt
@@ -0,0 +1,101 @@
+package com.github.y0ung3r.gitglobalhookslocator.ui.toolWindow
+
+import com.github.y0ung3r.gitglobalhookslocator.LocatorBundle
+import com.github.y0ung3r.gitglobalhookslocator.git.Git
+import com.github.y0ung3r.gitglobalhookslocator.git.HookEntry
+import com.github.y0ung3r.gitglobalhookslocator.git.HooksFolder
+import com.intellij.ide.wizard.withVisualPadding
+import com.intellij.openapi.wm.ToolWindow
+import com.intellij.ui.components.JBCheckBox
+import com.intellij.ui.dsl.builder.Align
+import com.intellij.ui.dsl.builder.LabelPosition
+import com.intellij.ui.dsl.builder.panel
+import com.intellij.ui.dsl.gridLayout.HorizontalAlign
+import com.intellij.ui.dsl.gridLayout.VerticalAlign
+import javax.swing.JComponent
+
+class GitGlobalHooksLocatorWindow(toolWindow: ToolWindow) {
+ private val hooksFolder: HooksFolder
+ = HooksFolder(Git.instance)
+
+ private val disableAllCheckBox: JBCheckBox
+ = createDisableAllCheckBox()
+
+ private val hookCheckBoxes: Array
+ = createHookCheckBoxes()
+
+ fun getContent(): JComponent {
+ if (hooksFolder.isEmpty()) {
+ return panel {
+ row {
+ val text = LocatorBundle
+ .getMessage("locator.ui.toolWindow.hooksFolderEmpty", hooksFolder.path)
+
+ label(text)
+ .align(Align.CENTER)
+ }
+ }
+ }
+
+ return panel {
+ val groupTitle = LocatorBundle
+ .getMessage("locator.ui.toolWindow.globalHooksGroup")
+
+ group(groupTitle) {
+ row {
+ cell(disableAllCheckBox)
+ }
+
+ hookCheckBoxes
+ .map {
+ row {
+ cell(it)
+ }
+ }
+ }
+ }
+ .withVisualPadding(true)
+ }
+
+ private fun createDisableAllCheckBox(): JBCheckBox {
+ val title = LocatorBundle
+ .getMessage("locator.ui.toolWindow.disableAll")
+
+ return JBCheckBox(title, hooksFolder.isAllDisabled())
+ .apply {
+ addActionListener {
+ when (this.isSelected) {
+ true -> hooksFolder.disableAll()
+ false -> hooksFolder.enableAll()
+ }
+
+ hookCheckBoxes.forEach {
+ it.isSelected = this.isSelected
+ }
+ }
+ }
+ }
+
+ private fun createHookCheckBox(hook: HookEntry): JBCheckBox {
+ val hookTitle = LocatorBundle
+ .getMessage("locator.ui.toolWindow.disable", hook.name)
+
+ return JBCheckBox(hookTitle, hook.isDisabled())
+ .apply {
+ addActionListener {
+ when (this.isSelected) {
+ true -> hook.disable()
+ false -> hook.enable()
+ }
+
+ disableAllCheckBox.isSelected = hooksFolder.isAllDisabled()
+ }
+ }
+ }
+
+ private fun createHookCheckBoxes(): Array
+ = hooksFolder
+ .hooks
+ .map { createHookCheckBox(it) }
+ .toTypedArray()
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindowFactory.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindowFactory.kt
new file mode 100644
index 0000000..79e8bfb
--- /dev/null
+++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindowFactory.kt
@@ -0,0 +1,26 @@
+package com.github.y0ung3r.gitglobalhookslocator.ui.toolWindow
+
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.wm.ToolWindow
+import com.intellij.openapi.wm.ToolWindowFactory
+import com.intellij.ui.content.ContentFactory
+
+class GitGlobalHooksLocatorWindowFactory : ToolWindowFactory {
+ override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
+ val locatorWindow = GitGlobalHooksLocatorWindow(toolWindow)
+
+ val content = ContentFactory
+ .getInstance()
+ .createContent(
+ locatorWindow.getContent(),
+ null,
+ false)
+
+ toolWindow
+ .contentManager
+ .addContent(content)
+ }
+
+ override fun shouldBeAvailable(project: Project)
+ = !project.isDefault
+}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index bcdea61..3da3534 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -1,18 +1,23 @@
com.github.y0ung3r.gitglobalhookslocator
- GitGlobalHooksLocator Template
+ Git Global Hooks Locator
y0ung3r
+
+
+
+
+
com.intellij.modules.platform
- messages.MyBundle
+ messages.LocatorBundle
-
+
-
-
-
-
diff --git a/src/main/resources/description.png b/src/main/resources/description.png
new file mode 100644
index 0000000..5c1e8c3
Binary files /dev/null and b/src/main/resources/description.png differ
diff --git a/src/main/resources/messages/LocatorBundle.properties b/src/main/resources/messages/LocatorBundle.properties
new file mode 100644
index 0000000..4eb8f0d
--- /dev/null
+++ b/src/main/resources/messages/LocatorBundle.properties
@@ -0,0 +1,4 @@
+locator.ui.toolWindow.globalHooksGroup=Global Hooks
+locator.ui.toolWindow.hooksFolderEmpty=Git global hooks from "{0}" not found
+locator.ui.toolWindow.disableAll=Disable all Git global hooks
+locator.ui.toolWindow.disable=Disable {0}
\ No newline at end of file
diff --git a/src/main/resources/messages/MyBundle.properties b/src/main/resources/messages/MyBundle.properties
deleted file mode 100644
index 2e041d8..0000000
--- a/src/main/resources/messages/MyBundle.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-projectService=Project service: {0}
-randomLabel=The random number is: {0}
-shuffle=Shuffle
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/MyPluginTest.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/MyPluginTest.kt
deleted file mode 100644
index 7152f43..0000000
--- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/MyPluginTest.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.github.y0ung3r.gitglobalhookslocator
-
-import com.intellij.ide.highlighter.XmlFileType
-import com.intellij.openapi.components.service
-import com.intellij.psi.xml.XmlFile
-import com.intellij.testFramework.TestDataPath
-import com.intellij.testFramework.fixtures.BasePlatformTestCase
-import com.intellij.util.PsiErrorElementUtil
-import com.github.y0ung3r.gitglobalhookslocator.services.MyProjectService
-
-@TestDataPath("\$CONTENT_ROOT/src/test/testData")
-class MyPluginTest : BasePlatformTestCase() {
-
- fun testXMLFile() {
- val psiFile = myFixture.configureByText(XmlFileType.INSTANCE, "bar")
- val xmlFile = assertInstanceOf(psiFile, XmlFile::class.java)
-
- assertFalse(PsiErrorElementUtil.hasErrors(project, xmlFile.virtualFile))
-
- assertNotNull(xmlFile.rootTag)
-
- xmlFile.rootTag?.let {
- assertEquals("foo", it.name)
- assertEquals("bar", it.value.text)
- }
- }
-
- fun testRename() {
- myFixture.testRename("foo.xml", "foo_after.xml", "a2")
- }
-
- fun testProjectService() {
- val projectService = project.service()
-
- assertNotSame(projectService.getRandomNumber(), projectService.getRandomNumber())
- }
-
- override fun getTestDataPath() = "src/test/testData/rename"
-}
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/ExecuteCommandTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/ExecuteCommandTests.kt
new file mode 100644
index 0000000..bd0a477
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/ExecuteCommandTests.kt
@@ -0,0 +1,35 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests
+
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.EmptyCliResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.Git
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.GitCommandNotFoundException
+import org.junit.Assert.assertNotEquals
+import org.junit.Test
+
+class ExecuteCommandTests {
+ @Test(expected = GitCommandNotFoundException::class)
+ fun `Should throws exception if Git commands is not exists`() {
+ // Arrange & Act & Assert
+ Git.instance.executeCommand(
+ "unknown",
+ Git.GIT_GLOBAL_COMMAND
+ )
+ }
+
+ @Test
+ fun `Should returns response properly`() {
+ // Arrange
+ val sut = Git.instance
+
+ // Act
+ val actualResponse = sut.executeCommand(
+ Git.GIT_CONFIG_COMMAND,
+ Git.GIT_GLOBAL_COMMAND,
+ Git.GIT_CONFIG_GET_COMMAND,
+ "user.name"
+ )
+
+ // Assert
+ assertNotEquals(actualResponse, EmptyCliResponse())
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetGlobalHooksPathTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetGlobalHooksPathTests.kt
new file mode 100644
index 0000000..b3419b8
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetGlobalHooksPathTests.kt
@@ -0,0 +1,28 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests
+
+import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.RespondInterchangeably
+import com.github.y0ung3r.gitglobalhookslocator.git.Git
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.extensions.getGlobalHooksPath
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class GetGlobalHooksPathTests {
+ @Test
+ fun `Should returns global hooks path`() {
+ // Arrange
+ val expectedPath = "~/.git/hooks"
+ val sut = Git(
+ RespondInterchangeably(
+ CliResponse(Git.minRequiredVersion.toString()),
+ CliResponse(expectedPath)
+ )
+ )
+
+ // Act
+ val actualPath = sut.getGlobalHooksPath()
+
+ // Assert
+ assertEquals(expectedPath, actualPath)
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetInstalledVersionTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetInstalledVersionTests.kt
new file mode 100644
index 0000000..70c1433
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetInstalledVersionTests.kt
@@ -0,0 +1,39 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests
+
+import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.RespondOnce
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.EmptyCliResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.Git
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.SemanticVersion
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.GitIsNotInstalledException
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class GetInstalledVersionTests {
+ @Test(expected = GitIsNotInstalledException::class)
+ fun `Should throws exception if Git is not installed`() {
+ // Arrange & Act & Assert
+ Git(
+ RespondOnce(
+ EmptyCliResponse()
+ )
+ )
+ }
+
+ @Test
+ fun `Should returns Git version info`() {
+ // Arrange
+ val expectedVersion = SemanticVersion(2, 9, 2)
+ val sut = Git(
+ RespondOnce(
+ CliResponse(expectedVersion.toString())
+ )
+ )
+
+ // Act
+ val actualVersion = sut.getInstalledVersion()
+
+ // Assert
+ assertEquals(expectedVersion, actualVersion)
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HookEntryTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HookEntryTests.kt
new file mode 100644
index 0000000..067efc6
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HookEntryTests.kt
@@ -0,0 +1,65 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests
+
+import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase
+import com.github.y0ung3r.gitglobalhookslocator.git.HookEntry
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.HookNotFoundException
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class HookEntryTests(private val hookName: String) : HookTestBase() {
+ @Test(expected = HookNotFoundException::class)
+ fun `Should throws exception if hook not found`() {
+ HookEntry.load(getEnabledHookPath("unknown"))
+ }
+
+ @Test
+ fun `Should load hook properly`() {
+ HookEntry.load(getEnabledHookPath(hookName))
+ }
+
+ @Test
+ fun `Should be disabled if hook name not contains in available Git hooks`() { // TODO: Rename test
+ // Arrange
+ val sut = HookEntry.load(getDisabledHookPath(hookName))
+
+ // Act & Assert
+ assertTrue(sut.isDisabled())
+ }
+
+ @Test
+ fun `Should be enabled if hook name contains in available Git hooks`() { // TODO: Rename test
+ // Arrange
+ val sut = HookEntry.load(getEnabledHookPath(hookName))
+
+ // Act & Assert
+ assertFalse(sut.isDisabled())
+ }
+
+ @Test
+ fun `Should disable hook`() {
+ // Arrange
+ val sut = HookEntry.load(getEnabledHookPath(hookName))
+
+ // Act
+ sut.disable()
+
+ // Assert
+ assertTrue(sut.isDisabled())
+ }
+
+ @Test
+ fun `Should enable hook`() {
+ // Arrange
+ val sut = HookEntry.load(getDisabledHookPath(hookName))
+
+ // Act
+ sut.enable()
+
+ // Assert
+ assertFalse(sut.isDisabled())
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt
new file mode 100644
index 0000000..0410649
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt
@@ -0,0 +1,93 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests
+
+import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase
+import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.RespondInterchangeably
+import com.github.y0ung3r.gitglobalhookslocator.git.Git
+import com.github.y0ung3r.gitglobalhookslocator.git.HooksFolder
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
+import org.junit.Assert
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.nio.file.Path
+
+class HooksFolderTests : HookTestBase() {
+ private companion object {
+ @JvmStatic
+ fun getGit(hooksPath: Path) = Git(
+ RespondInterchangeably(
+ CliResponse(Git.minRequiredVersion.toString()),
+ CliResponse(hooksPath.toString())
+ )
+ )
+ }
+
+ @Test
+ fun `Should load all hooks from disabled hooks path`() {
+ // Arrange
+ val git = getGit(getDisabledHooksPath())
+
+ // Act
+ val sut = HooksFolder(git)
+
+ // Assert
+ assertEquals(HooksFolder.availableHooks.size, sut.hooks.size)
+ }
+
+ @Test
+ fun `Should load all hooks from enabled hooks path`() {
+ // Arrange
+ val git = getGit(getEnabledHooksPath())
+
+ // Act
+ val sut = HooksFolder(git)
+
+ // Assert
+ assertEquals(HooksFolder.availableHooks.size, sut.hooks.size)
+ }
+
+ @Test
+ fun `All hooks should be disabled`() {
+ // Arrange
+ val git = getGit(getDisabledHooksPath())
+ val sut = HooksFolder(git)
+
+ // Act & Assert
+ Assert.assertTrue(sut.isAllDisabled())
+ }
+
+ @Test
+ fun `All hooks should be enabled`() {
+ // Arrange
+ val git = getGit(getEnabledHooksPath())
+ val sut = HooksFolder(git)
+
+ // Act & Assert
+ Assert.assertFalse(sut.isAllDisabled())
+ }
+
+ @Test
+ fun `Should disable all hooks`() {
+ // Arrange
+ val git = getGit(getEnabledHooksPath())
+ val sut = HooksFolder(git)
+
+ // Act
+ sut.disableAll()
+
+ // Assert
+ Assert.assertTrue(sut.isAllDisabled())
+ }
+
+ @Test
+ fun `Should enable all hooks`() {
+ // Arrange
+ val git = getGit(getDisabledHooksPath())
+ val sut = HooksFolder(git)
+
+ // Act
+ sut.enableAll()
+
+ // Assert
+ Assert.assertFalse(sut.isAllDisabled())
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/MinGitVersionTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/MinGitVersionTests.kt
new file mode 100644
index 0000000..ae76e94
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/MinGitVersionTests.kt
@@ -0,0 +1,44 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests
+
+import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.RespondOnce
+import com.github.y0ung3r.gitglobalhookslocator.git.Git
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.GitVersionIsNotSupportedException
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@RunWith(Parameterized::class)
+class MinGitVersionTests(private val unsupportedVersion: String, private val supportedVersion: String) {
+ companion object {
+ @JvmStatic
+ @Parameters
+ fun version() = arrayOf(
+ arrayOf("1.0.0", "2.9.0"),
+ arrayOf("1.0.1", "2.9.1"),
+ arrayOf("2.0.0", "2.43.0"),
+ arrayOf("2.0.1", "2.43.1"),
+ )
+ }
+
+ @Test(expected = GitVersionIsNotSupportedException::class)
+ fun `Should throws exception if Git version is not supported`() {
+ // Arrange & Act & Assert
+ Git(
+ RespondOnce(
+ CliResponse(unsupportedVersion)
+ )
+ )
+ }
+
+ @Test
+ fun `Should not throws exception if Git version supported`() {
+ // Arrange & Act & Assert
+ Git(
+ RespondOnce(
+ CliResponse(supportedVersion)
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/SemanticVersionTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/SemanticVersionTests.kt
new file mode 100644
index 0000000..e054a1e
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/SemanticVersionTests.kt
@@ -0,0 +1,54 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests
+
+import com.github.y0ung3r.gitglobalhookslocator.git.SemanticVersion
+import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.ProvidedSemanticVersionIsInvalidException
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+//@RunWith(Parameterized::class)
+class SemanticVersionTests(private val invalidVersion: String, private val validVersion: String) {
+ companion object {
+ @JvmStatic
+ @Parameters
+ fun versions() = arrayOf(
+ arrayOf("-1.0.0", "0.0.0"),
+ arrayOf("1.-1.0", "1.2.3-alpha.1+build"),
+ arrayOf("0.0.-1", "1.0"),
+ arrayOf("1", "git version 2.33.0.windows.2"),
+ arrayOf("", "0.99.9n"),
+ arrayOf("1.0", "1.7.12.4"),
+ arrayOf("1.0-alpha", "v2.43.0-rc1"),
+ arrayOf("1.0-alpha.01", "2.38.3"),
+ arrayOf("a1.0.0", "v2.6.0-rc0"),
+ arrayOf("1.a0.0", "1.2.3-alpha.b.3"),
+ arrayOf("1.0.a0", "2.3.1-alpha")
+ )
+ }
+
+ //@Test(expected = ProvidedSemanticVersionIsInvalidException::class)
+ fun `Should throws exception if provided version is not valid`() {
+ // Arrange & Act & Assert
+ SemanticVersion.parse(invalidVersion)
+ }
+
+ //@Test
+ fun `Should parse version properly`() {
+ // Arrange & Act & Assert
+ SemanticVersion.parse(validVersion)
+ }
+
+ //@Test
+ fun `Should returns string representation of version`() {
+ // Arrange
+ val sut = SemanticVersion.parse(validVersion)
+
+ // Act
+ val actualRepresentation = sut.toString()
+
+ // Assert
+ assertEquals(validVersion, actualRepresentation)
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt
new file mode 100644
index 0000000..50c0838
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt
@@ -0,0 +1,75 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine
+
+import com.github.y0ung3r.gitglobalhookslocator.git.HooksFolder
+import org.junit.runners.Parameterized
+import java.nio.file.Files
+import java.nio.file.Path
+
+abstract class HookTestBase {
+ companion object {
+ const val BASE_PATH = "src/test/testData/hooks"
+ private const val DISABLED_HOOK = "disabled"
+ private const val ENABLED_HOOK = "enabled"
+
+ @JvmStatic
+ private fun getHookPath(hookType: String, hookName: String): Path {
+ val categorizedHookName = when (hookType) {
+ DISABLED_HOOK -> "_$hookName"
+ else -> hookName
+ }
+
+ return Path.of(BASE_PATH, hookType, categorizedHookName)
+ }
+
+ @JvmStatic
+ private fun getHooksPath(hookType: String): Path
+ = Path.of(BASE_PATH, hookType)
+
+ @JvmStatic
+ fun getDisabledHookPath(hookName: String): Path
+ = getHookPath(DISABLED_HOOK, hookName)
+
+ @JvmStatic
+ fun getEnabledHookPath(hookName: String): Path
+ = getHookPath(ENABLED_HOOK, hookName)
+
+ @JvmStatic
+ fun getDisabledHooksPath(): Path
+ = getHooksPath(DISABLED_HOOK)
+
+ @JvmStatic
+ fun getEnabledHooksPath(): Path
+ = getHooksPath(ENABLED_HOOK)
+
+ @JvmStatic
+ private fun clearTestHooks(hookType: String) {
+ val hooksPath = getHooksPath(hookType)
+
+ hooksPath
+ .toFile()
+ .mkdirs()
+
+ Files.list(hooksPath).forEach { it.toFile().delete() }
+ }
+
+ @JvmStatic
+ private fun generateTestHooks(hookType: String) {
+ clearTestHooks(hookType)
+
+ hookNames()
+ .map { getHookPath(hookType, it).toFile() }
+ .filter { !it.exists() }
+ .forEach { it.createNewFile() }
+ }
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun hookNames()
+ = HooksFolder.availableHooks
+ }
+
+ init {
+ generateTestHooks(DISABLED_HOOK)
+ generateTestHooks(ENABLED_HOOK)
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondInterchangeably.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondInterchangeably.kt
new file mode 100644
index 0000000..920272a
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondInterchangeably.kt
@@ -0,0 +1,10 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine
+
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.interfaces.CliCommandExecutor
+
+class RespondInterchangeably(vararg responses: CliResponse) : CliCommandExecutor {
+ private val iterator = responses.iterator()
+ override fun execute(processBuilder: ProcessBuilder): CliResponse
+ = iterator.next()
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondOnce.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondOnce.kt
new file mode 100644
index 0000000..9d6f347
--- /dev/null
+++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondOnce.kt
@@ -0,0 +1,9 @@
+package com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine
+
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
+import com.github.y0ung3r.gitglobalhookslocator.git.cli.interfaces.CliCommandExecutor
+
+class RespondOnce(private val response: CliResponse) : CliCommandExecutor {
+ override fun execute(processBuilder: ProcessBuilder): CliResponse
+ = response
+}
\ No newline at end of file