Search code examples
javamavengradlebuildartifactory

Publishing a simple Java library to Maven


I might be missing something major here. However, I am struggling to publish a simple library to a maven repository (which will be consumed by other maven based projects in the organization)

The best guide I've found is on the official Gradle website: https://docs.gradle.org/current/userguide/publishing_maven.html

However, there are still many unanswered questions:

  • Is there no way to differentiate between SNAPSHOT and release builds other than to manually include the if-else statement?

  • What is from components.java? IDEA gives no autocomplete or documentation on most of these DSLs (unlike Maven, where the code intelligence works well)

  • How do I publish to a private repository that requires authentication? I understand somewhere there must be a block that uses:

        username = "${artifactory_user}"
        password = "${artifactory_password}"
    

With the values being read from ~/.gradle/gradle.properties

But where do I put this block?

Overall, I feel like I a missing something here, maybe some documentation that is popularly read ... using maven itself the process is fairly straight-forward and the official documentation makes the process relatively painless

With Gradle, I feel like the simplest publish to a repository requires quite a lot what feels like customized logic when my intuition says something so common must already be encapsulated in a plugin with reasonable defaults


Solution

  • I see you've found your solution already, but my answer will aim to give you detailed answers to your questions.

    Is there no way to differentiate between SNAPSHOT and release builds other than to manually include the if-else statement?

    Correct. An if-else statement is exactly what you need to distinguish between a snapshot and release build. Gradle itself does not provide any sort of versioning functionality. That is left to you to handle or a plugin such as Nebula Release.

    What is from components.java

    • The from is a method call from AbstractCopyTask which the Jar task type is a subclass of.
    • components is again another method call. You are actually calling getComponents() of Project.
      • components.java is sugar for components.getByName("java"). This works because of dynamic/magic of Groovy.

    IDEA gives no autocomplete or documentation on most of these DSLs (unlike Maven, where the code intelligence works well)

    This is due to the dynamic/weak typing of Groovy. The build.gradle file is written using Groovy. IntelliJ does try to infer the type of your build script, but it can't fully. Luckily you can now write your build script using Kotlin:

    I highly suggest using the Kotlin DSL going forward. You will know exactly where everything is coming from.

    How do I publish to a private repository that requires authentication?

    Unfortunately the docs for the maven-publish plugin merely mentions it in a single sentence. Even so, it just directs you to API docs which aren't always helpful, but you were able to figure it out.

    https://docs.gradle.org/current/userguide/publishing_maven.html

    You can also configure any authentication details that are required to connect to the repository. See MavenArtifactRepository for more details.

    And finally:

    (...) the values being read from ~/.gradle/gradle.properties

    Gradle will go out of its way to resolve a property. gradle.properties is just one of many locations that Gradle will look for properties. You can see more details under the Properties section on top here.


    I'd like to finish off by providing a complete example of your answer using the Kotlin DSL. Also using the buildscript { } is a legacy method of applying plugins as noted here. You should use the newer/preferred plugins { } block going forward. More info here.

    plugins {
        `maven-publish`
        id("org.jetbrains.kotlin.jvm") version "1.3.31"
    }
    
    group = "com.company"
    version = "1.0.0-SNAPSHOT"
    
    tasks.wrapper {
        gradleVersion = "5.6.1"
        distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
    }
    
    val sourcesJar by tasks.registering(Jar::class) {
        archiveClassifier.set("sources")
        from(sourceSets.main.get().allSource)
    }
    
    repositories {
        mavenCentral()
    }
    
    publishing {
        publications {
            register<MavenPublication>("mavenJava") {
                artifactId = "some-artifactId"
                from(components["java"])
                artifact(sourcesJar.get())
                pom {
                    name.set("Project Name")
                }
            }
        }
        repositories {
            maven {
                url = uri("https://company.jfrog.io/company/maven-local")
                credentials {
                    username = property("artifactory_user") as String
                    password = property("artifactory_password") as String
                }
            }
        }
    }
    
    val test by tasks.getting(Test::class) {
        useJUnitPlatform()
    }
    
    dependencies {
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
        // ...
    }