Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,37 @@ jobs:
working-directory: kotlin/
run: gradle bindings:test

# Publishes to ~/.m2 (no credentials, no signing) and checks the artifact set
# Maven Central validates on release: AAR + sources jar + javadoc jar + POM
# with required metadata, plus native libraries bundled for every ABI.
- name: Verify publishing configuration
working-directory: kotlin/
run: |
gradle bindings:publishToMavenLocal
VERSION=$(grep '^version=' gradle.properties | cut -d= -f2 | tr -d '[:space:]')
DIR="$HOME/.m2/repository/com/worldcoin/idkit/$VERSION"
echo "Published files:"
ls -la "$DIR"
for f in "idkit-$VERSION.aar" "idkit-$VERSION-sources.jar" "idkit-$VERSION-javadoc.jar" "idkit-$VERSION.pom"; do
if [ ! -f "$DIR/$f" ]; then
echo "::error::Expected publish artifact missing: $f"
exit 1
fi
done
for tag in "<name>" "<description>" "<url>" "<license>" "<developer>" "<scm>"; do
if ! grep -q "$tag" "$DIR/idkit-$VERSION.pom"; then
echo "::error::POM is missing required element: $tag"
exit 1
fi
done
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
if ! jar tf "$DIR/idkit-$VERSION.aar" | grep -q "^jni/$abi/libidkit.so$"; then
echo "::error::AAR missing native library: jni/$abi/libidkit.so"
exit 1
fi
done
echo "Publishing configuration OK"

- name: Build Kotlin sample app
working-directory: kotlin/Examples/IDKitSampleApp
run: ./gradlew :app:assembleDebug
26 changes: 24 additions & 2 deletions .github/workflows/publish-kotlin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ jobs:
retention-days: 1

publish:
name: Publish to GitHub Packages
name: Publish
runs-on: ubuntu-latest
needs: [prepare, build-host, build-android]
environment: ${{ needs.prepare.outputs.environment }}
Expand Down Expand Up @@ -324,11 +324,33 @@ jobs:
env:
PKG_VERSION: ${{ needs.prepare.outputs.version }}

# Central-first: if Portal validation fails, nothing has been published anywhere
# and the run can be retried. GitHub Packages versions are deletable for
# recovery; Maven Central releases are immutable.
# TODO: switch to publishAndReleaseToMavenCentral after the first release has
# been validated and released manually in the Central Portal UI.
- name: Publish to Maven Central
if: needs.prepare.outputs.environment == 'production'
uses: gradle/gradle-build-action@v3
with:
gradle-version: 8.9
arguments: -p kotlin bindings:publishToMavenCentral
env:
PKG_VERSION: ${{ needs.prepare.outputs.version }}
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_SIGNING_KEY_PASSWORD }}

# The repo-specific task keeps GitHub Packages publishes off Maven Central:
# the plain `publish` task would target the plugin-registered mavenCentral
# repository too. Signing env vars are deliberately absent here so these
# artifacts stay unsigned (dev releases have no signing secrets at all).
- name: Publish to GitHub Packages
uses: gradle/gradle-build-action@v3
with:
gradle-version: 8.9
arguments: -p kotlin bindings:publish
arguments: -p kotlin bindings:publishAllPublicationsToGitHubPackagesRepository
env:
PKG_VERSION: ${{ needs.prepare.outputs.version }}
GITHUB_ACTOR: ${{ github.actor }}
Expand Down
1 change: 1 addition & 0 deletions kotlin/Examples/IDKitSampleApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
plugins {
id("com.android.application") version "8.7.3" apply false
kotlin("android") version "1.9.24" apply false
id("com.vanniktech.maven.publish") version "0.34.0" apply false
}
31 changes: 23 additions & 8 deletions kotlin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,30 @@ Kotlin SDK for World ID verification, backed by the Rust core via UniFFI.

## Installation

The Kotlin package is published to GitHub Packages as `com.worldcoin:idkit`.
Releases are published to Maven Central as `com.worldcoin:idkit`.

GitHub Packages requires authentication for Maven downloads, even for public packages.
```kotlin
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
```

Then add the dependency:

```kotlin
implementation("com.worldcoin:idkit:<version>")
```

