Публикация мультиплатформенной библиотеки kotlin в Maven Central (несколько артефактов InvalidMavenPublicationException с одинаковым )

Поскольку Jcenter скоро будет закрыт, я пытаюсь перенести свои библиотеки в Maven Central. Я много искал, чтобы найти работающий скрипт, но безуспешно. Есть официальные документы, но это как в шутку, там просто сказано поместите плагин 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 без решения, это статья, которая не работает, и многие другие. Есть куча материалов как публиковать в бинтрей, но они сейчас неактуальны


person Oleksandr Albul    schedule 23.02.2021    source источник


Ответы (1)


Кажется, проблема была в этой строке artifact(tasks["sourcesJar"]), так как эта задача уже включена.
Здесь я хочу разместить свой рабочий скрипт для загрузки мультиплатформенной библиотеки kotlin в Maven Central.
Прежде всего нам нужно зарегистрировать учетную запись Sonatype, проверить наш домен и т. д., вот свежая статья с подробными инструкциями.
Тогда скрипт вашего проекта 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 = [email protected]
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 в подпроектах.
И, наконец, отдайте должное 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. Плагин nexus-staging нужен только для того, чтобы сделать это из командной строки.
  • Я попытался переместить публикуемую часть скрипта в отдельный файл и включить его с помощью apply(from = "publish.gradle.kts"), но это не сработало, так как он теряет контекст в отдельном файле.
  • Я использую более старую версию библиотеки dokka (1.4.0-rc), так как более новая версия не может генерировать javadocs для всех платформ. И этот javadocs требуется репозиторием для публикации. Как упоминали авторы, для этой цели мы могли бы сгенерировать пустой файл javadoc.jar.
person Oleksandr Albul    schedule 24.02.2021