将 kotlin 多平台库发布到 Maven Central(InvalidMavenPublicationException 多个工件具有相同的......)

Posted

技术标签:

【中文标题】将 kotlin 多平台库发布到 Maven Central(InvalidMavenPublicationException 多个工件具有相同的......)【英文标题】:Publish kotlin multiplatform library to Maven Central (InvalidMavenPublicationException multiple artifacts with the identical ...) 【发布时间】:2021-05-25 12:56:43 【问题描述】:

由于 Jcenter 即将关闭,我正在尝试将我的库迁移到 Maven Central。我进行了很多搜索以找到任何工作脚本,但没有运气。有official docs,但它就像一个笑话,只是告诉将maven-publish 插件放到gradle 脚本中,瞧,就是这样。

目前我遇到错误:

Caused by: org.gradle.api.publish.maven.InvalidMavenPublicationException: Invalid publication 'js': multiple artifacts with the identical extension and classifier ('jar', 'sources').

我的脚本如下所示:

plugins 
    id("kotlin-multiplatform")
    id("org.jetbrains.dokka") version "1.4.0-rc"
    `maven-publish`
    signing


kotlin 
    sourceSets 
        jvm()
        js() 
            nodejs()
            browser()
        
        linuxX64()
        linuxArm64()
        mingwX64()
        macosX64()
        iosArm64()
        iosX64()

        val commonMain by getting 
            dependencies 
            
        
        val commonTest by getting 
            dependencies 
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
            
        

        val jsMain by getting 
            dependencies 
            
        
        val jsTest by getting 
            dependencies 
                implementation(kotlin("test-js"))
            
        

        val jvmMain by getting 
            dependencies 
            
        
        val jvmTest by getting 
            dependencies 
                implementation(kotlin("test"))
                implementation(kotlin("test-junit"))
            
        

        val nativeMain by creating 
            dependsOn(commonMain)
            dependencies 
            
        

        val linuxX64Main by getting 
            dependsOn(nativeMain)
        
        val linuxArm64Main by getting 
            dependsOn(nativeMain)
        
        val mingwX64Main by getting 
            dependsOn(nativeMain)
        
        val macosX64Main by getting 
            dependsOn(nativeMain)
        
        val iosArm64Main by getting 
            dependsOn(nativeMain)
        
        val iosX64Main by getting 
            dependsOn(nativeMain)
        
    


tasks 
    create<Jar>("javadocJar") 
        dependsOn(dokkaJavadoc)
        archiveClassifier.set("javadoc")
        from(dokkaJavadoc.get().outputDirectory)
    

    dokkaJavadoc 
        println("Dokka !")
        dokkaSourceSets 
            create("commonMain") 
                displayName = "common"
                platform = "common"
            
        
    


//  Publishing

val fis = FileInputStream("local.properties")
val properties = Properties().apply 
    load(fis)

val ossUser = properties.getProperty("oss.user")
val ossPassword = properties.getProperty("oss.password")
extra["signing.keyId"] = properties.getProperty("signing.keyId")
extra["signing.password"] = properties.getProperty("signing.password")
extra["signing.secretKeyRingFile"] = properties.getProperty("signing.secretKeyRingFile")

val libraryVersion: String by project
val publishedGroupId: String by project
val artifactName: String by project
val libraryName: String by project
val libraryDescription: String by project
val siteUrl: String by project
val gitUrl: String by project
val licenseName: String by project
val licenseUrl: String by project
val developerOrg: String by project
val developerName: String by project
val developerEmail: String by project
val developerId: String by project

project.group = publishedGroupId
project.version = libraryVersion

signing 
    sign(publishing.publications)


publishing 
    publications.withType(MavenPublication::class) 
        groupId = publishedGroupId
        artifactId = artifactName
        version = libraryVersion

        artifact(tasks["javadocJar"])
        artifact(tasks["sourcesJar"])

        pom 
            name.set(libraryName)
            description.set(libraryDescription)
            url.set(siteUrl)

            licenses 
                license 
                    name.set(licenseName)
                    url.set(licenseUrl)
                
            
            developers 
                developer 
                    id.set(developerId)
                    name.set(developerName)
                    email.set(developerEmail)
                
            
            organization 
                name.set(developerOrg)
            
            scm 
                connection.set(gitUrl)
                developerConnection.set(gitUrl)
                url.set(siteUrl)
            
        
    

    repositories 
        maven("https://oss.sonatype.org/service/local/staging/deploy/maven2/") 
            name = "sonatype"
            credentials 
                username = ossUser
                password = ossPassword
            
        
    