> Note: `com.worldcoin:idkit-kotlin` (3.1.0 and earlier) is the superseded predecessor
> from the archived [idkit-kotlin](https://github.com/worldcoin/idkit-kotlin) repo.
> Use `com.worldcoin:idkit` going forward.

### Dev builds

Development builds (versions like `X.Y.Z-dev.<sha>`) are published only to GitHub
Packages, which requires authentication for Maven downloads even on public packages.
Create a token with `read:packages` and expose it through environment variables.

```kotlin
Expand All @@ -24,12 +45,6 @@ dependencyResolutionManagement {
}
```

Then add the dependency:

```kotlin
implementation("com.worldcoin:idkit:<version>")
```

## Local setup

From repo root:
Expand Down
134 changes: 90 additions & 44 deletions kotlin/bindings/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import com.vanniktech.maven.publish.AndroidSingleVariantLibrary
import org.gradle.api.publish.maven.tasks.PublishToMavenLocal
import org.gradle.api.publish.maven.tasks.PublishToMavenRepository

plugins {
id("com.android.library")
kotlin("android")
`maven-publish`
id("com.vanniktech.maven.publish")
}

group = "com.worldcoin"
Expand All @@ -28,12 +33,6 @@ android {
jvmTarget = "17"
}

publishing {
singleVariant("release") {
withSourcesJar()
}
}

testOptions {
unitTests.all { test ->
val rustLibDir = project.projectDir.resolve("../../target/release").canonicalPath
Expand All @@ -53,47 +52,94 @@ dependencies {
testImplementation("net.java.dev.jna:jna:5.14.0")
}

afterEvaluate {
publishing {
publications {
create<MavenPublication>("maven") {
from(components["release"])
groupId = "com.worldcoin"
artifactId = "idkit"
version = project.version.toString()
pom {
name.set("IDKit Kotlin")
description.set("Kotlin bindings for IDKit backed by the Rust core")
url.set("https://github.com/worldcoin/idkit")
licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
}
}
developers {
developer {
id.set("worldcoin")
name.set("Worldcoin")
}
}
scm {
connection.set("scm:git:https://github.com/worldcoin/idkit.git")
developerConnection.set("scm:git:ssh://git@github.com/worldcoin/idkit.git")
url.set("https://github.com/worldcoin/idkit")
}
}
mavenPublishing {
// Registers a repository named "mavenCentral", so the plain `publish` task now
// targets Central too. For GitHub-Packages-only publishing use
// `publishAllPublicationsToGitHubPackagesRepository` (what CI does for dev releases).
publishToMavenCentral()

// Sign only when CI supplies a key (the Maven Central publish step). Dev releases
// to GitHub Packages run without signing secrets and must not require them.
// Blank counts as absent: a missing GitHub secret renders as an empty env var.
if (providers.gradleProperty("signingInMemoryKey").getOrElse("").isNotBlank()) {
signAllPublications()
}

coordinates("com.worldcoin", "idkit", version.toString())

configure(
AndroidSingleVariantLibrary(
variant = "release",
sourcesJar = true,
// Maven Central requires a javadoc jar; AGP generates a valid (near-empty) one
publishJavadocJar = true,
)
)

pom {
name.set("IDKit Kotlin")
description.set("Kotlin bindings for IDKit backed by the Rust core")
url.set("https://github.com/worldcoin/idkit")
licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
}
}
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/worldcoin/idkit")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
developers {
developer {
id.set("worldcoin")
name.set("Worldcoin")
}
}
scm {
connection.set("scm:git:https://github.com/worldcoin/idkit.git")
developerConnection.set("scm:git:ssh://git@github.com/worldcoin/idkit.git")
url.set("https://github.com/worldcoin/idkit")
}
}
}

publishing {
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/worldcoin/idkit")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
}

val requiredNativeAbis = listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64")

val verifyKotlinNativeLibraries by tasks.registering {
group = "verification"
description = "Verifies that publishing includes native IDKit libraries for every Android ABI."

doLast {
val missing = requiredNativeAbis
.map { abi -> abi to layout.projectDirectory.file("src/main/jniLibs/$abi/libidkit.so").asFile }
.filter { (_, library) -> !library.isFile || library.length() == 0L }

if (missing.isNotEmpty()) {
val details = missing.joinToString(separator = "\n") { (abi, library) ->
"- $abi: ${library.relativeTo(projectDir)}"
}
throw GradleException(
"Missing native libraries required for publishing:\n$details\n" +
"Run `bash scripts/build-kotlin.sh` from the repository root before publishing.",
)
}
}
}

tasks.withType<PublishToMavenRepository>().configureEach {
dependsOn(verifyKotlinNativeLibraries)
}

tasks.withType<PublishToMavenLocal>().configureEach {
dependsOn(verifyKotlinNativeLibraries)
}
3 changes: 3 additions & 0 deletions kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
plugins {
id("com.android.library") version "8.7.3" apply false
kotlin("android") version "1.9.24" apply false
// 0.35.0+ requires Gradle >= 8.13 (0.36.0: Gradle 9 / AGP 8.13 / Kotlin 2.2);
// do not bump past 0.34.0 without upgrading the toolchain
id("com.vanniktech.maven.publish") version "0.34.0" apply false
}
Loading
Loading