Skip to content

Commit 2a7b4be

Browse files
nficanoclaude
andcommitted
build: migrate from Gradle to Maven
Replace Gradle build with Maven across all modules, examples, and recipes. Update CI, release, and pitest workflows to use mvn. Switch dependabot ecosystem from gradle to maven and update .gitignore for target/ outputs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8d81b1c commit 2a7b4be

94 files changed

Lines changed: 2746 additions & 1666 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.coderabbit.yaml

Lines changed: 0 additions & 21 deletions
This file was deleted.

.github/dependabot.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
version: 2
22
updates:
3-
- package-ecosystem: "gradle"
3+
- package-ecosystem: "maven"
44
directory: "/"
55
schedule:
66
interval: "weekly"
@@ -9,7 +9,7 @@ updates:
99
labels:
1010
- "dependencies"
1111
groups:
12-
gradle-dependencies:
12+
maven-dependencies:
1313
patterns:
1414
- "*"
1515

.github/workflows/ci.yml

Lines changed: 53 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -47,79 +47,83 @@ jobs:
4747
with:
4848
distribution: temurin
4949
java-version: ${{ matrix.jdk }}
50-
cache: gradle
51-
52-
- name: Validate Gradle wrapper
53-
uses: gradle/actions/wrapper-validation@v6
50+
# Built-in Maven cache: keys off pom.xml hashes and restores ~/.m2/repository.
51+
cache: maven
5452

5553
- name: Check formatting (Spotless)
5654
if: ${{ matrix.jdk == '21' }}
57-
run: ./gradlew spotlessCheck --no-daemon
55+
run: mvn -B -ntp -Darcp.skip.spotless=false spotless:check
5856

59-
- name: Build
57+
- name: Build and test
58+
# JDK 25 skips spotless because google-java-format embedded in the pinned
59+
# spotless version cannot run against JDK 25's javac internals.
6060
run: |
6161
if [[ "${{ matrix.jdk }}" == "21" ]]; then
62-
./gradlew build --no-daemon --stacktrace
62+
mvn -B -ntp -Dmaven.javadoc.skip=true verify
6363
else
64-
./gradlew build --no-daemon --stacktrace \
65-
-x spotlessCheck \
66-
-x spotlessJavaCheck \
67-
-x spotlessJava
64+
mvn -B -ntp -Dmaven.javadoc.skip=true -Darcp.skip.spotless=true verify
6865
fi
6966
7067
- name: Run all runnable examples and recipes
7168
run: |
72-
./gradlew --no-daemon --stacktrace \
73-
:examples:submit-and-stream:run \
74-
:examples:cancel:run \
75-
:examples:heartbeat:run \
76-
:examples:cost-budget:run \
77-
:examples:result-chunk:run \
78-
:examples:agent-versions:run \
79-
:examples:list-jobs:run \
80-
:examples:lease-expires-at:run \
81-
:examples:idempotent-retry:run \
82-
:examples:custom-auth:run \
83-
:examples:provisioned-credentials:run \
84-
:examples:ack-backpressure:run \
85-
:examples:delegate:run \
86-
:examples:spring-boot:run \
87-
:examples:jakarta:run \
88-
:examples:lease-violation:run \
89-
:examples:progress:run \
90-
:examples:resume:run \
91-
:examples:stdio:run \
92-
:examples:subscribe:run \
93-
:examples:tracing:run \
94-
:examples:vendor-extensions:run \
95-
:recipes:email-vendor-leases:run \
96-
:recipes:mcp-skill:run \
97-
:recipes:multi-agent-budget:run \
98-
:recipes:stream-resume:run
69+
for module in \
70+
examples/submit-and-stream \
71+
examples/cancel \
72+
examples/heartbeat \
73+
examples/cost-budget \
74+
examples/result-chunk \
75+
examples/agent-versions \
76+
examples/list-jobs \
77+
examples/lease-expires-at \
78+
examples/idempotent-retry \
79+
examples/custom-auth \
80+
examples/provisioned-credentials \
81+
examples/ack-backpressure \
82+
examples/delegate \
83+
examples/spring-boot \
84+
examples/jakarta \
85+
examples/lease-violation \
86+
examples/progress \
87+
examples/resume \
88+
examples/stdio \
89+
examples/subscribe \
90+
examples/tracing \
91+
examples/vendor-extensions \
92+
recipes/email-vendor-leases \
93+
recipes/mcp-skill \
94+
recipes/multi-agent-budget \
95+
recipes/stream-resume
96+
do
97+
echo "::group::run $module"
98+
mvn -B -ntp -pl "$module" -am -Darcp.skip.spotless=true -Dmaven.javadoc.skip=true \
99+
-Dmaven.test.skip=true -Dexec.cleanupDaemonThreads=false \
100+
exec:java
101+
echo "::endgroup::"
102+
done
99103
100104
- name: Upload test reports
101105
if: always()
102106
uses: actions/upload-artifact@v7
103107
with:
104108
name: test-reports-jdk${{ matrix.jdk }}
105109
path: |
106-
**/build/reports/tests/
107-
**/build/test-results/
110+
**/target/surefire-reports/
111+
**/target/site/jacoco/
108112
if-no-files-found: ignore
109113
retention-days: 7
110114

