Gradle脚本Kotlin中的锅炉项目配置

我正在尝试改进我们的项目共享他们的配置的方式。 我们有许多不同的多模块gradle项目,用于我们的所有库和微服务(即许多git仓库)。

我的主要目标是:

  • 为了不让我的Nexus存储库配置在每个项目中都重复(另外,我可以放心地认为URL不会改变)
  • 为了使我的自定义Gradle插件(发布到Nexus)可用于每个项目,最小的样板/重复(他们应该可用于每个项目,该项目唯一关心的是它使用的版本)
  • 没有魔法 – 开发人员应该很明白怎样配置一切

我目前的解决方案是一个定制的gradle分发与初始化脚本:

  • mavenLocal()和我们的Nexus存储库添加到项目回购(非常类似于Gradle 初始化脚本文档示例 ,除了添加回购以及validation它们)
  • 配置允许我们的gradle插件添加到buildscript类路径的扩展(使用此解决方法 )。 它还将我们的Nexus回购作为buildscript回购添加,因为这是插件托管的地方。 我们有很多插件(建立在Netflix优秀的星云插件上 ),用于各种样板:标准项目设置(kotlin设置,测试设置等),发布,发布,文档等等,这意味着我们的项目build.gradle文件非常多只是为了依赖。

这里是初始化脚本(消毒):

 /** * Gradle extension applied to all projects to allow automatic configuration of Corporate plugins. */ class CorporatePlugins { public static final String NEXUS_URL = "https://example.com/repository/maven-public" public static final String CORPORATE_PLUGINS = "com.example:corporate-gradle-plugins" def buildscript CorporatePlugins(buildscript) { this.buildscript = buildscript } void version(String corporatePluginsVersion) { buildscript.repositories { maven { url NEXUS_URL } } buildscript.dependencies { classpath "$CORPORATE_PLUGINS:$corporatePluginsVersion" } } } allprojects { extensions.create('corporatePlugins', CorporatePlugins, buildscript) } apply plugin: CorporateInitPlugin class CorporateInitPlugin implements Plugin { void apply(Gradle gradle) { gradle.allprojects { project -> project.repositories { all { ArtifactRepository repo -> if (!(repo instanceof MavenArtifactRepository)) { project.logger.warn "Non-maven repository ${repo.name} detected in project ${project.name}. What are you doing???" } else if(repo.url.toString() == CorporatePlugins.NEXUS_URL || repo.name == "MavenLocal") { // Nexus and local maven are good! } else if (repo.name.startsWith("MavenLocal") && repo.url.toString().startsWith("file:")){ // Duplicate local maven - remove it! project.logger.warn("Duplicate mavenLocal() repo detected in project ${project.name} - the corporate gradle distribution has already configured it, so you should remove this!") remove repo } else { project.logger.warn "External repository ${repo.url} detected in project ${project.name}. You should only be using Nexus!" } } mavenLocal() // define Nexus repo for downloads maven { name "CorporateNexus" url CorporatePlugins.NEXUS_URL } } } } } 

然后,我通过将以下内容添加到根build.gradle文件来配置每个新项目:

 buildscript { // makes our plugins (and any others in Nexus) available to all build scripts in the project allprojects { corporatePlugins.version "1.2.3" } } allprojects { // apply plugins relevant to all projects (other plugins are applied where required) apply plugin: 'corporate.project' group = 'com.example' // allows quickly updating the wrapper for our custom distribution task wrapper(type: Wrapper) { distributionUrl = 'https://com.example/repository/maven-public/com/example/corporate-gradle/3.5/corporate-gradle-3.5.zip' } } 

虽然这种方法有效,但允许重复生成(与我们以前的设置不同,这个设置使用了一个来自URL的生成脚本 – 当时没有缓存),并且允许离线工作,这确实使它有点神奇,我想知道如果我可以做得更好。

这一切都是通过Gradle dev Stefan Oehme 对Github的评论来触发的,他说,构建应该不依赖init脚本,也就是说,init脚本应该是装饰性的,并且可以像记录的例子一样执行,防止未经授权的回购等。

我的想法是编写一些扩展函数,允许我们将Nexus repo和插件添加到构建中,看起来像构建到gradle中(类似于扩展函数gradleScriptKotlin()kotlin-dsl() Gradle Kotlin DSL。

所以我在一个kotlin gradle项目中创建了我的扩展函数:

 package com.example import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.artifacts.dsl.RepositoryHandler import org.gradle.api.artifacts.repositories.MavenArtifactRepository fun RepositoryHandler.corporateNexus(): MavenArtifactRepository { return maven { with(it) { name = "Nexus" setUrl("https://example.com/repository/maven-public") } } } fun DependencyHandler.corporatePlugins(version: String) : Any { return "com.example:corporate-gradle-plugins:$version" } 

计划在我的项目build.gradle.kts使用它们如下:

 import com.example.corporateNexus import com.example.corporatePlugins buildscript { repositories { corporateNexus() } dependencies { classpath(corporatePlugins(version = "1.2.3")) } } 

但是,在buildscript脚本块中使用Gradle时无法看到我的函数(无法编译脚本)。 在正常的项目回购/依赖项中使用它们可以很好地工作(它们是可见的,并按预期工作)。

如果这工作,我希望捆绑到我的自定义分配的jar,这意味着我的初始化脚本可以做简单的validation,而不是隐藏了神奇的插件和回购配置。 扩展函数不需要改变,所以当插件改变时不需要发布新的Gradle发行版。

我试过的:

  • 将我的jar添加到测试项目的buildscript类路径(即buildscript.dependencies ) – 不起作用(也许这不起作用的设计,因为它似乎不是正确的增加依赖到建立buildscript在同一个块)
  • 把这些函数放在buildSrc (这对于正常的项目deps / repos而言,而不是buildscript ,但是不是真正的解决方案,因为它只是移动样板)
  • 将jar放入发行版的lib文件夹中

所以我的问题归结为:

  • 是我想实现的可能(是否有可能使自定义类/function可见的buildScript块)?
  • 是否有更好的方法来配置一个企业Nexus回购和定制插件(发布到Nexus)跨许多独立的项目(即完全不同的代码库)可用最小的样板配置?

如果你想从所有的Gradle Kotlin DSL良好中受益,你应该努力使用plugins {}块来应用所有的插件。 请参阅https://github.com/gradle/kotlin-dsl/blob/master/doc/getting-started/Configuring-Plugins.md

您可以在设置文件中管理插件库和解决策略(例如他们的版本)。 从Gradle 4.4开始,可以使用Kotlin DSL(aka settings.gradle.kts来编写此文件。 请参阅https://docs.gradle.org/4.4-rc-1/release-notes.html 。

考虑到这一点,你可以有一个集中的Settings脚本插件来设置和应用它在你的build.gradle.kts文件中:

 // corporate-settings.gradle.kts pluginManagement { repositories { maven { name = "Corporate Nexus" url = uri("https://example.com/repository/maven-public") } gradlePluginPortal() } } 

和:

 // settings.gradle.kts apply { from("https://url.to/corporate-settings.gradle.kts") } 

然后在您的项目构建脚本中,您可以简单地请求公司存储库中的插件:

 // build.gradle.kts plugins { id("my-corporate-plugin") version "1.2.3" } 

如果你希望在多项目构建中使用你的项目构建脚本来重复插件版本,你可以在Gradle 4.3中通过在你的根项目中声明版本来完成。 请注意,如果所有版本都使用相同的插件版本,您也可以使用pluginManagement.resolutionStrategy settings.gradle.kts的版本。

另外请注意,为了这一切工作,您的插件必须发布与他们的插件标记工件 。 这很容易通过使用java-gradle-plugin插件来完成。

在我的构建中,我一直在做这样的事情

 buildscript { project.apply { from("${rootProject.projectDir}/sharedValues.gradle.kts") } val configureRepository: (Any) -> Unit by extra configureRepository.invoke(repositories) } 

在我的sharedValues.gradle.kts文件中,我有这样的代码:

 /** * This method configures the repository handler to add all of the maven repos that your company relies upon. * When trying to pull this method out of the [ExtraPropertiesExtension] use the following code: * * For Kotlin: * ```kotlin * val configureRepository : (Any) -> Unit by extra * configureRepository.invoke(repositories) * ``` * Any other casting will cause a compiler error. * * For Groovy: * ```groovy * def configureRepository = project.configureRepository * configureRepository.invoke(repositories) * ``` * * @param repoHandler The RepositoryHandler to be configured with the company repositories. */ fun repositoryConfigurer(repoHandler : RepositoryHandler) { repoHandler.apply { // Do stuff here } } var configureRepository : (RepositoryHandler) -> Unit by extra configureRepository = this::repositoryConfigurer 

我遵循类似的模式来配置插件的分辨率策略。

关于这种模式的buildSrc是,您在sharedValues.gradle.kts配置的任何东西都可以从buildSrc项目中使用,这意味着您可以重新使用存储库声明。


更新:

您可以从URL应用另一个脚本,例如:

 apply { // This was actually a plugin that I used at one point. from("http://dl.bintray.com/shemnon/javafx-gradle/8.1.1/javafx.plugin") } 

简单地托管你的脚本,你希望你的所有版本在一些http服务器上共享(强烈建议使用HTTPS,这样你的构建就不能成为中间人攻击的目标)。

这样做的缺点是,我不认为应用于URL的脚本没有被缓存,所以每次运行构建时都会重新下载它们。 现在可能已经修好了,我不确定。

当我遇到类似的问题时,Stefan Oehme给我提供的解决方案是供应我自己的Gradle定制版本。 据他介绍,这是大公司常见的事情。

简单地创建一个Gradle回购的定制分支,使用这个定制版本的Gradle将你的公司的特殊酱添加到每个项目。