Publishing a Kotlin library to your Bintray repo using Gradle Kotlin DSL and Bintray Gradle Plugin
There are many benefits to working on Open Source Software, both for companies and individuals. Sharing your work with others opens much more possibilities than working in a “closed box”. By sharing your code you get numerous things literally for free, things like code reviews and contributions to your own projects, a better understanding of the technology you work with by contributing to other OSS projects, and networking and getting to know your “clients”.
This post was originally posted on Medium
While working on OSS within the company, chances are, you probably have all the “infra” already set up - things like where the source code is stored or where to publish the packages. But if you’re working on a “personal” OSS project, you will most likely come to a point where you will want to publish your artifacts to a publicly accessible repository (A Central Maven repository, for example, if you’re working on a java-based project.) The process can be somewhat unclear for someone who has never published an artifact to be available to others, and scarce documentation makes things even more daunting. If, on top of that, you’re using Gradle Kotlin DSL build scripts, then chances are documentation could be even sparser. Even today, some plugin maintainers don’t always “consider” kotlin dsl support simply because they don’t use it themselves and leave it up to users to “figure it out”. Even though I must admit, the documentation improves constantly and is much better than when Gradle Kotlin DSL was first introduced in 2017.
Until recently, I have been working in software test automation and have been coding for a living for some time now. Software Testing and Quality Assurance is one of my passions at work, and naturally, my “OSS pet projects” are also related to this area. Some time ago, I went through the process of publishing my first OSS project, a Kotlin library that generates “fake” data for tests and data anonymization: kotlin-faker. The process wasn’t entirely clear at first, but when I understood the basics of publishing the artifacts to a publicly available repository, things got quite simple.
Where to publish?
At first, I was thinking of publishing everything directly to Maven Central. This seems like a natural choice since this is one of the most commonly used repos out there and is directly accessible from most Maven and Gradle builds without the need to specify any additional information, such as the URL to your custom repo.
However, there are some caveats to doing this. I would generally recommend using a custom repository, for example, Bintray (which is what I am using myself and will be using as an example in this article) unless you’re absolutely sure what you’re doing.
Why is Bintray preferable to Maven Central?
First, with Maven Central, you must be sure about your setup. You can play around with your publishing setup and always undo things in Bintray, but not in Maven Central. Second is that you have complete control over your files, and if you’re not sure about the quality of your library and want to test things — this would be a good place to do this. In Maven Central you sort of lose all control after you publish. Last, Maven Central has more strict requirements for publishing artifacts.
Note: while you do have the ability to modify your files in your own Bintray repo, you should be careful while doing so, as this might break things for people who are already using these artifacts.
After publishing to Bintray and verifying that everything is as you want it to be, you can then sync your artifacts JCenter and to Maven Central directly from the UI, through the bintray plugin configuration, or even the APIs. You can read more about it in their blog post.
The Setup
Bintray Prerequisites
These are pretty self-explanatory and easy to find, so I won’t go into too many details here and will list what you will need to do.
- Create a new account (you can also use your existing GitHub, Google, or Twitter accounts for authentication).
- Create a new repository (Select the type as ‘Maven’.) - this is the repository where you will publish your packages and from where others will be able to download them.
- Get your API key — go to ‘edit profile,’ and you will see the ‘API Key’ tab there.
Gradle build file configuration
First, we need to add the maven-publish
and bintray
plugins to the gradle.build.kts
build script file:
plugins {
`maven-publish`
id("com.jfrog.bintray") version "1.8.4"
}
Configuring maven-publish plugin
After adding the maven-publush
plugin, you should have a publishing
extension function available, which is used to configure your publication:
publishing {
publications {
create<MavenPublication>(publicationName) {
groupId = artifactGroup
artifactId = artifactName
version = artifactVersion
from(components["java"])
pom.withXml {
asNode().apply {
appendNode("description", pomDesc)
appendNode("name", rootProject.name)
appendNode("url", pomUrl)
appendNode("licenses").appendNode("license").apply {
appendNode("name", pomLicenseName)
appendNode("url", pomLicenseUrl)
appendNode("distribution", pomLicenseDist)
}
appendNode("developers").appendNode("developer").apply {
appendNode("id", pomDeveloperId)
appendNode("name", pomDeveloperName)
}
appendNode("scm").apply {
appendNode("url", pomScmUrl)
}
}
}
}
}
}
Notice the pom.withXml
part. We need to set certain metadata before publishing a library. Maven Central, for example, has some requirements on what metadata needs to be present in the pom.
This can be done as seen in code above. Another way is to utilize the DSL that Maven Publish Plugin provides for this purpose:
pom {
name = 'My Library'
description = 'A concise description of my library'
url = 'http://www.example.com/library'
properties = [
myProp: "value",
"prop.with.dots": "anotherValue"
]
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'johnd'
name = 'John Doe'
email = 'john.doe@example.com'
}
}
scm {
connection = 'scm:git:git://example.com/my-library.git'
developerConnection = 'scm:git:ssh://example.com/my-library.git'
url = 'http://example.com/my-library/'
}
}
Note: it is not necessary to explicitly specify the pom
metadata - it will be generated by the plugin automatically, therefore the pom {}
configuration can be omitted altogether unless you want to set/modify some things in the generated pom explicitly.
Usually, you want to publish javadocs and sources as well. To create sources.jar we need to create a task that will generate a jar:
val sourcesJar by tasks.creating(Jar::class) {
archiveClassifier.set("sources")
from(sourceSets.getByName("main").allSource)
}
And then add it to the publishing
configuration:
publishing {
publications {
create<MavenPublication>("kotlin-faker") {
//groupId, version, etc
artifact(sourcesJar)
// pom, etc
}
}
}
To generate kdocs (javadoc in Kotlin world) you should use the Kotin/dokka plugin.
For more info on using Maven Publish Plugin also see the official userguide page.
Configuring Bintray plugin
The Bintray plugin takes care of actually uploading artifacts to your repository.
A basic setup would look something like this:
bintray {
user = project.findProperty("bintrayUser").toString()
key = project.findProperty("bintrayKey").toString()
publish = true
setPublications("kotlin-faker")
pkg.apply {
repo = "maven"
name = artifactName
userOrg = "serpro69"
githubRepo = githubRepo
vcsUrl = pomScmUrl
description = "Port of ruby faker gem written in kotlin"
setLabels("kotlin", "faker", "testing", "test-automation", "data", "generation")
setLicenses("MIT")
desc = description
websiteUrl = pomUrl
issueTrackerUrl = pomIssueUrl
githubReleaseNotesFile = githubReadme
version.apply {
name = artifactVersion
desc = pomDesc
released = Date().toString()
vcsTag = artifactVersion
}
}
}
A few things to keep in mind:
- There are four mandatory parameters that you need to set:
repo
- existing repository in bintray to add the artifacts toname
- package namelicenses
- your package licenses (mandatory if the package doesn’t exist yet and must be created, and if the package is an OSS package; optional otherwise)vcsUrl
- your VCS URL (mandatory if the package doesn’t exist yet and must be created, and if the package is an OSS package; optional otherwise)
- the
repo
should match with whatever repository name you specified when creating a new repo on Bintray. I simply named itmaven
, but it can be named any way you want. - the user and key creation were covered in the Prerequisites section of this post. You will need to pass them when uploading the artifacts, i.e. from the command line or from the CI tool.
The rest is pretty much self explanatory, but I’ve added a few comments in the code just in case.
Publishing to your Bintray repo
When you’re ready to upload a new release just use the command: $ ./gradlew bintrayUpload -PbintrayUser="my_bintray_username" -PbintrayKey="my_bintray_api_key"
This can also be set up with CI, where bintrayUser
and bintrayKey
properties will be stored as “secrets”.
Downloading artifacts from your repository
To be able to use the published package as a dependency in a build file we first need to add our repository to the list of repositories. In Gradle it would look something like this:
repositories {
maven {
setUrl("https://dl.bintray.com/serpro69/maven/")
}
}
After that the library can be added as a usual dependency:
dependencies {
implementation '$groupId:$artifactId:$version'
}
Alternatively, you can also sync packages from your bintray repo to JCenter and Maven Central.
Retrospective
All in all, this wasn’t actually that hard. I needed to collect some pieces of missing information here and there and understand what’s happening when you publish an artifact and what are the prerequisites for that. After that, the process looks quite straightforward.
You can achieve the same results with Maven if you’re using that as a build tool, but Gradle is way superior to Maven regarding build configurations. This shouldn’t be a surprise to anyone nowadays. Unfortunately, we are sometimes forced to use Maven, especially on legacy projects, but if you’re starting a new project I strongly suggest considering Gradle.
It helps a lot to have a personal repository on Bintray where you can actually play around and have control over your files. I’m glad I didn’t go with Maven Central straight away.