111-
# Jacoco XML reports are emitted to **/build/reports/jacoco/test/
112-
# because subprojects apply the jacoco plugin and Test is finalizedBy
113-
# jacocoTestReport (see root build.gradle.kts). Upload from the LTS
114-
# floor only — coverage is identical across JDKs. Non-blocking.
115+
# jacoco-maven-plugin (configured in the parent pom) writes per-module
116+
# XML reports to target/site/jacoco/jacoco.xml — the path the Codecov
117+
# example-java-maven repo uses. Upload from the LTS floor only; coverage
118+
# is identical across JDKs. Non-blocking.
115119
- name: Upload coverage to Codecov
116120
if: matrix.jdk == '21'
117121
# codecov/codecov-action v6.0.1
118122
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
119123
with:
120124
fail_ci_if_error: false
121125
flags: unittests
122-
files: "**/build/reports/jacoco/test/jacocoTestReport.xml"
126+
files: "**/target/site/jacoco/jacoco.xml"
123127
token: ${{ secrets.CODECOV_TOKEN }}
124128

125129
javadoc:
@@ -134,17 +138,9 @@ jobs:
134138
with:
135139
distribution: temurin
136140
java-version: "21"
137-
cache: gradle
141+
cache: maven
138142
- name: Build javadoc for all published modules
139143
run: |
140-
./gradlew --no-daemon \
141-
:arcp-core:javadoc \
142-
:arcp-client:javadoc \
143-
:arcp-runtime:javadoc \
144-
:arcp:javadoc \
145-
:arcp-otel:javadoc \
146-
:arcp-runtime-jetty:javadoc \
147-
:arcp-middleware-jakarta:javadoc \
148-
:arcp-middleware-spring-boot:javadoc \
149-
:arcp-middleware-vertx:javadoc \
150-
:arcp-tck:javadoc
144+
mvn -B -ntp -am -DskipTests -Darcp.skip.spotless=true \
145+
-pl arcp-core,arcp-client,arcp-runtime,arcp,arcp-otel,arcp-runtime-jetty,arcp-middleware-jakarta,arcp-middleware-spring-boot,arcp-middleware-vertx,arcp-tck \
146+
javadoc:javadoc

.github/workflows/pitest-nightly.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,17 @@ jobs:
2121
with:
2222
distribution: temurin
2323
java-version: "21"
24+
cache: maven
2425

25-
- uses: gradle/actions/setup-gradle@v6
26+
- name: Install dependencies of ${{ matrix.module }}
27+
run: mvn -B -ntp -pl ${{ matrix.module }} -am -DskipTests -Darcp.skip.spotless=true install
2628

2729
- name: Run PIT mutation analysis
28-
run: ./gradlew :${{ matrix.module }}:pitest --no-daemon --stacktrace
30+
run: mvn -B -ntp -pl ${{ matrix.module }} -Darcp.skip.spotless=true org.pitest:pitest-maven:mutationCoverage
2931

3032
- name: Upload mutation report
3133
if: always()
3234
uses: actions/upload-artifact@v4
3335
with:
3436
name: pitest-${{ matrix.module }}
35-
path: ${{ matrix.module }}/build/reports/pitest/
37+
path: ${{ matrix.module }}/target/pit-reports/

