feat(kotlin-sdk): add maven-publish for the release AAR#4182
Conversation
Publishes org.dashfoundation:dash-sdk-android with sources jar and POM (Room/DataStore/Biometric api deps carried). publishToMavenLocal works today; a remote repository block can be added when a hosting decision is made. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…api; POM scm/developers Address review of the release-AAR maven-publish. - Fail the publish tasks (verifyJniLibsPresent, wired to publishToMavenLocal / publishToMavenRepository) when src/main/jniLibs/<abi>/libdash_sdk_jni.so is absent for the arm64-v8a / x86_64 ABI policy build_android.sh produces. That directory is gitignored and empty in a clean checkout, so without this a publish could ship a coordinate that installs but dies at runtime with UnsatisfiedLinkError. Escape hatch: -PallowMissingJni warns and continues (metadata-only dry run). - Move kotlinx-coroutines-core from implementation to api: the public SDK surface returns Flow/StateFlow (Room DAOs, PlatformWalletManager.pendingIdentityKeys), so consumers of the coordinate need it on their compile classpath. - Add POM scm + developers sections (required for Maven Central / remote POM validation), pointed at github.com/dashpay/platform rather than a vanity domain, plus a note that the org.dashfoundation groupId can't go to Maven Central until the matching domain is owned (per HashEngineering). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l/Sonatype deploy Per HashEngineering's decision on PR dashpay#4045, switch the published Maven coordinate group from org.dashfoundation to org.dashj. Dash already owns dashj.org and ships its legacy Core/Platform SDKs there, so the org.dashj group needs no separate domain verification for Maven Central; the org.dashfoundation group would have (dashfoundation.org is third-party-held). This is a coordinates-only change: the Kotlin source packages stay org.dashfoundation.dashsdk, and the artifactId stays dash-sdk-android. Add a jreleaser deploy config mirroring dashj/core/build.gradle: maven-publish stages the signed artifacts into build/staging-deploy, and jreleaserDeploy uploads release versions to Maven Central (Sonatype portal) and -SNAPSHOT versions to the Sonatype snapshots repo. Signing + Sonatype credentials are read from jreleaser env/props at deploy time only. maven-publish still drives publishToMavenLocal / internal-repo publishing. The gradle signing plugin is guarded to require a key only when a real PublishToMavenRepository task is in the graph, so publishToMavenLocal and plain assembles need no GPG setup. Validated: `./gradlew :sdk:publishToMavenLocal` lands org.dashj:dash-sdk-android:0.1.0-SNAPSHOT under ~/.m2/repository/org/dashj/ with signReleasePublication SKIPPED. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…; wire in-memory signing keys Addresses the latest dashpay#4045 review round. - Missing javadoc jar (blocking 3acc85ab85d4): singleVariant("release") now calls withJavadocJar() in addition to withSourcesJar(). Maven Central's Publisher Portal rejects any non-POM component without a javadoc jar, and the snapshot deployer's applyMavenCentralRules(true) enforces it before upload. AGP emits a Kotlin-appropriate javadoc jar — no Dokka dependency added. - JNI-less AAR guard (blocking 8b899bc2e5e8): -PallowMissingJni previously downgraded verifyJniLibsPresent to a warning for EVERY publish task, so it could push a nativeless AAR to a remote repo / jreleaser's staging dir. Split into verifyJniLibsForRemotePublish (ALWAYS hard-fails; flag not honored — wired to every PublishToMavenRepository and to jreleaserDeploy/Upload/Release) and verifyJniLibsForLocalPublish (honors the flag; wired to PublishToMavenLocal). Verified end-to-end: remote task fails with -PallowMissingJni; local warns. - In-memory signing keys (blocking d251acfba63b, from last round): the signing block documented ORG_GRADLE_PROJECT_signingKey/signingPassword but never called useInMemoryPgpKeys, so a CI release on that path failed in signReleasePublication. Now reads the signingKey/signingPassword properties and calls useInMemoryPgpKeys when present; absent (local dev) it's skipped and required=false needs no key. - kotlinx-serialization-json scope (suggestion 96b7b2a236ff): Sdk.MasternodeEntry is a wire DTO touched only by a private envelope and internal parse — never returned by a public function. Marked it `internal` so its generated serializer isn't public API; the dependency correctly stays `implementation` (cleaner than promoting it to `api`, the reviewer's own preferred fix). Validated: :sdk:publishToMavenLocal lands sources + javadoc jars under ~/.m2/repository/org/dashj/; :sdk unit suite green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…al release One-page post-merge runbook now that hosting is settled on Maven Central under org.dashj (dashpay#4045): prerequisites (Central Portal token for org.dashj, GPG key, NDK/cargo-ndk toolchain), the env-var-only credential wiring (ORG_GRADLE_PROJECT_signingKey/Password for the staging publish, JRELEASER_MAVENCENTRAL_SONATYPE_* / JRELEASER_NEXUS2_SNAPSHOTS_* / JRELEASER_GPG_* for the deploy), the three release commands in order (build_android.sh --verify -> staged publish -> jreleaserDeploy) with -PsdkVersion for real releases (default stays 0.1.0-SNAPSHOT), what the verifyJniLibsForRemotePublish guard refuses and why, and the consumer coordinate org.dashj:dash-sdk-android:<version>. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
maven-publish adds/replaces the current coordinates but never removes other versions' directories from sdk/build/staging-deploy, so a release deploy run after an earlier snapshot/release staging could hand JReleaser stale coordinates. cleanStagingDeploy now runs before every *ToStagingRepository publish (verified in the task graph via --dry-run); PUBLISHING.md notes it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe Kotlin SDK now defines Maven publication metadata, release and snapshot deployment through JReleaser, conditional signing, JNI validation for published artifacts, and documentation for publishing and consumption. ChangesKotlin SDK release
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Gradle
participant verifyJniLibsForRemotePublish
participant StagingRepository
participant verifyStagedAarForRemotePublish
participant JReleaser
participant Sonatype
Gradle->>verifyJniLibsForRemotePublish: Verify required source JNI libraries
Gradle->>StagingRepository: Publish signed AAR and metadata
Gradle->>verifyStagedAarForRemotePublish: Validate staged layout and AAR JNI entries
JReleaser->>StagingRepository: Read staged artifacts
JReleaser->>Sonatype: Deploy release or snapshot
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🕓 Ready for review — 26 ahead in queue (commit 33b2a57) |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/kotlin-sdk/sdk/build.gradle.kts (1)
192-216: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winDual signing: Gradle
signingplugin and JReleaser both sign the same staged artifacts.The
signing {}block here signs thereleasepublication duringpublishReleasePublicationToStagingRepository(usingORG_GRADLE_PROJECT_signingKey/signingPassword), and separatelyjreleaser { signing { active.set(Active.ALWAYS) } }re-signs the staged files duringjreleaserDeploy(usingJRELEASER_GPG_*). Public JReleaser deploy logs confirm its own[sign]step signs the already-staged jar/pom/sources/javadoc files at deploy time, so this second signing pass is redundant with the first.This isn't necessarily broken — JReleaser likely just overwrites the
.ascfiles it finds — but it doubles the credential surface (two independent GPG key configurations must both be kept valid and in sync) and adds maintenance overhead for no clear benefit, since JReleaser alone is sufficient to produce Central-compliant signatures.Worth confirming whether the Gradle-side
signing {}block can be dropped entirely (lettingmaven-publishstage unsigned artifacts and JReleaser sign+deploy them, as shown in JReleaser's own Maven Central example), which would also simplify PUBLISHING.md's credentials section to a single GPG key path.Also applies to: 295-344
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/kotlin-sdk/sdk/build.gradle.kts` around lines 192 - 216, Remove the Gradle-side signing configuration from the build script, including the `signing {}` block and its `useInMemoryPgpKeys`, `setRequired`, and `sign` calls, so `maven-publish` stages unsigned artifacts and JReleaser remains the sole signer. Update the related JReleaser publishing credential documentation to describe only its GPG key configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/kotlin-sdk/sdk/build.gradle.kts`:
- Around line 192-216: Remove the Gradle-side signing configuration from the
build script, including the `signing {}` block and its `useInMemoryPgpKeys`,
`setRequired`, and `sign` calls, so `maven-publish` stages unsigned artifacts
and JReleaser remains the sole signer. Update the related JReleaser publishing
credential documentation to describe only its GPG key configuration.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 38fce34f-5bfc-419f-a0f0-3766c99ec06b
📒 Files selected for processing (4)
packages/kotlin-sdk/PUBLISHING.mdpackages/kotlin-sdk/gradle/libs.versions.tomlpackages/kotlin-sdk/sdk/build.gradle.ktspackages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/Sdk.kt
|
Publishing mechanics look sound (JNI guard gating remote publishes, staging wipe, Portal/nexus2 routing) and it merges clean against current v4.1-dev. Items before merge:
|
|
Additional review finding after checking the existing threads: The remote publish guard verifies the source JNI directory, not the AAR that JReleaser will upload. Please inspect the staged release AAR immediately before deploy and require both |
…comments The POM licenses/license/url pointed at blob/master/LICENSE, but the file in this repo is LICENSE.md, so the published link would 404 — point it at LICENSE.md. Also remove review-tracking tokens (PR/finding ids) from code comments, keeping the rationale text intact; the refs move to the PR description. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
verifyJniLibsForRemotePublish checks the source src/main/jniLibs tree at
staging time, but jreleaserDeploy uploads whatever already sits in
build/staging-deploy — a valid current source tree could green-light a stale
or malformed staged AAR.
Add verifyStagedAarForRemotePublish, which runs immediately before any
jreleaser deploy/upload/release task and hard-fails unless:
1. staged artifacts live under exactly the intended
<group>/<artifactId>/<version> path, with nothing staged outside it
(jreleaser uploads the whole staging repository, strays included);
2. exactly one release AAR is staged, named for the intended
artifactId+version (plain, or Maven unique-snapshot timestamped);
3. the AAR, opened as a zip, contains jni/arm64-v8a/libdash_sdk_jni.so
AND jni/x86_64/libdash_sdk_jni.so.
The source-tree guard stays (staging-time check); this adds the deploy-time
check on what is actually uploaded. All filesystem work happens in doLast, and
only jreleaser tasks depend on the new guard, so publishToMavenLocal and plain
builds are unaffected. The published GAV is hoisted into
publishGroupId/publishArtifactId so the publication and the guard share one
source of truth. PUBLISHING.md documents the new guard.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ish guards Review round 2: ZipFile.getEntry falls back to directory entries and a 0-byte .so passes an existence-only check, so both the source-tree and staged-AAR guards now require a real file entry with content. Guard lambdas capture plain values instead of script functions, keeping them configuration-cache safe. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
All items are done and pushed on this branch: license URL corrected to LICENSE.md, tracking refs moved into the PR description, and The version-coupling follow-up you suggested is also ready as its own PR: |
|
Re-verified the pushed fixes in two independent review passes — all three items are genuinely delivered: the license URL resolves, tracking refs moved to the description, and |
The manual Maven publish took -PsdkVersion by hand, so nothing tied it to the kotlin-sdk-* tag that drives the GitHub-release AAR — the two channels could ship different commits under one version (raised by shumkov on dashpay#4182). Fold jreleaserDeploy into kotlin-sdk-release.yml: - The Maven version is DERIVED from the tag (kotlin-sdk-vX.Y.Z -> X.Y.Z); there is no version input anywhere in the workflow, and the checkout is already pinned to the same tag, so the Maven Central artifact and the GitHub-release AAR are always the same commit + version. Malformed kotlin-sdk-v* tags and tag-derived -SNAPSHOT versions fail the run; non-versioned kotlin-sdk-* tags keep their AAR-only behavior. - Deploy steps are gated on the Sonatype Central Portal secrets being configured (the token is currently personal): absent secrets skip with a notice, a partial secret set fails rather than guessing. - The existing publish guards (cleanStagingDeploy, verifyJniLibsForRemotePublish, verifyStagedAarForRemotePublish) run in the CI path via the same task dependencies (verified with --dry-run). - workflow_dispatch stays for re-runs/emergencies and is also tag-derived (existing-tag input only). - PUBLISHING.md: document the CI path as preferred and mark the manual runbook as the fallback, pinned to the tag's commit/version. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds
maven-publishfor the kotlin-sdk release AAR under theorg.dashjgroup, with a JReleaser Maven Central/Sonatype deploy pipeline, a javadoc jar for Central, a JNI-presence guard that hard-fails remote publishes (and wipesstaging-deploybefore every staging publish), and aPUBLISHING.mdrunbook.Re-opens #4045 which was auto-closed when the #3999 base branch was deleted; rebased onto
v4.1-dev. All six original commits replayed cleanly — no hunks needed to be dropped as already-absorbed.Verified:
:sdk:assembleReleasebuilds the AAR, JReleaser/publish task graph configures, and the 175 sdk unit tests pass.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Provenance notes (moved out of code comments per review)
org.dashjgroup (build.gradle.kts, coordinate block)applyMavenCentralRules(true))ORG_GRADLE_PROJECT_signingKeydoesn't fail insignReleasePublication-PallowMissingJniescape closed for remote/staging publishesMasternodeEntrykeptinternalso its generatedserializer()stays out of the public ABI (Sdk.kt)Review-response commits
d517ba20bc— POM license URL →LICENSE.md; tracking refs moved from comments to this table9190ba3059— newverifyStagedAarForRemotePublish: before any jreleaser deploy, the staged repo must contain exactly the intended GAV, no stray artifacts, exactly one correctly-named release AAR, and that AAR must carryjni/arm64-v8a/libdash_sdk_jni.so+jni/x86_64/libdash_sdk_jni.so33b2a57188— round-2 hardening: empty/directory-shaped zip entries rejected (0-byte.sofrom an interrupted build no longer passes), source-tree guard also requires content, guards made configuration-cache safe