Skip to content

Commit ecb9bbd

Browse files
authored
Merge branch 'main' into renovate/netty-monorepo
2 parents 056f9ab + c9c7cf7 commit ecb9bbd

File tree

21 files changed

+8802
-1528
lines changed

21 files changed

+8802
-1528
lines changed

build-logic/src/main/kotlin/ktorbuild.publish.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ configureSigning()
5757
plugins.withId("ktorbuild.kmp") {
5858
tasks.withType<AbstractPublishToMaven>().configureEach {
5959
val os = ktorBuild.os.get()
60-
onlyIf { isAvailableForPublication(os) }
60+
// Workaround for https://github.com/gradle/gradle/issues/22641
61+
val predicate = provider { isAvailableForPublication(publication.name, os) }
62+
onlyIf("Available for publication on $os") { predicate.get() }
6163
}
6264

6365
registerTargetsPublishTasks(ktorBuild.targets)

build-logic/src/main/kotlin/ktorbuild.publish.verifier.gradle.kts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,10 @@
33
*/
44

55
import ktorbuild.*
6-
import ktorbuild.internal.publish.*
7-
import ktorbuild.internal.publish.TestRepository.configureTestRepository
8-
import ktorbuild.internal.publish.TestRepository.locateTestRepository
9-
10-
val cleanTestRepository by tasks.registering(Delete::class) {
11-
delete(locateTestRepository())
12-
}
6+
import ktorbuild.internal.publish.ValidatePublishedArtifactsTask
137

148
val publishedProjects = projectsWithTag(ProjectTag.Published)
159

1610
tasks.register<ValidatePublishedArtifactsTask>(ValidatePublishedArtifactsTask.NAME) {
17-
dependsOn(cleanTestRepository)
18-
19-
publishedProjects.get().forEach { project ->
20-
with(project) {
21-
configureTestRepository()
22-
val publishTasks = tasks.withType<PublishToMavenRepository>()
23-
.matching { it.repository.name == TestRepository.NAME }
24-
publishTasks.configureEach { mustRunAfter(cleanTestRepository) }
25-
dependsOn(publishTasks)
26-
}
27-
}
11+
dependsOn(publishedProjects)
2812
}

build-logic/src/main/kotlin/ktorbuild/internal/publish/PublishTasks.kt

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
package ktorbuild.internal.publish
66

77
import ktorbuild.internal.capitalized
8-
import ktorbuild.internal.gradle.maybeNamed
98
import ktorbuild.targets.KtorTargets
109
import org.gradle.api.Project
1110
import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven
11+
import org.gradle.api.publish.maven.tasks.PublishToMavenRepository
1212
import org.gradle.api.publish.plugins.PublishingPlugin
1313
import org.gradle.internal.os.OperatingSystem
14+
import org.gradle.kotlin.dsl.withType
1415

1516
private val jvmAndCommonPublications = setOf(
1617
"jvm",
@@ -27,8 +28,8 @@ private val windowsPublications = KtorTargets.resolveTargets("windows")
2728
private val darwinPublications = KtorTargets.resolveTargets("darwin")
2829
private val androidNativePublications = KtorTargets.resolveTargets("androidNative")
2930

30-
internal fun AbstractPublishToMaven.isAvailableForPublication(os: OperatingSystem): Boolean {
31-
return when (val name = publication.name) {
31+
internal fun AbstractPublishToMaven.isAvailableForPublication(publicationName: String, os: OperatingSystem): Boolean {
32+
return when (publicationName) {
3233
in linuxPublications -> os.isLinux
3334
in windowsPublications -> os.isWindows
3435
in darwinPublications -> os.isMacOsX
@@ -37,7 +38,7 @@ internal fun AbstractPublishToMaven.isAvailableForPublication(os: OperatingSyste
3738
in androidNativePublications -> true
3839

3940
else -> {
40-
logger.warn("Unknown publication: $name (project ${project.path})")
41+
logger.warn("Unknown publication: $publicationName (project ${project.path})")
4142
false
4243
}
4344
}
@@ -59,12 +60,17 @@ internal fun Project.registerTargetsPublishTasks(targets: KtorTargets) = with(ta
5960
if (hasAndroidNative) registerAggregatingPublishTask("AndroidNative", androidNativePublications)
6061
}
6162

62-
private fun Project.registerAggregatingPublishTask(name: String, targets: Set<String>) {
63+
private fun Project.registerAggregatingPublishTask(name: String, publications: Set<String>) {
6364
tasks.register("publish${name}Publications") {
6465
group = PublishingPlugin.PUBLISH_TASK_GROUP
65-
val targetsTasks = targets.mapNotNull { target ->
66-
tasks.maybeNamed("publish${target.capitalized()}PublicationToMavenRepository")
67-
}
68-
dependsOn(targetsTasks)
66+
67+
val repositoryName = providers.gradleProperty("repository").orElse(MAVEN_REPO_DEFAULT_NAME)
68+
val taskNames = publications
69+
.map { "publish${it.capitalized()}PublicationTo${repositoryName.get().capitalized()}Repository" }
70+
.toSet()
71+
72+
dependsOn(tasks.withType<PublishToMavenRepository>().named { it in taskNames })
6973
}
7074
}
75+
76+
private const val MAVEN_REPO_DEFAULT_NAME = "maven"

build-logic/src/main/kotlin/ktorbuild/internal/publish/ValidatePublishedArtifactsTask.kt

Lines changed: 107 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,77 +4,148 @@
44

55
package ktorbuild.internal.publish
66

7-
import ktorbuild.internal.gradle.directoryProvider
8-
import ktorbuild.internal.gradle.regularFileProvider
9-
import ktorbuild.internal.publish.TestRepository.locateTestRepository
7+
import ktorbuild.internal.gradle.maybeNamed
108
import org.gradle.api.DefaultTask
119
import org.gradle.api.GradleException
1210
import org.gradle.api.Project
13-
import org.gradle.api.file.DirectoryProperty
1411
import org.gradle.api.file.RegularFileProperty
12+
import org.gradle.api.provider.ListProperty
1513
import org.gradle.api.provider.Property
16-
import org.gradle.api.publish.PublishingExtension
17-
import org.gradle.api.tasks.*
14+
import org.gradle.api.provider.Provider
15+
import org.gradle.api.publish.maven.MavenPublication
16+
import org.gradle.api.publish.maven.tasks.PublishToMavenRepository
17+
import org.gradle.api.tasks.Input
18+
import org.gradle.api.tasks.Internal
19+
import org.gradle.api.tasks.OutputFile
20+
import org.gradle.api.tasks.TaskAction
1821
import org.gradle.api.tasks.options.Option
19-
import org.gradle.kotlin.dsl.configure
22+
import org.gradle.kotlin.dsl.assign
2023
import org.gradle.language.base.plugins.LifecycleBasePlugin
2124
import java.io.File
2225

2326
/**
2427
* Task that verifies the list of published artifacts matches the expected artifacts.
28+
* Should be used together with exactly one publishing task.
2529
*
26-
* The expected artifact list is stored in a [artifactsDump], and this task ensures
27-
* consistency between the expected and the actual artifacts found in the [repositoryDirectory].
30+
* Examples:
31+
* ```
32+
* // Validate JS artifacts
33+
* ./gradlew validatePublishedArtifacts publishJsPublications
34+
*
35+
* // Save dump of JVM and common artifacts
36+
* ./gradlew validatePublishedArtifacts --dump publishJvmAndCommonPublications
37+
*
38+
* // Run validation against single project
39+
* ./gradlew validatePublishedArtifacts :ktor-io:publishJvmAndCommonPublications
2840
*
29-
* By default, `artifactsDump` is located at `<rootProject>/gradle/artifacts.txt` and [TestRepository] location is used
30-
* as a `repositoryDirectory`.
41+
* // Dump artifacts locally (switched publishing repository to MavenLocal)
42+
* ./gradlew validatePublishedArtifacts --dump publishJvmAndCommonPublications -Prepository=MavenLocal
43+
* ```
44+
*
45+
* The expected artifact list is stored in a [artifactsDump], and this task ensures
46+
* consistency between the expected and the actual artifacts published by the task.
47+
* `artifactsDump` for the task is located at `<rootProject>/gradle/artifacts/<taskName>.txt`.
3148
*
3249
* Initially cherry-picked from kotlinx.coroutines build scripts
3350
* See: https://github.com/Kotlin/kotlinx.serialization/blob/v1.8.0/buildSrc/src/main/kotlin/publishing-check-conventions.gradle.kts
3451
*/
3552
@Suppress("LeakingThis")
3653
internal abstract class ValidatePublishedArtifactsTask : DefaultTask() {
3754

38-
@get:InputDirectory
39-
@get:PathSensitive(PathSensitivity.RELATIVE)
40-
abstract val repositoryDirectory: DirectoryProperty
41-
42-
@get:Input
43-
abstract val packageName: Property<String>
44-
4555
@get:Option(option = DUMP_OPTION, description = "Dumps the list of published artifacts to a file")
4656
@get:Input
4757
abstract val dump: Property<Boolean>
4858

59+
@get:Input
60+
protected abstract val publishedArtifacts: ListProperty<String>
61+
62+
@get:Internal
63+
protected abstract val publishTaskName: Property<String>
64+
4965
@get:OutputFile
50-
abstract val artifactsDump: RegularFileProperty
66+
protected abstract val artifactsDump: RegularFileProperty
5167

5268
init {
5369
group = LifecycleBasePlugin.VERIFICATION_GROUP
5470
description = "Checks that the list of published artifacts matches the expected one"
5571

56-
outputs.cacheIf("Enable cache only when collecting a dump") { dump.get() }
72+
outputs.cacheIf("Isn't worth caching") { false }
73+
dump.convention(false)
74+
configurePublishTaskName()
75+
76+
// Initialize the publishedArtifacts list to empty by default
77+
publishedArtifacts.convention(emptyList())
5778

58-
with(project) {
59-
repositoryDirectory.convention(directoryProvider { locateTestRepository() })
60-
artifactsDump.convention(regularFileProvider { rootDir.resolve("gradle/artifacts.txt") })
61-
packageName.convention(rootProject.group.toString())
79+
// Collect artifacts from the all publishing tasks in the task graph
80+
project.gradle.taskGraph.whenReady {
81+
val publishTasks = allTasks.filterIsInstance<PublishToMavenRepository>()
82+
if (publishTasks.isNotEmpty()) {
83+
publishTasks.forEach { publishTask ->
84+
publishedArtifacts.addAll(publishTask.publication.formatArtifacts())
85+
}
86+
}
87+
}
88+
}
89+
90+
private fun configurePublishTaskName() {
91+
val taskNames = project.gradle.startParameter.taskNames.filterNot { name ->
92+
// Filter out the validation task itself and task parameters
93+
name == NAME || name.startsWith("-")
94+
}
95+
96+
// Find publish tasks among the specified tasks
97+
val publishTasks = taskNames.filter { it.contains("publish", ignoreCase = true) }
98+
99+
if (publishTasks.isEmpty()) {
100+
// If no publish task is specified, use a default name for the artifacts dump
101+
publishTaskName.set("defaultPublish")
102+
artifactsDump.set(project.rootDir.resolve("gradle/artifacts/defaultPublish.txt"))
103+
return
104+
}
105+
106+
// Use the first publish task if multiple are specified
107+
publishTaskName.set(publishTasks.first())
108+
artifactsDump = publishTaskName.map { taskName ->
109+
val sanitizedTaskName = taskName.replace(":", "_")
110+
project.rootDir.resolve("gradle/artifacts/${sanitizedTaskName}.txt")
111+
}
112+
}
113+
114+
fun dependsOn(publishedProjects: Provider<List<Project>>) = with(project) {
115+
// Handle the case when an absolute task path is specified
116+
val taskName = publishTaskName.get()
117+
if (taskName.startsWith(":")) {
118+
val publishTask = tasks.getByPath(taskName)
119+
dependsOn(publishTask)
120+
return
121+
}
122+
123+
publishedProjects.get().forEach { project ->
124+
val task = project.tasks.maybeNamed(taskName)
125+
if (task != null) {
126+
dependsOn(task)
127+
}
62128
}
63-
dump.convention(false)
64129
}
65130

66131
@TaskAction
67-
fun validate() {
68-
val packagePath = packageName.get().replace(".", "/")
69-
val actualArtifacts = repositoryDirectory.get().asFile
70-
.resolve(packagePath)
71-
.list()
72-
?.toSortedSet()
73-
.orEmpty()
132+
fun runValidation() {
133+
publishedArtifacts.finalizeValue()
134+
135+
val actualArtifacts = publishedArtifacts.get().toSortedSet()
74136
val dumpFile = artifactsDump.get().asFile
75137

138+
// If no artifacts were collected and we're not in dump mode, skip validation
139+
if (actualArtifacts.isEmpty() && !dump.get()) {
140+
logger.lifecycle("No artifacts were collected for validation. Skipping.")
141+
return
142+
}
143+
76144
if (dump.get()) {
77-
if (!dumpFile.exists()) dumpFile.createNewFile()
145+
if (!dumpFile.exists()) {
146+
dumpFile.parentFile.mkdirs()
147+
dumpFile.createNewFile()
148+
}
78149

79150
dumpFile.bufferedWriter().use { writer ->
80151
actualArtifacts.forEach { artifact -> writer.appendLine(artifact) }
@@ -117,20 +188,6 @@ internal abstract class ValidatePublishedArtifactsTask : DefaultTask() {
117188
}
118189
}
119190

120-
internal object TestRepository {
121-
const val NAME = "test"
122-
123-
fun Project.configureTestRepository() {
124-
extensions.configure<PublishingExtension> {
125-
repositories {
126-
maven {
127-
name = NAME
128-
setUrl(locateTestRepository())
129-
}
130-
}
131-
}
132-
}
133-
134-
fun Project.locateTestRepository(): File =
135-
rootDir.resolve("build/${ValidatePublishedArtifactsTask.NAME}Repository")
136-
}
191+
private fun MavenPublication.formatArtifacts(): List<String> =
192+
artifacts.map { "${groupId}:${artifactId}/${it.classifier.orEmpty()}.${it.extension}" }
193+
.ifEmpty { listOf("$groupId:$artifactId") }

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ kotlin.code.style=official
77

88
# config
99
group=io.ktor
10-
version=3.1.2
10+
version=3.1.3-SNAPSHOT
1111

1212
## Performance
1313

0 commit comments

Comments
 (0)