Get uri of published artifact using maven-publish gradle plugin
Asked Answered
R

6

7

After a publish, I want to know what the url of the published artifact is (to use it in other gradle tasks for automated deployment).

Is there any way to capture this generated url?

Rabb answered 19/1, 2017 at 15:44 Comment(1)
FWIW, it also can be grepped/scraped from --info on publish, since it's there. The key phrase is "Uploading", you basically want all the lines starting with that. The format is Uploading your-artifact-0.1.0-20230711.160346-6.pom to /repository/snapshots-hosted/your/package/0.1.0-SNAPSHOT/your-artifact-0.1.0-20230711.160346-6.pom etc. It boggles the mind why the plugin doesn't emit this by default :/Cyrenaica
M
1

Sadly this information is unavailable via the gradle build system... What you can do is create a task that finalizes the publish task. Then have that task query the maven repository for the most recent build. Maven will look in the maven-metadata.xml file and return the <release> tag value or the most recent upload if that's missing. You can get the exact download url from the response's Location header.

Here is an example of how you would query a maven repo

$ curl -Is 'http://my.nexus.repo.com:8081/nexus/service/local/artifact/maven/redirect?r=Release&g=com.my.group.id&a=myArtifactId&v=RELEASE&p=war' | grep -Fi Location | cut -d' ' -f2

http://my.nexus.repo.com:8081/nexus/service/local/repositories/Release/content/com/my/group/id/myArtifactId/1.0.012/myArtifactId-1.0.012.war

Explaining the command

curl -Is http://<nexus-url>:<nexus-port>/nexus/service/local/artifact/maven/redirect?r=<nexus-repository>&g=<artifact-group-id>&a=<artifact-name>&v=<artifact-version>
    curl
        -I # only print return headers
        -s # quiet output of curl's downloading progress
    url params
        r  # nexus-repository name, tends to be Release or Snapshot
        g  # group id for the artifact
        a  # artifact id
        v  # artifact version or a link like RELEASE (don't use LATEST it's problematic)
        ##  you can also supply classifier and extension if needed
        c  # artifact classifier
        e  # artifact extension
Mutism answered 19/1, 2017 at 19:32 Comment(1)
you're wrong. This information is available in Gradle, because it gets it as a response during the publish task - you can see that yourself by running the task with --info.Cyrenaica
J
7

@Hollerweger answer is probably the least hackish, but it's wrong in couple of ways:

  1. AbstractPublishToMaven is the superclass of PublishToMavenLocal and PublishToMavenRepository. Extending it and printing a message saying artifact is published to Nexus is wrong, because that message is going to be printed even when publishing to the local Maven repo. The correct task class to use is PublishToMavenRepository that publishes to the remote repo.
  2. There's no need to know the remote repo URL; publication has a repository property.

Putting it all together:

tasks.withType(PublishToMavenRepository) {
    doFirst {
        println("Publishing ${publication.groupId}:${publication.artifactId}:${publication.version} to ${repository.url}")
    }
}
Jerboa answered 7/12, 2018 at 23:27 Comment(6)
Looks like in my case it's not the full url for snapshots, as the url also includes a "yyyyMMdd.HHmmSS" pattern and a publication index. Have you a trick for that by any chance?Fulgor
@Fulgor Open a new question with a minimal, reproducible example for your special case.Jerboa
Thanks for the suggestion, the trick with local maven-medata-remote.xml looks good to me. This approach is more clean but doesn't take into account the Maven/Nexus versioning mechanism for the SNAPSHOT policy. (This policy adds a date and a build number inside the urls, before the file extension, and as far as I know there is unfortunately no shortcut for that.)Fulgor
@Fulgor The URI is to the Maven repository you're publishing to, not to the artifact, so it should have nothing to do with SNAPSHOT. We use this solution in our company, and it works as expected. If you found a solution that works for you, that's cool, but otherwise, like I said before, please open a new question with a sample application.Jerboa
@ AbhijitSarkar you're wrong. The OPs question was about "getting the URI of the published artifact". Your solution returns the URL to which Gradle tries to publish the artifact, not the actual URL under which it is published. As @Fulgor correctly noticed, in many cases (Nexus snapshots e.g.) those URLs will differ. It's not a "special case", it's an ubiquitous behaviour.Cyrenaica
@spamove Since we can't read OP's mind, we don't know whether they want or care about the difference. Given that this answer is currently the most upvoted, clearly people find it useful. It may not work for your use case, and you're free to open your own question.Jerboa
S
4

I found a local copy of a file called maven-metadata-remote.xml that gets uploaded. It is under this dir: build/tmp/publishMavenPublicationToNexusRepository/

Example:

