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