From 4340079a31c00e39d229c4ca8d30a2ab0c41e1db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=92=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=D0=B9?= Date: Wed, 24 Jan 2024 22:24:17 +0500 Subject: [PATCH] (#10) Add a draft version of the change tracking --- .../gitglobalhookslocator/git/HookEvent.kt | 8 ++ .../git/HooksFolderVisitor.kt | 15 ++++ .../git/ObservableHooksFolder.kt | 74 +++++++++++++++++++ .../gitTests/HooksFolderTests.kt | 30 ++++++++ .../gitTests/testEngine/HookTestBase.kt | 11 +++ 5 files changed, 138 insertions(+) create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt new file mode 100644 index 0000000..a2a937c --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt @@ -0,0 +1,8 @@ +package com.github.y0ung3r.gitglobalhookslocator.git + +import java.nio.file.Path +import java.nio.file.WatchEvent + +data class HookEvent( + val kind: WatchEvent.Kind, + val entry: HookEntry) \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt new file mode 100644 index 0000000..2c3d70d --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt @@ -0,0 +1,15 @@ +package com.github.y0ung3r.gitglobalhookslocator.git + +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes + +class HooksFolderVisitor(private val watchService: WatchService) : SimpleFileVisitor() { + override fun preVisitDirectory(directory: Path, attributes: BasicFileAttributes): FileVisitResult { + directory.register( + watchService, + StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_DELETE) + + return FileVisitResult.CONTINUE + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt new file mode 100644 index 0000000..c30ede4 --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt @@ -0,0 +1,74 @@ +package com.github.y0ung3r.gitglobalhookslocator.git + +import com.github.y0ung3r.gitglobalhookslocator.git.extensions.getGlobalHooksPath +import com.intellij.openapi.diagnostic.thisLogger +import io.reactivex.rxjava3.subjects.PublishSubject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.nio.file.* +import kotlin.io.path.nameWithoutExtension + +class ObservableHooksFolder(git: Git) { + companion object { + @JvmStatic + val supportedHooks = 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" + ) + } + + private var count: Int = 0 + val hooks: PublishSubject + val path: Path + + init { + path = git.getGlobalHooksPath() + + val files = try { + Files.list(path) + } + catch (exception: NoSuchFileException) { + thisLogger() + .info("Provided hooks path is not valid", exception) + + emptyList() + .stream() + } + + hooks = PublishSubject.create() + + files + .filter { + supportedHooks.any { hookName -> it.fileName.nameWithoutExtension.contains(hookName) } + } + .map { HookEvent(StandardWatchEventKinds.ENTRY_CREATE, HookEntry.load(it)) } + .forEach { hooks.onNext(it) } + + val watchService = FileSystems.getDefault().newWatchService() + Files.walkFileTree(path, HooksFolderVisitor(watchService)) + + val changesTrackingJob = CoroutineScope(Dispatchers.IO).launch { + while (true) { + val folderEntry = watchService.take() + + folderEntry.pollEvents().forEach { + val hookPath = Path.of(path.toString(), it.context().toString()) + val hookEntry = HookEntry.load(hookPath) + hooks.onNext(HookEvent(it.kind() as WatchEvent.Kind, hookEntry)) + } + } + } + } +} diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt index 9c2369c..495e9a6 100644 --- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt @@ -2,12 +2,42 @@ package com.github.y0ung3r.gitglobalhookslocator.gitTests import com.github.y0ung3r.gitglobalhookslocator.git.Git import com.github.y0ung3r.gitglobalhookslocator.git.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.git.ObservableHooksFolder import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.RespondInterchangeably import org.junit.Assert.* import org.junit.Test import java.nio.file.Path +import java.nio.file.StandardWatchEventKinds + +class ObservableHooksFolderTests : HookTestBase() { + private companion object { + @JvmStatic + fun getGit(hooksPath: Path) = Git( + RespondInterchangeably( + CliResponse(Git.minRequiredVersion.toString()), + CliResponse(hooksPath.toString()) + ) + ) + } + + @Test + fun `Should observe changes`() { + // Arrange + val git = getGit(getEnabledHooksPath()) + + // Act + val sut = ObservableHooksFolder(git) + + sut.hooks.subscribe { + assertEquals(it.kind, StandardWatchEventKinds.ENTRY_CREATE) + } + + // Assert + createHook("kek") + } +} class HooksFolderTests : HookTestBase() { private companion object { 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 index 868377a..5a14efc 100644 --- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt @@ -41,6 +41,17 @@ abstract class HookTestBase { fun getEnabledHooksPath(): Path = getHooksPath(ENABLED_HOOK) + @JvmStatic + fun createHook(hookName: String) { + val hookPath = getHookPath(ENABLED_HOOK, hookName).toFile() + + if (hookPath.exists()) { + return + } + + hookPath.createNewFile() + } + @JvmStatic private fun clearTestHooks(hookType: String) { val hooksPath = getHooksPath(hookType)