Skip to content

Commit 247829e

Browse files
Merge pull request #16 from MarketDataApp/16_gh_action_release_publish
feat: Maven Central publishing pipeline
2 parents 5c9c587 + 5f0967b commit 247829e

5 files changed

Lines changed: 127 additions & 12 deletions

File tree

.github/workflows/publish.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Publish to Maven Central
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: "Version to publish (e.g. 0.1.0, without the 'v')"
8+
required: true
9+
type: string
10+
release:
11+
description: "Publish and release. Off = upload to the Portal and stop at VALIDATED for manual review."
12+
required: true
13+
type: boolean
14+
default: false
15+
16+
jobs:
17+
guard:
18+
name: Require green main.yml for this commit
19+
runs-on: ubuntu-latest
20+
permissions:
21+
actions: read
22+
steps:
23+
- name: Check latest main.yml conclusion
24+
env:
25+
GH_TOKEN: ${{ github.token }}
26+
run: |
27+
conclusion=$(gh api \
28+
"repos/${{ github.repository }}/actions/workflows/main.yml/runs?head_sha=${{ github.sha }}&status=completed" \
29+
--jq '.workflow_runs[0].conclusion // "missing"')
30+
echo "main.yml conclusion for ${{ github.sha }}: $conclusion"
31+
if [ "$conclusion" != "success" ]; then
32+
echo "::error::main.yml is not green for ${{ github.sha }} (got: $conclusion). Run publish from a commit on main whose CI passed."
33+
exit 1
34+
fi
35+
36+
publish:
37+
name: Publish to Maven Central
38+
needs: guard
39+
runs-on: ubuntu-latest
40+
environment:
41+
name: maven-central
42+
url: https://central.sonatype.com/artifact/app.marketdata/marketdata-sdk-java
43+
env:
44+
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
45+
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
46+
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }}
47+
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }}
48+
steps:
49+
- name: Checkout
50+
uses: actions/checkout@v4
51+
52+
- name: Set up JDK 17
53+
uses: actions/setup-java@v4
54+
with:
55+
distribution: temurin
56+
java-version: "17"
57+
58+
- name: Set up Gradle
59+
uses: gradle/actions/setup-gradle@v4
60+
61+
- name: Build and test
62+
run: ./gradlew clean build -PsdkVersion=${{ inputs.version }}
63+
64+
- name: Upload to Portal (validate only)
65+
if: ${{ !inputs.release }}
66+
run: ./gradlew publishToMavenCentral -PsdkVersion=${{ inputs.version }}
67+
68+
- name: Publish and release
69+
if: ${{ inputs.release }}
70+
run: ./gradlew publishAndReleaseToMavenCentral -PsdkVersion=${{ inputs.version }}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ exception taxonomy, and Kotlin-interop foundations are in place.
1717
```kotlin
1818
// build.gradle.kts
1919
dependencies {
20-
implementation("com.marketdata:marketdata-sdk-java:0.1.0")
20+
implementation("app.marketdata:marketdata-sdk-java:0.1.0")
2121
}
2222
```
2323

build.gradle.kts

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1+
import com.vanniktech.maven.publish.SonatypeHost
2+
13
plugins {
24
`java-library`
35
jacoco
46
alias(libs.plugins.spotless)
57
alias(libs.plugins.vanniktech.publish)
68
}
79

8-
group = "com.marketdata"
9-
version = "0.1.0-SNAPSHOT"
10+
// Maven groupId = the verified Central Portal namespace (domain marketdata.app,
11+
// reversed). Independent of the Java package, which stays com.marketdata.sdk.
12+
group = "app.marketdata"
13+
14+
// Version is overridable from the command line so a manual Central Portal
15+
// validation run can use a real release version (e.g. `-PsdkVersion=0.1.0`)
16+
// without committing it. Default stays on the in-development SNAPSHOT.
17+
version = (findProperty("sdkVersion") as String?) ?: "0.1.0-SNAPSHOT"
1018

