@ondeinference / onde / commits / f07c276

Add Kotlin/Maven Central to sdk-releases skill: 5th pipeline, --no-ff, secrets, pitfalls

keypair34 committed Apr 26, 2026 at 20:08 UTC f07c276012d3da624f00492c4b75cfcaf26ca54e
1 file changed +66 -20
.agents/skills/sdk-releases/SKILL.md
+66 -20
@@ -15,9 +15,10 @@ The complete release pipeline for shipping Onde across all registries:
15 - **Swift package** → Swift Package Index (`onde-swift`)
16 - **Dart/Flutter package** → pub.dev (`onde_inference`)
17 - **React Native** → npm (`@ondeinference/react-native`)
18 +- **Kotlin Multiplatform** → Maven Central (`com.ondeinference:onde-inference`)
19
19 -All four SDK workflows share the same tag trigger. A single tag push fires all
20 -four in parallel. When only a subset needs (re-)publishing, use the **manual
20 +All five SDK workflows share the same tag trigger. A single tag push fires all
21 +five in parallel. When only a subset needs (re-)publishing, use the **manual
22 publish fallback** documented below to avoid collateral re-runs.
23
24 ---
@@ -35,9 +36,10 @@ in a single commit before tagging.**
36 | 4 | `sdk/react-native/package.json` | `version` | `"version": "0.1.3"` |
37 | 5 | `sdk/react-native/rust/Cargo.toml` | `version` | `version = "0.1.3"` |
38 | 6 | `sdk/react-native/CHANGELOG.md` | New `## 0.1.3` section | Prepend at top of file |
38 -| 7 | `sdk/dart/rust/Cargo.lock` | `onde` package version | Run `cd sdk/dart/rust && cargo update -p onde` |
39 -| 8 | `sdk/react-native/rust/Cargo.lock` | `onde` package version | Run `cd sdk/react-native/rust && cargo update -p onde` |
40 -| 9 | `Cargo.lock` (root) | `onde` package version | Run `cargo check` at repo root |
39 +| 7 | `sdk/kotlin/gradle.properties` | `VERSION_NAME` | `VERSION_NAME=0.1.3` |
40 +| 8 | `sdk/dart/rust/Cargo.lock` | `onde` package version | Run `cd sdk/dart/rust && cargo update -p onde` |
41 +| 9 | `sdk/react-native/rust/Cargo.lock` | `onde` package version | Run `cd sdk/react-native/rust && cargo update -p onde` |
42 +| 10 | `Cargo.lock` (root) | `onde` package version | Run `cargo check` at repo root |
43
44 ### Files you do NOT manually edit
45
@@ -55,22 +57,24 @@ in a single commit before tagging.**
57 # 4. Edit sdk/react-native/package.json version (manual)
58 # 5. Edit sdk/react-native/rust/Cargo.toml version (manual)
59 # 6. Prepend new section to sdk/react-native/CHANGELOG.md (manual)
60 +# 7. Edit sdk/kotlin/gradle.properties VERSION_NAME (manual)
61
59 -# 7. Sync lockfiles
62 +# 8. Sync lockfiles
63 cargo check # updates root Cargo.lock
64 cd sdk/dart/rust && cargo update -p onde # updates Dart SDK's Cargo.lock
65 cd sdk/react-native/rust && cargo update -p onde # updates React Native SDK's Cargo.lock
66
64 -# 8. Verify
67 +# 9. Verify
68 grep '^version' Cargo.toml # "0.1.3"
69 grep '^version:' sdk/dart/pubspec.yaml # 0.1.3
70 grep '"version"' sdk/react-native/package.json # "0.1.3"
71 grep '^version' sdk/react-native/rust/Cargo.toml # "0.1.3"
72 +grep 'VERSION_NAME' sdk/kotlin/gradle.properties # 1.0.0
73 grep 'name = "onde"' -A1 Cargo.lock # version = "0.1.3"
74 grep 'name = "onde"' -A1 sdk/dart/rust/Cargo.lock # version = "0.1.3"
75 grep 'name = "onde"' -A1 sdk/react-native/rust/Cargo.lock # version = "0.1.3"
76
73 -# 9. Commit and tag
77 +# 10. Commit and tag
78 git add -A
79 git commit -m "0.1.3"
80 git tag 0.1.3
@@ -89,7 +93,7 @@ v0.1.3 ❌ will NOT trigger CI
93 0.1.3-beta ❌ will NOT match the tag pattern
94 ```
95
92 -Both CI workflows use the same trigger pattern:
96 +All five CI workflows use the same trigger pattern:
97
98 ```yaml
99 on:
@@ -329,6 +333,11 @@ All four must match. If any pair diverges, the relevant CI job fails.
333 | `ONDE_SWIFT_PAT` | `release-sdk-swift.yml` | Push commits + tags to `ondeinference/onde-swift`. Must be a PAT (not `GITHUB_TOKEN`) so it triggers workflows on `onde-swift`. Needs `contents: write` scope. |
334 | `PUB_CREDENTIALS` | `release-sdk-dart.yml` | Authenticate with pub.dev for `flutter pub publish`. Full JSON from `~/.config/dart/pub-credentials.json`. |
335 | `NPM_TOKEN` | `release-sdk-npm.yml` | Authenticate with npm for `npm publish`. Granular access token scoped to the `@ondeinference` org with read+write packages. Create at npmjs.com → Access Tokens. |
336 +| `ORG_GRADLE_PROJECT_MAVENCENTRALUSERNAME` | `release-sdk-kotlin.yml` | Sonatype Central Portal token username. Generate at central.sonatype.com → Profile → Generate User Token. |
337 +| `ORG_GRADLE_PROJECT_MAVENCENTRALPASSWORD` | `release-sdk-kotlin.yml` | Sonatype Central Portal token password. Same dialog as above. |
338 +| `ORG_GRADLE_PROJECT_SIGNINGKEYID` | `release-sdk-kotlin.yml` | Last 8 chars of GPG key fingerprint used to sign Maven Central artifacts. |
339 +| `ORG_GRADLE_PROJECT_SIGNINGKEY` | `release-sdk-kotlin.yml` | ASCII-armored GPG private key. Export with `gpg --armor --export-secret-keys <KEY_ID>`. |
340 +| `ORG_GRADLE_PROJECT_SIGNINGPASSWORD` | `release-sdk-kotlin.yml` | Passphrase for the GPG signing key. |
341 | `GRESIQ_API_KEY_DEV` | All release workflows | Build-time env for dev environment API key. |
342 | `GRESIQ_API_SECRET_DEV` | All release workflows | Build-time env for dev environment API secret. |
343 | `GRESIQ_API_KEY_PRODUCTION` | All release workflows | Build-time env for production API key. |
@@ -347,7 +356,8 @@ All four must match. If any pair diverges, the relevant CI job fails.
356 | Forgot to update `sdk/dart/rust/Cargo.lock` | Dart SDK's Rust bridge builds against stale `onde` version | Run `cd sdk/dart/rust && cargo update -p onde` |
357 | Forgot to update `sdk/react-native/rust/Cargo.lock` | npm SDK's Rust bridge builds against stale `onde` version | Run `cd sdk/react-native/rust && cargo update -p onde` |
358 | Forgot to update `sdk/dart/CHANGELOG.md` | pub.dev shows stale changelog | Prepend new `## 0.1.3` section before tagging |
350 -| Forgot to update `sdk/react-native/CHANGELOG.md` | npm shows stale changelog | Prepend new `## 0.1.3` section before tagging |
359 +| Forgot to bump `sdk/react-native/CHANGELOG.md` | npm shows stale changelog | Prepend new `## 0.1.3` section before tagging |
360 +| Forgot to bump `sdk/kotlin/gradle.properties` `VERSION_NAME` | Kotlin CI fails: tag vs gradle.properties mismatch | Bump `VERSION_NAME`, amend commit, re-tag |
361 | Tag has `v` prefix (`v0.1.3`) | CI does not trigger — tag pattern requires bare semver | Delete the tag, re-tag without `v` |
362 | `CARGO_REGISTRY_TOKEN` missing | `cargo publish` fails with auth error | Create a scoped token at crates.io/settings/tokens, add as repo secret |
363 | `ONDE_SWIFT_PAT` expired | `onde-swift` push fails with 403 | Regenerate PAT at github.com/settings/tokens, update repo secret |
@@ -356,8 +366,10 @@ All four must match. If any pair diverges, the relevant CI job fails.
366 | crates.io version already published | `cargo publish` fails with "already uploaded" | crates.io is immutable — bump to next version. `cargo yank` hides but doesn't delete. |
367 | npm version already published | npm publish step skips (idempotency guard) | Safe — the workflow exits 0. No action needed. |
368 | pub.dev version already published | `flutter pub publish` fails | pub.dev is immutable — bump to next version |
369 +| Maven Central version already published | `publishAndReleaseToMavenCentral` fails | Maven Central is immutable — bump to next version |
370 +| Maven Central namespace not verified | Deployment fails: "Namespace 'com.ondeinference' is not allowed" | Verify namespace on central.sonatype.com (add DNS TXT record, wait for Sonatype to poll) |
371 | Tag pushed before workflow exists on `main` | Workflow never fires for that tag | Use `workflow_dispatch` for dry-run, then publish manually. See **Manual Publish Fallback**. |
360 -| All 4 workflows fire but only 1–2 need re-publishing | Collateral re-runs of Swift/npm/Dart/Rust | Don't delete+re-push the tag. Use **Manual Publish Fallback** for the specific SDKs. |
372 +| All 5 workflows fire but only 1–2 need re-publishing | Collateral re-runs of Swift/npm/Dart/Rust/Kotlin | Don't delete+re-push the tag. Use **Manual Publish Fallback** for the specific SDKs. |
373 | `onde-swift` tag already exists | Tag push skipped (warning emitted) | Delete remote tag first: `git push origin :refs/tags/0.1.3` |
374 | Force-pushed a tag on `onde-swift` | SPM users get stale `Package.resolved` | Never force-push. Delete + re-create instead. Advise consumers to run `swift package resolve` |
375 | XCFramework URL 404 | `swift package resolve` fails on consumer side | Ensure the `onde` GitHub Release was created BEFORE the `onde-swift` tag was pushed (the workflow handles this order automatically) |
@@ -397,6 +409,23 @@ flutter pub publish --force
409
410 Requires `~/.config/dart/pub-credentials.json` from `dart pub login`.
411
412 +### Kotlin (Maven Central)
413 +
414 +```bash
415 +cd sdk/kotlin
416 +
417 +# Dry-run
418 +./gradlew :lib:publishToMavenCentral --dry-run --no-configuration-cache
419 +
420 +# Publish and release
421 +./gradlew :lib:publishAndReleaseToMavenCentral --no-configuration-cache
422 +```
423 +
424 +Requires `~/.gradle/gradle.properties` with `mavenCentralUsername`,
425 +`mavenCentralPassword`, `signingKeyId`, `signingKey`, `signingPassword`.
426 +Also requires the `com.ondeinference` namespace to be verified on
427 +central.sonatype.com.
428 +
429 ### npm (React Native)
430
431 ```bash
@@ -413,7 +442,7 @@ Requires `npm login` or `NPM_TOKEN` env var.
442 |----------|----------|
443 | Tag exists but workflow wasn't on `main` yet | Merge workflow, then publish manually |
444 | One SDK's CI failed, others succeeded | Fix the issue, publish that SDK manually |
416 -| Need to avoid re-triggering Swift XCFramework build | Publish Rust/Dart/npm manually |
445 +| Need to avoid re-triggering Swift XCFramework build | Publish Rust/Dart/npm/Kotlin manually |
446 | Testing a new workflow before first real tag | Use `workflow_dispatch` for dry-run only |
447
448 ---
@@ -443,10 +472,10 @@ git tag 0.1.3
472 git push origin main 0.1.3
473 ```
474
446 -### Dart (pub.dev) / Rust (crates.io)
475 +### Dart (pub.dev) / Rust (crates.io) / Kotlin (Maven Central)
476
448 -**Both pub.dev and crates.io are immutable.** Once a version is published, it
449 -cannot be re-published. You must bump to `0.1.4` instead.
477 +**pub.dev, crates.io, and Maven Central are all immutable.** Once a version is
478 +published, it cannot be re-published. You must bump to `0.1.4` instead.
479
480 - **crates.io:** `cargo yank --version 0.1.3` hides the version from dependency
481 resolution but does not delete it.
@@ -467,7 +496,7 @@ published content was wrong.
496 ## End-to-End Release Timeline
497
498 ```
470 -Developer pushes tag 1.0.0 to onde
499 +Developer merges feature → development → main (--no-ff) then tags
500 │
501 ├─── release-sdk-rust.yml fires ──────────────────────────────────────┐
502 │ ~2 min (dry-run + publish to crates.io) │
@@ -489,21 +518,30 @@ Developer pushes tag 1.0.0 to onde
518 ├─── release-sdk-npm.yml fires ───────────────────────────────────────┤
519 │ ~3 min (validate + publish to npm) │
520 │ │
521 + ├─── release-sdk-kotlin.yml fires ────────────────────────────────────┤
522 + │ ~10 min (Android ABIs + JVM dylib + publish to Maven Central) │
523 + │ │
524 └─── Swift Package Index picks up onde-swift tag ──────────────────────┘
525 ~30 min (SPI polling interval)
526 + klibs.io picks up Maven Central artifact within ~30 days
527 ```
528
496 -All four CI workflows fire **in parallel** from the single tag push. The only
529 +All five CI workflows fire **in parallel** from the single tag push. The only
530 ordering dependency is that `release-sdk-swift` must create the `onde` GitHub
531 Release (with XCFramework assets) **before** pushing the tag to `onde-swift` —
532 this is guaranteed by the step order within the workflow.
533
534 ### Merge train
535
503 -Always merge through the branch train before tagging:
536 +Always merge through the branch train before tagging. Use `--no-ff` to
537 +create explicit merge commits — they serve as bookmarks in the history
538 +showing where each feature landed.
539
505 -```
506 -feature/* → development → main → tag
540 +```bash
541 +git checkout development && git merge feature/my-branch --no-ff --no-edit
542 +git checkout main && git merge development --no-ff --no-edit
543 +git push origin development main
544 +git tag X.Y.Z && git push origin X.Y.Z
545 ```
546
547 Never tag from a feature branch. The tag must point to a commit on `main` that
@@ -511,6 +549,11 @@ contains all workflow files and fixes. If a workflow is added on a feature branc
549 and the tag is pushed before merging, the workflow won't exist at the tagged
550 commit and CI won't fire.
551
552 +**Why `--no-ff`:** Multi-commit feature branches (like the Kotlin SDK with 35
553 +files across 6 commits) look like they were committed directly to `main` with
554 +fast-forward merges. `--no-ff` preserves the branch context. It also allows
555 +reverting an entire feature with a single `git revert -m 1 <merge-commit>`.
556 +
557 ---
558
559 ## Distribution Registry Reference
@@ -521,6 +564,8 @@ commit and CI won't fire.
564 | Swift Package Index | `onde-swift` (org: `ondeinference`) | `import Onde` | `release-sdk-swift.yml` → `onde-swift/release.yml` | Tags are immutable by convention |
565 | pub.dev | `onde_inference` | `import 'package:onde_inference/onde_inference.dart'` | `release-sdk-dart.yml` | Yes (no yank) |
566 | npm | `@ondeinference/react-native` | `import { OndeChatEngine } from "@ondeinference/react-native"` | `release-sdk-npm.yml` | Yes (unpublish within 72h only) |
567 +| Maven Central | `com.ondeinference:onde-inference` | `implementation("com.ondeinference:onde-inference:1.0.0")` | `release-sdk-kotlin.yml` | Yes (cannot re-publish same version) |
568 +| klibs.io | `onde-inference` | Same Maven Central coordinate | Auto-indexed from Maven Central | N/A (discovery only) |
569
570 ---
571
@@ -596,6 +641,7 @@ All four registries are effectively immutable once a version is published:
641 - **crates.io** — `cargo yank` removes from resolver but the tarball stays forever
642 - **pub.dev** — No retraction mechanism at all
643 - **npm** — `npm unpublish` works within 72 hours, but only if no dependents
644 +- **Maven Central** — No retraction mechanism. Once published, it's permanent.
645 - **Swift (onde-swift tags)** — Convention: never force-push tags. Delete + re-create if needed.
646
647 **Bottom line:** get the release right before publishing. Use `--dry-run` locally