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 # Read the channel from rust-toolchain.toml so it is pinned in exactly one
57 # place. Without `toolchain:` the action installs plain stable and adds the
58 # Android targets to *that*, while bare `cargo` in this repo resolves to the
59 # pinned channel — a different toolchain with no Android std, which is why
60 # the 1.2.3 release failed on "can't find crate for `core`".
61 - name: Read Rust toolchain
62 run: |
63 rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
64 if [ -z "$rust_toolchain" ]; then
65 echo "::error::Failed to read channel from rust-toolchain.toml"
66 exit 1
67 fi
68 echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
69
70 - name: Install stable Rust toolchain
71 uses: dtolnay/rust-toolchain@stable
72 with:
73 toolchain: ${{ env.RUST_TOOLCHAIN }}
74 targets: >-
75 aarch64-linux-android,
76 armv7-linux-androideabi,
77 x86_64-linux-android,
78 i686-linux-android,
79 aarch64-apple-darwin
80
81 - name: Cache Rust build artifacts
82 uses: actions/cache@v4
83 with:
84 path: |
85 ~/.cargo/registry
86 ~/.cargo/git
87 target/
88 key: rust-kotlin-sdk-${{ hashFiles('Cargo.toml', 'Cargo.lock', 'src/**/*.rs') }}
89 restore-keys: rust-kotlin-sdk-
90
91 # ── Android NDK ───────────────────────────────────────────────────────────
92 - name: Install Android NDK 29
93 run: |
94 echo "y" | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager \
95 "ndk;29.0.13113456" || true
96 # Fall back to whatever 29.x is already installed
97 NDK_PATH=$(ls -d $ANDROID_SDK_ROOT/ndk/29.* 2>/dev/null | sort -V | tail -1)
98 if [ -z "$NDK_PATH" ]; then
99 echo "Error: No NDK 29.x found"
100 exit 1
101 fi
102 echo "ANDROID_NDK_HOME=$NDK_PATH" >> "$GITHUB_ENV"
103 echo "Using NDK: $NDK_PATH"
104
105 - name: Install cargo-ndk
106 run: cargo install cargo-ndk --locked
107
108 # ── Native builds ─────────────────────────────────────────────────────────
109 - name: Build Android .so files (all 4 ABIs)
110 run: |
111 chmod +x sdk/kotlin/scripts/build-android.sh
112 sdk/kotlin/scripts/build-android.sh
113
114 - name: Build JVM native library (macOS Apple Silicon)
115 run: |
116 chmod +x sdk/kotlin/scripts/build-jvm.sh
117 sdk/kotlin/scripts/build-jvm.sh
118
119 # ── UniFFI bindings ───────────────────────────────────────────────────────
120 - name: Build uniffi-bindgen
121 run: cargo build --manifest-path uniffi-bindgen/Cargo.toml --release
122
123 - name: Generate Kotlin UniFFI bindings
124 run: |
125 chmod +x sdk/kotlin/scripts/generate-bindings.sh
126 sdk/kotlin/scripts/generate-bindings.sh
127
128 # ── Verify ────────────────────────────────────────────────────────────────
129 - name: Verify build inputs
130 run: |
131 echo "=== Android JNI libs ==="
132 find sdk/kotlin/lib/src/androidMain/jniLibs -name "*.so" | sort
133
134 echo "=== JVM native libs ==="
135 find sdk/kotlin/lib/src/jvmMain/resources/native -type f | sort
136
137 echo "=== UniFFI bindings ==="
138 ls -lh sdk/kotlin/lib/src/generated/kotlin/uniffi/onde/
139
140 # ── Version validation ────────────────────────────────────────────────────
141 - name: Validate tag matches Cargo.toml version
142 if: startsWith(github.ref, 'refs/tags/')
143 run: |
144 TAG="${GITHUB_REF#refs/tags/}"
145 CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/version = "//;s/"//')
146 GRADLE_VERSION=$(grep '^VERSION_NAME' sdk/kotlin/gradle.properties | sed 's/VERSION_NAME=//')
147 echo "Tag: $TAG"
148 echo "Cargo.toml: $CARGO_VERSION"
149 echo "gradle.properties: $GRADLE_VERSION"
150 if [ "$TAG" != "$CARGO_VERSION" ]; then
151 echo "::error::Tag '$TAG' does not match Cargo.toml version '$CARGO_VERSION'."
152 exit 1
153 fi
154 if [ "$TAG" != "$GRADLE_VERSION" ]; then
155 echo "::error::Tag '$TAG' does not match gradle.properties VERSION_NAME '$GRADLE_VERSION'."
156 exit 1
157 fi
158
159 # ── Publish ───────────────────────────────────────────────────────────────
160 - name: Dry-run publish (always)
161 working-directory: sdk/kotlin
162 run: ./gradlew :lib:publishToMavenCentral --dry-run --no-configuration-cache
163
164 - name: Publish and release to Maven Central (tag push or manual publish)
165 if: startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && inputs.publish)
166 working-directory: sdk/kotlin
167 env:
168 ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALUSERNAME }}
169 ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALPASSWORD }}
170 ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEYID }}
171 ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEY }}
172 ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD }}
173 run: ./gradlew :lib:publishAndReleaseToMavenCentral --no-configuration-cache
174
175 # ── Artifacts ────────────────────────────────────────────────────────────
176 - name: Collect build outputs
177 run: |
178 mkdir -p dist/kotlin
179 find sdk/kotlin/lib/build/outputs/aar -name "*.aar" -exec cp {} dist/kotlin/ \;
180 find sdk/kotlin/lib/build/libs -name "*.jar" -exec cp {} dist/kotlin/ \;
181
182 - name: Upload build outputs as workflow artifact
183 uses: actions/upload-artifact@v7
184 with:
185 name: onde-inference-kotlin-${{ github.ref_name }}
186 path: dist/kotlin/
187 retention-days: 90
188
189 # ── GitHub Release ────────────────────────────────────────────────────────
190 - name: Create GitHub Release
191 if: startsWith(github.ref, 'refs/tags/')
192 uses: softprops/action-gh-release@v3
193 with:
194 tag_name: ${{ github.ref_name }}
195 name: Kotlin SDK ${{ github.ref_name }}
196 body: |
197 ## Onde Inference Kotlin SDK ${{ github.ref_name }}
198
199 On-device LLM inference for **Android** and **JVM (macOS Apple Silicon)**.
200
201 ### Add to your project
202
203 ```kotlin
204 // build.gradle.kts
205 dependencies {
206 implementation("com.ondeinference:onde-inference:${{ github.ref_name }}")
207 }
208 ```
209
210 ### What's included
211
212 | Target | Backend | Default model |
213 |--------|---------|---------------|
214 | Android (`arm64-v8a`, `armeabi-v7a`, `x86_64`, `x86`) | CPU (Candle) | Qwen 2.5 1.5B (~941 MB) |
215 | JVM (macOS Apple Silicon) | Metal | Qwen 2.5 3B (~1.93 GB) |
216
217 ### Usage
218
219 ```kotlin
220 // Android
221 val onde = OndeInference(applicationContext)
222
223 // JVM (macOS)
224 val onde = OndeInference()
225
226 // Shared API
227 val elapsed = onde.loadDefaultModel(systemPrompt = "You are helpful.")
228 val result = onde.chat("Hello!")
229 onde.stream("Tell me a story.").collect { chunk -> print(chunk.delta) }
230 ```
231
232 See the [README](https://github.com/ondeinference/onde/tree/main/sdk/kotlin/README.md) for full API docs.
233 files: dist/kotlin/*
234 draft: false
235 prerelease: false