From af78c4ac6fdc4b197e702d59862170e346d7e0e2 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Mon, 16 Feb 2026 23:36:47 +0100 Subject: [PATCH 1/3] Fix up-to-date checks for metadata changes When a project's dependencies change the `updateDevRepo` task should re-run. This change tracks dependency changes by relying on the `GenerateModuleMetadata` task for the publication. Using the `GenerateModuleMetadata` task is required because Gradle does not have an API to fetch the dependencies of a `SoftwareComponent`. --- api/dev-publish-plugin.api | 1 + src/main/kotlin/DevPublishPlugin.kt | 31 +++- src/main/kotlin/data/PublicationData.kt | 4 + .../checksums/CreatePublicationChecksum.kt | 14 ++ .../GeneratePublicationDataChecksumTask.kt | 1 + src/main/kotlin/utils/stdlibUtils.kt | 3 + src/test/kotlin/BuildCacheTest.kt | 9 +- src/test/kotlin/SingleProjectTest.kt | 158 +++++++++++++++--- 8 files changed, 193 insertions(+), 28 deletions(-) diff --git a/api/dev-publish-plugin.api b/api/dev-publish-plugin.api index 6eeb2f1..d811b5f 100644 --- a/api/dev-publish-plugin.api +++ b/api/dev-publish-plugin.api @@ -20,6 +20,7 @@ public abstract class dev/adamko/gradle/dev_publish/DevPublishPluginExtension { public abstract class dev/adamko/gradle/dev_publish/data/PublicationData : org/gradle/api/Named { public abstract fun getArtifacts ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getGradleModuleMetadata ()Lorg/gradle/api/file/ConfigurableFileCollection; public abstract fun getIdentifier ()Lorg/gradle/api/provider/Property; public fun getName ()Ljava/lang/String; } diff --git a/src/main/kotlin/DevPublishPlugin.kt b/src/main/kotlin/DevPublishPlugin.kt index 123d620..cc19891 100644 --- a/src/main/kotlin/DevPublishPlugin.kt +++ b/src/main/kotlin/DevPublishPlugin.kt @@ -25,6 +25,7 @@ import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.publish.maven.plugins.MavenPublishPlugin import org.gradle.api.publish.maven.tasks.PublishToMavenRepository +import org.gradle.api.publish.tasks.GenerateModuleMetadata import org.gradle.api.services.BuildServiceRegistry import org.gradle.api.tasks.PathSensitivity.RELATIVE import org.gradle.kotlin.dsl.* @@ -138,7 +139,12 @@ constructor( publicationData.addAllLater(providers.provider { publications .withType() - .mapNotNull { createPublicationData(it) } + .mapNotNull { publication -> + createPublicationData( + project = project, + publication = publication, + ) + } }) } } @@ -173,7 +179,12 @@ constructor( val currentProjectDir = layout.projectDirectory - val publicationData = providers.provider { createPublicationData(publication) } + val publicationData = providers.provider { + createPublicationData( + project = project, + publication = publication, + ) + } val currentChecksum = providers.createPublicationChecksum { this.projectDir.set(currentProjectDir) @@ -244,6 +255,7 @@ constructor( /** Create an instance of [PublicationData] from [publication]. */ private fun createPublicationData( + project: Project, publication: MavenPublication?, ): PublicationData? { if (publication == null) { @@ -257,11 +269,23 @@ constructor( .from(artifacts.map { it.file }) .builtBy(artifacts) } + val identifier = providers.provider { publication.run { "$groupId:$artifactId:$version" } } + val gmm = objects.fileCollection() + project.tasks + .withType() + .matching { task -> + task.name == publication.getGenerateModuleMetadataTaskName() + } + .all { + gmm.from(outputFile) + } + return objects.newInstance(publication.name).apply { this.identifier.set(identifier) this.artifacts.from(artifacts) + this.gradleModuleMetadata.from(gmm) } } @@ -282,5 +306,8 @@ constructor( const val DEV_PUB__PUBLICATION_OUTGOING = "devPublicationConsumableElements" private val logger = Logging.getLogger(DevPublishService::class.java) + + private fun MavenPublication.getGenerateModuleMetadataTaskName(): String = + "generateMetadataFileFor${name.uppercaseFirstChar()}Publication" } } diff --git a/src/main/kotlin/data/PublicationData.kt b/src/main/kotlin/data/PublicationData.kt index fc358ad..374b57f 100644 --- a/src/main/kotlin/data/PublicationData.kt +++ b/src/main/kotlin/data/PublicationData.kt @@ -43,6 +43,10 @@ constructor( @get:Input abstract val identifier: Property + @get:InputFiles + @get:PathSensitive(RELATIVE) + abstract val gradleModuleMetadata: ConfigurableFileCollection + @get:Internal internal val checksumFilename: String get() = "$name.txt" diff --git a/src/main/kotlin/internal/checksums/CreatePublicationChecksum.kt b/src/main/kotlin/internal/checksums/CreatePublicationChecksum.kt index 6da6577..9b53927 100644 --- a/src/main/kotlin/internal/checksums/CreatePublicationChecksum.kt +++ b/src/main/kotlin/internal/checksums/CreatePublicationChecksum.kt @@ -11,15 +11,20 @@ internal abstract class CreatePublicationChecksum : ValueSource + val gradleModuleMetadata: ConfigurableFileCollection } override fun obtain(): String? { val identifier = parameters.identifier.get() val artifactsChecksums = artifactsChecksums() + val gradleModuleMetadataChecksums = gradleModuleMetadataChecksums() return buildString { appendLine(identifier) appendLine("---") + gradleModuleMetadataChecksums.forEach { + appendLine(it) + } artifactsChecksums.forEach { appendLine(it) } @@ -37,6 +42,15 @@ internal abstract class CreatePublicationChecksum : ValueSource { + return parameters.gradleModuleMetadata + .map { gmm -> + val gmmPath = gmm.relativeTo(parameters.projectDir.get().asFile).invariantSeparatorsPath + "${gmmPath}$FileChecksumSeparator${gmm.checksum()}" + } + .sorted() + } + internal companion object { @Suppress("ConstPropertyName") const val FileChecksumSeparator = ":" diff --git a/src/main/kotlin/tasks/GeneratePublicationDataChecksumTask.kt b/src/main/kotlin/tasks/GeneratePublicationDataChecksumTask.kt index ed83004..a96c4c8 100644 --- a/src/main/kotlin/tasks/GeneratePublicationDataChecksumTask.kt +++ b/src/main/kotlin/tasks/GeneratePublicationDataChecksumTask.kt @@ -62,6 +62,7 @@ constructor( this.projectDir.set(currentProjectDir) this.artifacts.from(data.artifacts) this.identifier.set(data.identifier) + this.gradleModuleMetadata.from(data.gradleModuleMetadata) }.get() checksumFile.writeText(checksum) diff --git a/src/main/kotlin/utils/stdlibUtils.kt b/src/main/kotlin/utils/stdlibUtils.kt index 2b2fe42..4290138 100644 --- a/src/main/kotlin/utils/stdlibUtils.kt +++ b/src/main/kotlin/utils/stdlibUtils.kt @@ -3,3 +3,6 @@ package dev.adamko.gradle.dev_publish.utils /** Split a string to a [Pair], using [substringBefore] and [substringAfter] */ internal fun String.splitToPair(delimiter: String): Pair = substringBefore(delimiter) to substringAfter(delimiter, "") + +internal fun String.uppercaseFirstChar(): String = + replaceFirstChar { it.uppercase() } diff --git a/src/test/kotlin/BuildCacheTest.kt b/src/test/kotlin/BuildCacheTest.kt index 1fe95d3..6c4da97 100644 --- a/src/test/kotlin/BuildCacheTest.kt +++ b/src/test/kotlin/BuildCacheTest.kt @@ -40,8 +40,7 @@ class BuildCacheTest : FunSpec({ shouldNotHaveRunTask(":updateDevRepo") } - val initialBuildCacheSize = - expectedBuildCacheDir.walk().filter { it.isRegularFile() }.sumOf { it.fileSize() } + val initialBuildCacheSize = expectedBuildCacheDir.recursiveFileSize() project.runner .withArguments( @@ -104,9 +103,9 @@ class BuildCacheTest : FunSpec({ settingsGradleKts += """ | |buildCache { - | local { - | directory = file("local-cache").toURI() - | } + | local { + | directory = file("local-cache").toURI() + | } |} |""".trimMargin() diff --git a/src/test/kotlin/SingleProjectTest.kt b/src/test/kotlin/SingleProjectTest.kt index e1b9f04..0c3333c 100644 --- a/src/test/kotlin/SingleProjectTest.kt +++ b/src/test/kotlin/SingleProjectTest.kt @@ -5,6 +5,8 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.core.test.TestScope import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.intellij.lang.annotations.Language class SingleProjectTest : FunSpec({ @@ -38,6 +40,120 @@ class SingleProjectTest : FunSpec({ } } } + + context("check incremental build") { + val project = project() + test("1st time - updateDevRepo should run successfully") { + project.runner.withArguments( + ":clean", + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) + } + } + + test("2nd time - updateDevRepo should be UP_TO_DATE") { + project.runner.withArguments( + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) + } + } + + context("when dependency added") { + project.buildGradleKts += """ + |dependencies { + | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") + |} + """.trimMargin() + + test("1st time - updateDevRepo should run successfully") { + project.runner.withArguments( + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) + } + } + + test("2nd time - updateDevRepo should be UP_TO_DATE") { + project.runner.withArguments( + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) + } + } + } + + context("when dependency changes") { + project.buildGradleKts = project.buildGradleKts.replace( + """implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0")""", + """implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")""" + ) + + test("1st time - updateDevRepo should run successfully") { + project.runner.withArguments( + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) + } + } + + test("2nd time - updateDevRepo should be UP_TO_DATE") { + project.runner.withArguments( + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) + } + } + } + + context("when dependency removed - expect updateDevRepo re-runs") { + project.buildGradleKts = project.buildGradleKts.replace( + "implementation(\"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1\")", + "", + ) + + test("1st time - updateDevRepo should run successfully") { + project.runner.withArguments( + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) + } + } + + test("2nd time - updateDevRepo should be UP_TO_DATE") { + project.runner.withArguments( + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) + } + } + } + } }) { companion object { @@ -49,27 +165,27 @@ class SingleProjectTest : FunSpec({ ) { buildGradleKts = """ - plugins { - kotlin("jvm") version embeddedKotlinVersion - id("dev.adamko.dev-publish") version "+" - `maven-publish` - } - - group = "foo.project" - version = "0.0.1" - - dependencies { - //devPublication(project(":")) - } - - publishing { - publications { - create("mavenJava") { - from(components["java"]) - } - } - } - """.trimIndent() + |plugins { + | kotlin("jvm") version embeddedKotlinVersion + | id("dev.adamko.dev-publish") version "+" + | `maven-publish` + |} + | + |group = "foo.project" + |version = "0.0.1" + | + |dependencies { + | //devPublication(project(":")) + |} + | + |publishing { + | publications { + | create("mavenJava") { + | from(components["java"]) + | } + | } + |} + |""".trimMargin() } @Language("TEXT") From 4f4a1972e2a05237b6768fdbac0e37e37de06a4d Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Mon, 16 Feb 2026 23:50:02 +0100 Subject: [PATCH 2/3] Since the GMM file contains all details of the artifacts, remove manual tracking of the attached artifacts. --- src/main/kotlin/DevPublishPlugin.kt | 9 ----- src/main/kotlin/data/PublicationData.kt | 21 +++++++--- .../checksums/CreatePublicationChecksum.kt | 16 -------- .../GeneratePublicationDataChecksumTask.kt | 3 +- src/test/kotlin/SingleProjectTest.kt | 38 ++++++++++--------- 5 files changed, 37 insertions(+), 50 deletions(-) diff --git a/src/main/kotlin/DevPublishPlugin.kt b/src/main/kotlin/DevPublishPlugin.kt index cc19891..06f2bc1 100644 --- a/src/main/kotlin/DevPublishPlugin.kt +++ b/src/main/kotlin/DevPublishPlugin.kt @@ -188,7 +188,6 @@ constructor( val currentChecksum = providers.createPublicationChecksum { this.projectDir.set(currentProjectDir) - this.artifacts.from(publicationData.map { it.artifacts }) this.identifier.set(publicationData.flatMap { it.identifier }) } @@ -263,13 +262,6 @@ constructor( return null } - val artifacts = providers.provider { publication.artifacts } - .map { artifacts -> - objects.fileCollection() - .from(artifacts.map { it.file }) - .builtBy(artifacts) - } - val identifier = providers.provider { publication.run { "$groupId:$artifactId:$version" } } val gmm = objects.fileCollection() @@ -284,7 +276,6 @@ constructor( return objects.newInstance(publication.name).apply { this.identifier.set(identifier) - this.artifacts.from(artifacts) this.gradleModuleMetadata.from(gmm) } } diff --git a/src/main/kotlin/data/PublicationData.kt b/src/main/kotlin/data/PublicationData.kt index 374b57f..3c3fd11 100644 --- a/src/main/kotlin/data/PublicationData.kt +++ b/src/main/kotlin/data/PublicationData.kt @@ -25,12 +25,27 @@ constructor( ) : Named { /** - * The artifacts inside a [MavenPublication]. + * The Gradle Module Metadata files that describe this publication. + * + * Typically, there should only be one GMM file, but the Gradle API does not have a stable way to access it. + * Therefore, we accept multiple files, in case this assumption will change in the future. * * @see MavenPublication.getArtifacts */ @get:InputFiles @get:PathSensitive(RELATIVE) + abstract val gradleModuleMetadata: ConfigurableFileCollection + + /** + * The artifacts inside a [MavenPublication]. + * + * This property is no longer used. Instead, the data inside the Gradle Module Metadata files is used. + * These files contain all details (including checksums) of the artifacts attached to the publication. + * + * @see MavenPublication.getArtifacts + */ + @get:Internal + @Deprecated("No longer used. Scheduled for removal in version 2.0.0.") abstract val artifacts: ConfigurableFileCollection /** @@ -43,10 +58,6 @@ constructor( @get:Input abstract val identifier: Property - @get:InputFiles - @get:PathSensitive(RELATIVE) - abstract val gradleModuleMetadata: ConfigurableFileCollection - @get:Internal internal val checksumFilename: String get() = "$name.txt" diff --git a/src/main/kotlin/internal/checksums/CreatePublicationChecksum.kt b/src/main/kotlin/internal/checksums/CreatePublicationChecksum.kt index 9b53927..2ce3a51 100644 --- a/src/main/kotlin/internal/checksums/CreatePublicationChecksum.kt +++ b/src/main/kotlin/internal/checksums/CreatePublicationChecksum.kt @@ -9,14 +9,12 @@ internal abstract class CreatePublicationChecksum : ValueSource val gradleModuleMetadata: ConfigurableFileCollection } override fun obtain(): String? { val identifier = parameters.identifier.get() - val artifactsChecksums = artifactsChecksums() val gradleModuleMetadataChecksums = gradleModuleMetadataChecksums() return buildString { @@ -25,23 +23,9 @@ internal abstract class CreatePublicationChecksum : ValueSource { - val projectDir = parameters.projectDir.get().asFile - - return parameters.artifacts - .map { artifact -> - val artifactPath = artifact.relativeTo(projectDir).invariantSeparatorsPath - "${artifactPath}$FileChecksumSeparator${artifact.checksum()}" - } - .sorted() - } - private fun gradleModuleMetadataChecksums(): List { return parameters.gradleModuleMetadata .map { gmm -> diff --git a/src/main/kotlin/tasks/GeneratePublicationDataChecksumTask.kt b/src/main/kotlin/tasks/GeneratePublicationDataChecksumTask.kt index a96c4c8..57c7063 100644 --- a/src/main/kotlin/tasks/GeneratePublicationDataChecksumTask.kt +++ b/src/main/kotlin/tasks/GeneratePublicationDataChecksumTask.kt @@ -54,13 +54,12 @@ constructor( val currentProjectDir = layout.projectDirectory publicationData.forEach { data -> - logger.info("Creating publication data checksum for ${data.name} ${data.artifacts.asPath}") + logger.info("Creating publication data checksum for ${data.name} ${data.gradleModuleMetadata.asPath}") val checksumFile = tempDir.resolve(data.checksumFilename) val checksum = providers.createPublicationChecksum { this.projectDir.set(currentProjectDir) - this.artifacts.from(data.artifacts) this.identifier.set(data.identifier) this.gradleModuleMetadata.from(data.gradleModuleMetadata) }.get() diff --git a/src/test/kotlin/SingleProjectTest.kt b/src/test/kotlin/SingleProjectTest.kt index 0c3333c..67020ec 100644 --- a/src/test/kotlin/SingleProjectTest.kt +++ b/src/test/kotlin/SingleProjectTest.kt @@ -43,26 +43,28 @@ class SingleProjectTest : FunSpec({ context("check incremental build") { val project = project() - test("1st time - updateDevRepo should run successfully") { - project.runner.withArguments( - ":clean", - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) + context("initial clean run") { + test("1st time - updateDevRepo should run successfully") { + project.runner.withArguments( + ":clean", + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) + } } - } - test("2nd time - updateDevRepo should be UP_TO_DATE") { - project.runner.withArguments( - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) + test("2nd time - updateDevRepo should be UP_TO_DATE") { + project.runner.withArguments( + ":updateDevRepo", + "--stacktrace", + "--configuration-cache", + "--build-cache", + ).build { + shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) + } } } From 8d56716b741a4f1636769ef083c24b3e6599655f Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Tue, 17 Feb 2026 00:44:14 +0100 Subject: [PATCH 3/3] Split IncrementalBuildTest to separate class. Update assertions. Fix GMM tracking. --- src/main/kotlin/DevPublishPlugin.kt | 32 +++-- src/test/kotlin/IncrementalBuildTest.kt | 154 ++++++++++++++++++++++++ src/test/kotlin/SingleProjectTest.kt | 118 ------------------ 3 files changed, 177 insertions(+), 127 deletions(-) create mode 100644 src/test/kotlin/IncrementalBuildTest.kt diff --git a/src/main/kotlin/DevPublishPlugin.kt b/src/main/kotlin/DevPublishPlugin.kt index 06f2bc1..f063bc5 100644 --- a/src/main/kotlin/DevPublishPlugin.kt +++ b/src/main/kotlin/DevPublishPlugin.kt @@ -14,6 +14,7 @@ import dev.adamko.gradle.dev_publish.utils.* import javax.inject.Inject import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.file.FileCollection import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.ProjectLayout import org.gradle.api.logging.Logging @@ -186,9 +187,14 @@ constructor( ) } + inputs.files(publicationData.map { it.gradleModuleMetadata }) + .withPropertyName("devPubGradleModuleMetadata") + .withPathSensitivity(RELATIVE) + val currentChecksum = providers.createPublicationChecksum { this.projectDir.set(currentProjectDir) this.identifier.set(publicationData.flatMap { it.identifier }) + this.gradleModuleMetadata.from(publicationData.map { it.gradleModuleMetadata }) } val storedChecksum = providers.loadPublicationChecksum { @@ -264,15 +270,7 @@ constructor( val identifier = providers.provider { publication.run { "$groupId:$artifactId:$version" } } - val gmm = objects.fileCollection() - project.tasks - .withType() - .matching { task -> - task.name == publication.getGenerateModuleMetadataTaskName() - } - .all { - gmm.from(outputFile) - } + val gmm = getGmm(project, publication) return objects.newInstance(publication.name).apply { this.identifier.set(identifier) @@ -280,6 +278,22 @@ constructor( } } + + private fun getGmm( + project: Project, + publication: MavenPublication, + ): FileCollection { + val gmm = objects.fileCollection() + project.tasks + .withType() + .all { + if (name == publication.getGenerateModuleMetadataTaskName()) { + gmm.from(outputFile) + } + } + return gmm + } + companion object { const val DEV_PUB__EXTENSION_NAME = "devPublish" diff --git a/src/test/kotlin/IncrementalBuildTest.kt b/src/test/kotlin/IncrementalBuildTest.kt new file mode 100644 index 0000000..aaea67b --- /dev/null +++ b/src/test/kotlin/IncrementalBuildTest.kt @@ -0,0 +1,154 @@ +package dev.adamko.gradle.dev_publish + +import dev.adamko.gradle.dev_publish.test_utils.* +import io.kotest.core.spec.style.FunSpec +import io.kotest.core.test.TestCaseOrder +import io.kotest.core.test.TestScope +import org.gradle.testkit.runner.TaskOutcome.SKIPPED +import org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class IncrementalBuildTest : FunSpec({ + + context("check incremental build") { + val project = project() + + context("initial clean run") { + test("1st time - publish task should run successfully") { + project.runner.withArguments( + ":clean", + ":updateDevRepo", + ).build { + shouldHaveTaskWithOutcome(":publishMavenJavaPublicationToDevPublishMavenRepository", SUCCESS) + } + } + + test("2nd time - publish task should be UP_TO_DATE") { + project.runner + .withArguments(":updateDevRepo", "--info") + .forwardOutput() + .build { + shouldHaveTaskWithOutcome(":publishMavenJavaPublicationToDevPublishMavenRepository", SKIPPED) + } + } + } + + context("when dependency added") { + project.buildGradleKts = project.buildGradleKts.replace("// ", "/**/") + + test("1st time - publish task should run successfully") { + project.runner + .withArguments(":updateDevRepo", "--info") + .forwardOutput() + .build { + shouldHaveTaskWithOutcome(":publishMavenJavaPublicationToDevPublishMavenRepository", SUCCESS) + } + } + + test("2nd time - publish task should be UP_TO_DATE") { + project.runner + .withArguments(":updateDevRepo", "--info") + .forwardOutput() + .build { + shouldHaveTaskWithOutcome(":publishMavenJavaPublicationToDevPublishMavenRepository", SKIPPED) + } + } + } + + context("when dependency changes") { + project.buildGradleKts = project.buildGradleKts + .replace("/**/", "// ") + .replace("// ", "/**/") + + test("1st time - publish task should run successfully") { + project.runner + .withArguments(":updateDevRepo", "--info") + .forwardOutput() + .build { + shouldHaveTaskWithOutcome(":publishMavenJavaPublicationToDevPublishMavenRepository", SUCCESS) + } + } + + test("2nd time - publish task should be UP_TO_DATE") { + project.runner + .withArguments(":updateDevRepo", "--info") + .forwardOutput() + .build { + shouldHaveTaskWithOutcome(":publishMavenJavaPublicationToDevPublishMavenRepository", SKIPPED) + } + } + } + + context("when dependency removed - expect updateDevRepo re-runs") { + project.buildGradleKts = project.buildGradleKts + .replace("/**/", "// ") + + test("1st time - publish task should run successfully") { + project.runner + .withArguments(":updateDevRepo", "--info") + .forwardOutput() + .build { + shouldHaveTaskWithOutcome(":publishMavenJavaPublicationToDevPublishMavenRepository", SUCCESS) + } + } + + test("2nd time - publish task should be UP_TO_DATE") { + project.runner + .withArguments(":updateDevRepo", "--info") + .forwardOutput() + .build { + shouldHaveTaskWithOutcome(":publishMavenJavaPublicationToDevPublishMavenRepository", SKIPPED) + } + } + } + } +}) { + + override fun testCaseOrder(): TestCaseOrder = TestCaseOrder.Sequential + + companion object { + + private fun TestScope.project(): GradleProjectTest = + gradleKtsProjectTest( + projectName = "single-module-project", + testProjectPath = testCase.descriptor.slashSeparatedPath(), + ) { + + buildGradleKts = """ + |plugins { + | kotlin("jvm") version embeddedKotlinVersion + | id("dev.adamko.dev-publish") version "+" + | `maven-publish` + |} + | + |group = "foo.project" + |version = "0.0.1" + | + |dependencies { + | //devPublication(project(":")) + |} + | + |publishing { + | publications { + | create("mavenJava") { + | from(components["java"]) + | } + | } + |} + | + |dependencies { + | // implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") + | // implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1") + |} + |""".trimMargin() + + gradleProperties = """ + |org.gradle.jvmargs=-Dfile.encoding=UTF-8 + |org.gradle.caching=false + |org.gradle.configuration-cache=true + |org.gradle.logging.level=info + |org.gradle.logging.stacktrace=full + |org.gradle.parallel=true + |""".trimMargin() + } + } +} diff --git a/src/test/kotlin/SingleProjectTest.kt b/src/test/kotlin/SingleProjectTest.kt index 67020ec..a96b877 100644 --- a/src/test/kotlin/SingleProjectTest.kt +++ b/src/test/kotlin/SingleProjectTest.kt @@ -5,8 +5,6 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.core.test.TestScope import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain -import org.gradle.testkit.runner.TaskOutcome.SUCCESS -import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.intellij.lang.annotations.Language class SingleProjectTest : FunSpec({ @@ -40,122 +38,6 @@ class SingleProjectTest : FunSpec({ } } } - - context("check incremental build") { - val project = project() - context("initial clean run") { - test("1st time - updateDevRepo should run successfully") { - project.runner.withArguments( - ":clean", - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) - } - } - - test("2nd time - updateDevRepo should be UP_TO_DATE") { - project.runner.withArguments( - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) - } - } - } - - context("when dependency added") { - project.buildGradleKts += """ - |dependencies { - | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") - |} - """.trimMargin() - - test("1st time - updateDevRepo should run successfully") { - project.runner.withArguments( - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) - } - } - - test("2nd time - updateDevRepo should be UP_TO_DATE") { - project.runner.withArguments( - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) - } - } - } - - context("when dependency changes") { - project.buildGradleKts = project.buildGradleKts.replace( - """implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0")""", - """implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")""" - ) - - test("1st time - updateDevRepo should run successfully") { - project.runner.withArguments( - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) - } - } - - test("2nd time - updateDevRepo should be UP_TO_DATE") { - project.runner.withArguments( - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) - } - } - } - - context("when dependency removed - expect updateDevRepo re-runs") { - project.buildGradleKts = project.buildGradleKts.replace( - "implementation(\"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1\")", - "", - ) - - test("1st time - updateDevRepo should run successfully") { - project.runner.withArguments( - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", SUCCESS) - } - } - - test("2nd time - updateDevRepo should be UP_TO_DATE") { - project.runner.withArguments( - ":updateDevRepo", - "--stacktrace", - "--configuration-cache", - "--build-cache", - ).build { - shouldHaveTaskWithOutcome(":updateDevRepo", UP_TO_DATE) - } - } - } - } }) { companion object {