.github/workflows/release.yml

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ jobs:
3030
with:
3131
distribution: temurin
3232
java-version: "21"
33-
34-
- uses: gradle/actions/setup-gradle@v6
33+
cache: maven
34+
server-id: central
35+
server-username: CENTRAL_USERNAME
36+
server-password: CENTRAL_PASSWORD
37+
gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }}
38+
gpg-passphrase: GPG_SIGNING_PASSWORD
3539

3640
- name: Configure git for tagging
3741
run: |
@@ -40,25 +44,26 @@ jobs:
4044
4145
- name: Set release version
4246
run: |
43-
# Replace `version = "x.x.x-SNAPSHOT"` with the supplied release version.
44-
sed -i 's|version = ".*"|version = "${{ inputs.version }}"|' build.gradle.kts
45-
grep '^[ ]*version =' build.gradle.kts
47+
mvn -B -ntp versions:set -DnewVersion=${{ inputs.version }} -DgenerateBackupPoms=false
48+
mvn -B -ntp versions:commit || true
4649
4750
- name: Build + test
48-
run: ./gradlew build --no-daemon --stacktrace
51+
run: mvn -B -ntp verify
4952

5053
- name: Publish to Maven Central
5154
if: ${{ !inputs.dry_run }}
5255
env:
5356
CENTRAL_USERNAME: ${{ secrets.OSSRH_USERNAME }}
5457
CENTRAL_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
55-
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SIGNING_KEY }}
56-
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SIGNING_PASSWORD }}
57-
run: ./gradlew --no-daemon --stacktrace publishAggregationToCentralPortal
58+
GPG_SIGNING_PASSWORD: ${{ secrets.GPG_SIGNING_PASSWORD }}
59+
# MAVEN_GPG_PASSPHRASE is the env name maven-gpg-plugin reads by
60+
# default when no <passphrase> is set in the plugin config.
61+
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_SIGNING_PASSWORD }}
62+
run: mvn -B -ntp -Prelease -DskipTests deploy
5863

5964
- name: Local publish (dry run)
6065
if: ${{ inputs.dry_run }}
61-
run: ./gradlew publishToMavenLocal --no-daemon --stacktrace
66+
run: mvn -B -ntp install
6267

6368
- name: Tag the release
6469
if: ${{ !inputs.dry_run }}

.gitignore

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
# Gradle
2-
.gradle/
3-
build/
4-
**/build/
1+
# Maven
2+
target/
3+
**/target/
54
bin/
65
**/bin/
7-
!gradle/wrapper/gradle-wrapper.jar
86

97
# IDE
108
.idea/

CONFORMANCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ works" is decomposed into multiple binary rows.
146146

147147
## Tests
148148

149-
Counts after `./gradlew test`:
149+
Counts after `mvn test`:
150150

151151
| Module | Tests | Status |
152152
|---|---|---|

CONTRIBUTING.md

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,21 @@ than large ones; if a change is big, say so early and we'll help break it down.
5858

5959
## Development setup
6060

