現在、プロジェクトが構成を共有する方法を改善しようとしています。すべてのライブラリとマイクロサービス(つまり、多くのgitリポジトリ)に対応する、さまざまなマルチモジュールGradleプロジェクトがたくさんあります。
私の主な目標は:
私の現在のソリューションは、次のようなinitスクリプトを使用したカスタムGradleディストリビューションです。
mavenLocal()
とNexusリポジトリをプロジェクトリポジトリに追加します(Gradle init script documentation example に非常に似ていますが、リポジトリを追加するとともに検証する点が異なります)。build.gradle
_ファイルは、ほとんど依存関係のためのものです。以下はinitスクリプトです(サニタイズ):
_/**
* 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<Gradle> {
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スクリプトに依存せずに機能する必要があることを示しています。無許可レポなどの防止.
私のアイデアは、Nexusリポジトリとプラグインを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をカスタムディストリビューションにバンドルすることを望んでいました。つまり、私のinitスクリプトは、魔法のプラグインとリポジトリの構成を隠すのではなく、単純な検証を実行できました。拡張機能は変更する必要がないため、プラグインが変更されたときに新しいGradleディストリビューションをリリースする必要はありません。
私が試したこと:
buildscript.dependencies
_)に追加します-動作しません(おそらく、依存関係をbuildscript
に追加しているように思われないため、仕様上これは動作しません)同じブロックで参照されている)buildSrc
に配置します(これは通常のプロジェクトdeps/reposでは機能しますが、buildscript
では機能しませんが、ボイラープレートを移動するだけなので、実際のソリューションではありません)lib
フォルダーにjarをドロップするだから私の質問は本当に次のように要約されます:
buildScript
ブロックから見えるようにすることは可能ですか)?私は@eskatosに戻って彼の答えについてフィードバックすることを約束しました-ここにあります!
私の最終的な解決策は次のとおりです:
settings.gradle.kts
ファイル。settings.gradle.kts
ファイルには以下が含まれます。
pluginManagement {
repositories {
// local maven to facilitate easy testing of our plugins
mavenLocal()
// our plugins and their markers are now available via Nexus
maven {
name = "CorporateNexus"
url = uri("https://nexus.example.com/repository/maven-public")
}
// all external gradle plugins are now mirrored via Nexus
maven {
name = "Gradle Plugin Portal"
url = uri("https://nexus.example.com/repository/gradle-plugin-portal")
}
}
}
これは、すべてのプラグインとその依存関係がNexus経由でプロキシされるようになり、プラグインマーカーもNexusに公開されるため、GradleはプラグインをIDで検索することを意味します。 mavenLocal
もそこにあると、プラグインの変更をローカルで簡単にテストできるようになります。
次に、各プロジェクトのルートbuild.gradle.kts
ファイルは、次のようにプラグインを適用します。
plugins {
// plugin markers for our custom plugins allow us to apply our
// plugins by id as if they were hosted in gradle plugin portal
val corporatePluginsVersion = "1.2.3"
id("corporate-project") version corporatePluginsVersion
// 'apply false` means this plugin can be applied in a subproject
// without having to specify the version again
id("corporate-publishing") version corporatePluginsVersion apply false
// and so on...
}
そして、gradle wrapperを設定して、ミラーリングされたディストリビューションを使用します。これを上記と組み合わせると、すべて(gradle、プラグイン、依存関係)がすべてNexus経由で送られます):
tasks {
"wrapper"(Wrapper::class) {
distributionUrl = "https://nexus.example.com/repository/gradle-distributions/gradle-4.7-bin.Zip"
}
}
settings.gradle.kts
のリモートURLからスクリプトを適用するという@eskatosの提案を使用して、設定ファイルのボイラープレートを回避したいと思っていました。つまり.
apply { from("https://nexus.example.com/repository/maven-public/com/example/gradle/corporate-settings/1.2.3/corporate-settings-1.2.3.kts" }
以下のテンプレート化されたスクリプト(プラグインと一緒に公開)を生成することもできました。
ただし、ボイラープレートは削除されましたが、URLから適用されたスクリプトがキャッシュされていても、GradleはHEADを実行しているようで、Nexusリポジトリへの接続に依存していることを意味します=とにかく変更を確認するように要求します。また、ローカルのmavenディレクトリのスクリプトを手動で指定する必要があったため、プラグインの変更をローカルでテストするのも面倒でした。現在の構成では、プラグインをローカルのmavenに公開するだけで、プロジェクトのバージョンを更新します。
私は現在の設定にとても満足しています-プラグインがどのように適用されるかは、開発者にとってはるかに明白です。また、Gradleとプラグインを個別にアップグレードすることがはるかに簡単になり、2つの間に依存関係がなくなりました(カスタムのGradleディストリビューションは必要ありません)。
すべてのGradle Kotlin DSLのメリットを活用したい場合は、plugins {}
ブロックを使用してすべてのプラグインを適用するように努力する必要があります。 https://github.com/gradle/kotlin-dsl/blob/master/doc/getting-started/Configuring-Plugins.md を参照してください
設定ファイルでプラグインリポジトリと解決戦略(バージョンなど)を管理できます。 Gradle 4.4以降、このファイルはKotlin DSL、別名settings.gradle.kts
を使用して書き込むことができます。 https://docs.gradle.org/4.4-rc-1/release-notes.html を参照してください。
これを念頭に置いて、設定を一元化したSettings
スクリプトプラグインを作成し、ビルドsettings.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. を使用できます。すべてのビルドで同じプラグインバージョンを使用する必要がある場合は、settings.gradle.kts
を使用してpluginManagement.resolutionStrategy
にバージョンを設定することもできます。
また、これがすべて機能するためには、プラグインを プラグインマーカーアーティファクト で公開する必要があります。これは、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
プラグインの解決戦略を構成する場合も、同様のパターンに従います。
このパターンのいいところは、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を使用して、すべてのプロジェクトに会社の特別なソースを追加します。
共通の構成がすべてのプロジェクトで複製されるときに、同様の問題が発生しました。 initスクリプトで定義された一般的な設定を使用して、カスタムのgradleディストリビューションで解決しました。
そのようなカスタムディストリビューションを準備するためのgradleプラグインを作成しました- custom-gradle-dist 。それは私のプロジェクト、例えばライブラリプロジェクトのbuild.gradleは次のようになります(これは完全なファイルです)。
dependencies {
compile 'org.springframework.kafka:spring-kafka'
}