1 name: Release SDK Kotlin (Maven Central)
2
3 on:
4 push:
5 tags:
6 - "[0-9]+.[0-9]+.[0-9]+"
7 workflow_dispatch:
8 inputs:
9 publish:
10 description: "Publish 1.1.0 artifacts to Maven Central from the selected ref"
11 type: boolean
12 default: false
13
14 permissions:
15 contents: write
16
17 jobs:
18 # Cheap ubuntu gate: version agreement, crates.io publishability, tag match.
19 # Blocks the expensive job below so a metadata fault never costs runner time.
20 preflight:
21 uses: ./.github/workflows/release-preflight.yml
22 with:
23 tag: ${{ github.ref_type == 'tag' && github.ref_name || '' }}
24
25 publish:
26 needs: preflight
27 name: Build and publish KMP library to Maven Central
28 runs-on: macos-26
29
30 env:
31 GRESIQ_API_KEY_DEV: ${{ secrets.GRESIQ_API_KEY_DEV }}
32 GRESIQ_API_SECRET_DEV: ${{ secrets.GRESIQ_API_SECRET_DEV }}
33 GRESIQ_API_KEY_PRODUCTION: ${{ secrets.GRESIQ_API_KEY_PRODUCTION }}
34 GRESIQ_API_SECRET_PRODUCTION: ${{ secrets.GRESIQ_API_SECRET_PRODUCTION }}
35 HF_TOKEN: ${{ secrets.HF_TOKEN }}
36
37 steps:
38 - name: Checkout
39 uses: actions/checkout@v6
40
41 # ── Java ──────────────────────────────────────────────────────────────────
42 - name: Set up JDK 17
43 uses: actions/setup-java@v5
44 with:
45 java-version: "17"
46 distribution: temurin
47
48 # ── Gradle ───────────────────────────────────────────────────────────────
49 - name: Set up Gradle
50 uses: gradle/actions/setup-gradle@v4
51 with:
52 gradle-version: wrapper
53 build-root-directory: sdk/kotlin
54
55 # ── Rust ──────────────────────────────────────────────────────────────────
56 - name: Install stable Rust toolchain
57 uses: dtolnay/rust-toolchain@stable
58 with:
59 targets: >-
60 aarch64-linux-android,
61 armv7-linux-androideabi,
62 x86_64-linux-android,
63 i686-linux-android,
64 aarch64-apple-darwin
65
66 - name: Cache Rust build artifacts
67 uses: actions/cache@v4
68 with:
69 path: |
70 ~/.cargo/registry
71 ~/.cargo/git
72 target/
73 key: rust-kotlin-sdk-${{ hashFiles('Cargo.toml', 'Cargo.lock', 'src/**/*.rs') }}
74 restore-keys: rust-kotlin-sdk-
75
76 # ── Android NDK ───────────────────────────────────────────────────────────
77 - name: Install Android NDK 29
78 run: |
79 echo "y" | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager \
80 "ndk;29.0.13113456" || true
81 # Fall back to whatever 29.x is already installed
82 NDK_PATH=$(ls -d $ANDROID_SDK_ROOT/ndk/29.* 2>/dev/null | sort -V | tail -1)
83 if [ -z "$NDK_PATH" ]; then
84 echo "Error: No NDK 29.x found"
85 exit 1
86 fi
87 echo "ANDROID_NDK_HOME=$NDK_PATH" >> "$GITHUB_ENV"
88 echo "Using NDK: $NDK_PATH"
89
90 - name: Install cargo-ndk
91 run: cargo install cargo-ndk --locked
92
93 # ── Native builds ─────────────────────────────────────────────────────────
94 - name: Build Android .so files (all 4 ABIs)
95 run: |
96 chmod +x sdk/kotlin/scripts/build-android.sh
97 sdk/kotlin/scripts/build-android.sh
98
99 - name: Build JVM native library (macOS Apple Silicon)
100 run: |
101 chmod +x sdk/kotlin/scripts/build-jvm.sh
102 sdk/kotlin/scripts/build-jvm.sh
103
104 # ── UniFFI bindings ───────────────────────────────────────────────────────
105 - name: Build uniffi-bindgen
106 run: cargo build --manifest-path uniffi-bindgen/Cargo.toml --release
107
108 - name: Generate Kotlin UniFFI bindings
109 run: |
110 chmod +x sdk/kotlin/scripts/generate-bindings.sh
111 sdk/kotlin/scripts/generate-bindings.sh
112
113 # ── Verify ────────────────────────────────────────────────────────────────
114 - name: Verify build inputs
115 run: |
116 echo "=== Android JNI libs ==="
117 find sdk/kotlin/lib/src/androidMain/jniLibs -name "*.so" | sort
118
119 echo "=== JVM native libs ==="
120 find sdk/kotlin/lib/src/jvmMain/resources/native -type f | sort
121
122 echo "=== UniFFI bindings ==="
123 ls -lh sdk/kotlin/lib/src/generated/kotlin/uniffi/onde/
124
125 # ── Version validation ────────────────────────────────────────────────────
126 - name: Validate tag matches Cargo.toml version
127 if: startsWith(github.ref, 'refs/tags/')
128 run: |
129 TAG="${GITHUB_REF#refs/tags/}"
130 CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/version = "//;s/"//')
131 GRADLE_VERSION=$(grep '^VERSION_NAME' sdk/kotlin/gradle.properties | sed 's/VERSION_NAME=//')
132 echo "Tag: $TAG"
133 echo "Cargo.toml: $CARGO_VERSION"
134 echo "gradle.properties: $GRADLE_VERSION"
135 if [ "$TAG" != "$CARGO_VERSION" ]; then
136 echo "::error::Tag '$TAG' does not match Cargo.toml version '$CARGO_VERSION'."
137 exit 1
138 fi
139 if [ "$TAG" != "$GRADLE_VERSION" ]; then
140 echo "::error::Tag '$TAG' does not match gradle.properties VERSION_NAME '$GRADLE_VERSION'."
141 exit 1
142 fi
143
144 # ── Publish ───────────────────────────────────────────────────────────────
145 - name: Dry-run publish (always)
146 working-directory: sdk/kotlin
147 run: ./gradlew :lib:publishToMavenCentral --dry-run --no-configuration-cache
148
149 - name: Publish and release to Maven Central (tag push or manual publish)
150 if: startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && inputs.publish)
151 working-directory: sdk/kotlin
152 env:
153 ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALUSERNAME }}
154 ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALPASSWORD }}
155 ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEYID }}
156 ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEY }}
157 ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD }}
158 run: ./gradlew :lib:publishAndReleaseToMavenCentral --no-configuration-cache
159
160 # ── Artifacts ────────────────────────────────────────────────────────────
161 - name: Collect build outputs
162 run: |
163 mkdir -p dist/kotlin
164 find sdk/kotlin/lib/build/outputs/aar -name "*.aar" -exec cp {} dist/kotlin/ \;
165 find sdk/kotlin/lib/build/libs -name "*.jar" -exec cp {} dist/kotlin/ \;
166
167 - name: Upload build outputs as workflow artifact
168 uses: actions/upload-artifact@v7
169 with:
170 name: onde-inference-kotlin-${{ github.ref_name }}
171 path: dist/kotlin/
172 retention-days: 90
173
174 # ── GitHub Release ────────────────────────────────────────────────────────
175 - name: Create GitHub Release
176 if: startsWith(github.ref, 'refs/tags/')
177 uses: softprops/action-gh-release@v3
178 with:
179 tag_name: ${{ github.ref_name }}
180 name: Kotlin SDK ${{ github.ref_name }}
181 body: |
182 ## Onde Inference Kotlin SDK ${{ github.ref_name }}
183
184 On-device LLM inference for **Android** and **JVM (macOS Apple Silicon)**.
185
186 ### Add to your project
187
188 ```kotlin
189 // build.gradle.kts
190 dependencies {
191 implementation("com.ondeinference:onde-inference:${{ github.ref_name }}")
192 }
193 ```
194
195 ### What's included
196
197 | Target | Backend | Default model |
198 |--------|---------|---------------|
199 | Android (`arm64-v8a`, `armeabi-v7a`, `x86_64`, `x86`) | CPU (Candle) | Qwen 2.5 1.5B (~941 MB) |
200 | JVM (macOS Apple Silicon) | Metal | Qwen 2.5 3B (~1.93 GB) |
201
202 ### Usage
203
204 ```kotlin
205 // Android
206 val onde = OndeInference(applicationContext)
207
208 // JVM (macOS)
209 val onde = OndeInference()
210
211 // Shared API
212 val elapsed = onde.loadDefaultModel(systemPrompt = "You are helpful.")
213 val result = onde.chat("Hello!")
214 onde.stream("Tell me a story.").collect { chunk -> print(chunk.delta) }
215 ```
216
217 See the [README](https://github.com/ondeinference/onde/tree/main/sdk/kotlin/README.md) for full API docs.
218 files: dist/kotlin/*
219 draft: false
220 prerelease: false