From 3aa546ffe30b40fb58419a71f4792968d106b78b Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Tue, 17 Jun 2025 13:06:24 -0400 Subject: [PATCH] fix(gradle): add build-ci target even if atomized=false (#31537) ## Current Behavior when atomized=false, it does not generate build-ci and check-ci targets ## Expected Behavior - when atomized=false, it should still generate build-ci and check-ci targets - upgrade dev.nx.gradle.project-graph version to 0.1.2 ## Related Issue(s) Fixes # --- docs/generated/manifests/new-nx-api.json | 10 + docs/generated/packages-metadata.json | 10 + .../change-plugin-version-0-1-2.json | 14 ++ packages/gradle/migrations.json | 6 + .../gradle/project-graph/build.gradle.kts | 2 +- .../dev/nx/gradle/utils/CiTargetsUtils.kt | 9 +- .../dev/nx/gradle/utils/ProjectUtils.kt | 64 +++-- .../dev/nx/gradle/utils/ClassDetectionTest.kt | 14 +- .../gradle/utils/CreateNodeForProjectTest.kt | 103 +------- .../utils/ProcessTargetsForProjectTest.kt | 226 ++++++++++++++++++ .../21-3-0/change-plugin-version-0-1-2.md | 26 ++ .../change-plugin-version-0-1-2.spec.ts | 128 ++++++++++ .../21-3-0/change-plugin-version-0-1-2.ts | 16 ++ 13 files changed, 488 insertions(+), 140 deletions(-) create mode 100644 docs/generated/packages/gradle/migrations/change-plugin-version-0-1-2.json create mode 100644 packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/ProcessTargetsForProjectTest.kt create mode 100644 packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.md create mode 100644 packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.spec.ts create mode 100644 packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.ts diff --git a/docs/generated/manifests/new-nx-api.json b/docs/generated/manifests/new-nx-api.json index d30f7d3567..a1667b6f2e 100644 --- a/docs/generated/manifests/new-nx-api.json +++ b/docs/generated/manifests/new-nx-api.json @@ -2076,6 +2076,16 @@ } }, "migrations": { + "/technologies/java/api/migrations/change-plugin-version-0-1-2": { + "description": "Change dev.nx.gradle.project-graph to version 0.1.2 in build file", + "file": "generated/packages/gradle/migrations/change-plugin-version-0-1-2.json", + "hidden": false, + "name": "change-plugin-version-0-1-2", + "version": "21.3.0-beta.0", + "originalFilePath": "/packages/gradle", + "path": "/technologies/java/api/migrations/change-plugin-version-0-1-2", + "type": "migration" + }, "/technologies/java/api/migrations/change-plugin-version-0-1-0": { "description": "Change dev.nx.gradle.project-graph to version 0.1.0 in build file", "file": "generated/packages/gradle/migrations/change-plugin-version-0-1-0.json", diff --git a/docs/generated/packages-metadata.json b/docs/generated/packages-metadata.json index 5edcd0bf1d..2eedb3ee53 100644 --- a/docs/generated/packages-metadata.json +++ b/docs/generated/packages-metadata.json @@ -2249,6 +2249,16 @@ } ], "migrations": [ + { + "description": "Change dev.nx.gradle.project-graph to version 0.1.2 in build file", + "file": "generated/packages/gradle/migrations/change-plugin-version-0-1-2.json", + "hidden": false, + "name": "change-plugin-version-0-1-2", + "version": "21.3.0-beta.0", + "originalFilePath": "/packages/gradle", + "path": "gradle/migrations/change-plugin-version-0-1-2", + "type": "migration" + }, { "description": "Change dev.nx.gradle.project-graph to version 0.1.0 in build file", "file": "generated/packages/gradle/migrations/change-plugin-version-0-1-0.json", diff --git a/docs/generated/packages/gradle/migrations/change-plugin-version-0-1-2.json b/docs/generated/packages/gradle/migrations/change-plugin-version-0-1-2.json new file mode 100644 index 0000000000..f11240552c --- /dev/null +++ b/docs/generated/packages/gradle/migrations/change-plugin-version-0-1-2.json @@ -0,0 +1,14 @@ +{ + "name": "change-plugin-version-0-1-2", + "version": "21.3.0-beta.0", + "cli": "nx", + "description": "Change dev.nx.gradle.project-graph to version 0.1.2 in build file", + "factory": "./src/migrations/21-3-0/change-plugin-version-0-1-2", + "implementation": "/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.ts", + "aliases": [], + "hidden": false, + "path": "/packages/gradle", + "schema": null, + "type": "migration", + "examplesFile": "#### Change dev.nx.gradle.project-graph to version 0.1.2\n\nChange dev.nx.gradle.project-graph to version 0.1.2 in build file\n\n#### Sample Code Changes\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```{% fileName=\"build.gradle\" %}\nplugins {\n\tid \"dev.nx.gradle.project-graph\" version \"0.1.0\"\n}\n```\n\n{% /tab %}\n{% tab label=\"After\" %}\n\n```{% fileName=\"build.gradle\" %}\nplugins {\n id \"dev.nx.gradle.project-graph\" version \"0.1.2\"\n}\n```\n\n{% /tab %}\n{% /tabs %}\n" +} diff --git a/packages/gradle/migrations.json b/packages/gradle/migrations.json index 0ad4c2bfae..4997f376f5 100644 --- a/packages/gradle/migrations.json +++ b/packages/gradle/migrations.json @@ -35,6 +35,12 @@ "cli": "nx", "description": "Change dev.nx.gradle.project-graph to version 0.1.0 in build file", "factory": "./src/migrations/21-1-2/change-plugin-version-0-1-0" + }, + "change-plugin-version-0-1-2": { + "version": "21.3.0-beta.0", + "cli": "nx", + "description": "Change dev.nx.gradle.project-graph to version 0.1.2 in build file", + "factory": "./src/migrations/21-3-0/change-plugin-version-0-1-2" } }, "packageJsonUpdates": {} diff --git a/packages/gradle/project-graph/build.gradle.kts b/packages/gradle/project-graph/build.gradle.kts index 42a17d87ba..d063ce7739 100644 --- a/packages/gradle/project-graph/build.gradle.kts +++ b/packages/gradle/project-graph/build.gradle.kts @@ -10,7 +10,7 @@ plugins { group = "dev.nx.gradle" -version = "0.0.1-alpha.6" +version = "0.1.2" repositories { mavenCentral() } diff --git a/packages/gradle/project-graph/src/main/kotlin/dev/nx/gradle/utils/CiTargetsUtils.kt b/packages/gradle/project-graph/src/main/kotlin/dev/nx/gradle/utils/CiTargetsUtils.kt index 1c3a7eb990..5b54aa3976 100644 --- a/packages/gradle/project-graph/src/main/kotlin/dev/nx/gradle/utils/CiTargetsUtils.kt +++ b/packages/gradle/project-graph/src/main/kotlin/dev/nx/gradle/utils/CiTargetsUtils.kt @@ -12,10 +12,13 @@ private val testFileNameRegex = Regex("^(?!(abstract|fake)).*?(Test)(s)?\\d*", RegexOption.IGNORE_CASE) private val packageDeclarationRegex = - Regex("""package\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)""") + Regex( + """^\s*package\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)""", + RegexOption.MULTILINE) private val classDeclarationRegex = - Regex("""^\s*(?:@[\w]+)?\s*(class|object)\s+([A-Za-z_][A-Za-z0-9_]*)""") -private val privateClassRegex = Regex("""\bprivate\s+(class|object)\s+([A-Za-z_][A-Za-z0-9_]*)""") + Regex( + """^\s*(?:@[\w]+\s*)*(?:public|protected|internal|open|sealed|final|data|enum|annotation)?\s*(class)\s+([A-Za-z_][A-Za-z0-9_]*)""") +private val privateClassRegex = Regex("""\bprivate\s+class\s+([A-Za-z_][A-Za-z0-9_]*)""") // Essential annotations (most common subset) private val essentialTestAnnotations = diff --git a/packages/gradle/project-graph/src/main/kotlin/dev/nx/gradle/utils/ProjectUtils.kt b/packages/gradle/project-graph/src/main/kotlin/dev/nx/gradle/utils/ProjectUtils.kt index 9454d6a132..21b0b73e2b 100644 --- a/packages/gradle/project-graph/src/main/kotlin/dev/nx/gradle/utils/ProjectUtils.kt +++ b/packages/gradle/project-graph/src/main/kotlin/dev/nx/gradle/utils/ProjectUtils.kt @@ -84,8 +84,8 @@ fun processTargetsForProject( val testTasks = project.getTasksByName("test", false) val intTestTasks = project.getTasksByName("intTest", false) - val hasCiTestTarget = ciTestTargetName != null && testTasks.isNotEmpty() - val hasCiIntTestTarget = ciIntTestTargetName != null && intTestTasks.isNotEmpty() + val hasCiTestTarget = ciTestTargetName != null && testTasks.isNotEmpty() && atomized + val hasCiIntTestTarget = ciIntTestTargetName != null && intTestTasks.isNotEmpty() && atomized project.tasks.forEach { task -> try { @@ -111,7 +111,7 @@ fun processTargetsForProject( targets[taskName] = target - if (hasCiTestTarget && task.name.startsWith("compileTest") && atomized) { + if (hasCiTestTarget && task.name.startsWith("compileTest")) { addTestCiTargets( task.inputs.sourceFiles, projectBuildPath, @@ -124,7 +124,7 @@ fun processTargetsForProject( ciTestTargetName!!) } - if (hasCiIntTestTarget && task.name.startsWith("compileIntTest") && atomized) { + if (hasCiIntTestTarget && task.name.startsWith("compileIntTest")) { addTestCiTargets( task.inputs.sourceFiles, projectBuildPath, @@ -137,23 +137,22 @@ fun processTargetsForProject( ciIntTestTargetName!!) } - val ciCheckTargetName = targetNameOverrides.getOrDefault("ciCheckTargetName", "check-ci") - if (task.name == "check") { - val replacedDependencies = - (target["dependsOn"] as? List<*>)?.map { dep -> - val dependsOn = dep.toString() - if (hasCiTestTarget && dependsOn == "${project.name}:$testTargetName" && atomized) { - "${project.name}:$ciTestTargetName" - } else if (hasCiIntTestTarget && - dependsOn == "${project.name}:$intTestTargetName" && - atomized) { - "${project.name}:$ciIntTestTargetName" - } else { - dep - } - } ?: emptyList() + if (ciTestTargetName != null || ciIntTestTargetName != null) { + val ciCheckTargetName = targetNameOverrides.getOrDefault("ciCheckTargetName", "check-ci") + if (task.name == "check") { + val replacedDependencies = + (target["dependsOn"] as? List<*>)?.map { dep -> + val dependsOn = dep.toString() + if (hasCiTestTarget && dependsOn == "${project.name}:$testTargetName") { + "${project.name}:$ciTestTargetName" + } else if (hasCiIntTestTarget && + dependsOn == "${project.name}:$intTestTargetName") { + "${project.name}:$ciIntTestTargetName" + } else { + dep + } + } ?: emptyList() - if (atomized) { val newTarget: MutableMap = mutableMapOf( "dependsOn" to replacedDependencies, @@ -165,21 +164,19 @@ fun processTargetsForProject( ensureTargetGroupExists(targetGroups, testCiTargetGroup) targetGroups[testCiTargetGroup]?.add(ciCheckTargetName) } - } - if (task.name == "build") { - val ciBuildTargetName = targetNameOverrides.getOrDefault("ciBuildTargetName", "build-ci") - val replacedDependencies = - (target["dependsOn"] as? List<*>)?.map { dep -> - val dependsOn = dep.toString() - if (dependsOn == "${project.name}:check" && atomized) { - "${project.name}:$ciCheckTargetName" - } else { - dep - } - } ?: emptyList() + if (task.name == "build") { + val ciBuildTargetName = targetNameOverrides.getOrDefault("ciBuildTargetName", "build-ci") + val replacedDependencies = + (target["dependsOn"] as? List<*>)?.map { dep -> + val dependsOn = dep.toString() + if (dependsOn == "${project.name}:check") { + "${project.name}:$ciCheckTargetName" + } else { + dep + } + } ?: emptyList() - if (atomized) { val newTarget: MutableMap = mutableMapOf( "dependsOn" to replacedDependencies, @@ -199,5 +196,6 @@ fun processTargetsForProject( } } + logger.info("Final targets in processTargetsForProject: $targets") return GradleTargets(targets, targetGroups, externalNodes) } diff --git a/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/ClassDetectionTest.kt b/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/ClassDetectionTest.kt index 29729c4dde..0469fcd31b 100644 --- a/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/ClassDetectionTest.kt +++ b/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/ClassDetectionTest.kt @@ -9,6 +9,10 @@ class ClassDetectionTest { fun `detects top-level and annotated nested classes only`() { val kotlinSource = """ + import some.package + + package real.package + class ClassA { @Nested class ClassB @@ -18,6 +22,10 @@ class ClassDetectionTest { private class ClassD } + internal class ClassU + + public abstract class ClassV + @Nested class ClassX // not nested — should be ignored @@ -36,7 +44,11 @@ class ClassDetectionTest { val result = getAllVisibleClassesWithNestedAnnotation(tempFile) // Expected output - val expected = mapOf("ClassAClassB" to "ClassA\$ClassB", "ClassZ" to "ClassZ") + val expected = + mapOf( + "ClassAClassB" to "real.package.ClassA\$ClassB", + "ClassU" to "real.package.ClassU", + "ClassZ" to "real.package.ClassZ") assertEquals(expected, result) } diff --git a/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/CreateNodeForProjectTest.kt b/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/CreateNodeForProjectTest.kt index a7ad43147e..55337c4199 100644 --- a/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/CreateNodeForProjectTest.kt +++ b/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/CreateNodeForProjectTest.kt @@ -1,6 +1,5 @@ package dev.nx.gradle.utils -import dev.nx.gradle.data.* import java.io.File import kotlin.test.* import org.gradle.testfixtures.ProjectBuilder @@ -11,7 +10,7 @@ class CreateNodeForProjectTest { fun `should return GradleNodeReport with targets and metadata`() { // Arrange val workspaceRoot = createTempDir("workspace").absolutePath - val projectDir = createTempDir("project") + val projectDir = File(workspaceRoot, "project-a").apply { mkdirs() } val project = ProjectBuilder.builder().withProjectDir(projectDir).build() // Create a couple of dummy tasks @@ -51,104 +50,4 @@ class CreateNodeForProjectTest { assertTrue(result.dependencies.isEmpty(), "Expected no dependencies") assertTrue(result.externalNodes.isEmpty(), "Expected no external nodes") } - - @Test - fun `should not generate atomized targets when atomized is false`() { - // Arrange - val workspaceRoot = createTempDir("workspace").absolutePath - val projectDir = createTempDir("project") - val project = ProjectBuilder.builder().withProjectDir(projectDir).build() - - // Create tasks that would normally trigger atomized targets - val testFile1 = - File(projectDir, "src/test/kotlin/MyFirstTest.kt").apply { - parentFile.mkdirs() - writeText("@Test class MyFirstTest") - } - - val testTask = - project.task("test").apply { - group = "verification" - description = "Runs the tests" - inputs.files(project.files(testFile1)) - } - project - .task("compileTestKotlin") - .dependsOn(testTask) // This task name triggers ci test target logic - - val checkTask = - project.task("check").apply { - group = "verification" - description = "Runs all checks" - dependsOn(testTask) - } - project.task("build").apply { - group = "build" - description = "Assembles and tests" - dependsOn(checkTask) - } - - val targetNameOverrides = - mapOf( - "ciTestTargetName" to "ci-test", - "ciCheckTargetName" to "ci-check", - "ciBuildTargetName" to "ci-build") - - // Act - val result = - createNodeForProject( - project = project, - targetNameOverrides = targetNameOverrides, - workspaceRoot = workspaceRoot, - atomized = false) // Test with atomized = false - - // Assert - val projectRoot = project.projectDir.absolutePath - val projectNode = result.nodes[projectRoot] - assertNotNull(projectNode, "ProjectNode should not be null") - - // Verify that individual atomized targets are NOT created - assertFalse( - projectNode.targets.containsKey("ci--MyFirstTest"), - "Expected ci--MyFirstTest target NOT to be present") - - // Verify 'test' and 'check' targets are present but not their 'ci' counterparts if atomized is - // false - assertNotNull(projectNode.targets["test"], "Expected 'test' target to be present") - assertNotNull(projectNode.targets["check"], "Expected 'check' target to be present") - assertNotNull(projectNode.targets["build"], "Expected 'build' target to be present") - - // Verify that 'ci-test', 'ci-check', 'ci-build' are not created as main targets if atomized is - // false - assertFalse( - projectNode.targets.containsKey("ci-test"), - "Expected ci-test target NOT to be present as a main target") - assertFalse( - projectNode.targets.containsKey("ci-check"), - "Expected ci-check target NOT to be present as a main target") - assertFalse( - projectNode.targets.containsKey("ci-build"), - "Expected ci-build target NOT to be present as a main target") - - // Verify dependencies are NOT rewritten for 'check' and 'build' tasks - val checkTarget = projectNode.targets["check"] - assertNotNull(checkTarget, "Check target should exist") - val checkDependsOn = checkTarget["dependsOn"] as? List<*> - assertNotNull(checkDependsOn, "Check dependsOn should not be null") - assertTrue( - checkDependsOn.contains("${project.name}:test"), "Expected 'check' to depend on 'test'") - assertFalse( - checkDependsOn.contains("${project.name}:ci-test"), - "Expected 'check' NOT to depend on 'ci-test'") - - val buildTarget = projectNode.targets["build"] - assertNotNull(buildTarget, "Build target should exist") - val buildDependsOn = buildTarget["dependsOn"] as? List<*> - assertNotNull(buildDependsOn, "Build dependsOn should not be null") - assertTrue( - buildDependsOn.contains("${project.name}:check"), "Expected 'build' to depend on 'check'") - assertFalse( - buildDependsOn.contains("${project.name}:ci-check"), - "Expected 'build' NOT to depend on 'ci-check'") - } } diff --git a/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/ProcessTargetsForProjectTest.kt b/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/ProcessTargetsForProjectTest.kt new file mode 100644 index 0000000000..09da6b0594 --- /dev/null +++ b/packages/gradle/project-graph/src/test/kotlin/dev/nx/gradle/utils/ProcessTargetsForProjectTest.kt @@ -0,0 +1,226 @@ +package dev.nx.gradle.utils + +import dev.nx.gradle.data.* +import java.io.File +import kotlin.test.* +import org.gradle.testfixtures.ProjectBuilder + +class ProcessTargetsForProjectTest { + + @Test + fun `should process targets correctly when atomized is true`() { + // Arrange + val workspaceRoot = createTempDir("workspace").absolutePath + val projectDir = File(workspaceRoot, "project-a").apply { mkdirs() } + val project = ProjectBuilder.builder().withProjectDir(projectDir).build() + + // Create tasks that would normally trigger atomized targets + val testFile1 = + File(projectDir, "src/test/kotlin/MyFirstTest.kt").apply { + parentFile.mkdirs() + writeText("@Test class MyFirstTest") + } + + val testTask = + project.task("test").apply { + group = "verification" + description = "Runs the tests" + inputs.files(project.files(testFile1)) + } + project.task("compileTestKotlin").apply { inputs.files(project.files(testFile1)) } + + val checkTask = + project.task("check").apply { + group = "verification" + description = "Runs all checks" + dependsOn(testTask) + } + project.task("build").apply { + group = "build" + description = "Assembles and tests" + dependsOn(checkTask) + } + + val targetNameOverrides = + mapOf( + "ciTestTargetName" to "ci-test", + "ciCheckTargetName" to "ci-check", + "ciBuildTargetName" to "ci-build") + + val dependencies = mutableSetOf() // Empty for this test's scope + + // Act + val gradleTargets = + processTargetsForProject( + project = project, + dependencies = dependencies, + targetNameOverrides = targetNameOverrides, + workspaceRoot = workspaceRoot, + atomized = true) + + // Assert + // Verify 'ci-test' should not be presented + // But 'ci-check' and 'ci-build' targets should be present in the returned targets + assertNull( + gradleTargets.targets["ci-test"], + "Expected ci-test target to NOT be present in processed targets") + assertNotNull( + gradleTargets.targets["ci-check"], + "Expected ci-check target to be present in processed targets") + assertNotNull( + gradleTargets.targets["ci-build"], + "Expected ci-build target to be present in processed targets") + + // Verify dependencies are rewritten for 'check' and 'build' tasks in the returned targets + val checkTarget = gradleTargets.targets["check"] + assertNotNull(checkTarget, "Check target should exist in processed targets") + val checkDependsOn = checkTarget["dependsOn"] as? List<*> + assertNotNull(checkDependsOn, "Check dependsOn should not be null in processed targets") + assertTrue( + checkDependsOn.contains("${project.name}:test"), + "Expected 'check' to depend on 'test' in processed targets") + + val checkCiTarget = gradleTargets.targets["ci-check"] + assertNotNull(checkCiTarget, "Check CI target should exist in processed targets") + val checkCiDependsOn = checkCiTarget["dependsOn"] as? List<*> + assertNotNull(checkCiDependsOn, "Check CI dependsOn should not be null in processed targets") + assertTrue( + checkCiDependsOn.contains("${project.name}:ci-test"), + "Expected 'ci-check' to depend on 'ci-test' in processed targets") + + val buildTarget = gradleTargets.targets["build"] + assertNotNull(buildTarget, "Build target should exist in processed targets") + val buildDependsOn = buildTarget["dependsOn"] as? List<*> + assertNotNull(buildDependsOn, "Build dependsOn should not be null in processed targets") + assertTrue( + buildDependsOn.contains("${project.name}:check"), + "Expected 'build' to depend on 'check' in processed targets") + + val buildCiTarget = gradleTargets.targets["ci-build"] + assertNotNull(buildCiTarget, "Build CI target should exist in processed targets") + val buildCiDependsOn = buildCiTarget["dependsOn"] as? List<*> + assertNotNull(buildCiDependsOn, "Build CI dependsOn should not be null in processed targets") + assertTrue( + buildCiDependsOn.contains("${project.name}:ci-check"), + "Expected 'ci-build' to depend on 'ci-check' in processed targets") + } + + @Test + fun `should process targets correctly when atomized is false`() { + // Arrange + val workspaceRoot = createTempDir("workspace").absolutePath + val projectDir = File(workspaceRoot, "project-a").apply { mkdirs() } + val project = ProjectBuilder.builder().withProjectDir(projectDir).build() + + // Create tasks that would normally trigger atomized targets + val testFile1 = + File(projectDir, "src/test/kotlin/MyFirstTest.kt").apply { + parentFile.mkdirs() + writeText("@Test class MyFirstTest") + } + + val testTask = + project.task("test").apply { + group = "verification" + description = "Runs the tests" + inputs.files(project.files(testFile1)) + } + project + .task("compileTestKotlin") + .dependsOn(testTask) // This task name triggers ci test target logic + + val checkTask = + project.task("check").apply { + group = "verification" + description = "Runs all checks" + dependsOn(testTask) + } + project.task("build").apply { + group = "build" + description = "Assembles and tests" + dependsOn(checkTask) + } + + val targetNameOverrides = + mapOf( + "ciTestTargetName" to "ci-test", + "ciCheckTargetName" to "ci-check", + "ciBuildTargetName" to "ci-build") + + val dependencies = mutableSetOf() // Empty for this test's scope + + // Act + val gradleTargets = + processTargetsForProject( + project = project, + dependencies = dependencies, + targetNameOverrides = targetNameOverrides, + workspaceRoot = workspaceRoot, + atomized = false) // Test with atomized = false + + // Assert + // Verify that individual atomized targets are NOT created + assertFalse( + gradleTargets.targets.containsKey("ci--MyFirstTest"), + "Expected ci--MyFirstTest target NOT to be present in processed targets") + + // Verify 'test' and 'check' targets are present but not their 'ci' counterparts if atomized is + // false + assertNotNull(gradleTargets.targets["test"], "Expected 'test' target to be present") + assertNotNull(gradleTargets.targets["check"], "Expected 'check' target to be present") + assertNotNull(gradleTargets.targets["build"], "Expected 'build' target to be present") + + // Verify that 'ci-test' is not created as main targets if atomized is false + // 'ci-check' and 'ci-build should still be presented if ciTestTargetName is presented + // regardless of atomized value + assertFalse( + gradleTargets.targets.containsKey("ci-test"), + "Expected ci-test target NOT to be present as a main target") + assertTrue( + gradleTargets.targets.containsKey("ci-check"), + "Expected ci-check target to be present as a main target") + assertTrue( + gradleTargets.targets.containsKey("ci-build"), + "Expected ci-build target to be present as a main target") + + // Verify dependencies are NOT rewritten for 'check' and 'build' tasks + val checkTarget = gradleTargets.targets["check"] + assertNotNull(checkTarget, "Check target should exist") + val checkDependsOn = checkTarget["dependsOn"] as? List<*> + assertNotNull(checkDependsOn, "Check dependsOn should not be null") + assertTrue( + checkDependsOn.contains("${project.name}:test"), "Expected 'check' to depend on 'test'") + assertFalse( + checkDependsOn.contains("${project.name}:ci-test"), + "Expected 'check' NOT to depend on 'ci-test'") + + val checkCiTarget = gradleTargets.targets["ci-check"] + assertNotNull(checkCiTarget, "Check CI target should exist in processed targets") + val checkCiDependsOn = checkCiTarget["dependsOn"] as? List<*> + assertNotNull(checkCiDependsOn, "Check CI dependsOn should not be null in processed targets") + assertFalse( + checkCiDependsOn.contains("${project.name}:ci-test"), + "Expected 'ci-check' to NOT depend on 'ci-test' in processed targets") + assertTrue( + checkCiDependsOn.contains("${project.name}:test"), + "Expected 'ci-check' to depend on 'test' in processed targets") + + val buildTarget = gradleTargets.targets["build"] + assertNotNull(buildTarget, "Build target should exist") + val buildDependsOn = buildTarget["dependsOn"] as? List<*> + assertNotNull(buildDependsOn, "Build dependsOn should not be null") + assertTrue( + buildDependsOn.contains("${project.name}:check"), "Expected 'build' to depend on 'check'") + assertFalse( + buildDependsOn.contains("${project.name}:ci-check"), + "Expected 'build' NOT to depend on 'ci-check'") + + val buildCiTarget = gradleTargets.targets["ci-build"] + assertNotNull(buildCiTarget, "Build CI target should exist in processed targets") + val buildCiDependsOn = buildCiTarget["dependsOn"] as? List<*> + assertNotNull(buildCiDependsOn, "Build CI dependsOn should not be null in processed targets") + assertTrue( + buildCiDependsOn.contains("${project.name}:ci-check"), + "Expected 'ci-build' to depend on 'ci-check' in processed targets") + } +} diff --git a/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.md b/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.md new file mode 100644 index 0000000000..f7b3b7a98c --- /dev/null +++ b/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.md @@ -0,0 +1,26 @@ +#### Change dev.nx.gradle.project-graph to version 0.1.2 + +Change dev.nx.gradle.project-graph to version 0.1.2 in build file + +#### Sample Code Changes + +{% tabs %} +{% tab label="Before" %} + +```{% fileName="build.gradle" %} +plugins { + id "dev.nx.gradle.project-graph" version "0.1.0" +} +``` + +{% /tab %} +{% tab label="After" %} + +```{% fileName="build.gradle" %} +plugins { + id "dev.nx.gradle.project-graph" version "0.1.2" +} +``` + +{% /tab %} +{% /tabs %} diff --git a/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.spec.ts b/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.spec.ts new file mode 100644 index 0000000000..791f257e3c --- /dev/null +++ b/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.spec.ts @@ -0,0 +1,128 @@ +import { TempFs } from 'nx/src/internal-testing-utils/temp-fs'; +import { Tree } from '@nx/devkit'; +import { FsTree } from 'nx/src/generators/tree'; +import update from './change-plugin-version-0-1-2'; +import { gradleProjectGraphPluginName } from '../../utils/versions'; + +describe('change-plugin-version-0-1-2 migration', () => { + let tempFs: TempFs; + let cwd: string; + let tree: Tree; + + beforeEach(async () => { + tempFs = new TempFs('test'); + cwd = process.cwd(); + process.chdir(tempFs.tempDir); + tree = new FsTree(tempFs.tempDir, false); + }); + + afterEach(() => { + jest.resetModules(); + process.chdir(cwd); + }); + + it('should update plugin version to 0.1.2 in Groovy DSL', async () => { + await tempFs.createFiles({ + 'nx.json': JSON.stringify({ + plugins: ['@nx/gradle'], + }), + 'proj/settings.gradle': '', + 'proj/build.gradle': `plugins { + id 'java' + id "${gradleProjectGraphPluginName}" version "0.0.1" +}`, + }); + + await update(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).toContain( + `id "${gradleProjectGraphPluginName}" version "0.1.2"` + ); + expect(content).not.toContain('version "0.0.1"'); + }); + + it('should update plugin version to 0.1.2 in Kotlin DSL', async () => { + await tempFs.createFiles({ + 'nx.json': JSON.stringify({ + plugins: ['@nx/gradle'], + }), + 'proj/settings.gradle.kts': '', + 'proj/build.gradle.kts': `plugins { + id("java") + id("${gradleProjectGraphPluginName}") version("0.0.1") +}`, + }); + + await update(tree); + + const content = tree.read('proj/build.gradle.kts', 'utf-8'); + expect(content).toContain( + `id("${gradleProjectGraphPluginName}") version("0.1.2")` + ); + expect(content).not.toContain('version("0.0.1")'); + }); + + it('should not update if nx.json is missing', async () => { + await tempFs.createFiles({ + 'proj/settings.gradle': '', + 'proj/build.gradle': `plugins { + id 'java' + id "${gradleProjectGraphPluginName}" version "0.0.1" +}`, + }); + + await update(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).toContain('version "0.0.1"'); + expect(content).not.toContain('version "0.1.2"'); + }); + + it('should not update if Gradle plugin is not present', async () => { + await tempFs.createFiles({ + 'nx.json': JSON.stringify({}), + 'proj/settings.gradle': '', + 'proj/build.gradle': `plugins { + id 'java' +}`, + }); + + await update(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).not.toContain(gradleProjectGraphPluginName); + }); + + it('should handle multiple build.gradle files', async () => { + await tempFs.createFiles({ + 'nx.json': JSON.stringify({ + plugins: ['@nx/gradle'], + }), + 'proj1/settings.gradle': '', + 'proj1/build.gradle': `plugins { + id 'java' + id "${gradleProjectGraphPluginName}" version "0.0.1" +}`, + 'proj2/settings.gradle': '', + 'proj2/build.gradle': `plugins { + id 'java' + id "${gradleProjectGraphPluginName}" version "0.0.1" +}`, + }); + + await update(tree); + + const proj1Content = tree.read('proj1/build.gradle', 'utf-8'); + const proj2Content = tree.read('proj2/build.gradle', 'utf-8'); + + expect(proj1Content).toContain( + `id "${gradleProjectGraphPluginName}" version "0.1.2"` + ); + expect(proj2Content).toContain( + `id "${gradleProjectGraphPluginName}" version "0.1.2"` + ); + expect(proj1Content).not.toContain('version "0.0.1"'); + expect(proj2Content).not.toContain('version "0.0.1"'); + }); +}); diff --git a/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.ts b/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.ts new file mode 100644 index 0000000000..3daefcb972 --- /dev/null +++ b/packages/gradle/src/migrations/21-3-0/change-plugin-version-0-1-2.ts @@ -0,0 +1,16 @@ +import { Tree, readNxJson } from '@nx/devkit'; +import { hasGradlePlugin } from '../../utils/has-gradle-plugin'; +import { addNxProjectGraphPlugin } from '../../generators/init/gradle-project-graph-plugin-utils'; + +/* Change the plugin version to 0.1.2 + */ +export default async function update(tree: Tree) { + const nxJson = readNxJson(tree); + if (!nxJson) { + return; + } + if (!hasGradlePlugin(tree)) { + return; + } + await addNxProjectGraphPlugin(tree, '0.1.2'); +}