Tuesday, July 14, 2015

How to publish Grails 3 plugin snapshots to your local Artifactory server

One my co worker Rob Bugh got stumped on this for a while and eventually threw it over to me. I was stumped as well but I finally figured this out.

In this case we're trying to make a custom fork of the Grails 3 plugin - we had to tweak it a bit to work with Terracotta OS, because it only supports Quartz 2.1:

I ran into a couple tidbits of info, but mostly the solution in part lies with a part of Grails 3:

https://raw.githubusercontent.com/grails/grails-profile-repository/master/profiles/plugin/templates/grailsCentralPublishing.gradle


If you look in there you see a bit of this code:

maven(MavenPublication) {
            pom.withXml {
                def pomNode = asNode()
                pomNode.dependencyManagement.replaceNode {}

                // simply remove dependencies without a version
                // version-less dependencies are handled with dependencyManagement
                // see https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/8 for more complete solutions
                pomNode.dependencies.dependency.findAll {
                    it.version.text().isEmpty()
                }.each {
                    it.replaceNode {}
                }
            }



The part with pom.withXml is part of the magic witchery here - all of the spring   boot dependencies don't include version numbers which makes gradle very angry and  confused. However I couldn't get the maven(MavenPublication) to work with the      latest Artifactory plugin. It appears they are just using maven publish instead in Grails, unless it's using the Bintray support. What it is doing here is stripping out all of the stuff without versions on it - those are managed by Grails 3/Boot. It seems like a bit of a hack, until there is a profile called 'artifactoryPublishing' this will have to do.

You also can't use these types of plugins as 'apply: plugin' - you need to reference them under dependencies like Benoit mentioned in his blog: https://medium.com/@benorama/how-to-publish-your-grails-3-plugin-to-bintray-c341b24f567d .

So let's bring those pieces together in your plugin like so:

publishing {
        publications {
            mavenJava(MavenPublication) {
              pom.withXml {
                def pomNode = asNode()
                pomNode.dependencyManagement.replaceNode {}

                // simply remove dependencies without a version
                // version-less dependencies are handled with dependencyManagement
                // see https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/8 for more complete solutions
                pomNode.dependencies.dependency.findAll {
                    it.version.text().isEmpty()
                }.each {
                    it.replaceNode {}
                }
            }
            from components.java
            def descriptor =
                artifacts = ["build/libs/quartz-${version}.jar",sourcesJar]
                
           }
        }
}
artifactory {
  contextUrl = 'http://your.artifactory.server/artifactory'

  publish {
    defaults {
      publications('mavenJava')
      publishArtifacts = true
      publishPom = true

    }
    repository {
      repoKey = 'plugins-snapshot-local'
      username = 'yourusername'
      password = 'password'
    }
 }

}


also don't forget to include your Artifactory plugin in your build.gradle file:
```
plugins {
    id "io.spring.dependency-management" version "0.3.1.RELEASE"
    id "com.jfrog.bintray" version "1.1"
    id "com.jfrog.artifactory" version "3.1.1"
}


And then in your app (or another Grails 3 plugin) don't forget your private repo:

repositories {
      maven { url "http://your.artifactory.server/artifactory/plugins-snapshot-local" }
}


And also your dependency:

dependencies {
    compile("org.grails.plugins:quartz:2.0.1-SNAPSHOT") {
    exclude group: 'slf4j-api', module: 'c3p0'
  }
}


Now I'll build my plugin that uses this local version of the quartz grails 3 plugin:

 $ gradle assemble
[buildinfo] Not using buildInfo properties file for this build.
:assetCompile
Download http://xxx.xxx.xxx:8081/artifactory/plugins-snapshot-local/org/grails/plugins/quartz/2.0.1-SNAPSHOT/quartz-2.0.1-20150714.194739-4.pom
Download http://xxx.xxx.xxx:8081/artifactory/plugins-snapshot-local/org/grails/plugins/quartz/2.0.1-SNAPSHOT/quartz-2.0.1-20150714.194739-4.jar
Processing File 1 of 1 - manifest.properties
Compressing File 1 of 1 - manifest
Finished Precompiling Assets
:compileAstJava UP-TO-DATE
:compileAstGroovy UP-TO-DATE
:processAstResources UP-TO-DATE
:astClasses UP-TO-DATE
:compileJava UP-TO-DATE
:configScript UP-TO-DATE
:compileGroovy

Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 warning
:copyAstClasses UP-TO-DATE
:copyAssets UP-TO-DATE
:copyCommands UP-TO-DATE
:copyTemplates UP-TO-DATE
:processResources UP-TO-DATE
:classes
:compileWebappGroovyPages UP-TO-DATE
:compileGroovyPages
:jar
:findMainClass
:startScripts
:distTar
:distZip
:bootRepackage
:assemble

BUILD SUCCESSFUL

Yes! We are in business!

follow up. On some versions of OS Artifactory you might get this error first time you publish:

Failed while reading the response from: PUT http://artifactory.aws.reachforce.com:8081/artifactory/plugins-snapshot-local/com/reachforce/plugins/mmdp-quartz/0.5-SNAPSHOT/mmdp-quartz-0.5-SNAPSHOT.jar;build.timestamp=1436911730122;build.name=mmdp-quartz;build.number=1436911734140 HTTP/1.1
org.codehaus.jackson.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
 at [Source: java.io.StringReader@1a4ae53d; line: 1, column: 2]
at org.codehaus.jackson.JsonParser._constructError(JsonParser.java:1433)
at org.codehaus.jackson.impl.JsonParserMinimalBase._reportError(JsonParserMinimalBase.java:521)
at org.codehaus.jackson.impl.JsonParserMinimalBase._reportUnexpectedChar(JsonParserMinimalBase.java:442)
at org.codehaus.jackson.impl.ReaderBasedParser._handleUnexpectedValue(ReaderBasedParser.java:1198)
at org.codehaus.jackson.impl.ReaderBasedParser.nextToken(ReaderBasedParser.java:485)
at org.codehaus.jackson.map.ObjectMapper._initForReading(ObjectMapper.java:2770)
at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2691)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1286)
at org.codehaus.jackson.JsonParser.readValueAs(JsonParser.java:1337)
at org.jfrog.build.client.ArtifactoryHttpClient.execute(ArtifactoryHttpClient.java:209)
at org.jfrog.build.extractor.clientConfiguration.client.ArtifactoryBuildInfoClient.tryChecksumDeploy(ArtifactoryBuildInfoClient.java:649)
at org.jfrog.build.extractor.clientConfiguration.client.ArtifactoryBuildInfoClient.uploadFile(ArtifactoryBuildInfoClient.java:607)
at org.jfrog.build.extractor.clientConfiguration.client.ArtifactoryBuildInfoClient.deployArtifact(ArtifactoryBuildInfoClient.java:330)
at org.jfrog.gradle.plugin.artifactory.task.BuildInfoBaseTask.deployArtifacts(BuildInfoBaseTask.java:409)
at org.jfrog.gradle.plugin.artifactory.task.BuildInfoBaseTask.prepareAndDeploy(BuildInfoBaseTask.java:284)
at org.jfrog.gradle.plugin.artifactory.task.BuildInfoBaseTask.collectProjectBuildInfo(BuildInfoBaseTask.java:363)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:63)
at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:218)
at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:211)
at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:200)
at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:585)
at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:568)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:42)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:306)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.executeTask(AbstractTaskPlanExecutor.java:79)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:63)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:51)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:23)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:88)
at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:29)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:68)
at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:55)
at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:149)
at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)
at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:86)
at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:80)
at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:33)
at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:24)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:36)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:51)
at org.gradle.internal.Actions$RunnableActionAdapter.execute(Actions.java:169)
at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:237)
at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:210)
at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:35)
at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:24)
at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:206)
at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:169)
at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
at org.gradle.launcher.Main.doAction(Main.java:33)
at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:54)
at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:35)
at org.gradle.launcher.GradleMain.main(GradleMain.java:23)

Not to worry, just run the command again and it works. Appears to be a bug in Artifactory :)