Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v0.0.2 #14

Merged
merged 16 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<!-- Keep a Changelog guide -> https://keepachangelog.com -->

# GitGlobalHooksLocator Changelog
# Changelog

## [Unreleased]

## [0.0.1]
## [0.0.2]
### Fixed
- Fix empty core.hooksPath value handling
- Fix relative paths handling

- Initial release
### Changed
- Git version handling
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pluginGroup = com.github.y0ung3r.gitglobalhookslocator
pluginName = GitGlobalHooksLocator
pluginRepositoryUrl = https://github.com/y0ung3r/GitGlobalHooksLocator
# SemVer format -> https://semver.org
pluginVersion = 0.0.1
pluginVersion = 0.0.2

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 223
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ private const val BUNDLE = "messages.LocatorBundle"

object LocatorBundle : DynamicBundle(BUNDLE) {

@Suppress("unused")
@JvmStatic
fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =
getMessage(key, *params)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.github.y0ung3r.gitglobalhookslocator.git

import com.github.y0ung3r.gitglobalhookslocator.git.cli.DefaultCliCommandExecutor
import com.github.y0ung3r.gitglobalhookslocator.git.cli.EmptyCliResponse
import com.github.y0ung3r.gitglobalhookslocator.git.cli.NotFoundCliResponse
import com.github.y0ung3r.gitglobalhookslocator.git.cli.interfaces.CliCommandExecutor
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 {
Expand All @@ -16,9 +17,10 @@ class Git(private val commandExecutor: CliCommandExecutor) {
const val GIT_GLOBAL_COMMAND = "--global"
const val GIT_CONFIG_GET_COMMAND = "--get"
const val GIT_HOOKS_PATH_SECTION = "core.hooksPath"
const val DEFAULT_HOOKS_PATH = "~/.git/hooks"

@JvmStatic
val minRequiredVersion = SemanticVersion(2, 9, 0)
val minRequiredVersion = GitVersion(2, 9, 0)

@JvmStatic
val instance = Git(DefaultCliCommandExecutor())
Expand All @@ -41,16 +43,20 @@ class Git(private val commandExecutor: CliCommandExecutor) {
processBuilder.redirectErrorStream(true)

return when (val response = commandExecutor.execute(processBuilder)) {
is NotFoundCliResponse -> throw GitCommandNotFoundException(*params)
is NotFoundCliResponse -> throw GitCommandNotFoundException(response.value, *params)
else -> response.toGitResponse()
}
}

fun getInstalledVersion() : SemanticVersion {
val installedVersion = executeCommand(GIT_VERSION_COMMAND)
fun getInstalledVersion() : GitVersion {
val installedVersion = try {
executeCommand(GIT_VERSION_COMMAND)
} catch (exception: GitCommandNotFoundException) {
GitResponse(EmptyCliResponse())
}

return when {
!installedVersion.isEmpty() -> SemanticVersion.parse(installedVersion.value)
!installedVersion.isEmpty() -> GitVersion.parse(installedVersion.value)
else -> throw GitIsNotInstalledException()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.github.y0ung3r.gitglobalhookslocator.git

import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.ProvidedGitVersionIsInvalidException

class GitVersion(val major: Int, val minor: Int, val patch: Int) : Comparable<GitVersion> {
companion object {
/*
* From git sources:
* The format of this string should be kept stable for compatibility
* with external projects that rely on the output of "git version"
* https://github.com/git/git/blob/564d0252ca632e0264ed670534a51d18a689ef5d/help.c#L741
*/
private const val PREFIX = "git version"
private const val VERSION_PATTERN = "^$PREFIX (0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)?"

fun parse(version: String) : GitVersion {
val match = Regex(VERSION_PATTERN).findAll(version).firstOrNull()
?: throw ProvidedGitVersionIsInvalidException(version)

return try {
match.groupValues.let {
GitVersion(it[1].toInt(), it[2].toInt(), it[3].toInt())
}
} catch (exception: NumberFormatException) {
throw ProvidedGitVersionIsInvalidException(version)
}
}
}

override fun compareTo(other: GitVersion): 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? GitVersion

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
= "$PREFIX $major.$minor.$patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ class HookEntry(private var file: File) {

val name: String
= HooksFolder
.availableHooks
.supportedHooks
.first { file.nameWithoutExtension.contains(it) }

fun isDisabled()
= HooksFolder
.availableHooks
.supportedHooks
.all { it != file.nameWithoutExtension }

fun enable() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import java.nio.file.NoSuchFileException
class HooksFolder(git: Git) {
companion object {
@JvmStatic
val availableHooks = arrayOf(
val supportedHooks = arrayOf(
"pre-commit",
"prepare-commit-msg",
"commit-msg",
Expand All @@ -34,22 +34,22 @@ class HooksFolder(git: Git) {
= hooks.isEmpty()

init {
path = Path.of(git.getGlobalHooksPath())
path = git.getGlobalHooksPath()

val files = try {
Files.list(path)
}
catch (exception: NoSuchFileException) {
thisLogger()
.info("Provided hooks path doesn't exists", exception)
.info("Provided hooks path is not valid", exception)

emptyList<Path>()
.stream()
}

hooks = files
.filter {
availableHooks
supportedHooks
.any { hookName ->
it.fileName.nameWithoutExtension.contains(hookName) }
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,48 @@ package com.github.y0ung3r.gitglobalhookslocator.git.cli

import com.github.y0ung3r.gitglobalhookslocator.git.cli.interfaces.CliCommandExecutor
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets

class DefaultCliCommandExecutor : CliCommandExecutor {
private companion object {
const val TERMINATION_EXIT_CODE = 0
const val NOT_FOUND_EXIT_CODE = 1
}

override fun execute(processBuilder: ProcessBuilder) : CliResponse {
override fun execute(processBuilder: ProcessBuilder): CliResponse
= try {
handle(processBuilder)
} catch (exception: Exception) {
when (exception) {
is IOException, is InterruptedException, is RuntimeException
-> NotFoundCliResponse(exception.message)

else -> throw exception
}
}

private fun handle(processBuilder: ProcessBuilder) : CliResponse {
val process = processBuilder.start()
val streamReader = InputStreamReader(process.inputStream, StandardCharsets.UTF_8)
val bufferedReader = BufferedReader(streamReader)

val response = StringBuilder()
val responseBuilder = StringBuilder()
var line: String? = bufferedReader.readLine()

while (line != null) {
response.append(line)
response.append(System.lineSeparator())
responseBuilder.append(line)
responseBuilder.append(System.lineSeparator())
line = bufferedReader.readLine()
}

val response = responseBuilder.toString()

return when (process.waitFor()) {
TERMINATION_EXIT_CODE -> CliResponse(response.toString())
else -> NotFoundCliResponse(response.toString())
TERMINATION_EXIT_CODE -> CliResponse(response)
NOT_FOUND_EXIT_CODE -> NotFoundCliResponse(response)
else -> EmptyCliResponse()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.y0ung3r.gitglobalhookslocator.git.cli

class NotFoundCliResponse(details: String)
: CliResponse(details)
class NotFoundCliResponse(details: String? = null)
: CliResponse(details ?: "")
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
package com.github.y0ung3r.gitglobalhookslocator.git.exceptions

class GitCommandNotFoundException(vararg params: String)
: Exception("Native Git command \"git ${params.joinToString(" ")}\" not found")
class GitCommandNotFoundException(details: String, vararg params: String)
: Exception("Native Git command \"git ${params.joinToString(" ")}\" not found ${mapDetails(details)}")
{
private companion object {
fun mapDetails(details: String): String
= when (details) {
"" -> details
else -> "($details)"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.github.y0ung3r.gitglobalhookslocator.git.exceptions

class ProvidedGitVersionIsInvalidException(version: String)
: Exception("The provided version does not conform to git versioning: $version")

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
package com.github.y0ung3r.gitglobalhookslocator.git.extensions

import com.github.y0ung3r.gitglobalhookslocator.git.Git
import com.github.y0ung3r.gitglobalhookslocator.git.GitResponse
import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse
import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.GitCommandNotFoundException
import com.github.y0ung3r.gitglobalhookslocator.git.utils.SystemPathUtils
import java.nio.file.Path

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
)
private const val SLASHES_PATTERN = "[/\\\\]"
private const val EMPTY_PATTERN = "^$"
private const val HOME_PATTERN = "^~$SLASHES_PATTERN"
private const val CURRENT_DIR_PATTERN = "^.$SLASHES_PATTERN"

return path.value
fun Git.getGlobalHooksPath(): Path {
val rawPath = try {
executeCommand(
Git.GIT_CONFIG_COMMAND,
Git.GIT_GLOBAL_COMMAND,
Git.GIT_CONFIG_GET_COMMAND,
Git.GIT_HOOKS_PATH_SECTION
)
} catch (exception: GitCommandNotFoundException) {
GitResponse(CliResponse(Git.DEFAULT_HOOKS_PATH))
}

val targetPath = rawPath.value
.replaceFirst(
Regex(EMPTY_PATTERN),
Git.DEFAULT_HOOKS_PATH
)
.replaceFirst(
Regex(HOME_PATTERN),
SystemPathUtils.getUserHomePath()
)
.replaceFirst(
Regex(CURRENT_DIR_PATTERN),
SystemPathUtils.getCurrentDirectoryPath()
)

return Path
.of(targetPath)
.toAbsolutePath()
}
Loading