Skip to content

Commit 58ff24c

Browse files
authored
fixing parsing yaml files when settings or build gradle files are not included (#280)
1 parent efd2120 commit 58ff24c

7 files changed

Lines changed: 290 additions & 15 deletions

File tree

cli/src/main/kotlin/io/github/cdsap/projectgenerator/cli/Main.kt

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
package io.github.cdsap.projectgenerator.cli
22

3-
import com.fasterxml.jackson.annotation.JsonSetter
4-
import com.fasterxml.jackson.annotation.Nulls
5-
import com.fasterxml.jackson.databind.ObjectMapper
6-
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
7-
import com.fasterxml.jackson.module.kotlin.KotlinModule
8-
import com.fasterxml.jackson.module.kotlin.readValue
93
import com.github.ajalt.clikt.core.CliktCommand
104
import com.github.ajalt.clikt.core.UsageError
115
import com.github.ajalt.clikt.core.main
@@ -102,14 +96,6 @@ class GenerateProjects : CliktCommand(name = "generate-project") {
10296
).write()
10397
}
10498

105-
private fun parseYaml(rules: File): Versions {
106-
val mapper = ObjectMapper(YAMLFactory()).apply {
107-
registerModule(KotlinModule())
108-
configOverride(List::class.java).setterInfo = JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)
109-
}
110-
return mapper.readValue(rules)
111-
}
112-
11399
private fun getDevelocityEnabled(develocity: Boolean, develocityUrl: String?): Boolean {
114100
return if (develocity) {
115101
return true
@@ -125,7 +111,7 @@ class GenerateProjects : CliktCommand(name = "generate-project") {
125111
kotlinMultiplatformLibrary: Boolean
126112
): Versions {
127113
val versions = if (fileVersions != null) {
128-
parseYaml(fileVersions)
114+
VersionsParser.fromFile(fileVersions)
129115
} else {
130116
Versions()
131117
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.github.cdsap.projectgenerator.cli
2+
3+
import com.fasterxml.jackson.annotation.JsonSetter
4+
import com.fasterxml.jackson.annotation.Nulls
5+
import com.fasterxml.jackson.databind.ObjectMapper
6+
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
7+
import com.fasterxml.jackson.module.kotlin.KotlinModule
8+
import io.github.cdsap.projectgenerator.model.VersionsFile
9+
import io.github.cdsap.projectgenerator.model.Versions
10+
import java.io.File
11+
12+
object VersionsParser {
13+
14+
private val mapper = ObjectMapper(YAMLFactory()).apply {
15+
registerModule(KotlinModule())
16+
configOverride(List::class.java).setterInfo = JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)
17+
}
18+
19+
/**
20+
* Parses a YAML file into [Versions].
21+
* [VersionsFile] keeps plugin list fields nullable so omitted YAML keys can resolve to empty lists
22+
* instead of inheriting the runtime defaults from [Versions].
23+
*/
24+
fun fromFile(file: File): Versions = mapper.readValue(file, VersionsFile::class.java).resolve()
25+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package io.github.cdsap.projectgenerator.cli
2+
3+
import io.github.cdsap.projectgenerator.model.AdditionalPlugin
4+
import org.junit.jupiter.api.Assertions.assertEquals
5+
import org.junit.jupiter.api.Assertions.assertTrue
6+
import org.junit.jupiter.api.Test
7+
import org.junit.jupiter.api.io.TempDir
8+
import java.io.File
9+
import java.nio.file.Path
10+
11+
class VersionsParserTest {
12+
13+
@TempDir
14+
lateinit var tempDir: Path
15+
16+
@Test
17+
fun `parses YAML without additionalSettingsPlugins and additionalBuildGradleRootPlugins as empty lists`() {
18+
val yaml = """
19+
project:
20+
jdk: "17"
21+
kotlin:
22+
kgp: "2.0.0"
23+
""".trimIndent()
24+
25+
val file = File(tempDir.toFile(), "versions.yaml").apply { writeText(yaml) }
26+
val versions = VersionsParser.fromFile(file)
27+
28+
assertTrue(versions.additionalSettingsPlugins.isEmpty())
29+
assertTrue(versions.additionalBuildGradleRootPlugins.isEmpty())
30+
assertEquals("17", versions.project.jdk)
31+
assertEquals("2.0.0", versions.kotlin.kgp)
32+
}
33+
34+
@Test
35+
fun `parses YAML with additionalSettingsPlugins and additionalBuildGradleRootPlugins when present`() {
36+
val yaml = """
37+
project:
38+
jdk: "21"
39+
additionalSettingsPlugins:
40+
- id: com.fueledbycaffeine.spotlight
41+
version: 1.4.1
42+
apply: true
43+
additionalBuildGradleRootPlugins:
44+
- id: com.autonomousapps.dependency-analysis
45+
version: 2.19.0
46+
apply: true
47+
""".trimIndent()
48+
49+
val file = File(tempDir.toFile(), "versions.yaml").apply { writeText(yaml) }
50+
val versions = VersionsParser.fromFile(file)
51+
52+
assertEquals(1, versions.additionalSettingsPlugins.size)
53+
assertEquals(AdditionalPlugin("com.fueledbycaffeine.spotlight", "1.4.1", true), versions.additionalSettingsPlugins.first())
54+
55+
assertEquals(1, versions.additionalBuildGradleRootPlugins.size)
56+
assertEquals(AdditionalPlugin("com.autonomousapps.dependency-analysis", "2.19.0", true), versions.additionalBuildGradleRootPlugins.first())
57+
}
58+
59+
@Test
60+
fun `parses YAML with empty additionalSettingsPlugins and additionalBuildGradleRootPlugins arrays`() {
61+
val yaml = """
62+
project:
63+
jdk: "17"
64+
additionalSettingsPlugins: []
65+
additionalBuildGradleRootPlugins: []
66+
""".trimIndent()
67+
68+
val file = File(tempDir.toFile(), "versions.yaml").apply { writeText(yaml) }
69+
val versions = VersionsParser.fromFile(file)
70+
71+
assertTrue(versions.additionalSettingsPlugins.isEmpty())
72+
assertTrue(versions.additionalBuildGradleRootPlugins.isEmpty())
73+
}
74+
75+
@Test
76+
fun `parses YAML with only one plugin key present uses empty for the absent one`() {
77+
val yaml = """
78+
project:
79+
jdk: "17"
80+
additionalSettingsPlugins:
81+
- id: com.fueledbycaffeine.spotlight
82+
version: 1.4.1
83+
""".trimIndent()
84+
85+
val file = File(tempDir.toFile(), "versions.yaml").apply { writeText(yaml) }
86+
val versions = VersionsParser.fromFile(file)
87+
88+
assertEquals(1, versions.additionalSettingsPlugins.size)
89+
assertTrue(versions.additionalBuildGradleRootPlugins.isEmpty())
90+
}
91+
92+
@Test
93+
fun `parses YAML with additionalBuildGradleRootPlugins present and additionalSettingsPlugins absent`() {
94+
val yaml = """
95+
project:
96+
jdk: "17"
97+
additionalBuildGradleRootPlugins:
98+
- id: com.autonomousapps.dependency-analysis
99+
version: 2.19.0
100+
""".trimIndent()
101+
102+
val file = File(tempDir.toFile(), "versions.yaml").apply { writeText(yaml) }
103+
val versions = VersionsParser.fromFile(file)
104+
105+
assertTrue(versions.additionalSettingsPlugins.isEmpty())
106+
assertEquals(1, versions.additionalBuildGradleRootPlugins.size)
107+
assertEquals(AdditionalPlugin("com.autonomousapps.dependency-analysis", "2.19.0", true), versions.additionalBuildGradleRootPlugins.first())
108+
}
109+
110+
@Test
111+
fun `parses YAML with null plugin lists as empty lists`() {
112+
val yaml = """
113+
project:
114+
jdk: "17"
115+
additionalSettingsPlugins:
116+
additionalBuildGradleRootPlugins:
117+
""".trimIndent()
118+
119+
val file = File(tempDir.toFile(), "versions.yaml").apply { writeText(yaml) }
120+
val versions = VersionsParser.fromFile(file)
121+
122+
assertTrue(versions.additionalSettingsPlugins.isEmpty())
123+
assertTrue(versions.additionalBuildGradleRootPlugins.isEmpty())
124+
}
125+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.github.cdsap.projectgenerator.model
2+
3+
data class VersionsFile(
4+
val kotlin: Kotlin = Kotlin(),
5+
val android: Android = Android(),
6+
val testing: Testing = Testing(),
7+
val project: Project = Project(),
8+
val di: DependencyInjection = DependencyInjection.HILT,
9+
val additionalBuildGradleRootPlugins: List<AdditionalPlugin>? = null,
10+
val additionalSettingsPlugins: List<AdditionalPlugin>? = null
11+
) {
12+
fun resolve(base: Versions = Versions()): Versions = base.copy(
13+
kotlin = kotlin,
14+
android = android,
15+
testing = testing,
16+
project = project,
17+
di = di,
18+
additionalBuildGradleRootPlugins = additionalBuildGradleRootPlugins ?: emptyList(),
19+
additionalSettingsPlugins = additionalSettingsPlugins ?: emptyList()
20+
)
21+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.github.cdsap.projectgenerator.generator.rootproject
2+
3+
import io.github.cdsap.projectgenerator.model.AdditionalPlugin
4+
import io.github.cdsap.projectgenerator.model.Versions
5+
import org.junit.jupiter.api.Assertions.assertFalse
6+
import org.junit.jupiter.api.Assertions.assertTrue
7+
import org.junit.jupiter.api.Test
8+
9+
class SettingsGradleTest {
10+
11+
@Test
12+
fun `does not include plugins block when additionalSettingsPlugins is empty and develocity is false`() {
13+
val versions = Versions(additionalSettingsPlugins = emptyList())
14+
val result = SettingsGradle().get(versions, develocity = false, projectName = "testProject")
15+
16+
assertFalse(
17+
result.contains("plugins {"),
18+
"settings.gradle.kts should not contain plugins block when additionalSettingsPlugins is empty and develocity is false"
19+
)
20+
assertFalse(
21+
result.contains("spotlight") || result.contains("com.fueledbycaffeine.spotlight"),
22+
"settings.gradle.kts should not contain Spotlight when additionalSettingsPlugins is empty"
23+
)
24+
}
25+
26+
@Test
27+
fun `includes settings plugins when additionalSettingsPlugins is provided`() {
28+
val versions = Versions(
29+
additionalSettingsPlugins = listOf(
30+
AdditionalPlugin("com.fueledbycaffeine.spotlight", "1.4.1")
31+
)
32+
)
33+
val result = SettingsGradle().get(versions, develocity = false, projectName = "testProject")
34+
35+
assertTrue(result.contains("plugins {"))
36+
assertTrue(result.contains("com.fueledbycaffeine.spotlight"))
37+
assertTrue(result.contains("1.4.1"))
38+
}
39+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.github.cdsap.projectgenerator.model
2+
3+
import org.junit.jupiter.api.Assertions.assertEquals
4+
import org.junit.jupiter.api.Assertions.assertTrue
5+
import org.junit.jupiter.api.Test
6+
7+
class VersionsFileTest {
8+
9+
@Test
10+
fun `resolve uses empty plugin lists when plugin overrides are omitted`() {
11+
val versions = VersionsFile(project = Project(jdk = "21")).resolve()
12+
13+
assertEquals("21", versions.project.jdk)
14+
assertTrue(versions.additionalSettingsPlugins.isEmpty())
15+
assertTrue(versions.additionalBuildGradleRootPlugins.isEmpty())
16+
}
17+
18+
@Test
19+
fun `resolve keeps explicit plugin overrides`() {
20+
val versions = VersionsFile(
21+
additionalSettingsPlugins = listOf(
22+
AdditionalPlugin("com.fueledbycaffeine.spotlight", "1.4.1")
23+
),
24+
additionalBuildGradleRootPlugins = listOf(
25+
AdditionalPlugin("com.autonomousapps.dependency-analysis", "2.19.0", apply = false)
26+
)
27+
).resolve()
28+
29+
assertEquals(
30+
listOf(AdditionalPlugin("com.fueledbycaffeine.spotlight", "1.4.1")),
31+
versions.additionalSettingsPlugins
32+
)
33+
assertEquals(
34+
listOf(AdditionalPlugin("com.autonomousapps.dependency-analysis", "2.19.0", apply = false)),
35+
versions.additionalBuildGradleRootPlugins
36+
)
37+
}
38+
}

project-generator/src/test/kotlin/io/github/cdsap/projectgenerator/writer/ProjectWriterTest.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,47 @@ class ProjectWriterTest {
197197
}
198198
}
199199

200+
@Test
201+
fun `does not include settings or build plugins when additionalSettingsPlugins and additionalBuildGradleRootPlugins are empty`() {
202+
val nodes = listOf(
203+
ProjectGraph("module_1_1", 1, emptyList(), TypeProject.ANDROID_APP, 10),
204+
)
205+
val language = LanguageAttributes("gradle.kts", "${tempDir}/project_no_plugins")
206+
val versions = Versions(
207+
additionalSettingsPlugins = emptyList(),
208+
additionalBuildGradleRootPlugins = emptyList()
209+
)
210+
val projectWriter = ProjectWriter(
211+
nodes,
212+
listOf(language),
213+
versions,
214+
TypeProjectRequested.ANDROID,
215+
TypeOfStringResources.NORMAL,
216+
false,
217+
GradleWrapper(LATEST_GRADLE),
218+
false,
219+
"no_plugins_project"
220+
)
221+
222+
projectWriter.write()
223+
224+
val settingsFile = File("${language.projectName}/settings.gradle.kts")
225+
assertTrue(settingsFile.exists(), "Expected settings.gradle.kts to exist")
226+
val settingsContent = settingsFile.readText()
227+
assertTrue(
228+
!settingsContent.contains("spotlight") && !settingsContent.contains("com.fueledbycaffeine.spotlight"),
229+
"settings.gradle.kts should not contain Spotlight plugin when additionalSettingsPlugins is empty, but contained: $settingsContent"
230+
)
231+
232+
val buildFile = File("${language.projectName}/build.gradle.kts")
233+
assertTrue(buildFile.exists(), "Expected build.gradle.kts to exist")
234+
val buildContent = buildFile.readText()
235+
assertTrue(
236+
!buildContent.contains("dependency-analysis") && !buildContent.contains("com.autonomousapps.dependency-analysis"),
237+
"build.gradle.kts should not contain dependency-analysis plugin when additionalBuildGradleRootPlugins is empty, but contained: $buildContent"
238+
)
239+
}
240+
200241
@Test
201242
fun `writes android library sources into androidMain when kotlin multiplatform library is enabled`() {
202243
val node = ProjectGraph("module_1_1", 1, emptyList(), TypeProject.ANDROID_LIB, 10)

0 commit comments

Comments
 (0)