build scripts: Maven Central publication validation code decoupled from Jewel-related code

GitOrigin-RevId: 8cd53e83176e74ceffa948122c14707834117971
This commit is contained in:
Dmitriy.Panov
2025-09-03 12:39:30 +00:00
committed by intellij-monorepo-bot
parent 8a81aae38d
commit 6c222d8345
5 changed files with 83 additions and 59 deletions
@@ -74,6 +74,9 @@ open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIde
"intellij.platform.util.base.multiplatform",
"intellij.platform.util.zip",
))
mavenArtifacts.validateForMavenCentralPublication = { module ->
JewelMavenArtifacts.isPublishedJewelModule(module)
}
mavenArtifacts.patchCoordinates = { module, coordinates ->
when {
JewelMavenArtifacts.isPublishedJewelModule(module) -> JewelMavenArtifacts.patchCoordinates(module, coordinates)
@@ -2,30 +2,22 @@
package org.jetbrains.intellij.build
import com.intellij.util.text.SemVer
import kotlin.io.path.exists
import kotlin.io.path.name
import org.apache.maven.model.Developer
import org.apache.maven.model.License
import org.apache.maven.model.Model
import org.apache.maven.model.Scm
import org.jetbrains.intellij.build.BuildPaths.Companion.COMMUNITY_ROOT
import org.jetbrains.intellij.build.dependencies.DependenciesProperties
import org.jetbrains.intellij.build.impl.libraries.isLibraryModule
import org.jetbrains.intellij.build.impl.maven.DependencyScope
import org.jetbrains.intellij.build.impl.maven.GeneratedMavenArtifacts
import org.jetbrains.intellij.build.impl.maven.MavenArtifactDependency
import org.jetbrains.intellij.build.impl.maven.MavenCentralPublication
import org.jetbrains.intellij.build.impl.maven.MavenCoordinates
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.java.JpsJavaDependencyScope
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsDependencyElement
import org.jetbrains.jps.model.module.JpsModule
import org.jetbrains.jps.model.module.JpsModuleDependency
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.copyTo
import kotlin.io.path.createDirectories
import kotlin.io.path.deleteRecursively
import kotlin.io.path.exists
internal object JewelMavenArtifacts {
private const val GROUP_ID: String = "org.jetbrains.jewel"
@@ -155,16 +147,10 @@ internal object JewelMavenArtifacts {
check(isPublishedJewelModule(module))
model.name = "Jewel"
model.description = "A theme for Compose for Desktop that implements the IntelliJ Platform look and feel."
model.url = "https://github.com/JetBrains/intellij-community"
model.addLicense(License().apply {
name = "Apache License 2.0"
url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
})
model.scm = Scm().apply {
connection = "scm:git:https://github.com/JetBrains/intellij-community.git"
developerConnection = "scm:git:https://github.com/JetBrains/intellij-community.git"
url = "https://github.com/JetBrains/intellij-community"
}
model.addDeveloper(Developer().apply {
id = "Google"
name = "Google Team"
@@ -188,7 +174,7 @@ internal object JewelMavenArtifacts {
return (scope == JpsJavaDependencyScope.COMPILE || scope == JpsJavaDependencyScope.PROVIDED)
}
suspend fun validate(context: BuildContext, mavenArtifacts: Collection<GeneratedMavenArtifacts>) {
fun validate(context: BuildContext, mavenArtifacts: Collection<GeneratedMavenArtifacts>) {
ALL_MODULES.asSequence()
.map(context::findRequiredModule)
.flatMap { it.modulesTree() }
@@ -207,7 +193,6 @@ internal object JewelMavenArtifacts {
"The module ${module.name} has groupId=${artifact.coordinates.groupId} " +
"but it's expected to have groupId=$GROUP_ID because Maven Central publication credentials are issues per namespace/groupId"
}
validateForMavenCentralPublication(artifact, context)
}
}
for ((jewelModuleName, artifactId) in ALL) {
@@ -221,40 +206,4 @@ internal object JewelMavenArtifacts {
}
}
}
/** See https://central.sonatype.org/publish/requirements */
@OptIn(ExperimentalPathApi::class)
private suspend fun validateForMavenCentralPublication(artifacts: GeneratedMavenArtifacts, context: BuildContext) {
if (artifacts.module.getSourceRoots(JavaSourceRootType.SOURCE).any()) {
val sources = artifacts.coordinates.getFileName("sources", "jar")
check(artifacts.files.any { it.name == sources }) {
"No $sources is generated for the module ${artifacts.module.name}"
}
val javadoc = artifacts.coordinates.getFileName("javadoc", "jar")
check(artifacts.files.any { it.name == javadoc }) {
"No $javadoc is generated for the module ${artifacts.module.name}"
}
}
val pom = artifacts.coordinates.getFileName(packaging = "pom")
val pomXml = artifacts.files.singleOrNull { it.name == pom }
check(pomXml != null) {
"No $pom is generated for the module ${artifacts.module.name}"
}
val workDir = context.paths.tempDir.resolve("${artifacts.module.name}-maven-central-publication-test")
workDir.deleteRecursively()
workDir.createDirectories()
artifacts.files.forEach { it.copyTo(workDir.resolve(it.name)) }
val publication = MavenCentralPublication(
context = context,
workDir = workDir,
dryRun = true,
)
// making sure that a bundle.zip can be built without issues
publication.bundle()
val coordinatesToBePublished = publication.artifacts.map { it.coordinates }
check(coordinatesToBePublished.count() == 1 && coordinatesToBePublished.contains(artifacts.coordinates)) {
"Maven coordinates ${artifacts.coordinates} generated for the module ${artifacts.module.name} " +
"don't match the coordinates $coordinatesToBePublished from $pomXml"
}
}
}
@@ -20,6 +20,7 @@ import com.jetbrains.plugin.structure.intellij.problems.TemplateWordInPluginName
import com.jetbrains.plugin.structure.intellij.verifiers.DEFAULT_ILLEGAL_PREFIXES
import com.jetbrains.plugin.structure.intellij.verifiers.PRODUCT_ID_RESTRICTED_WORDS
import kotlinx.collections.immutable.plus
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.intellij.build.SoftwareBillOfMaterials.Companion.Suppliers
import org.jetbrains.intellij.build.impl.PlatformJarNames.PLATFORM_CORE_NIO_FS
import org.jetbrains.jps.model.module.JpsModule
@@ -31,6 +32,15 @@ import java.util.function.BiPredicate
* Describes a distribution of an IntelliJ-based IDE hosted in the IntelliJ repository.
*/
abstract class JetBrainsProductProperties : ProductProperties() {
companion object {
@ApiStatus.Internal
fun isCommunityModule(module: JpsModule, context: BuildContext): Boolean {
return module.contentRootsList.urls.all { url ->
Path.of(JpsPathUtil.urlToPath(url)).startsWith(context.paths.communityHomeDir)
}
}
}
init {
scrambleMainJar = true
includeIntoSourcesArchiveFilter = BiPredicate(::isCommunityModule)
@@ -40,10 +50,9 @@ abstract class JetBrainsProductProperties : ProductProperties() {
productLayout.addPlatformSpec { layout, _ -> layout.withModule(IJENT_BOOT_CLASSPATH_MODULE, PLATFORM_CORE_NIO_FS) }
}
protected fun isCommunityModule(module: JpsModule, context: BuildContext): Boolean =
module.contentRootsList.urls.all { url ->
Path.of(JpsPathUtil.urlToPath(url)).startsWith(context.paths.communityHomeDir)
}
protected fun isCommunityModule(module: JpsModule, context: BuildContext): Boolean {
return JetBrainsProductProperties.isCommunityModule(module, context)
}
override suspend fun copyAdditionalFiles(context: BuildContext, targetDir: Path) { }
@@ -53,6 +53,12 @@ class MavenArtifactsProperties {
module.contentRootsList.urls.all { Path.of(JpsPathUtil.urlToPath(it)).startsWith(context.paths.communityHomeDir) }
}
/**
* A predicate which returns `true` for modules which Maven artifacts should be validated according to https://central.sonatype.org/publish/requirements
*/
@ApiStatus.Internal
var validateForMavenCentralPublication: (JpsModule) -> Boolean = { false }
@ApiStatus.Internal
var patchCoordinates: (JpsModule, MavenCoordinates) -> MavenCoordinates = { _, coordinates -> coordinates }
@@ -13,10 +13,12 @@ import org.apache.maven.model.Developer
import org.apache.maven.model.Exclusion
import org.apache.maven.model.Model
import org.apache.maven.model.Organization
import org.apache.maven.model.Scm
import org.apache.maven.model.io.xpp3.MavenXpp3Writer
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.intellij.build.BuildContext
import org.jetbrains.intellij.build.DirSource
import org.jetbrains.intellij.build.JetBrainsProductProperties
import org.jetbrains.intellij.build.ZipSource
import org.jetbrains.intellij.build.buildJar
import org.jetbrains.intellij.build.impl.commonModuleExcludes
@@ -41,6 +43,11 @@ import java.nio.file.Files
import java.nio.file.Path
import java.util.Locale
import java.util.function.BiConsumer
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.copyTo
import kotlin.io.path.createDirectories
import kotlin.io.path.deleteRecursively
import kotlin.io.path.name
/**
* Generates Maven artifacts for IDE and plugin modules. Artifacts aren't generated for modules which depend on non-repository libraries.
@@ -321,9 +328,51 @@ open class MavenArtifactsBuilder(protected val context: BuildContext) {
}
internal suspend fun validate(builtArtifacts: Map<MavenArtifactData, List<Path>>) {
context.productProperties.mavenArtifacts.validate(context, builtArtifacts.map { (data, files) ->
val artifacts = builtArtifacts.map { (data, files) ->
GeneratedMavenArtifacts(data.module, data.coordinates, files)
})
}
artifacts.forEach {
if (context.productProperties.mavenArtifacts.validateForMavenCentralPublication(it.module)) {
validateForMavenCentralPublication(it, context)
}
}
context.productProperties.mavenArtifacts.validate(context, artifacts)
}
/** See https://central.sonatype.org/publish/requirements */
@OptIn(ExperimentalPathApi::class)
private suspend fun validateForMavenCentralPublication(artifacts: GeneratedMavenArtifacts, context: BuildContext) {
if (artifacts.module.getSourceRoots(JavaSourceRootType.SOURCE).any()) {
val sources = artifacts.coordinates.getFileName("sources", "jar")
check(artifacts.files.any { it.name == sources }) {
"No $sources is generated for the module ${artifacts.module.name}"
}
val javadoc = artifacts.coordinates.getFileName("javadoc", "jar")
check(artifacts.files.any { it.name == javadoc }) {
"No $javadoc is generated for the module ${artifacts.module.name}"
}
}
val pom = artifacts.coordinates.getFileName(packaging = "pom")
val pomXml = artifacts.files.singleOrNull { it.name == pom }
check(pomXml != null) {
"No $pom is generated for the module ${artifacts.module.name}"
}
val workDir = context.paths.tempDir.resolve("${artifacts.module.name}-maven-central-publication-test")
workDir.deleteRecursively()
workDir.createDirectories()
artifacts.files.forEach { it.copyTo(workDir.resolve(it.name)) }
val publication = MavenCentralPublication(
context = context,
workDir = workDir,
dryRun = true,
)
// making sure that a bundle.zip can be built without issues
publication.bundle()
val coordinatesToBePublished = publication.artifacts.map { it.coordinates }
check(coordinatesToBePublished.count() == 1 && coordinatesToBePublished.contains(artifacts.coordinates)) {
"Maven coordinates ${artifacts.coordinates} generated for the module ${artifacts.module.name} " +
"don't match the coordinates $coordinatesToBePublished from $pomXml"
}
}
}
@@ -381,6 +430,14 @@ private fun generatePomXmlData(artifactData: MavenArtifactData, file: Path, cont
organization = "JetBrains"
organizationUrl = "https://www.jetbrains.com"
})
if (JetBrainsProductProperties.isCommunityModule(artifactData.module, context)) {
pomModel.url = "https://github.com/JetBrains/intellij-community"
pomModel.scm = Scm().apply {
connection = "scm:git:https://github.com/JetBrains/intellij-community.git"
developerConnection = "scm:git:https://github.com/JetBrains/intellij-community.git"
url = "https://github.com/JetBrains/intellij-community"
}
}
context.productProperties.mavenArtifacts.addPomMetadata(artifactData.module, pomModel)
pomModel.setOrFailIfAlreadySet("Model version", value = "4.0.0", { modelVersion }, { modelVersion = it })
pomModel.setOrFailIfAlreadySet("GroupId", value = artifactData.coordinates.groupId, { groupId }, { groupId = it })