Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ Layered, from the bottom up:
public entry point (e.g. only `OkioIoProvider` is public in `sdk-io-okio3`).
- **`sdk-core` has zero non-SLF4J runtime deps** — I/O, Jackson, and concurrency libraries live only in
adapter modules. SLF4J is `compileOnly` (added by the root build to every Kotlin module).
- **Published modules apply `id("dexpace.published-module")`** — the convention plugin in the `build-logic`
included build (`build-logic/src/main/kotlin/dexpace.published-module.gradle.kts`) carries the
`maven-publish` + `signing` setup, shared POM, staging repo, and CI-gated signing. Do not re-inline a
`publishing {}`/`signing {}` block in a module; a new publishable module just applies the plugin, and a
module that must not be published simply omits it. Coordinates (`group`/`version`) come from
`gradle.properties` and apply to every project.
- **Commit style:** `feat:` / `test:` / `docs:` / `chore:` prefixes; `merge:` for work-unit merge commits.

## Things That Will Bite You
Expand Down
48 changes: 48 additions & 0 deletions build-logic/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2026 dexpace and Omar Aljarrah
*
* Licensed under the MIT License. See LICENSE in the project root.
* SPDX-License-Identifier: MIT
*/

// The `kotlin-dsl` plugin lets this build compile precompiled script plugins — every
// `src/main/kotlin/*.gradle.kts` file becomes a plugin whose id is its file name minus the
// `.gradle.kts` suffix (e.g. `dexpace.published-module`). Consumers in the main build apply
// it by that id once `settings.gradle.kts` has `includeBuild("build-logic")` on the plugin
// classpath.
plugins {
`kotlin-dsl`
// Style-gate this included build's own scripts. `build-logic` is a separate build with its
// own settings, so the root build's `subprojects { ktlint }` block does not reach it; without
// this the convention-plugin `.kts` files would escape the repository's Kotlin style checks.
// The version is pinned literally (not via the version catalog) to keep this build catalog-free
// — see the rationale in `settings.gradle.kts`; it must match `ktlint-plugin` in
// `gradle/libs.versions.toml`.
id("org.jlleitschuh.gradle.ktlint") version "12.1.1"
}

repositories {
mavenCentral()
gradlePluginPortal()
}

// Pin the toolchain so this included build compiles reproducibly regardless of the JDK running
// the Gradle daemon. This is plugin code for the build JVM, not shipped bytecode, so the version
// only needs to be recent enough for `kotlin-dsl`.
kotlin {
jvmToolchain(21)
}

ktlint {
ignoreFailures.set(false)
}

// `kotlin-dsl` adds its plugin wrappers and DSL accessors (under build/generated-sources) to the
// `main` Kotlin source set, so the source-set ktlint tasks would otherwise lint tool-generated
// code. Drop the generated tree from those tasks' inputs; the hand-written convention scripts under
// src/ — and the build script via `runKtlintCheckOverKotlinScripts` — are still checked.
val generatedSourcesDir = layout.buildDirectory.dir("generated-sources").get().asFile
tasks.withType<org.jlleitschuh.gradle.ktlint.tasks.BaseKtLintCheckTask>().configureEach {
val handWritten = source.filter { file -> !file.startsWith(generatedSourcesDir) }.files
setSource(handWritten)
}
18 changes: 18 additions & 0 deletions build-logic/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2026 dexpace and Omar Aljarrah
*
* Licensed under the MIT License. See LICENSE in the project root.
* SPDX-License-Identifier: MIT
*/

// Standalone settings for the `build-logic` included build. This build compiles the
// repository's convention plugins (precompiled `*.gradle.kts` script plugins) so that the
// production modules can apply them by id instead of duplicating configuration.
//
// `build-logic` deliberately depends on nothing from the version catalog: its sole convention
// plugin wires the core `maven-publish` and `signing` plugins, which ship with Gradle itself
// and therefore need no version. Keeping the included build catalog-free avoids extra
// `dependencyResolutionManagement { versionCatalogs { ... } }` plumbing and the associated
// classpath coupling between the main build and its own build logic.

rootProject.name = "build-logic"
84 changes: 84 additions & 0 deletions build-logic/src/main/kotlin/dexpace.published-module.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2026 dexpace and Omar Aljarrah
*
* Licensed under the MIT License. See LICENSE in the project root.
* SPDX-License-Identifier: MIT
*/

// Convention plugin for every module that is published to Maven Central. It carries the
// `maven-publish` + `signing` setup, the shared POM metadata, the staging repository, and the
// CI-gated signing configuration that was previously copied verbatim into all nine module
// build scripts.
//
// A module opts in with `plugins { id("dexpace.published-module") }`. The publication name,
// coordinates, POM, repository, and signing behaviour are then identical across modules; the
// `name`/`description` fields derive from `project.name`, so a module needs no further
// publishing configuration. A module that must NOT be published simply does not apply this
// plugin.