我还发现这个reddit topic 没有解决方案,这个article 不起作用,还有很多其他的。有大量材料如何发布到 bintray,但现在它们无关紧要

【问题讨论】:

【参考方案1】:

问题似乎出在artifact(tasks["sourcesJar"]) 这一行,因为该任务已包含在内。 在这里,我想将我的工作脚本用于将 kotlin 多平台库上传到 Maven Central。 首先我们需要注册 Sonatype 账户,验证我们的域名等,这里是一个fresh article with detailed steps。 那么您的项目脚本build.gradle.kts 可能如下所示:

import java.io.FileInputStream
import java.util.Properties
import org.gradle.api.publish.PublishingExtension

plugins 
    id("kotlin-multiplatform")
    id("org.jetbrains.dokka") version "1.4.0-rc"
    id("io.codearte.nexus-staging") version "0.22.0"
    `maven-publish`
    signing


enum class OS 
    LINUX, WINDOWS, MAC


fun getHostOsName(): OS =
    System.getProperty("os.name").let  osName ->
        when 
            osName == "Linux" -> OS.LINUX
            osName.startsWith("Windows") -> OS.WINDOWS
            osName.startsWith("Mac") -> OS.MAC
            else -> throw GradleException("Unknown OS: $osName")
        
    

kotlin 
    sourceSets 
        jvm()
        js() 
            browser()
            nodejs()
        
        when (getHostOsName()) 
            OS.LINUX -> 
                linuxX64()
                linuxArm64()
            
            OS.WINDOWS -> 
                mingwX64()
            
            OS.MAC -> 
                macosX64()
                iosArm64()
                iosX64()
            
        

        val commonMain by getting 
            dependencies 
                implementation(kotlin("stdlib-common"))
                implementation(Libs.olekdia.common)
            
        
        val commonTest by getting 
            dependencies 
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
            
        
        val jvmMain by getting 
            dependencies 
            
        
        val jvmTest by getting 
            dependencies 
                implementation(kotlin("test"))
                implementation(kotlin("test-junit"))
            
        
        val jsMain by getting 
            dependencies 
            
        
        val nativeMain by creating 
            dependsOn(commonMain)
        
        when (getHostOsName()) 
            OS.LINUX -> 
                val linuxX64Main by getting 
                    dependsOn(nativeMain)
                
                val linuxArm64Main by getting 
                    dependsOn(nativeMain)
                
            
            OS.WINDOWS -> 
                val mingwX64Main by getting 
                    dependsOn(nativeMain)
                
            
            OS.MAC -> 
                val macosX64Main by getting 
                    dependsOn(nativeMain)
                
                val iosArm64Main by getting 
                    dependsOn(nativeMain)
                
                val iosX64Main by getting 
                    dependsOn(nativeMain)
                
            
        
    



tasks 
    create<Jar>("javadocJar") 
        dependsOn(dokkaJavadoc)
        archiveClassifier.set("javadoc")
        from(dokkaJavadoc.get().outputDirectory)
    

    dokkaJavadoc 
        dokkaSourceSets 
            create("commonMain") 
                displayName = "common"
                platform = "common"
            
        
    


//--------------------------------------------------------------------------------------------------
//  Publishing
//--------------------------------------------------------------------------------------------------

val fis = FileInputStream("local.properties")
val properties = Properties().apply 
    load(fis)

val ossUser = properties.getProperty("oss.user")
val ossPassword = properties.getProperty("oss.password")
extra["signing.keyId"] = properties.getProperty("signing.keyId")
extra["signing.password"] = properties.getProperty("signing.password")
extra["signing.secretKeyRingFile"] = properties.getProperty("signing.secretKeyRingFile")

