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 api/dev-publish-plugin.api
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
52 changes: 42 additions & 10 deletions src/main/kotlin/DevPublishPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,6 +26,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.*
Expand Down Expand Up @@ -138,7 +140,12 @@ constructor(
publicationData.addAllLater(providers.provider {
publications
.withType<MavenPublication>()
.mapNotNull { createPublicationData(it) }
.mapNotNull { publication ->
createPublicationData(
project = project,
publication = publication,
)
}
})
}
}
Expand Down Expand Up @@ -173,12 +180,21 @@ constructor(

val currentProjectDir = layout.projectDirectory

val publicationData = providers.provider { createPublicationData(publication) }
val publicationData = providers.provider {
createPublicationData(
project = project,
publication = publication,
)
}

inputs.files(publicationData.map { it.gradleModuleMetadata })
.withPropertyName("devPubGradleModuleMetadata")
.withPathSensitivity(RELATIVE)

val currentChecksum = providers.createPublicationChecksum {
this.projectDir.set(currentProjectDir)
this.artifacts.from(publicationData.map { it.artifacts })
this.identifier.set(publicationData.flatMap { it.identifier })
this.gradleModuleMetadata.from(publicationData.map { it.gradleModuleMetadata })
}

val storedChecksum = providers.loadPublicationChecksum {
Expand Down Expand Up @@ -244,27 +260,40 @@ constructor(

/** Create an instance of [PublicationData] from [publication]. */
private fun createPublicationData(
project: Project,
publication: MavenPublication?,
): PublicationData? {
if (publication == null) {
logger.warn("cannot create PublicationData - MavenPublication is null")
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 = getGmm(project, publication)

return objects.newInstance<PublicationData>(publication.name).apply {
this.identifier.set(identifier)
this.artifacts.from(artifacts)
this.gradleModuleMetadata.from(gmm)
}
}


private fun getGmm(
project: Project,
publication: MavenPublication,
): FileCollection {
val gmm = objects.fileCollection()
project.tasks
.withType<GenerateModuleMetadata>()
.all {
if (name == publication.getGenerateModuleMetadataTaskName()) {
gmm.from(outputFile)
}
}
return gmm
}

companion object {
const val DEV_PUB__EXTENSION_NAME = "devPublish"

Expand All @@ -282,5 +311,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"
}
}
17 changes: 16 additions & 1 deletion src/main/kotlin/data/PublicationData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down
18 changes: 8 additions & 10 deletions src/main/kotlin/internal/checksums/CreatePublicationChecksum.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,28 @@ internal abstract class CreatePublicationChecksum : ValueSource<String, CreatePu

interface Parameters : ValueSourceParameters {
val projectDir: DirectoryProperty
val artifacts: ConfigurableFileCollection
val identifier: Property<String>
val gradleModuleMetadata: ConfigurableFileCollection
}

override fun obtain(): String? {
val identifier = parameters.identifier.get()
val artifactsChecksums = artifactsChecksums()
val gradleModuleMetadataChecksums = gradleModuleMetadataChecksums()

return buildString {
appendLine(identifier)
appendLine("---")
artifactsChecksums.forEach {
gradleModuleMetadataChecksums.forEach {
appendLine(it)
}
}.trim()
}

private fun artifactsChecksums(): List<String> {
val projectDir = parameters.projectDir.get().asFile

return parameters.artifacts
.map { artifact ->
val artifactPath = artifact.relativeTo(projectDir).invariantSeparatorsPath
"${artifactPath}$FileChecksumSeparator${artifact.checksum()}"
private fun gradleModuleMetadataChecksums(): List<String> {
return parameters.gradleModuleMetadata
.map { gmm ->
val gmmPath = gmm.relativeTo(parameters.projectDir.get().asFile).invariantSeparatorsPath
"${gmmPath}$FileChecksumSeparator${gmm.checksum()}"
}
.sorted()
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/tasks/GeneratePublicationDataChecksumTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ 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()

checksumFile.writeText(checksum)
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/utils/stdlibUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> =
substringBefore(delimiter) to substringAfter(delimiter, "")

internal fun String.uppercaseFirstChar(): String =
replaceFirstChar { it.uppercase() }
9 changes: 4 additions & 5 deletions src/test/kotlin/BuildCacheTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -104,9 +103,9 @@ class BuildCacheTest : FunSpec({
settingsGradleKts += """
|
|buildCache {
| local {
| directory = file("local-cache").toURI()
| }
| local {
| directory = file("local-cache").toURI()
| }
|}
|""".trimMargin()

Expand Down
154 changes: 154 additions & 0 deletions src/test/kotlin/IncrementalBuildTest.kt
Original file line number Diff line number Diff line change
@@ -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("//<dep1> ", "/*<dep1>*/")

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("/*<dep1>*/", "//<dep1> ")
.replace("//<dep2> ", "/*<dep2>*/")

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("/*<dep2>*/", "//<dep2> ")

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<MavenPublication>("mavenJava") {
| from(components["java"])
| }
| }
|}
|
|dependencies {
| //<dep1> implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0")
| //<dep2> 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()
}
}
}
Loading