plugins {
`maven-publish`
signing
}

// Coordinates (`group`/`version`) are not set here. Gradle applies them from the repository-root
// `gradle.properties` to the root project and every subproject, so each consuming module already
// carries the shared `org.dexpace` coordinates and current version by the time this plugin runs —
// a coordinate bump is a one-line edit in that file.

// The `library` publication is built from the `java` software component, which only exists once a
// `java`/`java-library`/`kotlin("jvm")` plugin is applied. Every current consumer applies
// `kotlin("jvm")`, but this plugin neither applies nor requires one, so the whole publication +
// signing setup is guarded on the Kotlin JVM plugin. Without the guard, a module that opted in
// without a Java/Kotlin plugin would fail with an opaque "SoftwareComponent with name java not
// found".
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
publishing {
publications {
create<MavenPublication>("library") {
from(components["java"])
pom {
name.set(project.name)
description.set("Dexpace Java SDK — ${project.name}")
url.set("https://github.com/dexpace/java-sdk")
licenses {
license {
name.set("MIT License")
url.set("https://github.com/dexpace/java-sdk/blob/main/LICENSE")
distribution.set("repo")
}
}
developers {
developer {
id.set("dexpace")
name.set("Dexpace SDK Team")
}
}
scm {
connection.set("scm:git:https://github.com/dexpace/java-sdk.git")
developerConnection.set("scm:git:ssh://github.com/dexpace/java-sdk.git")
url.set("https://github.com/dexpace/java-sdk")
}
}
}
}
repositories {
// Local staging repository. CI must override this to publish to a real remote.
maven {
name = "local"
url = uri(rootProject.layout.buildDirectory.dir("staging-repo"))
}
}
}

signing {
isRequired = (System.getenv("CI") == "true")
val signingKey = project.findProperty("signing.key") as String? ?: System.getenv("SIGNING_KEY")
val signingPassword =
project.findProperty("signing.password") as String? ?: System.getenv("SIGNING_PASSWORD")
if (!signingKey.isNullOrBlank() && !signingPassword.isNullOrBlank()) {
useInMemoryPgpKeys(signingKey, signingPassword)
}
sign(publishing.publications["library"])
}
}
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ plugins {
alias(libs.plugins.detekt)
}

group = "org.dexpace"
version = "0.0.1-alpha.1"
// `group` and `version` are set once in `gradle.properties` and applied by Gradle to the root
// project and every subproject — see that file.