val libraryVersion: String by project
val publishedGroupId: String by project
val artifactName: String by project
val libraryName: String by project
val libraryDescription: String by project
val siteUrl: String by project
val gitUrl: String by project
val licenseName: String by project
val licenseUrl: String by project
val developerOrg: String by project
val developerName: String by project
val developerEmail: String by project
val developerId: String by project

project.group = publishedGroupId
project.version = libraryVersion

signing 
    sign(publishing.publications)


afterEvaluate 
    configure<PublishingExtension> 
        publications.all 
            val mavenPublication = this as? MavenPublication
            mavenPublication?.artifactId =
                "$project.name$"-$name".takeUnless  "kotlinMultiplatform" in name .orEmpty()"
        
    


publishing 
    publications.withType(MavenPublication::class) 
        groupId = publishedGroupId
        artifactId = artifactName
        version = libraryVersion

        artifact(tasks["javadocJar"])

        pom 
            name.set(libraryName)
            description.set(libraryDescription)
            url.set(siteUrl)

            licenses 
                license 
                    name.set(licenseName)
                    url.set(licenseUrl)
                
            
            developers 
                developer 
                    id.set(developerId)
                    name.set(developerName)
                    email.set(developerEmail)
                
            
            organization 
                name.set(developerOrg)
            
            scm 
                connection.set(gitUrl)
                developerConnection.set(gitUrl)
                url.set(siteUrl)
            
        
    

    repositories 
        maven("https://oss.sonatype.org/service/local/staging/deploy/maven2/") 
            name = "sonatype"
            credentials 
                username = ossUser
                password = ossPassword
            
        
    


nexusStaging 
    username = ossUser
    password = ossPassword
    packageGroup = publishedGroupId

gradle.properties中提供所需的库详细信息:

libraryVersion = 0.1.1
libraryName = Your library name
libraryDescription = Your library description
publishedGroupId = com.yourdomain
artifactName = your-cool-librayr
siteUrl = https://gitlab.com/yourlibrayr
gitUrl = https://gitlab.com/yourlibrayr.git
developerId = ...
developerOrg = ...
developerName = Your Name
developerEmail = yourmail@mail.com
licenseName = The Apache Software License, Version 2.0
licenseUrl = http://www.apache.org/licenses/LICENSE-2.0.txt
allLicenses = ["Apache-2.0"]
kotlin.mpp.enableGranularSourceSetsMetadata = true
gnsp.disableApplyOnlyOnRootProjectEnforcement = true

这里需要gnsp.disableApplyOnlyOnRootProjectEnforcement = true 属性来声明nexusStaging in subprojects。 最后把你的学分给local.properties

oss.user=your_user_name
oss.password=your_pass
signing.keyId=last_8_numbers_of_key
signing.password=your_pass
signing.secretKeyRingFile=/path/to/keystorage.gpg

现在在项目目录中发布打开的终端:

./gradlew build
./gradlew publish
./gradlew closeAndReleaseRepository
您可以跳过最后一个命令,关闭并从Nexus repository manager 释放暂存包。 nexus-staging 插件只需要从命令行执行即可。 我曾尝试将脚本的发布部分移动到单独的文件中,并将其包含在 apply(from = "publish.gradle.kts") 中,但它不起作用,因为它在单独的文件中丢失了上下文 我使用旧版本的 dokka 库 (1.4.0-rc),因为新版本无法为所有平台生成 javadocs。存储库需要此 javadocs 才能发布。作为authors mentioned,我们可以为此目的生成空的javadoc.jar 文件。

【讨论】:

这是否意味着您发布了无源代码?如果我希望它们也包含在出版物中怎么办? @dimsuz 在./gradlew build 之后调用./gradlew sourcesJar

以上是关于将 kotlin 多平台库发布到 Maven Central(InvalidMavenPublicationException 多个工件具有相同的......)的主要内容,如果未能解决你的问题,请参考以下文章

使用多平台模拟 kotlin 中的常见测试

从 ktor 提供 kotlin 多平台 javascript

如何在 Kotlin 标准库(多平台)上获取当前的 unixtime

使用 Kotlin 多平台实现相机库

Kotlin 通用库可在多个 MPP 中重用

无法将 FirebaseAnalytics 或 Crashlytics cocoapod 添加到 kotlin 多平台共享模块