1119
// ADR-002: minimum JDK 17, build with --release 17, single bytecode level.
1220
java {
@@ -174,13 +182,46 @@ spotless {
174182
}
175183

176184
// ADR-003 / requirements §15: Maven Central publishing via Vanniktech.
177-
// Coordinates and POM metadata below are placeholders — fill in before
178-
// the first publication.
185+
//
186+
// Publishes to the Sonatype Central Portal (central.sonatype.com).
187+
// `automaticRelease = false` uploads the deployment but leaves it in the
188+
// VALIDATED state for manual review/release (or drop) from the portal UI —
189+
// the safe path for a first manual validation run.
190+
//
191+
// Upload + signing credentials are read from Gradle properties / env vars by
192+
// the plugin (never hard-coded here):
193+
// - ORG_GRADLE_PROJECT_mavenCentralUsername / _mavenCentralPassword
194+
// - ORG_GRADLE_PROJECT_signingInMemoryKey / _signingInMemoryKeyPassword
195+
// (optionally _signingInMemoryKeyId)
179196
mavenPublishing {
197+
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = false)
198+
signAllPublications()
199+
180200
coordinates(group.toString(), "marketdata-sdk-java", version.toString())
181201
pom {
182202
name.set("Market Data Java SDK")
183203
description.set("Java SDK for the Market Data API.")
184-
// TODO: set url, scm, license, developers before publishing.
204+
url.set("https://github.com/MarketDataApp/sdk-java")
205+
inceptionYear.set("2026")
206+
207+
licenses {
208+
license {
209+
name.set("MIT License")
210+
url.set("https://github.com/MarketDataApp/sdk-java/blob/main/LICENSE")
211+
distribution.set("repo")
212+
}
213+
}
214+
developers {
215+
developer {
216+
id.set("marketdata")
217+
name.set("Market Data")
218+
url.set("https://www.marketdata.app")
219+
}
220+
}
221+
scm {
222+
url.set("https://github.com/MarketDataApp/sdk-java")
223+
connection.set("scm:git:git://github.com/MarketDataApp/sdk-java.git")
224+
developerConnection.set("scm:git:ssh://git@github.com/MarketDataApp/sdk-java.git")
225+
}
185226
}
186227
}

examples/consumer-test/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ kotlin {
1414
}
1515

1616
dependencies {
17-
implementation("com.marketdata:marketdata-sdk-java:0.1.0-SNAPSHOT")
17+
implementation("app.marketdata:marketdata-sdk-java:0.1.0-SNAPSHOT")
1818
}
1919

2020
// Default `./gradlew run` lands on the stocks resource example (live API).

src/test/java/com/marketdata/sdk/AsyncSemaphoreTest.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,16 @@ void closeCompletesAllQueuedWaitersWithCancellation() {
222222

223223
sem.close();
224224

225-
// CompletableFuture#join unwraps CancellationException specifically: it surfaces directly
226-
// rather than being wrapped in CompletionException. That's the same propagation downstream
227-
// observers see, so we assert the bare exception shape here.
225+
// join() surfaces a CancellationException, but its shape is JDK-dependent: JDK 17 rethrows the
226+
// original directly (message "AsyncSemaphore is closed"), while JDK 21+ wraps it in a fresh
227+
// CancellationException (message "join") carrying the original as its cause. Accept either.
228228
for (CompletableFuture<Void> w : List.of(w1, w2, w3)) {
229229
assertThat(w).isCompletedExceptionally();
230230
assertThatThrownBy(w::join)
231231
.isInstanceOf(CancellationException.class)
232-
.hasMessageContaining("closed");
232+
.satisfiesAnyOf(
233+
t -> assertThat(t).hasMessageContaining("closed"),
234+
t -> assertThat(t).hasRootCauseMessage("AsyncSemaphore is closed"));
233235
}
234236
assertThat(sem.queueLength()).isZero();
235237
}
@@ -244,7 +246,9 @@ void acquireAfterCloseReturnsFailedFutureImmediately() {
244246
assertThat(failed).isCompletedExceptionally();
245247
assertThatThrownBy(failed::join)
246248
.isInstanceOf(CancellationException.class)
247-
.hasMessageContaining("closed");
249+
.satisfiesAnyOf(
250+
t -> assertThat(t).hasMessageContaining("closed"),
251+
t -> assertThat(t).hasRootCauseMessage("AsyncSemaphore is closed"));
248252
assertThat(sem.queueLength()).isZero();
249253
}
250254

0 commit comments

Comments
 (0)