Browse Source

Merge pull request #17128 from rjernst/maven_publish

Build: Switch to maven-publish plugin
Ryan Ernst 9 years ago
parent
commit
6af4c43c4f

+ 44 - 36
build.gradle

@@ -17,54 +17,61 @@
  * under the License.
  */
 
-import com.bmuschko.gradle.nexus.NexusPlugin
 import org.gradle.plugins.ide.eclipse.model.SourceFolder
 
 // common maven publishing configuration
 subprojects {
+  if (path.startsWith(':x-plugins')) {
+    // don't try to configure publshing for extra plugins attached to this build
+    return
+  }
   group = 'org.elasticsearch'
   version = org.elasticsearch.gradle.VersionProperties.elasticsearch
 
-  plugins.withType(NexusPlugin).whenPluginAdded {
-    modifyPom {
-      project {
-        url 'https://github.com/elastic/elasticsearch'
-        inceptionYear '2009'
-
-        scm {
-          url 'https://github.com/elastic/elasticsearch'
-          connection 'scm:https://elastic@github.com/elastic/elasticsearch'
-          developerConnection 'scm:git://github.com/elastic/elasticsearch.git'
-        }
+  plugins.withType(MavenPublishPlugin).whenPluginAdded {
+    publishing {
+      publications {
+        // add license information to generated poms
+        all {
+          pom.withXml { XmlProvider xml ->
+            Node node = xml.asNode()
+            node.appendNode('inceptionYear', '2009')
 
-        licenses {
-          license {
-            name 'The Apache Software License, Version 2.0'
-            url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-            distribution 'repo'
+            Node license = node.appendNode('licenses').appendNode('license')
+            license.appendNode('name', 'The Apache Software License, Version 2.0')
+            license.appendNode('url', 'http://www.apache.org/licenses/LICENSE-2.0.txt')
+            license.appendNode('distribution', 'repo')
           }
         }
       }
-    }
-    extraArchive {
-      javadoc = true
-      tests = false
-    }
-    // we have our own username/password prompts so that they only happen once
-    // TODO: add gpg signing prompts
-    project.gradle.taskGraph.whenReady { taskGraph ->
-      if (taskGraph.allTasks.any { it.name == 'uploadArchives' }) {
-        Console console = System.console()
-        if (project.hasProperty('nexusUsername') == false) {
-          String nexusUsername = console.readLine('\nNexus username: ')
-          project.rootProject.allprojects.each {
-            it.ext.nexusUsername = nexusUsername
-          }
+      repositories.maven {
+        name 'sonatype'
+        if (version.endsWith('-SNAPSHOT')) {
+          url 'https://oss.sonatype.org/content/repositories/snapshots/'
+        } else {
+          url 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
         }
-        if (project.hasProperty('nexusPassword') == false) {
-          String nexusPassword = new String(console.readPassword('\nNexus password: '))
-          project.rootProject.allprojects.each {
-            it.ext.nexusPassword = nexusPassword
+
+        // It would be nice to pass a custom impl of PasswordCredentials
+        // that could lazily read username/password from the console if not
+        // present as properties. However, gradle's credential handling is
+        // completely broken for custom impls. It checks that the class
+        // passed in is exactly PasswordCredentials or AwsCredentials.
+        // So instead, we must rely on heuristics of "are we publishing"
+        // by inspecting the command line, stash the credentials
+        // once read in the root project, and set them on each project
+        if (gradle.startParameter.taskNames.contains('publish')) {
+          Console console = System.console()
+          if (project.rootProject.hasProperty('nexusUsername') == false) {
+            project.rootProject.ext.nexusUsername = console.readLine('\nNexus username: ')
+          }
+          if (project.rootProject.hasProperty('nexusPassword') == false) {
+            project.rootProject.ext.nexusPassword = new String(console.readPassword("\nNexus password: "))
+          }
+
+          credentials {
+            username = project.rootProject.nexusUsername 
+            password = project.rootProject.nexusPassword
           }
         }
       }
@@ -72,6 +79,7 @@ subprojects {
   }
 }
 
+
 allprojects {
   // injecting groovy property variables into all projects
   project.ext {

+ 1 - 0
buildSrc/.gitignore

@@ -0,0 +1 @@
+build-bootstrap/

+ 87 - 58
buildSrc/build.gradle

@@ -1,5 +1,3 @@
-import java.nio.file.Files
-
 /*
  * Licensed to Elasticsearch under one or more contributor
  * license agreements. See the NOTICE file distributed with
@@ -19,25 +17,21 @@ import java.nio.file.Files
  * under the License.
  */
 
-// we must use buildscript + apply so that an external plugin
-// can apply this file, since the plugins directive is not
-// supported through file includes
-buildscript {
-  repositories {
-    jcenter()
-  }
-  dependencies {
-    classpath 'com.bmuschko:gradle-nexus-plugin:2.3.1'
-  }
-}
+import java.nio.file.Files
+
 apply plugin: 'groovy'
-apply plugin: 'com.bmuschko.nexus'
-// TODO: move common IDE configuration to a common file to include
-apply plugin: 'idea'
-apply plugin: 'eclipse'
 
 group = 'org.elasticsearch.gradle'
-archivesBaseName = 'build-tools'
+
+if (project == rootProject) {
+  // change the build dir used during build init, so that doing a clean
+  // won't wipe out the buildscript jar
+  buildDir = 'build-bootstrap'
+}
+
+/*****************************************************************************
+ *         Propagating version.properties to the rest of the build           *
+ *****************************************************************************/
 
 Properties props = new Properties()
 props.load(project.file('version.properties').newDataInputStream())
@@ -51,13 +45,29 @@ if (snapshot) {
   props.put("elasticsearch", version);
 }
 
+File tempPropertiesFile = new File(project.buildDir, "version.properties")
+task writeVersionProperties {
+  inputs.properties(props)
+  doLast {
+    OutputStream stream = Files.newOutputStream(tempPropertiesFile.toPath());
+    try {
+      props.store(stream, "UTF-8");
+    } finally {
+      stream.close();
+    }
+  }
+}
+
+processResources {
+  dependsOn writeVersionProperties
+  from tempPropertiesFile
+}
+
+/*****************************************************************************
+ *                    Dependencies used by the entire build                  *
+ *****************************************************************************/
 
 repositories {
-  mavenCentral()
-  maven {
-    name 'sonatype-snapshots'
-    url "https://oss.sonatype.org/content/repositories/snapshots/"
-  }
   jcenter()
 }
 
@@ -69,6 +79,7 @@ dependencies {
     transitive = false
   }
   compile 'com.netflix.nebula:gradle-extra-configurations-plugin:3.0.3'
+  compile 'com.netflix.nebula:nebula-publishing-plugin:4.4.4'
   compile 'com.netflix.nebula:gradle-info-plugin:3.0.3'
   compile 'org.eclipse.jgit:org.eclipse.jgit:3.2.0.201312181205-r'
   compile 'com.perforce:p4java:2012.3.551082' // THIS IS SUPPOSED TO BE OPTIONAL IN THE FUTURE....
@@ -77,49 +88,67 @@ dependencies {
   compile 'org.apache.rat:apache-rat:0.11'
 }
 
-File tempPropertiesFile = new File(project.buildDir, "version.properties")
-task writeVersionProperties {
-  inputs.properties(props)
-  doLast {
-    OutputStream stream = Files.newOutputStream(tempPropertiesFile.toPath());
-    try {
-      props.store(stream, "UTF-8");
-    } finally {
-      stream.close();
+
+/*****************************************************************************
+ *                    Bootstrap repositories and IDE setup                   *
+ *****************************************************************************/
+// this will only happen when buildSrc is built on its own during build init
+if (project == rootProject) {
+
+  repositories {
+    mavenCentral()
+    maven {
+      name 'sonatype-snapshots'
+      url "https://oss.sonatype.org/content/repositories/snapshots/"
     }
   }
-}
 
-processResources {
-  dependsOn writeVersionProperties
-  from tempPropertiesFile
-}
+  apply plugin: 'idea'
+  apply plugin: 'eclipse'
 
-extraArchive {
-  javadoc = false
-  tests = false
-}
+  idea {
+    module {
+      inheritOutputDirs = false
+      outputDir = file('build-idea/classes/main')
+      testOutputDir = file('build-idea/classes/test')
+    }
+  }
 
-idea {
-  module {
-    inheritOutputDirs = false
-    outputDir = file('build-idea/classes/main')
-    testOutputDir = file('build-idea/classes/test')
+  eclipse {
+    classpath {
+      defaultOutputDir = file('build-eclipse')
+    }
   }
-}
 
-eclipse {
-  classpath {
-    defaultOutputDir = file('build-eclipse')
+  task copyEclipseSettings(type: Copy) {
+    from project.file('src/main/resources/eclipse.settings')
+    into '.settings'
   }
+  // otherwise .settings is not nuked entirely
+  tasks.cleanEclipse {
+    delete '.settings'
+  }
+  tasks.eclipse.dependsOn(cleanEclipse, copyEclipseSettings)
 }
 
-task copyEclipseSettings(type: Copy) {
-  from project.file('src/main/resources/eclipse.settings')
-  into '.settings'
-}
-// otherwise .settings is not nuked entirely
-tasks.cleanEclipse {
-  delete '.settings'
+/*****************************************************************************
+ *                           Normal project checks                           *
+ *****************************************************************************/
+
+// this happens when included as a normal project in the build, which we do
+// to enforce precommit checks like forbidden apis, as well as setup publishing
+if (project != rootProject) {
+  apply plugin: 'nebula.maven-base-publish'
+  apply plugin: 'nebula.maven-scm'
+  apply plugin: 'nebula.source-jar'
+  apply plugin: 'nebula.javadoc-jar'
+
+  publishing {
+    publications {
+      nebula {
+        artifactId 'build-tools'
+      }
+    }
+  }
 }
-tasks.eclipse.dependsOn(cleanEclipse, copyEclipseSettings)
+

+ 56 - 44
buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy

@@ -32,7 +32,8 @@ import org.gradle.api.artifacts.ModuleVersionIdentifier
 import org.gradle.api.artifacts.ProjectDependency
 import org.gradle.api.artifacts.ResolvedArtifact
 import org.gradle.api.artifacts.dsl.RepositoryHandler
-import org.gradle.api.artifacts.maven.MavenPom
+import org.gradle.api.publish.maven.MavenPublication
+import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
 import org.gradle.api.tasks.bundling.Jar
 import org.gradle.api.tasks.compile.JavaCompile
 import org.gradle.internal.jvm.Jvm
@@ -60,7 +61,6 @@ class BuildPlugin implements Plugin<Project> {
         project.pluginManager.apply('nebula.info-java')
         project.pluginManager.apply('nebula.info-scm')
         project.pluginManager.apply('nebula.info-jar')
-        project.pluginManager.apply('com.bmuschko.nexus')
         project.pluginManager.apply(ProvidedBasePlugin)
 
         globalBuildInfo(project)
@@ -68,6 +68,7 @@ class BuildPlugin implements Plugin<Project> {
         configureConfigurations(project)
         project.ext.versions = VersionProperties.versions
         configureCompile(project)
+        configurePublishing(project)
 
         configureTest(project)
         configurePrecommit(project)
@@ -260,48 +261,6 @@ class BuildPlugin implements Plugin<Project> {
         project.configurations.compile.dependencies.all(disableTransitiveDeps)
         project.configurations.testCompile.dependencies.all(disableTransitiveDeps)
         project.configurations.provided.dependencies.all(disableTransitiveDeps)
-
-        // add exclusions to the pom directly, for each of the transitive deps of this project's deps
-        project.modifyPom { MavenPom pom ->
-            pom.withXml { XmlProvider xml ->
-                // first find if we have dependencies at all, and grab the node
-                NodeList depsNodes = xml.asNode().get('dependencies')
-                if (depsNodes.isEmpty()) {
-                    return
-                }
-
-                // check each dependency for any transitive deps
-                for (Node depNode : depsNodes.get(0).children()) {
-                    String groupId = depNode.get('groupId').get(0).text()
-                    String artifactId = depNode.get('artifactId').get(0).text()
-                    String version = depNode.get('version').get(0).text()
-
-                    // collect the transitive deps now that we know what this dependency is
-                    String depConfig = transitiveDepConfigName(groupId, artifactId, version)
-                    Configuration configuration = project.configurations.findByName(depConfig)
-                    if (configuration == null) {
-                        continue // we did not make this dep non-transitive
-                    }
-                    Set<ResolvedArtifact> artifacts = configuration.resolvedConfiguration.resolvedArtifacts
-                    if (artifacts.size() <= 1) {
-                        // this dep has no transitive deps (or the only artifact is itself)
-                        continue
-                    }
-
-                    // we now know we have something to exclude, so add the exclusion elements
-                    Node exclusions = depNode.appendNode('exclusions')
-                    for (ResolvedArtifact transitiveArtifact : artifacts) {
-                        ModuleVersionIdentifier transitiveDep = transitiveArtifact.moduleVersion.id
-                        if (transitiveDep.group == groupId && transitiveDep.name == artifactId) {
-                            continue; // don't exclude the dependency itself!
-                        }
-                        Node exclusion = exclusions.appendNode('exclusion')
-                        exclusion.appendNode('groupId', transitiveDep.group)
-                        exclusion.appendNode('artifactId', transitiveDep.name)
-                    }
-                }
-            }
-        }
     }
 
     /** Adds repositores used by ES dependencies */
@@ -381,6 +340,59 @@ class BuildPlugin implements Plugin<Project> {
         }
     }
 
+    /**
+     * Adds a hook to all publications that will effectively make the maven pom transitive dependency free.
+     */
+    private static void configurePublishing(Project project) {
+        project.plugins.withType(MavenPublishPlugin.class).whenPluginAdded {
+            project.publishing {
+                publications {
+                    all { MavenPublication publication -> // we only deal with maven
+                        // add exclusions to the pom directly, for each of the transitive deps of this project's deps
+                        publication.pom.withXml { XmlProvider xml ->
+                            // first find if we have dependencies at all, and grab the node
+                            NodeList depsNodes = xml.asNode().get('dependencies')
+                            if (depsNodes.isEmpty()) {
+                                return
+                            }
+
+                            // check each dependency for any transitive deps
+                            for (Node depNode : depsNodes.get(0).children()) {
+                                String groupId = depNode.get('groupId').get(0).text()
+                                String artifactId = depNode.get('artifactId').get(0).text()
+                                String version = depNode.get('version').get(0).text()
+
+                                // collect the transitive deps now that we know what this dependency is
+                                String depConfig = transitiveDepConfigName(groupId, artifactId, version)
+                                Configuration configuration = project.configurations.findByName(depConfig)
+                                if (configuration == null) {
+                                    continue // we did not make this dep non-transitive
+                                }
+                                Set<ResolvedArtifact> artifacts = configuration.resolvedConfiguration.resolvedArtifacts
+                                if (artifacts.size() <= 1) {
+                                    // this dep has no transitive deps (or the only artifact is itself)
+                                    continue
+                                }
+
+                                // we now know we have something to exclude, so add the exclusion elements
+                                Node exclusions = depNode.appendNode('exclusions')
+                                for (ResolvedArtifact transitiveArtifact : artifacts) {
+                                    ModuleVersionIdentifier transitiveDep = transitiveArtifact.moduleVersion.id
+                                    if (transitiveDep.group == groupId && transitiveDep.name == artifactId) {
+                                        continue; // don't exclude the dependency itself!
+                                    }
+                                    Node exclusion = exclusions.appendNode('exclusion')
+                                    exclusion.appendNode('groupId', transitiveDep.group)
+                                    exclusion.appendNode('artifactId', transitiveDep.name)
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     /** Returns a closure of common configuration shared by unit and integration tests. */
     static Closure commonTestConfig(Project project) {
         return {

+ 37 - 0
buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy

@@ -18,11 +18,14 @@
  */
 package org.elasticsearch.gradle.plugin
 
+import nebula.plugin.publishing.maven.MavenManifestPlugin
+import nebula.plugin.publishing.maven.MavenScmPlugin
 import org.elasticsearch.gradle.BuildPlugin
 import org.elasticsearch.gradle.test.RestIntegTestTask
 import org.elasticsearch.gradle.test.RunTask
 import org.gradle.api.Project
 import org.gradle.api.artifacts.Dependency
+import org.gradle.api.publish.maven.MavenPublication
 import org.gradle.api.tasks.SourceSet
 import org.gradle.api.tasks.bundling.Zip
 
@@ -34,6 +37,7 @@ public class PluginBuildPlugin extends BuildPlugin {
     @Override
     public void apply(Project project) {
         super.apply(project)
+
         configureDependencies(project)
         // this afterEvaluate must happen before the afterEvaluate added by integTest creation,
         // so that the file name resolution for installing the plugin will be setup
@@ -50,6 +54,10 @@ public class PluginBuildPlugin extends BuildPlugin {
             } else {
                 project.integTest.clusterConfig.plugin(name, project.bundlePlugin.outputs.files)
                 project.tasks.run.clusterConfig.plugin(name, project.bundlePlugin.outputs.files)
+
+                if (project.pluginProperties.extension.publish) {
+                    configurePublishing(project)
+                }
             }
 
             project.namingConventions {
@@ -59,6 +67,7 @@ public class PluginBuildPlugin extends BuildPlugin {
         }
         createIntegTestTask(project)
         createBundleTask(project)
+        configurePublishing(project)
         project.tasks.create('run', RunTask) // allow running ES with this plugin in the foreground of a build
     }
 
@@ -125,4 +134,32 @@ public class PluginBuildPlugin extends BuildPlugin {
         project.configurations.getByName('default').extendsFrom = []
         project.artifacts.add('default', bundle)
     }
+
+    /**
+     * Adds the plugin jar and zip as publications.
+     */
+    private static void configurePublishing(Project project) {
+        project.plugins.apply(MavenScmPlugin.class)
+        project.plugins.apply(MavenManifestPlugin.class)
+
+        project.publishing {
+            publications {
+                nebula {
+                    artifact project.bundlePlugin
+                    pom.withXml {
+                        // overwrite the name/description in the pom nebula set up
+                        Node root = asNode()
+                        for (Node node : root.children()) {
+                            if (node.name() == 'name') {
+                                node.setValue(name)
+                            } else if (node.name() == 'description') {
+                                node.setValue(project.pluginProperties.extension.description)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+    }
 }

+ 4 - 0
buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginPropertiesExtension.groovy

@@ -42,6 +42,10 @@ class PluginPropertiesExtension {
     @Input
     boolean isolated = true
 
+    /** Whether the plugin should be published to maven. */
+    @Input
+    boolean publish = false
+
     PluginPropertiesExtension(Project project) {
         name = project.name
         version = project.version

+ 12 - 3
core/build.gradle

@@ -22,10 +22,19 @@ import com.carrotsearch.gradle.junit4.RandomizedTestingTask
 import org.elasticsearch.gradle.BuildPlugin
 
 apply plugin: 'elasticsearch.build'
-apply plugin: 'com.bmuschko.nexus'
 apply plugin: 'nebula.optional-base'
-
-archivesBaseName = 'elasticsearch'
+apply plugin: 'nebula.maven-base-publish'
+apply plugin: 'nebula.maven-scm'
+apply plugin: 'nebula.source-jar'
+apply plugin: 'nebula.javadoc-jar'
+
+publishing {
+  publications {
+    nebula {
+      artifactId 'elasticsearch'
+    }
+  }
+}
 
 dependencies {
 

+ 13 - 0
distribution/build.gradle

@@ -157,6 +157,19 @@ subprojects {
       MavenFilteringHack.filter(it, expansions)
     }
   }
+
+  /*****************************************************************************
+   *                           Publishing setup                                *
+   *****************************************************************************/
+  apply plugin: 'nebula.maven-base-publish'
+  apply plugin: 'nebula.maven-scm'
+  publishing {
+    publications {
+      nebula {
+        artifactId 'elasticsearch'
+      }
+    }
+  }
 }
 
 /*****************************************************************************

+ 8 - 1
distribution/deb/build.gradle

@@ -36,7 +36,14 @@ task buildDeb(type: Deb) {
 
 artifacts {
   'default' buildDeb
-  archives buildDeb
+}
+
+publishing {
+  publications {
+    nebula {
+      artifact buildDeb
+    }
+  }
 }
 
 integTest {

+ 8 - 1
distribution/integ-test-zip/build.gradle

@@ -24,7 +24,14 @@ task buildZip(type: Zip) {
 
 artifacts {
   'default' buildZip
-  archives buildZip
+}
+
+publishing {
+  publications {
+    nebula {
+      artifact buildZip
+    }
+  }
 }
 
 integTest.dependsOn buildZip

+ 8 - 1
distribution/rpm/build.gradle

@@ -33,7 +33,14 @@ task buildRpm(type: Rpm) {
 
 artifacts {
   'default' buildRpm
-  archives buildRpm
+}
+
+publishing {
+  publications {
+    nebula {
+      artifact buildRpm
+    }
+  }
 }
 
 integTest {

+ 8 - 1
distribution/tar/build.gradle

@@ -26,5 +26,12 @@ task buildTar(type: Tar) {
 
 artifacts {
   'default' buildTar
-  archives buildTar
+}
+
+publishing {
+  publications {
+    nebula {
+      artifact buildTar
+    }
+  }
 }

+ 8 - 1
distribution/zip/build.gradle

@@ -24,7 +24,14 @@ task buildZip(type: Zip) {
 
 artifacts {
   'default' buildZip
-  archives buildZip
+}
+
+publishing {
+  publications {
+    nebula {
+      artifact buildZip
+    }
+  }
 }
 
 integTest.dependsOn buildZip

+ 0 - 4
modules/build.gradle

@@ -40,8 +40,4 @@ subprojects {
       throw new InvalidModelException("Modules cannot disable isolation")
     }
   }
-
-  // these are implementation details of our build, no need to publish them!
-  install.enabled = false
-  uploadArchives.enabled = false
 }

+ 2 - 0
plugins/build.gradle

@@ -27,5 +27,7 @@ configure(subprojects.findAll { it.parent.path == project.path }) {
   esplugin {
     // for local ES plugins, the name of the plugin is the same as the directory
     name project.name
+    // only publish non examples
+    publish project.name.contains('example') == false
   }
 }

+ 3 - 0
settings.gradle

@@ -1,6 +1,7 @@
 rootProject.name = 'elasticsearch'
 
 List projects = [
+  'build-tools',
   'rest-api-spec',
   'core',
   'distribution:integ-test-zip',
@@ -59,6 +60,8 @@ if (isEclipse) {
 
 include projects.toArray(new String[0])
 
+project(':build-tools').projectDir = new File(rootProject.projectDir, 'buildSrc')
+
 if (isEclipse) {
   project(":core").projectDir = new File(rootProject.projectDir, 'core/src/main')
   project(":core").buildFileName = 'eclipse-build.gradle'

+ 5 - 0
test/build.gradle

@@ -40,4 +40,9 @@ subprojects {
   // TODO: why is the test framework pulled in...
   forbiddenApisMain.enabled = false
   jarHell.enabled = false
+
+  apply plugin: 'nebula.maven-base-publish'
+  apply plugin: 'nebula.maven-scm'
+  apply plugin: 'nebula.source-jar'
+  apply plugin: 'nebula.javadoc-jar'
 }