As Jcenter will be shutdown soon I’m trying to migrate my libs to Maven Central. I have searched a lot to find any working script but with no luck. There is official docs, but it is like a joke, there just told to put maven-publish
plugin to the gradle script and voila that’s it.
Currently I'm getting error:
Caused by: org.gradle.api.publish.maven.InvalidMavenPublicationException: Invalid publication 'js': multiple artifacts with the identical extension and classifier ('jar', 'sources').
My script looks like this:
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
}
}
}
}
I also find this reddit topic with no solution, this article that doesn't work, and lot of others. There are tons of materials how to publish to bintray, but they are irrelevant now
It seems the issue was in this line artifact(tasks["sourcesJar"])
as this task already included.
Here I want to put my working script for uploading kotlin multiplatform library to Maven Central.
First of all we need to register Sonatype account, validate our domain, etc, here is a fresh article with detailed steps.
Then your project script build.gradle.kts
may look like this:
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
}
Provide needed library details in 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
Here gnsp.disableApplyOnlyOnRootProjectEnforcement = true
property needed for declaring nexusStaging
in subprojects.
And finally put your credits to 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
Now for publishing open terminal in project directory:
./gradlew build
./gradlew publish
./gradlew closeAndReleaseRepository
nexus-staging
plugin is only needed to do it from command line.apply(from = "publish.gradle.kts")
, but it didn't work, as it loses context in separate fileIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With