В настоящее время я пытаюсь улучшить способ совместного использования наших проектов. У нас есть много различных мультимодульных проектов gradle для всех наших библиотек и микросервисов (т.е. Много репозиториев git).
Мои главные цели:
- Чтобы не дублировать конфигурацию репозитория Nexus в каждом проекте (также я могу смело предположить, что URL-адрес не изменится)
- Чтобы мои пользовательские плагины gradle (опубликованные в Nexus) доступны для каждого проекта с минимальным шаблоном/дублированием (они должны быть доступны для каждого проекта, и единственное, что волнует проект, - это версия, которую он использует)
- Без магии - разработчикам должно быть очевидно, как все настроено.
Мое текущее решение - это пользовательский дистрибутив gradle с init script, который:
- добавляет
mavenLocal()
и наш репозиторий Nexus к репозиториям проекта (очень похож на пример документации gradle init script, за исключением он добавляет репозиции, а также проверяет их). - настраивает расширение, которое позволяет нашим плагинам gradle добавлять в путь класса buildscript (используя это обходное решение). Он также добавляет наше Nexus repo в качестве реплики buildscript как такового, где размещаются плагины. У нас есть довольно много плагинов (построенных на Netflix отлично плагины туманности) для различных шаблонов: стандартная настройка проекта (настройка kotlin, настройка теста и т.д.), выпуске, публикации, документации и т.д., и это означает, что наши файлы проекта
build.gradle
в значительной степени предназначены только для зависимостей.
Вот инициализация script (санированная):
/**
* 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'
}
}
В то время как этот подход работает, он позволяет воспроизводимые сборки (в отличие от нашей предыдущей настройки, которая применяла конструкцию script из URL-адреса, которая в то время не кэшировалась) и позволяет работать в автономном режиме, она делает ее немного волшебной и Мне было интересно, могу ли я сделать что-то лучше.
Все это было вызвано чтением комментария к Github от gradle dev Stefan Oehme, в котором говорится, что сборка должна работать, не полагаясь на init script, то есть сценарии инициализации должны быть просто декоративными и делать такие вещи, как документированный пример - предотвращение несанкционированных репозиториев и т.д.
Моя идея состояла в том, чтобы написать некоторые функции расширения, которые позволили бы мне добавить наши репозитории и плагины Nexus в сборку таким образом, чтобы они были встроены в gradle (аналогично функциям расширения gradleScriptKotlin()
и kotlin-dsl()
, предоставленный gradle DSL Kotlin.
Итак, я создал свои функции расширения в проекте 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"))
}
}
Однако gradle не смог увидеть мои функции при использовании в блоке buildscript
(не удалось скомпилировать script). Использование их в нормальных проектных репозиториях/зависимостях работало нормально (они видны и работают, как ожидалось).
Если это сработало, я надеялся объединить банку в свой пользовательский дистрибутив, то есть мой init script мог просто выполнить простое подтверждение, а не скрывать магическую конфигурацию плагина и репо. Функции расширения не нуждаются в изменении, поэтому для плагинов не потребуется выпускать новый дистрибутив gradle.
Что я пробовал:
- добавление моей банки в тестовый проект buildscript classpath (т.е.
buildscript.dependencies
) - не работает (возможно, это не работает по дизайну, так как не кажется правильным добавить зависимость отbuildscript
в том же блоке) - поместив функции в
buildSrc
(который работает для нормальных депов/репозиций проектов, но неbuildscript
, но не является реальным решением, поскольку он просто перемещает шаблон) - удаление контейнера в папке
lib
дистрибутива
Итак, мой вопрос действительно сводится к следующему:
- Я пытаюсь достичь того, чего я пытаюсь сделать (возможно ли сделать пользовательские классы/функции видимыми для блока
buildscript
)? - Есть ли лучший подход к настройке корпоративного репозитория Nexus и созданию пользовательских плагинов (опубликованных в Nexus), доступных для множества отдельных проектов (т.е. полностью разных кодовых баз) с минимальной конфигурацией шаблонов?