Aliaksandr Arashkevich
4 min readJul 31

--

import net.researchgate.release.ReleaseExtension

plugins {
`version-catalog`
`maven-publish`
id ("net.researchgate.release") version "3.0.2"
}

group = "com.company"

catalog {
// declare the aliases, bundles and versions in this block
versionCatalog {
version("sdk", rootProject.version.toString())

version("springBoot", "2.1.1.RELEASE")
version("springCloud", "Finchley.RELEASE") // replace with Greenwich

version("junit", "5.3.2")
version("assertj", "3.24.2")
version("testcontainers", "1.17.3")

library("javax-inject", "javax.inject:javax.inject:1")

library("junit-api", "org.junit.jupiter", "junit-jupiter-api").versionRef("junit")
library("junit-engine", "org.junit.jupiter", "junit-jupiter-engine").versionRef("junit")
library("assertj", "org.assertj", "assertj-core").versionRef("assertj")

bundle("junit", listOf("junit-api", "junit-engine", "assertj"))
}
}

publishing {
publications {
create<MavenPublication>("maven") {
from(components["versionCatalog"])
}
}
repositories {
maven {
name = "myRepository"
val releasesRepoUrl = "https://repo.company.com/repository/internal"
val snapshotsRepoUrl = "https://repo.company.com/repository/snapshots"
url = uri(if (project.version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
credentials(PasswordCredentials::class)
}
}
}

configure<ReleaseExtension> {
ignoredSnapshotDependencies.set(listOf("net.researchgate:gradle-release"))
tagTemplate.set(
"${property("version")}".replace("-SNAPSHOT", "") + "-release"
)
failOnUnversionedFiles.set(false)
failOnSnapshotDependencies.set(true)
with(git) {
requireBranch.set("master")
pushToRemote.set("origin")
}
}
rootProject.name = "version-catalog"

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {

maven {
url = uri("https://repo.company.com/repository/internal")
mavenContent {
releasesOnly()
}
}

exclusiveContent {
forRepository {
maven {
url = uri("https://repo.company.com/repository/snapshots")
mavenContent {
snapshotsOnly()
}
}
}
filter {
includeGroupByRegex("com\\.company.*")
}
}
mavenCentral()
}
}
rootProject.name = "sdk"

// loads all modules into composite build without hard coding their names
// https://docs.gradle.org/current/samples/sample_composite_builds_hierarchical_multirepo.html#running_multirepo_app_with_dependencies_from_included_builds
file("modules").listFiles()
?.filter { !it.name.startsWith(".") }
?.forEach { moduleBuild: File ->
includeBuild(moduleBuild)
}

dependencyResolutionManagement {

repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)

repositories {
mavenLocal()
mavenCentral()
maven {
url = uri("https://repo.company.com/repository/internal")
mavenContent {
releasesOnly()
}
}
maven {
url = uri("https://repo.company.com/repository/snapshots")
mavenContent {
snapshotsOnly()
}
}
}

versionCatalogs {
create("libs") {
from("com.company:version-catalog:DEV-SNAPSHOT")
}
}
}
plugins {
`java-platform`
`maven-publish`
}

group = "com.company"
version = libs.versions.sdkDev.get()

dependencies {
constraints {

val sdkVersion = libs.versions.sdkDev.get()

api(libs.javax.inject)
api("com.company:logging:$sdkVersion")
api("com.company:core-api:${sdkVersion}")
// .. more constraints goes in here
}
}

publishing {
...// the same as for version-catalog, omitted
}

tasks.clean {
gradle.includedBuilds.forEach { build ->
dependsOn(build.task(":lib:clean"))
}
}

tasks.register("cleanTest") {
gradle.includedBuilds.forEach { build ->
dependsOn(build.task(":lib:cleanTest"))
}
}

tasks.build {
gradle.includedBuilds.forEach { build ->
dependsOn(build.task(":lib:build"))
}
}

tasks.register("publishModules") {
gradle.includedBuilds.forEach { build ->
dependsOn(build.task(":lib:publish"))
}
}

tasks.register("publishModulesToMavenLocal") {
gradle.includedBuilds.forEach { build ->
dependsOn(build.task(":lib:publishToMavenLocal"))
}
}
# publish SDK itself
./gradlew publish

# publish modules that do not depends on any other corporate modules first
./gradlew :independent-module-A:lib:publish

# publish dependent modules next
./gradlew :depends-on-A:lib:publish
./gradlew :depends-on-A-and-above:lib:publish
...
git submodule add -b dev git@gitlab.company.com:libs/your-library.git modules/module
rootProject.name = "your-library"
include("lib")

dependencyResolutionManagement {

repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)

repositories {
mavenLocal()
mavenCentral()
// ... your corporate repos goes in here
}

versionCatalogs {
create("libs") {
from("com.company:version-catalog:DEV-SNAPSHOT")
}
}
}
// use automatic library version from SDK
version = libs.versions.sdk.get()

dependencies {
api(platform("com.company:sdk:${libs.versions.sdkDev.get()}"))

implementation("com.company:library")
}
dependencies {
constraints {
...
}
}
org.gradle.parallel=true
variables:
DOCKER_DRIVER: overlay2
CI_IMAGE: ${CI_REGISTRY}/build-images/gradle:4-jdk8-docker
GRADLE_OPTS: "-Dhttps.protocols=TLSv1.2,TLSv1.3 -Dorg.gradle.daemon=false -Dorg.gradle.vfs.watch=false -Dorg.gradle.caching=true -Dorg.gradle.jvmargs=-Xmx12288m -XX:+HeapDumpOnOutOfMemoryError"
JAVA_OPTS: "-Xms6194m -Xmx12288m"
REPO_DEPLOYER: $REPO_DEPLOYER

stages:
- build
- snapshot

default:
image: ${CI_IMAGE}
before_script:
- git submodule sync --recursive
- git submodule update --init --remote --merge
cache:
key:
files:
- /home/gradle/.gradle/wrapper/gradle-wrapper.properties
paths:
- /home/gradle/.gradle/caches/
- /home/gradle/.gradle/notifications/
- /home/gradle/.gradle/wrapper/

build:
stage: build
script:
# because modules depends on each other through artifacts (not via classpath) we need to publish them first
# the order of publications doesn't matter in here, so we can do that in parallel
- ./gradlew --refresh-dependencies --gradle-user-home /home/gradle/.gradle/ publishToMavenLocal publishModulesToMavenLocal

# now all components of the SDK has the latest versions of each other and could be properly compiled
- ./gradlew --refresh-dependencies --build-cache --gradle-user-home /home/gradle/.gradle/ build
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
when: never
- if: $CI_COMMIT_BRANCH == "master"
when: always

SNAPSHOT:
stage: snapshot
script:
# here release order should be manually controlled, because parallel publication could break dependency tree
- ./bin/release.sh
needs: ["build"]
rules:
- if: $CI_COMMIT_BRANCH == "master"
when: on_success
git submodule sync --recursive
[submodule "modules/module"]
path = modules/module
url = git@gitlab.company.com:company/libs/your-library.git
branch = dev
[...] // more submodules goes here
git submodule update --init --remote --merge
/gradlew --refresh-dependencies publishToMavenLocal publishModulesToMavenLocal
./gradlew publishModules

--

--

Aliaksandr Arashkevich

Technical Project Manager, Software & Solutions Architect