From 1379eb57ee84bfe3d5269e6fa4276616afbc9256 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Tue, 24 Jun 2025 10:43:42 +0200 Subject: [PATCH] Only update maven-metadata.xml once all the files have been uploaded --- .../internal/task/nmcpPublishFileByFile.kt | 209 +++++++++--------- src/main/kotlin/nmcp/internal/utils.kt | 2 - 2 files changed, 108 insertions(+), 103 deletions(-) diff --git a/src/main/kotlin/nmcp/internal/task/nmcpPublishFileByFile.kt b/src/main/kotlin/nmcp/internal/task/nmcpPublishFileByFile.kt index d73ad62..3255432 100644 --- a/src/main/kotlin/nmcp/internal/task/nmcpPublishFileByFile.kt +++ b/src/main/kotlin/nmcp/internal/task/nmcpPublishFileByFile.kt @@ -5,6 +5,8 @@ import gratatouille.GInputFiles import gratatouille.GLogger import gratatouille.GTask import java.security.MessageDigest +import kotlin.text.lastIndexOf +import kotlin.text.substring import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import okio.ByteString.Companion.toByteString @@ -37,123 +39,128 @@ fun nmcpPublishFileByFile( } } - inputFiles - .filter { it.file.isFile } - .groupBy { - it.normalizedPath.substringBeforeLast('/') - }.forEach { (gavPath, files) -> - val gav = Gav.from(gavPath) - val version = gav.version - - if (files.all { it.normalizedPath.substringAfterLast('/').startsWith("maven-metadata") }) { - /** - * Update the [artifact metadata](https://maven.apache.org/repositories/metadata.html). - * - * See https://repo1.maven.org/maven2/com/apollographql/apollo/apollo-api-jvm/maven-metadata.xml for an example. - */ - val localArtifactMetadataFile = files.firstOrNull {it.normalizedPath.substringAfterLast('/') == "maven-metadata.xml" } - if (localArtifactMetadataFile == null) { - error("Nmcp: cannot find artifact maven-metadata.xml in '${gav.groupId.toPath()}/${gav.artifactId}'") - } - val artifactMetadataPath = localArtifactMetadataFile.normalizedPath + val allFiles = inputFiles.filter { it.file.isFile } + val gavPaths = allFiles.filter { it.normalizedPath.endsWith(".pom") || it.normalizedPath.endsWith(".module") } + .map { it.normalizedPath.substringBeforeLast('/') } + .distinct() - val localArtifactMetadata = xml.decodeFromString(localArtifactMetadataFile.file.readText()) - val remoteArtifactMetadata = transport.get(artifactMetadataPath) + gavPaths.forEach { gavPath -> + val gav = Gav.from(gavPath) + val version = gav.version + val gavFiles = allFiles.filter { it.normalizedPath.startsWith(gavPath) } - val existingVersions = if (remoteArtifactMetadata != null) { - xml.decodeFromString(remoteArtifactMetadata.use { it.readUtf8() }).versioning.versions - } else { - emptyList() - } + /** + * This is a proper directory containing artifacts + */ + if (version.endsWith("-SNAPSHOT")) { + /** + * This is a snapshot: + * - update the [version metadata](https://maven.apache.org/repositories/metadata.html). + * - path the file names to include the new build number. + * + * See https://s01.oss.sonatype.org/content/repositories/snapshots/com/apollographql/apollo/apollo-api-jvm/maven-metadata.xml for an example. + * + * For snapshots, it's not 100% clear who owns the metadata as the repository might expire some snapshot and therefore need to rewrite the + * metadata to keep things consistent. This means, there are 2 possibly concurrent writers to maven-metadata.xml: the repository and the + * publisher. Hopefully it's not too much of a problem in practice. + * + * See https://github.com/gradle/gradle/blob/d1ee068b1ee7f62ffcbb549352469307781af72e/platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/publisher/MavenRemotePublisher.java#L70. + */ + val versionMetadataPath = "$gavPath/maven-metadata.xml" + val localVersionMetadataFile = gavFiles.firstOrNull { + it.normalizedPath == versionMetadataPath + } + if (localVersionMetadataFile == null) { + error("Nmcp: cannot find version maven-metadata.xml in '$gavPath'") + } - /** - * See https://github.com/gradle/gradle/blob/cb0c615fb8e3690971bb7f89ad80f58943360624/platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/publisher/AbstractMavenPublisher.java#L116. - */ - val versions = existingVersions.toMutableList() - if (!versions.none { it == gav.version }) { - versions.add(gav.version) - } - val newArtifactMetadata = localArtifactMetadata.copy( - versioning = localArtifactMetadata.versioning.copy( - versions = versions, - ), - ) - - val bytes = encodeToXml(newArtifactMetadata).toByteArray() - transport.put(artifactMetadataPath, bytes) - setOf("md5", "sha1", "sha256", "sha512").forEach { - transport.put("$artifactMetadataPath.$it", bytes.digest(it.uppercase())) + val localVersionMetadata = + xml.decodeFromString(localVersionMetadataFile.file.readText()) + val remoteVersionMetadata = transport.get(versionMetadataPath) + + val buildNumber = if (remoteVersionMetadata == null) { + 1 + } else { + xml.decodeFromString(remoteVersionMetadata.use { it.readUtf8() }).versioning.snapshot.buildNumber + 1 + } + + val newVersionMetadata = localVersionMetadata.copy( + versioning = localVersionMetadata.versioning.copy( + snapshot = localVersionMetadata.versioning.snapshot.copy(buildNumber = buildNumber), + ), + ) + + val renamedFiles = gavFiles.mapNotNull { + if (it.file.name.startsWith("maven-metadata.xml")) { + return@mapNotNull null } + val newName = it.file.name.replaceBuildNumber(gav.artifactId, gav.version, buildNumber) + FileWithPath(it.file, "$gavPath/$newName") + } + + transport.uploadFiles(renamedFiles) - return@forEach + val bytes = encodeToXml(newVersionMetadata).toByteArray() + transport.put(versionMetadataPath, bytes) + setOf("md5", "sha1", "sha256", "sha512").forEach { + transport.put("$versionMetadataPath.$it", bytes.digest(it.uppercase())) } + } else { /** - * This is a proper directory containing artifacts + * Not a snapshot, plainly update all the files */ - if (version.endsWith("-SNAPSHOT")) { - /** - * This is a snapshot: - * - update the [version metadata](https://maven.apache.org/repositories/metadata.html). - * - path the file names to include the new build number. - * - * See https://s01.oss.sonatype.org/content/repositories/snapshots/com/apollographql/apollo/apollo-api-jvm/maven-metadata.xml for an example. - * - * For snapshots, it's not 100% clear who owns the metadata as the repository might expire some snapshot and therefore need to rewrite the - * metadata to keep things consistent. This means, there are 2 possibly concurrent writers to maven-metadata.xml: the repository and the - * publisher. Hopefully it's not too much of a problem in practice. - * - * See https://github.com/gradle/gradle/blob/d1ee068b1ee7f62ffcbb549352469307781af72e/platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/publisher/MavenRemotePublisher.java#L70. - */ - val versionMetadataPath = "$gavPath/maven-metadata.xml" - val localVersionMetadataFile = files.firstOrNull { - it.normalizedPath == versionMetadataPath - } - if (localVersionMetadataFile == null) { - error("Nmcp: cannot find version maven-metadata.xml in '$gavPath'") - } - - val localVersionMetadata = - xml.decodeFromString(localVersionMetadataFile.file.readText()) - val remoteVersionMetadata = transport.get(versionMetadataPath) + transport.uploadFiles(gavFiles) + } - val buildNumber = if (remoteVersionMetadata == null) { - 1 - } else { - xml.decodeFromString(remoteVersionMetadata.use { it.readUtf8() }).versioning.snapshot.buildNumber + 1 - } + /** + * Update the [artifact metadata](https://maven.apache.org/repositories/metadata.html). + * + * See https://repo1.maven.org/maven2/com/apollographql/apollo/apollo-api-jvm/maven-metadata.xml for an example. + */ + val index = gavPath.lastIndexOf('/') + check (index != -1) { + "Nmcp: invalid gav path: '$gavPath'" + } + val artifactMetadataPath = "${gavPath.substring(0, index)}/maven-metadata.xml" + val localArtifactMetadataFile = allFiles.firstOrNull { it.normalizedPath == artifactMetadataPath } + if (localArtifactMetadataFile == null) { + error("Nmcp: cannot find artifact metadata at '${artifactMetadataPath}'") + } - val newVersionMetadata = localVersionMetadata.copy( - versioning = localVersionMetadata.versioning.copy( - snapshot = localVersionMetadata.versioning.snapshot.copy(buildNumber = buildNumber), - ), - ) - - val renamedFiles = files.mapNotNull { - if (it.file.name.startsWith("maven-metadata.xml")) { - return@mapNotNull null - } - val newName = it.file.name.replaceBuildNumber(gav.artifactId, gav.version, buildNumber) - FileWithPath(it.file, "$gavPath/$newName") - } + val localArtifactMetadata = + xml.decodeFromString(localArtifactMetadataFile.file.readText()) + val remoteArtifactMetadata = transport.get(artifactMetadataPath) - transport.uploadFiles(renamedFiles) + val existingVersions = if (remoteArtifactMetadata != null) { + xml.decodeFromString(remoteArtifactMetadata.use { it.readUtf8() }).versioning.versions + } else { + emptyList() + } - val bytes = encodeToXml(newVersionMetadata).toByteArray() - transport.put(versionMetadataPath, bytes) - setOf("md5", "sha1", "sha256", "sha512").forEach { - transport.put("$versionMetadataPath.$it", bytes.digest(it.uppercase())) - } - } else { - /** - * Not a snapshot, plainly update all the files - */ - transport.uploadFiles(files) - } + /** + * See https://github.com/gradle/gradle/blob/cb0c615fb8e3690971bb7f89ad80f58943360624/platforms/software/maven/src/main/java/org/gradle/api/publish/maven/internal/publisher/AbstractMavenPublisher.java#L116. + */ + val versions = existingVersions.toMutableList() + if (!versions.none { it == gav.version }) { + versions.add(gav.version) } + val newArtifactMetadata = localArtifactMetadata.copy( + versioning = localArtifactMetadata.versioning.copy( + versions = versions, + ), + ) + + val bytes = encodeToXml(newArtifactMetadata).toByteArray() + transport.put(artifactMetadataPath, bytes) + setOf("md5", "sha1", "sha256", "sha512").forEach { + transport.put("$artifactMetadataPath.$it", bytes.digest(it.uppercase())) + } + + } } private fun Transport.uploadFiles(filesWithPath: List) { - filesWithPath.forEach { + filesWithPath.sortedBy { it.normalizedPath }.forEach { put(it.normalizedPath, it.file) } } diff --git a/src/main/kotlin/nmcp/internal/utils.kt b/src/main/kotlin/nmcp/internal/utils.kt index a94aeda..3ef43cb 100644 --- a/src/main/kotlin/nmcp/internal/utils.kt +++ b/src/main/kotlin/nmcp/internal/utils.kt @@ -15,8 +15,6 @@ import org.gradle.api.attributes.HasConfigurableAttributes import org.gradle.api.attributes.Usage import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE import org.gradle.api.file.FileCollection -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.bundling.Zip internal fun Project.withRequiredPlugin(id: String, block: () -> Unit) { var hasPlugin = false