61-
The build targets JDK 21 LTS (`--release 21`) and is driven entirely by the
62-
bundled Gradle wrapper — do not install Gradle separately. CI runs on Temurin
63-
21 and 25; either works locally. Clone, point `JAVA_HOME` at a JDK 21+, and
64-
let the wrapper resolve everything else:
61+
The build targets JDK 21 LTS (`--release 21`) and runs through Apache Maven
62+
3.9+. CI runs on Temurin 21 and 25; either works locally. Install Maven (or use
63+
your IDE's bundled copy), clone, and point `JAVA_HOME` at a JDK 21+:
6564

6665
```sh
6766
git clone https://github.com/nficano/arpc.git
6867
cd arpc/java-sdk
6968
export JAVA_HOME=$(/usr/libexec/java_home -v 21) # macOS; use your distro's equivalent
70-
./gradlew help # bootstraps the wrapper + toolchain
69+
mvn -version # confirm Maven 3.9+ on JDK 21+
7170
```
7271

73-
The build is a multi-module Gradle project (`arcp-core`, `arcp-client`,
72+
The build is a Maven multi-module reactor (`arcp-core`, `arcp-client`,
7473
`arcp-runtime`, the `arcp` umbrella, transport adapters, middleware, the TCK,
75-
and example/recipe projects); `settings.gradle.kts` is the canonical list.
74+
and example/recipe projects); `pom.xml` at the repo root is the canonical
75+
`<modules>` list.
7676

7777
## Tests and conformance
7878

@@ -81,13 +81,13 @@ Two layers must pass before a PR merges:
8181
- **Unit tests** — this SDK's own suite:
8282

8383
```sh
84-
./gradlew build
84+
mvn verify
8585
```
8686

87-
`build` compiles every module, runs Spotless, and executes the JUnit 5 +
88-
jqwik suites across all subprojects. Use `./gradlew test` (or
89-
`./gradlew :arcp-core:test`) to skip compilation of unrelated modules during
90-
iteration.
87+
`verify` compiles every module, runs Spotless, and executes the JUnit 5 +
88+
jqwik suites across all subprojects. Use `mvn -pl arcp-core test` (with
89+
`-am` to also rebuild upstream modules) to skip compilation of unrelated
90+
modules during iteration.
9191

9292
- **Conformance** — the SDK's behavior against the reference runtime. New
9393
protocol-facing code (session negotiation, event sequencing, lease handling,
@@ -97,7 +97,7 @@ Two layers must pass before a PR merges:
9797
point it at any `Transport` pair — `MemoryTransport.pair()` for the in-process
9898
reference runtime, or a live WebSocket pair via `arcp-runtime-jetty` +
9999
`arcp-client`'s `WebSocketTransport.connect(uri)`. Run with
100-
`./gradlew :arcp-tck:test`.
100+
`mvn -pl arcp-tck -am test`.
101101

102102
CI runs both on every PR. A PR that changes which feature flags the SDK
103103
negotiates must also update the README feature matrix in the same change.
@@ -107,15 +107,19 @@ negotiates must also update the README feature matrix in the same change.
107107
Formatting is enforced by [Spotless](https://github.com/diffplug/spotless)
108108
configured with Google Java Format and unused-import removal. The same JDK 21
109109
`javac` settings (`-Xlint:all`, `-parameters`, UTF-8) and Javadoc generation
110-
that CI uses are applied to every `java-library` subproject:
110+
that CI uses are applied to every library module via the parent POM:
111111

112112
```sh
113-
./gradlew spotlessApply # auto-format
114-
./gradlew spotlessCheck # verify (CI gate on JDK 21)
115-
./gradlew build # compile + lint warnings + tests
116-
./gradlew javadoc # Javadoc for the published modules
113+
mvn spotless:apply # auto-format
114+
mvn spotless:check # verify (CI gate on JDK 21)
115+
mvn verify # compile + lint warnings + tests
116+
mvn javadoc:javadoc # Javadoc for the published modules
117117
```
118118

119+
The pinned Spotless 2.44.x uses a google-java-format build that can't run
120+
on JDK 25's javac internals; pass `-Darcp.skip.spotless=true` when iterating
121+
on JDK 25 locally. CI pins the Spotless gate to JDK 21.
122+
119123
Beyond formatting, the house style is captured in the existing code: records
120124
for value types, sealed hierarchies (`Message`, `EventBody`, `ArcpException`)
121125
for exhaustive dispatch, JSpecify `@Nullable` on anything that may return null,
@@ -143,8 +147,8 @@ the changelog. Prefer clarity over cleverness in a library others build on.
143147

144148
Releases are cut by maintainers. The `release` GitHub Actions workflow is
145149
dispatched manually with a version input; it builds, signs with the project
146-
PGP key, publishes the aggregated set of `dev.arcp:*` artifacts to Maven
147-
Central via the `com.gradleup.nmcp` plugin, and pushes a `vX.Y.Z` tag.
150+
PGP key, publishes the `dev.arcp:*` artifacts to Maven Central via the
151+
`central-publishing-maven-plugin`, and pushes a `vX.Y.Z` tag.
148152
Detailed operator steps live in [RELEASING.md](RELEASING.md). The SDK is
149153
versioned with semantic versioning independently of the protocol version it
150154
speaks; a protocol version bump is noted in the changelog when the negotiated

0 commit comments

Comments
 (0)