-
Notifications
You must be signed in to change notification settings - Fork 415
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use project-local Maven dev repos for integration testing. ### Impact Using a Maven Repo is useful for integration tests, because it verifies that the artifacts are published correctly with the expected coordinates. It is also useful for Gradle Plugins, because we can verify that the [plugin marker](https://docs.gradle.org/8.6/userguide/plugins.html#sec:plugin_markers) was correctly published. ### Summary * Local convention plugin `dokkabuild.dev-maven-publish.gradle.kts` publishes subprojects to a project-local directory. Benefits: * compatible with: config cache, build cache, isolated projects * doesn't use Maven Local, so there's no chance of cross-contamination with outdated artifacts or other projects * Dynamically inject the local Maven dir: * Gradle projects: find/replace `/* %{PROJECT_LOCAL_MAVEN_DIR}% */` with an exclusive Maven repo * Maven projects: provide a `settings.xml` with the local plugin repo. --- * Maven publish artifacts to project-local directory. This helps with Gradle Build & Config cache. * merge cleanup * change plugin `test-suite-base` -> `jvm-test-suite` * move registerTestProjectSuite() to top-level function * move gradle-specific test utils to gradle integration test * move unused systemVariableProviders.kt back * move `multimodule-inter-module-links` to test-suite * register DOKKA_TEST_OUTPUT_PATH as a test task input, and the dir as an output * rm old import * make testOutputPath optional * re-add integrationTest lifecycle task * fix `it-multimodule-inter-module-links` dir, add property name to templateProjectDir for better error messages * comment to explain test-suites are independent * add property names to coroutines/serialization project dirs * use jvm11 for kotlinx.serialization * update maven-dev-plugin usage * revert MaxMetaspaceSize in Coroutines test, and add comment to explain * environment workaround for gradle/gradle#11534 * update CI build properties * remove `allWarningsAsErrors = false` * remove 'check' task dependency * tidy * remove `integrationTestPreparation` (it was less useful than I thought), plus misc tidying * update git patches, and dev-maven injection * tidying * fix dokka version in IT projects * remove unnecessary `.toString()` * revert change to Dokkatoo util * move dev maven repo task input registration to util function * replace `System.getenv("dokka_it_dokka_version")` with `providers.gradleProperty("dokka_it_dokka_version").get()` in all test projects * update dev-repos after git patches * update coroutines and serialization patches * fix coroutines git patch * fomatting * bump dev-publish-plugin version * restore deleted it-multimodule-inter-module-links/gradle.properties * replace `dev.adamko.dev-publish` with custom implementation that uses `maven.repo.local` * remove old kotlinx-serialization git submodule * fix CC compatibility * fixin' build caching - only register deterministic repo files as a task input - cache the output of Gradle integration test tasks * use `tasks.check {}` dsl accessor * update IB comment * update comment regarding Maven Metadata exclusions
- Loading branch information
Showing
29 changed files
with
768 additions
and
426 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
build-logic/src/main/kotlin/dokkabuild.dev-maven-publish.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* | ||
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
import dokkabuild.DevMavenPublishExtension | ||
import dokkabuild.DevMavenPublishExtension.Companion.DEV_MAVEN_PUBLISH_EXTENSION_NAME | ||
import dokkabuild.internal.consumable | ||
import dokkabuild.internal.declarable | ||
import dokkabuild.internal.resolvable | ||
import org.gradle.kotlin.dsl.support.uppercaseFirstChar | ||
|
||
/** | ||
* Utility for publishing a project to a local Maven directory for use in integration tests. | ||
* | ||
* Using a local directory is beneficial because Maven Local | ||
* [has some flaws](https://docs.gradle.org/8.6/userguide/declaring_repositories.html#sec:case-for-maven-local), | ||
* and we can more tightly control what artifacts are published and are persisted. | ||
* | ||
* It's possible to publish to a local directory using a regular [PublishToMavenRepository] task, | ||
* but when the project has a SNAPSHOT version the output will be timestamped, so Gradle will | ||
* _always_ publish a new artifact. This causes two issues: | ||
* | ||
* - The publication tasks, and any test tasks, will _always_ be out-of-date, even if no code changed. | ||
* - The local directory will endlessly grow in size | ||
* (which can be remedied by running `./gradlew clean`, but this is not ideal) | ||
* | ||
* To overcome this we manually set the system property `maven.repo.local` to a local directory. | ||
* Gradle will respect this property, and publish artifacts to the local directory only when | ||
* they have changed, improving performance. | ||
*/ | ||
plugins { | ||
base | ||
} | ||
|
||
/** | ||
* Directory for the output of the current subproject's 'publishToMavenLocal' | ||
*/ | ||
val currentProjectDevMavenRepo = gradle.rootProject.layout.buildDirectory.dir("dev-maven-repo") | ||
|
||
val devMavenPublishAttribute = Attribute.of("dev-maven-publish", String::class.java) | ||
|
||
dependencies { | ||
attributesSchema { | ||
attribute(devMavenPublishAttribute) | ||
} | ||
} | ||
|
||
val publishToDevMavenRepo by tasks.registering { | ||
description = "Publishes all Maven publications to the dev Maven repository." | ||
group = PublishingPlugin.PUBLISH_TASK_GROUP | ||
} | ||
|
||
|
||
plugins.withType<MavenPublishPlugin>().all { | ||
extensions | ||
.getByType<PublishingExtension>() | ||
.publications | ||
.withType<MavenPublication>().all publication@{ | ||
val publicationName = this@publication.name | ||
val installTaskName = "publish${publicationName.uppercaseFirstChar()}PublicationToDevMavenRepo" | ||
|
||
// Register a new publication task for each publication. | ||
val installTask = tasks.register<PublishToMavenLocal>(installTaskName) { | ||
description = "Publishes Maven publication '$publicationName' to the test Maven repository." | ||
group = PublishingPlugin.PUBLISH_TASK_GROUP | ||
publication = this@publication | ||
|
||
val destinationDir = currentProjectDevMavenRepo.get().asFile | ||
inputs.property("currentProjectDevMavenRepoPath", destinationDir.invariantSeparatorsPath) | ||
|
||
doFirst { | ||
/** | ||
* `maven.repo.local` will set the destination directory for this [PublishToMavenLocal] task. | ||
* | ||
* @see org.gradle.api.internal.artifacts.mvnsettings.DefaultLocalMavenRepositoryLocator.getLocalMavenRepository | ||
*/ | ||
System.setProperty("maven.repo.local", destinationDir.absolutePath) | ||
} | ||
} | ||
|
||
publishToDevMavenRepo.configure { | ||
dependsOn(installTask) | ||
} | ||
|
||
tasks.check { | ||
mustRunAfter(installTask) | ||
} | ||
} | ||
} | ||
|
||
|
||
val devPublication: Configuration by configurations.creating { | ||
description = "Depend on project-local Dev Maven repositories" | ||
declarable() | ||
} | ||
|
||
val devPublicationResolver: Configuration by configurations.creating { | ||
description = "Resolve project-local Dev Maven repositories" | ||
resolvable() | ||
extendsFrom(devPublication) | ||
attributes { | ||
attribute(devMavenPublishAttribute, "devMavenRepo") | ||
} | ||
} | ||
|
||
val devPublicationConsumable: Configuration by configurations.creating { | ||
description = "Provide project-local Dev Maven repositories dependencies" | ||
consumable() | ||
attributes { | ||
attribute(devMavenPublishAttribute, "devMavenRepo") | ||
} | ||
outgoing { | ||
artifact(currentProjectDevMavenRepo) { | ||
builtBy(publishToDevMavenRepo) | ||
} | ||
} | ||
} | ||
|
||
val devMavenPublishExtension = extensions.create<DevMavenPublishExtension>( | ||
DEV_MAVEN_PUBLISH_EXTENSION_NAME, | ||
// fetch Dev Maven Repos from the dependencies | ||
devPublicationResolver.incoming.files, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
package dokkabuild | ||
|
||
import org.gradle.api.Task | ||
import org.gradle.api.file.FileCollection | ||
import org.gradle.api.file.FileTree | ||
import org.gradle.api.tasks.PathSensitivity.RELATIVE | ||
import org.gradle.process.JavaForkOptions | ||
|
||
abstract class DevMavenPublishExtension( | ||
/** | ||
* Resolves Dev Maven repos from the current project's `devPublication` dependencies. | ||
* | ||
* Must only contain directories. | ||
*/ | ||
private val devMavenRepositories: FileCollection, | ||
) { | ||
|
||
/** | ||
* Files suitable for registering as a task input (as in, the files are reproducible-build compatible). | ||
*/ | ||
private val devMavenRepositoriesInputFiles: FileTree = devMavenRepositories | ||
.asFileTree | ||
.matching { | ||
// Exclude Maven Metadata files because they contain timestamps, meaning tasks that use | ||
// devMavenRepositories as an input will never be up-to-date. | ||
// The Gradle Module Metadata contains the same information (and more), | ||
// so the Maven metadata is redundant. | ||
exclude("**/maven-metadata*.xml") | ||
} | ||
|
||
/** | ||
* Configures [task] to register [devMavenRepositories] as a task input, | ||
* and (if possible) adds `devMavenRepository` as a [JavaForkOptions.systemProperty]. | ||
*/ | ||
fun configureTask(task: Task) { | ||
task.inputs.files(devMavenRepositoriesInputFiles) | ||
.withPropertyName("devMavenPublish.devMavenRepositoriesInputFiles") | ||
.withPathSensitivity(RELATIVE) | ||
|
||
task.dependsOn(devMavenRepositories) | ||
|
||
if (task is JavaForkOptions) { | ||
task.doFirst("devMavenRepositories systemProperty") { | ||
// workaround https://github.com/gradle/gradle/issues/24267 | ||
task.systemProperty( | ||
"devMavenRepositories", | ||
devMavenRepositories.joinToString(",") { it.canonicalFile.invariantSeparatorsPath } | ||
) | ||
} | ||
} | ||
} | ||
|
||
companion object { | ||
const val DEV_MAVEN_PUBLISH_EXTENSION_NAME = "devMavenPublish" | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
build-logic/src/main/kotlin/dokkabuild/internal/gradleUtils.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
|
||
package dokkabuild.internal | ||
|
||
import org.gradle.api.artifacts.Configuration | ||
|
||
/** | ||
* Mark this [Configuration] as one that will be consumed by other subprojects. | ||
* | ||
* ``` | ||
* isCanBeResolved = false | ||
* isCanBeConsumed = true | ||
* isCanBeDeclared = false | ||
* ``` | ||
*/ | ||
fun Configuration.consumable( | ||
visible: Boolean = false | ||
) { | ||
isVisible = visible | ||
isCanBeResolved = false | ||
isCanBeConsumed = true | ||
isCanBeDeclared = false | ||
} | ||
|
||
/** | ||
* Mark this [Configuration] as one that will consume artifacts from other subprojects (also known as 'resolving') | ||
* | ||
* ``` | ||
* isCanBeResolved = true | ||
* isCanBeConsumed = false | ||
* isCanBeDeclared = false | ||
* ``` | ||
*/ | ||
fun Configuration.resolvable( | ||
visible: Boolean = false | ||
) { | ||
isVisible = visible | ||
isCanBeResolved = true | ||
isCanBeConsumed = false | ||
isCanBeDeclared = false | ||
} | ||
|
||
/** | ||
* Mark this [Configuration] as one that will be used to declare dependencies. | ||
* | ||
* ``` | ||
* isCanBeResolved = false | ||
* isCanBeConsumed = false | ||
* isCanBeDeclared = true | ||
* ``` | ||
*/ | ||
fun Configuration.declarable( | ||
visible: Boolean = false | ||
) { | ||
isVisible = visible | ||
isCanBeResolved = false | ||
isCanBeConsumed = false | ||
isCanBeDeclared = true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.