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
2 changes: 0 additions & 2 deletions cli/src/main/kotlin/com/mitteloupe/cag/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import com.mitteloupe.cag.core.request.GenerateUseCaseRequest
import com.mitteloupe.cag.core.request.GenerateViewModelRequest
import java.io.File
import java.nio.file.Paths
import java.util.UUID
import kotlin.system.exitProcess

fun main(arguments: Array<String>) {
Expand Down Expand Up @@ -93,7 +92,6 @@ fun main(arguments: Array<String>) {
}
val projectTemplateRequest =
GenerateProjectTemplateRequest(
requestId = UUID.randomUUID().toString(),
destinationRootDirectory = projectTemplateDestinationDirectory,
projectName = request.projectName,
packageName = request.packageName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.mitteloupe.cag.core.request
import java.io.File

data class GenerateProjectTemplateRequest(
val requestId: String,
val destinationRootDirectory: File,
val projectName: String,
val packageName: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import java.util.concurrent.ConcurrentSkipListSet
@Service(Service.Level.PROJECT)
class GitAddQueueService(private val project: Project) {
private val queue = ConcurrentSkipListSet<String>()
private val gitStager = GitStager(ProcessExecutor())
private val gitStager = GitStager()

fun enqueue(file: File) {
val path = file.absolutePath
Expand All @@ -27,44 +27,7 @@ class GitAddQueueService(private val project: Project) {
return
}

gitStager.stageAll(projectRoot, items.map(::File))
gitStager.stage(projectRoot, items.map(::File))
queue.removeAll(items)
}
}

internal class GitStager(private val executor: ProcessExecutor) {
fun stageAll(
projectRoot: File,
files: Collection<File>
) {
if (files.isEmpty()) {
return
}
val gitCommandWithArguments =
listOf("git", "add", "--") +
files.map { file ->
val absolutePath = file.absolutePath
val file = File(absolutePath)
file.relativeToOrNull(projectRoot)?.path ?: absolutePath
}
executor.run(projectRoot, gitCommandWithArguments)
}
}

internal class ProcessExecutor {
fun run(
directory: File,
args: List<String>
) {
try {
val process =
ProcessBuilder(args)
.directory(directory)
.redirectErrorStream(true)
.start()
process.inputStream.bufferedReader().use { it.readText() }
process.waitFor()
} catch (_: Exception) {
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mitteloupe.cag.cleanarchitecturegenerator.git

import java.io.File

internal class GitInitializer(private val executor: ProcessExecutor = ProcessExecutor()) {
fun initialize(projectRoot: File) {
executor.run(projectRoot, listOf("git", "init"))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.mitteloupe.cag.cleanarchitecturegenerator.git

import java.io.File

internal class GitStager(private val executor: ProcessExecutor = ProcessExecutor()) {
fun stage(
projectRoot: File,
files: Collection<File>
) {
if (files.isEmpty()) {
return
}
val gitCommandWithArguments =
listOf("git", "add", "--") +
files.map { file ->
val absolutePath = file.absolutePath
val file = File(absolutePath)
file.relativeToOrNull(projectRoot)?.path ?: absolutePath
}
executor.run(projectRoot, gitCommandWithArguments)
}

fun stageAll(projectRoot: File) {
executor.run(projectRoot, listOf("git", "add", "-A"))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.mitteloupe.cag.cleanarchitecturegenerator.git

import com.intellij.openapi.diagnostic.Logger
import java.io.File

internal class ProcessExecutor {
private val logger = Logger.getInstance(ProcessExecutor::class.java)

fun run(
directory: File,
args: List<String>
) {
try {
val process =
ProcessBuilder(args)
.directory(directory)
.redirectErrorStream(true)
.start()
val output = process.inputStream.bufferedReader().use { it.readText() }
val exitCode = process.waitFor()
if (exitCode != 0) {
logger.warn("Command failed: '${args.joinToString(" ")}' in '${directory.absolutePath}' (exit=$exitCode)\nOutput:\n$output")
}
} catch (e: Exception) {
logger.warn("Process execution failed for '${args.joinToString(" ")}' in '${directory.absolutePath}'", e)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,21 @@ import com.android.tools.idea.wizard.template.WizardUiContext
import com.android.tools.idea.wizard.template.booleanParameter
import com.android.tools.idea.wizard.template.impl.activities.common.MIN_API
import com.android.tools.idea.wizard.template.template
import com.intellij.openapi.application.ApplicationManager
import com.mitteloupe.cag.cleanarchitecturegenerator.CleanArchitectureGeneratorBundle
import com.mitteloupe.cag.cleanarchitecturegenerator.IdeBridge
import com.mitteloupe.cag.cleanarchitecturegenerator.filesystem.GeneratorProvider
import com.mitteloupe.cag.cleanarchitecturegenerator.git.GitInitializer
import com.mitteloupe.cag.cleanarchitecturegenerator.git.GitStager
import com.mitteloupe.cag.cleanarchitecturegenerator.settings.AppSettingsService
import com.mitteloupe.cag.core.GenerationException
import com.mitteloupe.cag.core.request.GenerateProjectTemplateRequest
import java.io.File
import java.lang.reflect.Field
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap

class CleanArchitectureWizardTemplateProvider : WizardTemplateProvider() {
private val ideBridge = IdeBridge()
private val generatorProvider = GeneratorProvider()

private val processedRequests = ConcurrentHashMap<String, Boolean>()

private val executionId = UUID.randomUUID().toString()

override fun getTemplates(): List<Template> = listOf(cleanArchitectureTemplate)

private val cleanArchitectureTemplate =
Expand Down Expand Up @@ -81,22 +77,30 @@ class CleanArchitectureWizardTemplateProvider : WizardTemplateProvider() {
help = CleanArchitectureGeneratorBundle.message("wizard.parameter.retrofit.help")
}

val initializeGitRepository =
booleanParameter {
name = CleanArchitectureGeneratorBundle.message("wizard.parameter.git.init.name")
default = false
help = CleanArchitectureGeneratorBundle.message("wizard.parameter.git.init.help")
}

widgets(
CheckBoxWidget(enableKtlint),
CheckBoxWidget(enableDetekt),
CheckBoxWidget(enableCompose),
CheckBoxWidget(enableKtor),
CheckBoxWidget(enableRetrofit)
CheckBoxWidget(enableRetrofit),
CheckBoxWidget(initializeGitRepository)
)

thumb {
File("viewmodel-activity").resolve("template_blank_activity.png")
}

recipe = { data: TemplateData ->
val moduleData = (data as ModuleTemplateData)
if (this !is FindReferencesRecipeExecutor) {
val moduleData = (data as ModuleTemplateData)

if (processedRequests.putIfAbsent(executionId, true) == null) {
try {
val projectRootDirectory = moduleData.rootDir.parentFile
createProject(
Expand All @@ -106,7 +110,8 @@ class CleanArchitectureWizardTemplateProvider : WizardTemplateProvider() {
enableKtlint,
enableDetekt,
enableKtor,
enableRetrofit
enableRetrofit,
initializeGitRepository
)
ideBridge.refreshIde(projectRootDirectory)
} catch (exception: GenerationException) {
Expand All @@ -129,23 +134,33 @@ class CleanArchitectureWizardTemplateProvider : WizardTemplateProvider() {
enableKtlint: BooleanParameter,
enableDetekt: BooleanParameter,
enableKtor: BooleanParameter,
enableRetrofit: BooleanParameter
enableRetrofit: BooleanParameter,
initializeGitRepository: BooleanParameter
) {
ApplicationManager.getApplication().executeOnPooledThread {
val request =
GenerateProjectTemplateRequest(
requestId = executionId,
destinationRootDirectory = projectRootDirectory,
projectName = readProjectName(projectRootDirectory.name),
packageName = data.packageName,
enableCompose = enableCompose.value,
enableKtlint = enableKtlint.value,
enableDetekt = enableDetekt.value,
enableKtor = enableKtor.value,
enableRetrofit = enableRetrofit.value
)

generatorProvider.prepare(project = null).generate().generateProjectTemplate(request)
val request =
GenerateProjectTemplateRequest(
destinationRootDirectory = projectRootDirectory,
projectName = readProjectName(projectRootDirectory.name),
packageName = data.packageName,
enableCompose = enableCompose.value,
enableKtlint = enableKtlint.value,
enableDetekt = enableDetekt.value,
enableKtor = enableKtor.value,
enableRetrofit = enableRetrofit.value
)

generatorProvider.prepare(project = null).generate().generateProjectTemplate(request)

val initializeGitRepository = initializeGitRepository.value
if (initializeGitRepository) {
GitInitializer().initialize(projectRootDirectory)
}

if (AppSettingsService.getInstance().autoAddGeneratedFilesToGit) {
val gitDirectory = File(projectRootDirectory, ".git")
if (gitDirectory.exists()) {
runCatching { GitStager().stageAll(projectRootDirectory) }
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import com.intellij.openapi.components.service
)
@Service(Service.Level.APP)
class AppSettingsService : PersistentStateComponent<AppSettingsService.State> {
data class State(
val autoAddGeneratedFilesToGit: Boolean = false
)
class State {
var autoAddGeneratedFilesToGit: Boolean = false
}

private var state: State = State()

Expand All @@ -27,7 +27,7 @@ class AppSettingsService : PersistentStateComponent<AppSettingsService.State> {
var autoAddGeneratedFilesToGit: Boolean
get() = state.autoAddGeneratedFilesToGit
set(value) {
state = state.copy(autoAddGeneratedFilesToGit = value)
state.autoAddGeneratedFilesToGit = value
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ wizard.parameter.ktor.name=Include Ktor
wizard.parameter.ktor.help=Include Ktor for HTTP client networking
wizard.parameter.retrofit.name=Include Retrofit
wizard.parameter.retrofit.help=Include Retrofit for HTTP client networking
wizard.parameter.git.init.name=Initialize Git repository
wizard.parameter.git.init.help=Run 'git init' in the new project directory after generation
wizard.error.generation.failed=Failed to generate Clean Architecture project: {0}

error.symbol.not.found=Symbol not found.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class GitStagerTest {
every { executor.run(projectRoot, expectedArguments) } returns Unit

// When
classUnderTest.stageAll(projectRoot, givenFiles)
classUnderTest.stage(projectRoot, givenFiles)

// Then
verify { executor.run(projectRoot, expectedArguments) }
Expand All @@ -53,7 +53,7 @@ class GitStagerTest {
@Test
fun `Given empty queue When flush Then does not run git`() {
// When
classUnderTest.stageAll(projectRoot, emptyList())
classUnderTest.stage(projectRoot, emptyList())

// Then
verify(exactly = 0) { executor.run(any(), any()) }
Expand All @@ -71,7 +71,7 @@ class GitStagerTest {

try {
// When
classUnderTest.stageAll(projectRoot, givenFiles)
classUnderTest.stage(projectRoot, givenFiles)

// Then
verify { executor.run(projectRoot, expectedArguments) }
Expand Down