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
1 change: 1 addition & 0 deletions cli/src/main/kotlin/com/mitteloupe/cag/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ fun main(arguments: Array<String>) {
projectName = request.projectName,
packageName = request.packageName,
overrideMinimumAndroidSdk = null,
overrideAndroidGradlePluginVersion = null,
enableCompose = request.enableCompose,
enableKtlint = request.enableKtlint,
enableDetekt = request.enableDetekt,
Expand Down
35 changes: 16 additions & 19 deletions core/src/main/kotlin/com/mitteloupe/cag/core/Generator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import com.mitteloupe.cag.core.generation.architecture.CoroutineModuleContentGen
import com.mitteloupe.cag.core.generation.versioncatalog.DependencyConfiguration
import com.mitteloupe.cag.core.generation.versioncatalog.LibraryConstants
import com.mitteloupe.cag.core.generation.versioncatalog.PluginConstants
import com.mitteloupe.cag.core.generation.versioncatalog.SectionEntryRequirement
import com.mitteloupe.cag.core.generation.versioncatalog.VersionCatalogConstants
import com.mitteloupe.cag.core.generation.versioncatalog.VersionCatalogUpdater
import com.mitteloupe.cag.core.generation.withoutSpaces
Expand Down Expand Up @@ -392,12 +391,13 @@ class Generator(
throw GenerationException("Package name is missing.")
}

println("Comparing ${request.destinationRootDirectory.name} to ${projectName.withoutSpaces()}")
val sanitizedProjectName = projectName.withoutSpaces()
println("Comparing ${request.destinationRootDirectory.name} to $sanitizedProjectName")
val projectRoot =
if (request.destinationRootDirectory.name == projectName.withoutSpaces()) {
if (request.destinationRootDirectory.name.matches("$sanitizedProjectName\\d*".toRegex())) {
request.destinationRootDirectory
} else {
File(request.destinationRootDirectory, projectName.withoutSpaces())
File(request.destinationRootDirectory, sanitizedProjectName)
}

if (projectRoot != request.destinationRootDirectory && projectRoot.exists()) {
Expand Down Expand Up @@ -434,24 +434,21 @@ class Generator(
add(PluginConstants.DETEKT)
}
}
val overrideVersions =
if (request.overrideMinimumAndroidSdk == null) {
VersionCatalogConstants.ANDROID_VERSIONS
} else {
VersionCatalogConstants.ANDROID_VERSIONS.map { androidVersion ->
if (androidVersion.key == VersionCatalogConstants.MIN_SDK_VERSION.key) {
SectionEntryRequirement.VersionRequirement(
key = androidVersion.key,
version = request.overrideMinimumAndroidSdk.toString()
)
} else {
androidVersion
}
}
val versionOverrides =
listOf(
VersionCatalogConstants.MIN_SDK_VERSION to request.overrideMinimumAndroidSdk?.toString(),
VersionCatalogConstants.ANDROID_GRADLE_PLUGIN_VERSION to request.overrideAndroidGradlePluginVersion
).mapNotNull { override -> override.second?.let { override.first to it } }
.toMap()
val androidVersions =
VersionCatalogConstants.ANDROID_VERSIONS.map { version ->
versionOverrides[version]?.let { versionOverride ->
version.copy(version = versionOverride)
} ?: version
}
val dependencyConfiguration =
DependencyConfiguration(
versions = overrideVersions,
versions = androidVersions,
libraries = libraries,
plugins = plugins
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ fun buildAndroidManifest(appName: String): String =
</manifest>
""".trimIndent()

fun buildStringsXml(packageName: String): String =
fun buildStringsXml(appName: String): String =
"""
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">${packageName.split('.').last().capitalized}</string>
<string name="app_name">$appName</string>
</resources>
""".trimIndent()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,15 @@ class AppModuleContentGenerator(

fileCreator.createDirectoryIfNotExists(basePackageDir)

val sanitizedAppName = appName.withoutSpaces()
val mainActivityFile = File(basePackageDir, "MainActivity.kt")
val mainActivityContent =
fileCreator.createFileIfNotExists(mainActivityFile) {
buildMainActivityKotlinFile(
appName = appName,
appName = sanitizedAppName,
projectNamespace = projectNamespace,
enableCompose = enableCompose
)
fileCreator.createFileIfNotExists(mainActivityFile) { mainActivityContent }
}

val applicationFile = File(basePackageDir, "Application.kt")
val applicationContent = buildApplicationKotlinFile(projectNamespace)
Expand All @@ -91,24 +92,25 @@ class AppModuleContentGenerator(
packageName: String,
enableCompose: Boolean
) {
val sanitizedAppName = appName.withoutSpaces()
val manifestFile = File(appModuleDirectory, "src/main/AndroidManifest.xml")
fileCreator.createOrUpdateFile(manifestFile) { buildAndroidManifest(appName) }
fileCreator.createOrUpdateFile(manifestFile) { buildAndroidManifest(sanitizedAppName) }

val valuesDirectory = File(appModuleDirectory, "src/main/res/values")
fileCreator.createDirectoryIfNotExists(valuesDirectory)
val stringsFile = File(valuesDirectory, "strings.xml")
fileCreator.createFileIfNotExists(stringsFile) { buildStringsXml(packageName) }
fileCreator.createFileIfNotExists(stringsFile) { buildStringsXml(appName) }
val xmlDirectory = File(appModuleDirectory, "src/main/res/xml")
fileCreator.createDirectoryIfNotExists(xmlDirectory)

if (enableCompose) {
val themeFile = File(valuesDirectory, "themes.xml")
fileCreator.createFileIfNotExists(themeFile) { buildThemesXml(appName) }
fileCreator.createFileIfNotExists(themeFile) { buildThemesXml(sanitizedAppName) }

val uiDirectory = File(appModuleDirectory, "src/main/java/${packageName.replace('.', '/')}/ui/theme")
fileCreator.createDirectoryIfNotExists(uiDirectory)
val themeKtFile = File(uiDirectory, "Theme.kt")
fileCreator.createFileIfNotExists(themeKtFile) { buildThemeKt(appName = appName, packageName = packageName) }
fileCreator.createFileIfNotExists(themeKtFile) { buildThemeKt(appName = sanitizedAppName, packageName = packageName) }

val colorsKtFile = File(uiDirectory, "Color.kt")
fileCreator.createFileIfNotExists(colorsKtFile) { buildColorsKt(packageName) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@ class GradleWrapperCreator(private val fileCreator: FileCreator) {
fileCreator.createDirectoryIfNotExists(gradleWrapperDirectory)

val gradleWrapperPropertiesFile = File(gradleWrapperDirectory, "gradle-wrapper.properties")
val gradleWrapperPropertiesContent = buildGradleWrapperPropertiesFile()

fileCreator.createFileIfNotExists(gradleWrapperPropertiesFile) { gradleWrapperPropertiesContent }
fileCreator.createOrUpdateFile(gradleWrapperPropertiesFile) { buildGradleWrapperPropertiesFile() }

val gradleWrapperJarFile = File(gradleWrapperDirectory, "gradle-wrapper.jar")
fileCreator.createBinaryFileIfNotExists(gradleWrapperJarFile) { getGradleWrapperResourceAsBytes() }

val gradlewFile = File(projectRoot, "gradlew")
val gradlewContent = getResourceAsString("gradlew")
fileCreator.createFileIfNotExists(gradlewFile) { gradlewContent }
fileCreator.createFileIfNotExists(gradlewFile) { getResourceAsString("gradlew") }
if (!gradlewFile.setExecutable(true)) {
throw GenerationException("Failed to make gradlew executable")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@ class SettingsFileUpdater(private val fileCreator: FileCreator) {
featureNames: List<String>
) {
val settingsFile = File(projectRoot, "settings.gradle.kts")
val content = buildSettingsGradleScript(projectName, featureNames)
runCatching { fileCreator.createOrUpdateFile(settingsFile) { content } }
.onFailure { throw GenerationException("Failed to create settings.gradle.kts: ${it.message}") }
runCatching {
fileCreator.createOrUpdateFile(settingsFile) {
buildSettingsGradleScript(projectName.withoutSpaces(), featureNames)
}
}.onFailure { throw GenerationException("Failed to create settings.gradle.kts: ${it.message}") }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ class VersionCatalogUpdater(
insertPositionIfMissing = CatalogInsertPosition.Start,
requirements =
(dependencyConfiguration.versions + pluginRequirements.versions + libraryRequirements.versions)
.distinct()
.associateBy { it.key }
.values
.toList()
),
SectionTransaction(
insertPositionIfMissing = CatalogInsertPosition.End,
Expand Down Expand Up @@ -221,8 +223,10 @@ class VersionCatalogUpdater(
}

val versionRequirements =
(dependencyConfiguration.versions + pluginRequirements.versions + libraryRequirements.versions)
.distinct()
(pluginRequirements.versions + libraryRequirements.versions + dependencyConfiguration.versions)
.associateBy { it.key }
.values
.toList()

updateVersionCatalogIfPresent(
projectRootDir = projectRootDir,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ data class GenerateProjectTemplateRequest(
val projectName: String,
val packageName: String,
val overrideMinimumAndroidSdk: Int?,
val overrideAndroidGradlePluginVersion: String?,
val enableCompose: Boolean,
val enableKtlint: Boolean,
val enableDetekt: Boolean,
Expand Down
6 changes: 3 additions & 3 deletions core/src/test/kotlin/com/mitteloupe/cag/core/GeneratorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -527,12 +527,12 @@ class GeneratorTest {
val expectedContent =
"""
[versions]
kotlin = "2.2.10"
ksp = "2.2.10-2.0.2"
androidGradlePlugin = "8.12.2"
compileSdk = "36"
minSdk = "24"
targetSdk = "36"
androidGradlePlugin = "8.12.2"
kotlin = "2.2.10"
ksp = "2.2.10-2.0.2"

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class GradleWrapperCreatorTest {
}

@Test
fun `Given existing gradle-wrapper-properties when writeGradleWrapperFiles then does not overwrite existing file`() {
fun `Given existing gradle-wrapper-properties when writeGradleWrapperFiles then overwrites existing file`() {
// Given
val projectRoot = createTempDirectory(prefix = "projectRoot").toFile()
val gradleWrapperDirectory = File(projectRoot, "gradle/wrapper")
Expand All @@ -98,12 +98,21 @@ class GradleWrapperCreatorTest {
val gradleWrapperPropertiesFile = File(gradleWrapperDirectory, "gradle-wrapper.properties")
val initialContent = "initial content"
gradleWrapperPropertiesFile.writeText(initialContent)
val expectedContent =
"""
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
""".trimIndent()

// When
classUnderTest.writeGradleWrapperFiles(projectRoot)
val actualContent = gradleWrapperPropertiesFile.readText()

// Then
assertEquals("Existing gradle wrapper properties should not be overwritten", initialContent, gradleWrapperPropertiesFile.readText())
assertEquals("Existing gradle wrapper properties should be overwritten", expectedContent, actualContent)
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ ktlint = "12.1.1"
shadow = "8.1.1"
mockk = "1.13.11"
junit4 = "4.13.2"
androidStudio = "2025.1.4.2"
androidStudio = "2025.1.4.7"
hamcrest = "3.0"

[libraries]
Expand Down
3 changes: 3 additions & 0 deletions plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ repositories {
intellijPlatform {
pluginVerification {
ides {
ide(IntelliJPlatformType.AndroidStudio, "2024.3.1.13")
ide(IntelliJPlatformType.AndroidStudio, "2025.1.2.11")
ide(IntelliJPlatformType.AndroidStudio, "2025.1.3.7")
ide(IntelliJPlatformType.AndroidStudio, libs.versions.androidStudio.get())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.ValidationInfo
import com.intellij.ui.dsl.builder.bindText
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.builder.whenItemChangedFromUi
import com.intellij.ui.dsl.builder.whenItemSelectedFromUi
import com.intellij.util.ui.UIUtil
import com.mitteloupe.cag.cleanarchitecturegenerator.form.PredicateDocumentFilter
import com.mitteloupe.cag.cleanarchitecturegenerator.validation.SymbolValidator
Expand Down Expand Up @@ -91,7 +91,7 @@ class CreateUseCaseDialog(
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.input.type.label")) {
@Suppress("UnstableApiUsage")
comboBox(inputDataTypeModel)
.whenItemChangedFromUi { _inputDataType = it }
.whenItemSelectedFromUi { _inputDataType = it }
.applyToComponent {
inputDataTypeComboBox = this
isEditable = true
Expand All @@ -108,7 +108,7 @@ class CreateUseCaseDialog(
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.output.type.label")) {
@Suppress("UnstableApiUsage")
comboBox(outputDataTypeModel)
.whenItemChangedFromUi { _outputDataType = it }
.whenItemSelectedFromUi { _outputDataType = it }
.applyToComponent {
outputDataTypeComboBox = this
isEditable = true
Expand Down
Loading