$ cat build/tmp/publishMavenPublicationToNexusRepository/com/example/my.package/0.1.0-SNAPSHOT/maven-metadata-remote.xml

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
  <groupId>com.example</groupId>
  <artifactId>my.package</artifactId>
  <version>0.1.0-SNAPSHOT</version>
  <versioning>
    <snapshot>
      <timestamp>20180326.191238</timestamp>
      <buildNumber>36</buildNumber>
    </snapshot>
    <lastUpdated>20180326191238</lastUpdated>
  </versioning>
</metadata>

So I was able to ingest that file by adding something like this to the bottom of the build.gradle file:

tasks.withType(PublishToMavenRepository) {
  doLast {
    def metadata_file_path = "build/tmp/publishMavenPublicationToNexusRepository/com/example/my.package/0.1.0-SNAPSHOT"
    def metadata_XML = new File("${metadata_file_path}/maven-metadata-remote.xml").text
    def metadata = new groovy.util.XmlSlurper().parseText(metadata_XML)
    def BN = metadata.versioning.snapshot.buildNumber
    def TS = metadata.versioning.snapshot.timestamp
    println "INFO: uploaded version 0.1.0-${TS}-${BN}.pom to Nexus"
...
Sinuation answered 26/3, 2018 at 19:31 Comment(2)
in general, the name of the dir depends on the exact task name, so it can differ a little, but it's still like build/tmp/publishYour-name-herePublicationToRepotypeRepository/ - I wouldn't hardcode this path but get it based on publish task name instead.Cyrenaica
also, FWIW, this is brilliant in the wider scope, because it allows you to easily integrate this data into CI pipeline etc., without having to depend on build.gradle[.kts] extensions (which the users of the pipeline might e.g. forget to add etc.)Cyrenaica
I
2

You can extend all maven publish tasks and compute the the url again: I just needed the folder - For release artifacts you can also add the artifact at the end but I haven't found a good solution for SNAPSHOTs. The nexus base repo URL is expected to be known.

tasks.withType(AbstractPublishToMaven) {
    doLast {
        String urlString = nexusUrl.toString() + convertPackageNameToPath(publication.groupId) + "/" + publication.artifactId + "/" + publication.version
        println "Artifact URL: " + urlString
    }
}

static String convertPackageNameToPath(String packageName) {
    return packageName.replace(".", "/");
}
Ignoramus answered 27/3, 2018 at 14:5 Comment(0)
M
1

Sadly this information is unavailable via the gradle build system... What you can do is create a task that finalizes the publish task. Then have that task query the maven repository for the most recent build. Maven will look in the maven-metadata.xml file and return the <release> tag value or the most recent upload if that's missing. You can get the exact download url from the response's Location header.

Here is an example of how you would query a maven repo

$ curl -Is 'http://my.nexus.repo.com:8081/nexus/service/local/artifact/maven/redirect?r=Release&g=com.my.group.id&a=myArtifactId&v=RELEASE&p=war' | grep -Fi Location | cut -d' ' -f2

http://my.nexus.repo.com:8081/nexus/service/local/repositories/Release/content/com/my/group/id/myArtifactId/1.0.012/myArtifactId-1.0.012.war

Explaining the command

curl -Is http://<nexus-url>:<nexus-port>/nexus/service/local/artifact/maven/redirect?r=<nexus-repository>&g=<artifact-group-id>&a=<artifact-name>&v=<artifact-version>
    curl
        -I # only print return headers
        -s # quiet output of curl's downloading progress
    url params
        r  # nexus-repository name, tends to be Release or Snapshot
        g  # group id for the artifact
        a  # artifact id
        v  # artifact version or a link like RELEASE (don't use LATEST it's problematic)
        ##  you can also supply classifier and extension if needed
        c  # artifact classifier
        e  # artifact extension
Mutism answered 19/1, 2017 at 19:32 Comment(1)
you're wrong. This information is available in Gradle, because it gets it as a response during the publish task - you can see that yourself by running the task with --info.Cyrenaica
B
1

Starting from @Abhijit Sarkar's answer, you can write universal solution for both remote and local publish direction:

tasks.withType(AbstractPublishToMaven) {
    doLast {
        println("Published ${publication.groupId}:${publication.artifactId}:${publication.version} to ${name.contains('Local') ? "local maven repository" : repositories.maven.url}")
    }
}

Tasks publishing to local repo, contain 'Local' in its name (letter case matter), so the ternary operator defines a correct direction.

Brainwash answered 21/9, 2022 at 12:53 Comment(0)
M
1

The DefaultMavenPublication class exposes the following method:

@Override
public PublishedFile getPublishedFile(final PublishArtifact source) {
    final String publishedUrl = getPublishedUrl(source);
    final String publishedName = source.getFile().getName();
    return new PublishedFile() {
        @Override
        public String getName() {
            return publishedName;
        }
        @Override
        public String getUri() {
            return publishedUrl;
        }
    };
}

introduced in this commit. Maybe this could do the trick.

Misusage answered 6/12, 2022 at 16:18 Comment(1)
Can you share how to use it in a build.gradle?Expiration

© 2022 - 2024 — McMap. All rights reserved.