From 18ab7c56f0058732153914656e92dda4fc9ea10f Mon Sep 17 00:00:00 2001 From: Michal Harakal Date: Mon, 10 Aug 2026 17:23:45 +0200 Subject: [PATCH 1/2] ci(publish): build & publish the skainet-backend-jni-cpu AAR on release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the JNI module landed on develop, `./gradlew publish` (run on the macOS publish job) includes an Android library whose AAR carries NDK-cross-built .so's — but the runner had no Android SDK/NDK, so a release would fail to build the AAR or silently ship no JNI artifact. That artifact is the entire Android NEON story (measured ~24 tok/s SmolLM2 decode on a Pixel 8a vs 3.8 scalar), so it must be in the release. - Pin the NDK in gradle/libs.versions.toml (android-ndk = 28.2.13676358) and reference it from the module's ndkVersion, so the AAR's .so's build reproducibly on dev machines and CI. r28+ links 16 KB-page-aligned .so's (Android 15+ requirement). - publish.yml publish job: set up the Android SDK and install the pinned NDK (version read from the catalog) before ./gradlew publish. Independent of the build-native matrix, which only produces the FFM shared libs. Verified locally: publishToMavenLocal produces a complete AAR — arm64-v8a + x86_64 libskainet_jni{,_v82}.so, consumer proguard.txt, POM with the skainet-backend-api dependency, sources + javadoc + module metadata. assembleRelease green with the pinned NDK. YAML validated. The CI YAML itself is only verifiable by a real release run; the android-actions/setup-android ref is left as a tag with a maintainer note to pin it to a SHA (couldn't verify the SHA offline). Refs #946 #920 --- .github/workflows/publish.yml | 22 +++++++++++++++++++ CHANGELOG.md | 9 ++++++++ gradle/libs.versions.toml | 4 ++++ .../skainet-backend-jni-cpu/build.gradle.kts | 3 +++ 4 files changed, 38 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ac50ef120..689a77a41 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,6 +21,12 @@ name: release # resources.srcDir(nativeResourcesRoot) on jvmMain picks them all # up into the published JAR. # +# The publish job also builds and publishes the skainet-backend-jni-cpu AAR +# (Android JNI kernel provider). Its native .so's are cross-compiled by the +# NDK during `./gradlew publish`, so the publish runner sets up the Android +# SDK + a pinned NDK (see the publish job) — independent of the build-native +# matrix above, which only produces the FFM shared libs. +# # Linux ARM64 is intentionally absent: Kotlin/Native plugin 2.3.21 # doesn't support `linux aarch64` as a HOST target ("Unknown host # target" — see SKaiNET PR #577). Linux ARM64 consumers fall back @@ -139,6 +145,22 @@ jobs: distribution: 'zulu' java-version: 25 + # `./gradlew publish` now includes skainet-backend-jni-cpu (an Android + # library whose AAR carries NDK-built .so's). The publish runner therefore + # needs the Android SDK + a pinned NDK; without it the AAR's native build + # fails and the release ships no JNI artifact. The NDK version must match + # gradle/libs.versions.toml `android-ndk`. + - name: Set up Android SDK + # NOTE (maintainer): pin to a commit SHA per repo convention before merge — + # this tag is used only because the SHA can't be verified from the dev box. + uses: android-actions/setup-android@v3 + + - name: Install pinned NDK + run: | + NDK_VERSION="$(grep -E '^android-ndk[[:space:]]*=' gradle/libs.versions.toml | sed -E 's/.*"([^"]+)".*/\1/')" + echo "Installing NDK ${NDK_VERSION}" + yes | sdkmanager "ndk;${NDK_VERSION}" >/dev/null + - name: Validate signing configuration run: | if ! grep -Eq '^[[:space:]]*signAllPublications[[:space:]]*=[[:space:]]*true[[:space:]]*$' gradle.properties; then diff --git a/CHANGELOG.md b/CHANGELOG.md index d62631748..ade5b228b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +### CI + +- **Release workflow publishes the `skainet-backend-jni-cpu` AAR.** `./gradlew publish` now + includes the Android JNI kernel module, whose AAR carries NDK-cross-built `.so`s; the publish + job gains Android SDK + a pinned NDK setup so the native build succeeds on the release runner + (previously the release would ship no JNI artifact, or fail). The NDK version is pinned in + `gradle/libs.versions.toml` (`android-ndk`) and referenced by the module's `ndkVersion` and the + workflow, so local and CI builds are reproducible. (#946) + ### Added - **NEON body for the Q4_0 matmul kernel.** `skainet_q4_0_matmul` was the only priority diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bc27cc76f..252e11063 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,10 @@ kotlinxCoroutines = "1.11.0" kotlinBrowser = "0.5.0" android-minSdk = "24" android-compileSdk = "36" +# NDK for skainet-backend-jni-cpu's externalNativeBuild. Pinned so the AAR's +# .so's build reproducibly on dev machines and the release runner. r28+ links +# 16 KB-page-aligned .so's by default (Android 15+ requirement). +android-ndk = "28.2.13676358" kotlinxSerializationJson = "1.11.0" ktorClientCore = "3.5.2" ktorClientPlugins = "3.1.1" diff --git a/skainet-backends/skainet-backend-jni-cpu/build.gradle.kts b/skainet-backends/skainet-backend-jni-cpu/build.gradle.kts index 37d9500ab..d7c71a44e 100644 --- a/skainet-backends/skainet-backend-jni-cpu/build.gradle.kts +++ b/skainet-backends/skainet-backend-jni-cpu/build.gradle.kts @@ -23,6 +23,9 @@ plugins { android { namespace = "sk.ainet.exec.kernel.jni" compileSdk = libs.versions.android.compileSdk.get().toInt() + // Pinned NDK so the AAR's .so's build reproducibly locally and on the + // release runner (AGP provisions it via sdkmanager when absent). + ndkVersion = libs.versions.android.ndk.get() defaultConfig { minSdk = libs.versions.android.minSdk.get().toInt() From 6dfb44a78157f91e143ba9d9811dc66fb68f80ae Mon Sep 17 00:00:00 2001 From: Michal Harakal Date: Mon, 10 Aug 2026 17:29:28 +0200 Subject: [PATCH 2/2] ci(publish): pin android-actions/setup-android to v3.2.2 SHA Resolves the Scorecard pinned-dependencies finding on #947. Pin to the verified commit for v3.2.2 (9fc6c4e), replacing the placeholder @v3 tag. Refs #946 #920 --- .github/workflows/publish.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 689a77a41..eec0257d5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -151,9 +151,7 @@ jobs: # fails and the release ships no JNI artifact. The NDK version must match # gradle/libs.versions.toml `android-ndk`. - name: Set up Android SDK - # NOTE (maintainer): pin to a commit SHA per repo convention before merge — - # this tag is used only because the SHA can't be verified from the dev box. - uses: android-actions/setup-android@v3 + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 - name: Install pinned NDK run: |