// Coverage: aggregate every Kover-enabled subproject through this root project's reports.
dependencies {
Expand Down
8 changes: 8 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ kotlin.code.style=official
# (org.gradle.configuration-cache is intentionally left out and tracked separately.)
org.gradle.caching=true
org.gradle.parallel=true

# Project coordinates. Gradle applies `group` and `version` from gradle.properties to every
# project in this build, so the root and all published modules share one source of truth — no
# per-module or per-script literal. A coordinate bump is a one-line edit here. (The `build-logic`
# included build is a separate build with its own scope and does not read these; it needs no
# coordinates of its own.)
group=org.dexpace
version=0.0.1-alpha.1
55 changes: 3 additions & 52 deletions sdk-async-coroutines/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
plugins {
kotlin("jvm")
id("org.jetbrains.kotlinx.kover")
`maven-publish`
signing
// Publishing, signing, POM metadata, and coordinates come from this convention plugin
// (build-logic/src/main/kotlin/dexpace.published-module.gradle.kts).
id("dexpace.published-module")
}

group = "org.dexpace"
version = "0.0.1-alpha.1"

// Java 8 bytecode is inherited from the root build script — the module ships to JDK 8 consumers
// just like `sdk-core` does.

Expand All @@ -40,50 +38,3 @@ dependencies {
tasks.test {
useJUnitPlatform()
}

publishing {
publications {
create<MavenPublication>("library") {
from(components["java"])
pom {
name.set(project.name)
description.set("Dexpace Java SDK — ${project.name}")
url.set("https://github.com/dexpace/java-sdk")
licenses {
license {
name.set("MIT License")
url.set("https://github.com/dexpace/java-sdk/blob/main/LICENSE")
distribution.set("repo")
}
}
developers {
developer {
id.set("dexpace")
name.set("Dexpace SDK Team")
}
}
scm {
connection.set("scm:git:https://github.com/dexpace/java-sdk.git")
developerConnection.set("scm:git:ssh://github.com/dexpace/java-sdk.git")
url.set("https://github.com/dexpace/java-sdk")
}
}
}
}
repositories {
maven {
name = "local"
url = uri(rootProject.layout.buildDirectory.dir("staging-repo"))
}
}
}

signing {
isRequired = (System.getenv("CI") == "true")
val signingKey = project.findProperty("signing.key") as String? ?: System.getenv("SIGNING_KEY")
val signingPassword = project.findProperty("signing.password") as String? ?: System.getenv("SIGNING_PASSWORD")
if (!signingKey.isNullOrBlank() && !signingPassword.isNullOrBlank()) {
useInMemoryPgpKeys(signingKey, signingPassword)
}
sign(publishing.publications["library"])
}
55 changes: 3 additions & 52 deletions sdk-async-netty/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
plugins {
kotlin("jvm")
id("org.jetbrains.kotlinx.kover")
`maven-publish`
signing
// Publishing, signing, POM metadata, and coordinates come from this convention plugin
// (build-logic/src/main/kotlin/dexpace.published-module.gradle.kts).
id("dexpace.published-module")
}

group = "org.dexpace"
version = "0.0.1-alpha.1"

dependencies {
implementation(project(":sdk-core"))
// `netty-common` carries `io.netty.util.concurrent.Future`/`Promise`/`EventExecutor` —
Expand All @@ -30,50 +28,3 @@ dependencies {
tasks.test {
useJUnitPlatform()
}

publishing {
publications {
create<MavenPublication>("library") {
from(components["java"])
pom {
name.set(project.name)
description.set("Dexpace Java SDK — ${project.name}")
url.set("https://github.com/dexpace/java-sdk")
licenses {
license {
name.set("MIT License")
url.set("https://github.com/dexpace/java-sdk/blob/main/LICENSE")
distribution.set("repo")
}
}
developers {
developer {
id.set("dexpace")
name.set("Dexpace SDK Team")
}
}
scm {
connection.set("scm:git:https://github.com/dexpace/java-sdk.git")
developerConnection.set("scm:git:ssh://github.com/dexpace/java-sdk.git")
url.set("https://github.com/dexpace/java-sdk")
}
}
}
}
repositories {
maven {
name = "local"
url = uri(rootProject.layout.buildDirectory.dir("staging-repo"))
}
}
}

signing {
isRequired = (System.getenv("CI") == "true")
val signingKey = project.findProperty("signing.key") as String? ?: System.getenv("SIGNING_KEY")
val signingPassword = project.findProperty("signing.password") as String? ?: System.getenv("SIGNING_PASSWORD")
if (!signingKey.isNullOrBlank() && !signingPassword.isNullOrBlank()) {
useInMemoryPgpKeys(signingKey, signingPassword)
}
sign(publishing.publications["library"])
}
55 changes: 3 additions & 52 deletions sdk-async-reactor/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
plugins {
kotlin("jvm")
id("org.jetbrains.kotlinx.kover")
`maven-publish`
signing
// Publishing, signing, POM metadata, and coordinates come from this convention plugin
// (build-logic/src/main/kotlin/dexpace.published-module.gradle.kts).
id("dexpace.published-module")
}

group = "org.dexpace"
version = "0.0.1-alpha.1"

dependencies {
implementation(project(":sdk-core"))
// Reactor itself is Java 8 compatible and ships with `Mono.fromFuture(...)` / `Mono.toFuture()`
Expand All @@ -33,50 +31,3 @@ dependencies {
tasks.test {
useJUnitPlatform()
}

publishing {
publications {
create<MavenPublication>("library") {
from(components["java"])
pom {
name.set(project.name)
description.set("Dexpace Java SDK — ${project.name}")
url.set("https://github.com/dexpace/java-sdk")
licenses {
license {
name.set("MIT License")
url.set("https://github.com/dexpace/java-sdk/blob/main/LICENSE")
distribution.set("repo")
}
}
developers {
developer {
id.set("dexpace")
name.set("Dexpace SDK Team")
}
}
scm {
connection.set("scm:git:https://github.com/dexpace/java-sdk.git")
developerConnection.set("scm:git:ssh://github.com/dexpace/java-sdk.git")
url.set("https://github.com/dexpace/java-sdk")
}
}
}
}
repositories {
maven {
name = "local"
url = uri(rootProject.layout.buildDirectory.dir("staging-repo"))
}
}
}

signing {
isRequired = (System.getenv("CI") == "true")
val signingKey = project.findProperty("signing.key") as String? ?: System.getenv("SIGNING_KEY")
val signingPassword = project.findProperty("signing.password") as String? ?: System.getenv("SIGNING_PASSWORD")
if (!signingKey.isNullOrBlank() && !signingPassword.isNullOrBlank()) {
useInMemoryPgpKeys(signingKey, signingPassword)
}
sign(publishing.publications["library"])
}
Loading
Loading