Skip to content
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
5 changes: 4 additions & 1 deletion cli/src/main/kotlin/com/mitteloupe/cag/cli/HelpContent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -200,6 +201,7 @@ object HelpContent {
[git]
autoInitialize=true
autoStage=true
path=/usr/bin/git

Example ./.cagrc (project overrides):
[new.versions]
Expand All @@ -210,6 +212,7 @@ object HelpContent {

[git]
autoInitialize=false
path=/opt/homebrew/bin/git
""".trimIndent()
)
}
Expand Down
14 changes: 13 additions & 1 deletion cli/src/main/kotlin/com/mitteloupe/cag/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ fun main(arguments: Array<String>) {
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 =
Expand All @@ -111,6 +111,9 @@ fun main(arguments: Array<String>) {

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) }
Expand All @@ -135,6 +138,9 @@ fun main(arguments: Array<String>) {

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) }
}
}
Expand All @@ -159,6 +165,9 @@ fun main(arguments: Array<String>) {

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) }
}
}
Expand All @@ -177,6 +186,9 @@ fun main(arguments: Array<String>) {

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) }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
)

Expand Down Expand Up @@ -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)
}
}
22 changes: 12 additions & 10 deletions git/src/main/kotlin/com/mitteloupe/cag/git/Git.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> = 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(
Expand All @@ -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)
Expand All @@ -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
}
}
16 changes: 10 additions & 6 deletions git/src/main/kotlin/com/mitteloupe/cag/git/ProcessExecutor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion git/src/test/kotlin/com/mitteloupe/cag/git/GitTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ 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

@Service(Service.Level.PROJECT)
class GitAddQueueService(private val project: Project) {
private val queue = ConcurrentSkipListSet<String>()
private val git = Git()
private val git: Git by lazy {
Git(gitBinaryPath = AppSettingsService.getInstance().gitPath)
}

fun enqueue(file: File) {
val path = file.absolutePath
Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Template> = listOf(cleanArchitectureTemplate)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.intellij.openapi.components.service
class AppSettingsService : PersistentStateComponent<AppSettingsService.State> {
class State {
var autoAddGeneratedFilesToGit: Boolean = false
var gitPath: String? = null
}

private var state: State = State()
Expand All @@ -30,6 +31,12 @@ class AppSettingsService : PersistentStateComponent<AppSettingsService.State> {
state.autoAddGeneratedFilesToGit = value
}

var gitPath: String?
get() = state.gitPath
set(value) {
state.gitPath = value
}

companion object {
fun getInstance(): AppSettingsService = service()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package com.mitteloupe.cag.cleanarchitecturegenerator.settings.versioncatalog

import com.intellij.icons.AllIcons
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.options.BoundSearchableConfigurable
import com.intellij.openapi.options.Configurable
import com.intellij.openapi.options.ConfigurableProvider
import com.intellij.ui.dsl.builder.bindSelected
import com.intellij.ui.dsl.builder.bindText
import com.intellij.ui.dsl.builder.panel
import com.jetbrains.rd.generator.nova.GenerationSpec.Companion.nullIfEmpty
import com.mitteloupe.cag.cleanarchitecturegenerator.CleanArchitectureGeneratorBundle
import com.mitteloupe.cag.cleanarchitecturegenerator.settings.AppSettingsService
import com.mitteloupe.cag.git.Git
import java.io.File
import javax.swing.JLabel
import javax.swing.event.DocumentEvent
import javax.swing.event.DocumentListener

class RootConfigurableProvider : ConfigurableProvider() {
override fun createConfigurable(): Configurable = RootConfigurable()
Expand All @@ -18,11 +27,23 @@ private class RootConfigurable : BoundSearchableConfigurable(
) {
private val serviceAutoAddToGit: Boolean
get() = AppSettingsService.getInstance().autoAddGeneratedFilesToGit
private val serviceGitPath: String?
get() = AppSettingsService.getInstance().gitPath

private var autoAddToGit: Boolean = serviceAutoAddToGit
private var gitPath: String = serviceGitPath.orEmpty()

override fun createPanel() =
panel {
var warningLabel: JLabel? = null

fun isGitAvailableForState(pathText: String): Boolean =
Git(gitBinaryPath = pathText.nullIfEmpty()).isAvailable(File(System.getProperty("user.home")))

fun updateWarning(currentPath: String) {
val showWarning = !isGitAvailableForState(currentPath.trim())
warningLabel?.isVisible = showWarning
}
row {
text(CleanArchitectureGeneratorBundle.message("settings.root.description"))
}
Expand All @@ -31,18 +52,69 @@ private class RootConfigurable : BoundSearchableConfigurable(
checkBox(CleanArchitectureGeneratorBundle.message("settings.auto.add.to.git.label"))
.applyToComponent {
toolTipText = CleanArchitectureGeneratorBundle.message("settings.auto.add.to.git.tooltip")
addChangeListener { updateWarning(gitPath) }
}
.bindSelected(::autoAddToGit)
}

row(CleanArchitectureGeneratorBundle.message("settings.git.path.label")) {
val descriptor = FileChooserDescriptorFactory.createSingleFileOrExecutableAppDescriptor()
@Suppress("UnstableApiUsage")
textFieldWithBrowseButton(
project = null,
fileChooserDescriptor = descriptor,
fileChosen = { it.path }
)
.applyToComponent {
toolTipText = CleanArchitectureGeneratorBundle.message("settings.git.path.tooltip")
textField.document.addDocumentListener(
object : DocumentListener {
override fun insertUpdate(event: DocumentEvent) {
updateWarning(text)
}

override fun removeUpdate(event: DocumentEvent) {
updateWarning(text)
}

override fun changedUpdate(evenet: DocumentEvent) {
updateWarning(text)
}
}
)
}
.bindText(::gitPath)
}

row {
label(CleanArchitectureGeneratorBundle.message("settings.git.not.found.warning")).applyToComponent {
icon = AllIcons.General.Warning
warningLabel = this
isVisible = false
}
}

onApply {
updateWarning(gitPath)
}
onReset {
updateWarning(gitPath)
}
onIsModified {
updateWarning(gitPath)
false
}

onApply {
AppSettingsService.getInstance().autoAddGeneratedFilesToGit = autoAddToGit
AppSettingsService.getInstance().gitPath = gitPath.ifBlank { null }
}
onReset {
autoAddToGit = serviceAutoAddToGit
gitPath = serviceGitPath ?: ""
}
onIsModified {
autoAddToGit != serviceAutoAddToGit
autoAddToGit != serviceAutoAddToGit || gitPath != (serviceGitPath ?: "")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,6 @@ settings.versions.reset.all.tooltip=Remove all overrides and restore default val

settings.auto.add.to.git.label=Automatically add generated files to Git
settings.auto.add.to.git.tooltip=When enabled, newly generated files will be added to the staging area of the project Git repository automatically
settings.git.path.label=Git executable path (optional)
settings.git.path.tooltip=Absolute path to git executable, e.g., /usr/bin/git. Leave empty to use PATH.
settings.git.not.found.warning=Git not found. Set a valid path.