diff --git a/cli/src/main/kotlin/com/mitteloupe/cag/cli/HelpContent.kt b/cli/src/main/kotlin/com/mitteloupe/cag/cli/HelpContent.kt index 667eaea..171bb74 100644 --- a/cli/src/main/kotlin/com/mitteloupe/cag/cli/HelpContent.kt +++ b/cli/src/main/kotlin/com/mitteloupe/cag/cli/HelpContent.kt @@ -186,7 +186,8 @@ object HelpContent { Git Configuration: - autoInitialize=true|false - Whether to automatically initialize a git repository for new projects (default: false) - - autoStage=true|false - Whether to automatically stage changes after generation (default: true) + - autoStage=true|false - Whether to automatically stage changes after generation (default: false) + - path=/absolute/path/to/git - Optional path to the git executable (default: resolved via PATH) Example ~/.cagrc: [new.versions] @@ -200,6 +201,7 @@ object HelpContent { [git] autoInitialize=true autoStage=true + path=/usr/bin/git Example ./.cagrc (project overrides): [new.versions] @@ -210,6 +212,7 @@ object HelpContent { [git] autoInitialize=false + path=/opt/homebrew/bin/git """.trimIndent() ) } diff --git a/cli/src/main/kotlin/com/mitteloupe/cag/cli/Main.kt b/cli/src/main/kotlin/com/mitteloupe/cag/cli/Main.kt index 9e72082..ee9b188 100644 --- a/cli/src/main/kotlin/com/mitteloupe/cag/cli/Main.kt +++ b/cli/src/main/kotlin/com/mitteloupe/cag/cli/Main.kt @@ -84,7 +84,7 @@ fun main(arguments: Array) { val destinationRootDirectory = projectModel.selectedModuleRootDir() ?: projectRoot val projectNamespace = basePackage ?: "com.unknown.app." - val git = Git() + val git = Git(gitBinaryPath = configuration.git.path) projectTemplateRequests.forEach { request -> val projectTemplateDestinationDirectory = @@ -111,6 +111,9 @@ fun main(arguments: Array) { val shouldInitGit = request.enableGit || configuration.git.autoInitialize == true if (shouldInitGit) { + if (!git.isAvailable(projectTemplateDestinationDirectory)) { + println("Warning: Git is not available. Configure [git].path in .cagrc or install git.") + } val didInit = git.initializeRepository(projectTemplateDestinationDirectory) if (!didInit || configuration.git.autoStage == true) { runCatching { git.stageAll(projectTemplateDestinationDirectory) } @@ -135,6 +138,9 @@ fun main(arguments: Array) { if (request.enableGit || configuration.git.autoStage == true) { val gitRoot = projectModel.selectedModuleRootDir() ?: projectRoot + if (!git.isAvailable(gitRoot)) { + println("Warning: Git is not available. Configure [git].path in .cagrc or install git.") + } runCatching { git.stageAll(gitRoot) } } } @@ -159,6 +165,9 @@ fun main(arguments: Array) { if (requestFeature.enableGit || configuration.git.autoStage == true) { val gitRoot = projectModel.selectedModuleRootDir() ?: projectRoot + if (!git.isAvailable(gitRoot)) { + println("Warning: Git is not available. Configure [git].path in .cagrc or install git.") + } runCatching { git.stageAll(gitRoot) } } } @@ -177,6 +186,9 @@ fun main(arguments: Array) { if (request.enableGit || configuration.git.autoStage == true) { val gitRoot = projectModel.selectedModuleRootDir() ?: projectRoot + if (!git.isAvailable(gitRoot)) { + println("Warning: Git is not available. Configure [git].path in .cagrc or install git.") + } runCatching { git.stageAll(gitRoot) } } } diff --git a/cli/src/main/kotlin/com/mitteloupe/cag/cli/configuration/ClientConfiguration.kt b/cli/src/main/kotlin/com/mitteloupe/cag/cli/configuration/ClientConfiguration.kt index 3109366..430039f 100644 --- a/cli/src/main/kotlin/com/mitteloupe/cag/cli/configuration/ClientConfiguration.kt +++ b/cli/src/main/kotlin/com/mitteloupe/cag/cli/configuration/ClientConfiguration.kt @@ -2,7 +2,8 @@ package com.mitteloupe.cag.cli.configuration data class GitConfiguration( val autoInitialize: Boolean? = null, - val autoStage: Boolean? = null + val autoStage: Boolean? = null, + val path: String? = null ) data class ClientConfiguration( diff --git a/cli/src/main/kotlin/com/mitteloupe/cag/cli/configuration/ClientConfigurationLoader.kt b/cli/src/main/kotlin/com/mitteloupe/cag/cli/configuration/ClientConfigurationLoader.kt index fb8f125..27a4a86 100644 --- a/cli/src/main/kotlin/com/mitteloupe/cag/cli/configuration/ClientConfigurationLoader.kt +++ b/cli/src/main/kotlin/com/mitteloupe/cag/cli/configuration/ClientConfigurationLoader.kt @@ -40,7 +40,8 @@ class ClientConfigurationLoader { git = GitConfiguration( autoInitialize = override.git.autoInitialize ?: baseConfiguration.git.autoInitialize, - autoStage = override.git.autoStage ?: baseConfiguration.git.autoStage + autoStage = override.git.autoStage ?: baseConfiguration.git.autoStage, + path = override.git.path ?: baseConfiguration.git.path ) ) @@ -83,23 +84,30 @@ class ClientConfigurationLoader { } private fun extractGitConfiguration(text: String): GitConfiguration { + var isInGitSection = false var autoInitializeGit: Boolean? = null var autoStageGit: Boolean? = null + var gitPath: String? = null + text.lineSequence().forEach { rawLine -> val line = rawLine.trim() - if ( - line.startsWith("[") && line.endsWith("]") && - line.substring(1, line.length - 1).equals("git", ignoreCase = true) - ) { + if (line.isEmpty() || line.startsWith("#") || line.startsWith(";")) return@forEach + + if (line.startsWith("[") && line.endsWith("]")) { + isInGitSection = line.substring(1, line.length - 1).equals("git", ignoreCase = true) return@forEach } - if (line.startsWith("autoInitialize")) { + if (!isInGitSection) return@forEach + + if (line.startsWith("autoInitialize", ignoreCase = true)) { autoInitializeGit = line.substringAfter('=', "false").trim().toBoolean() - } else if (line.startsWith("autoStage")) { + } else if (line.startsWith("autoStage", ignoreCase = true)) { autoStageGit = line.substringAfter('=', "true").trim().toBoolean() + } else if (line.startsWith("path", ignoreCase = true)) { + gitPath = line.substringAfter('=', "").trim().ifEmpty { null } } } - return GitConfiguration(autoInitialize = autoInitializeGit, autoStage = autoStageGit) + return GitConfiguration(autoInitialize = autoInitializeGit, autoStage = autoStageGit, path = gitPath) } } diff --git a/git/src/main/kotlin/com/mitteloupe/cag/git/Git.kt b/git/src/main/kotlin/com/mitteloupe/cag/git/Git.kt index b1414ad..524ca26 100644 --- a/git/src/main/kotlin/com/mitteloupe/cag/git/Git.kt +++ b/git/src/main/kotlin/com/mitteloupe/cag/git/Git.kt @@ -3,17 +3,19 @@ package com.mitteloupe.cag.git import java.io.File class Git( + private val gitBinaryPath: String?, private val processExecutor: ProcessExecutor = ProcessExecutor() ) { + private fun command(vararg args: String): List = listOf(gitBinaryPath ?: "git") + args + + fun isAvailable(workingDirectory: File): Boolean = processExecutor.run(workingDirectory, command("--version")) + fun initializeRepository(directory: File): Boolean { if (isGitRepository(directory)) { return false } - return processExecutor.run( - directory, - listOf("git", "init") - ) + return processExecutor.run(directory, command("init")) } fun stage( @@ -24,7 +26,7 @@ class Git( return } val gitCommandWithArguments = - listOf("git", "add", "--") + + command("add", "--") + files.map { file -> val absolutePath = file.absolutePath val file = File(absolutePath) @@ -40,12 +42,12 @@ class Git( return processExecutor.run( directory, - listOf("git", "add", "-A") + command("add", "-A") ) } - fun isGitRepository(directory: File): Boolean { - val gitDirectory = File(directory, ".git") - return gitDirectory.exists() && gitDirectory.isDirectory - } + fun isGitRepository(directory: File): Boolean = + File(directory, ".git").let { gitDirectory -> + gitDirectory.exists() && gitDirectory.isDirectory + } } diff --git a/git/src/main/kotlin/com/mitteloupe/cag/git/ProcessExecutor.kt b/git/src/main/kotlin/com/mitteloupe/cag/git/ProcessExecutor.kt index 1265a5b..fbc24a9 100644 --- a/git/src/main/kotlin/com/mitteloupe/cag/git/ProcessExecutor.kt +++ b/git/src/main/kotlin/com/mitteloupe/cag/git/ProcessExecutor.kt @@ -9,12 +9,16 @@ class ProcessExecutor { ): Boolean = try { val process = - ProcessBuilder(command) - .directory(workingDirectory) - .redirectErrorStream(true) - .start() - val output = process.inputStream.bufferedReader().use { it.readText() } - val exitCode = process.waitFor() + try { + ProcessBuilder(command) + .directory(workingDirectory) + .redirectErrorStream(true) + .start() + } catch (_: Exception) { + null + } + val output = process?.inputStream?.bufferedReader().use { it?.readText() } + val exitCode = process?.waitFor() if (exitCode == 0) { true } else { diff --git a/git/src/test/kotlin/com/mitteloupe/cag/git/GitTest.kt b/git/src/test/kotlin/com/mitteloupe/cag/git/GitTest.kt index 145e434..a2290dc 100644 --- a/git/src/test/kotlin/com/mitteloupe/cag/git/GitTest.kt +++ b/git/src/test/kotlin/com/mitteloupe/cag/git/GitTest.kt @@ -22,7 +22,7 @@ class GitTest { fun setUp() { MockKAnnotations.init(this) projectRoot = createTempDirectory(prefix = "projRoot_").toFile() - classUnderTest = Git(executor) + classUnderTest = Git(gitBinaryPath = null, processExecutor = executor) } @After diff --git a/plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/git/GitAddQueueService.kt b/plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/git/GitAddQueueService.kt index a9d85af..13e767f 100644 --- a/plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/git/GitAddQueueService.kt +++ b/plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/git/GitAddQueueService.kt @@ -2,6 +2,7 @@ package com.mitteloupe.cag.cleanarchitecturegenerator.git import com.intellij.openapi.components.Service import com.intellij.openapi.project.Project +import com.mitteloupe.cag.cleanarchitecturegenerator.settings.AppSettingsService import com.mitteloupe.cag.git.Git import java.io.File import java.util.concurrent.ConcurrentSkipListSet @@ -9,7 +10,9 @@ import java.util.concurrent.ConcurrentSkipListSet @Service(Service.Level.PROJECT) class GitAddQueueService(private val project: Project) { private val queue = ConcurrentSkipListSet() - private val git = Git() + private val git: Git by lazy { + Git(gitBinaryPath = AppSettingsService.getInstance().gitPath) + } fun enqueue(file: File) { val path = file.absolutePath @@ -28,6 +31,8 @@ class GitAddQueueService(private val project: Project) { return } + if (!git.isAvailable(projectRoot)) return + git.stage(projectRoot, items.map(::File)) queue.removeAll(items) } diff --git a/plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/projectwizard/CleanArchitectureWizardTemplateProvider.kt b/plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/projectwizard/CleanArchitectureWizardTemplateProvider.kt index 883ebaf..22bcafd 100644 --- a/plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/projectwizard/CleanArchitectureWizardTemplateProvider.kt +++ b/plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/projectwizard/CleanArchitectureWizardTemplateProvider.kt @@ -28,7 +28,7 @@ import java.lang.reflect.Field class CleanArchitectureWizardTemplateProvider : WizardTemplateProvider() { private val ideBridge = IdeBridge() private val generatorProvider = GeneratorProvider() - private val git = Git() + private val git = Git(gitBinaryPath = AppSettingsService.getInstance().gitPath) override fun getTemplates(): List