Skip to content
Draft
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
137 changes: 137 additions & 0 deletions .github/workflows/maven-central-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
name: Publish to Maven Central

on:
workflow_dispatch:
inputs:
version:
description: "Version to publish (leave empty for release version)"
required: false
type: string
dry_run:
description: "Dry run - publish to local Maven repo only"
required: false
default: true
type: boolean

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
lib_ext: so
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
lib_ext: so
- os: macos-latest
target: x86_64-apple-darwin
lib_ext: dylib
- os: macos-latest
target: aarch64-apple-darwin
lib_ext: dylib
- os: windows-latest
target: x86_64-pc-windows-msvc
lib_ext: dll
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: "21"
distribution: "temurin"

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- name: Build the bindings
run: make kotlin

- name: Upload native library
uses: actions/upload-artifact@v4
with:
name: native-lib-${{ matrix.os }}-${{ matrix.target }}
path: bindings/kotlin/lib/*iota_sdk_ffi*

publish:
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: "21"
distribution: "temurin"

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Download native libraries
uses: actions/download-artifact@v4
with:
path: libs

- name: Prepare libraries
run: |
echo "Listing contents of libs directory:"
ls -la libs/
echo "Listing contents of native-lib-ubuntu-latest-x86_64-unknown-linux-gnu:"
ls -la libs/native-lib-ubuntu-latest-x86_64-unknown-linux-gnu/
echo "Listing contents of bindings/kotlin/lib before copying:"
ls -la bindings/kotlin/lib/
cd bindings/kotlin/lib
echo "Copying libraries..."
cp ../../../libs/native-lib-ubuntu-latest-x86_64-unknown-linux-gnu/libiota_sdk_ffi.so .
cp ../../../libs/native-lib-ubuntu-latest-aarch64-unknown-linux-gnu/libiota_sdk_ffi.so libiota_sdk_ffi_arm64.so
cp ../../../libs/native-lib-macos-latest-x86_64-apple-darwin/libiota_sdk_ffi.dylib .
cp ../../../libs/native-lib-macos-latest-aarch64-apple-darwin/libiota_sdk_ffi.dylib libiota_sdk_ffi_arm64.dylib
cp ../../../libs/native-lib-windows-latest-x86_64-pc-windows-msvc/iota_sdk_ffi.dll .
echo "Contents after copying:"
ls -la

- name: Generate Kotlin bindings
run: |
cd bindings/kotlin
# Generate bindings using the Linux library (arbitrary choice)
cargo run --bin iota_sdk_bindings -- generate --library "lib/libiota_sdk_ffi.so" --language kotlin --out-dir lib --no-format -c uniffi.toml

- name: Set version for release
if: github.event_name == 'release'
run: |
cd bindings/kotlin
sed -i "s/version = .*/version = \"${{ github.event.release.tag_name }}\"/" build.gradle.kts

- name: Set version for manual dispatch
if: github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''
run: |
cd bindings/kotlin
sed -i "s/version = .*/version = \"${{ github.event.inputs.version }}\"/" build.gradle.kts

- name: Publish to Maven Central
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false')
run: |
cd bindings/kotlin
./gradlew publishAndReleaseToMavenCentral --no-daemon --no-parallel --no-configuration-cache
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ORG_GRADLE_PROJECT_BASE64_ENCODED_ASCII_ARMORED_SIGNING_KEY }}

- name: Dry run - Local publish only
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'true')
run: |
cd bindings/kotlin
echo "Dry run: Publishing to local Maven repository only"
./gradlew publishToMavenLocal --no-daemon --no-parallel --no-configuration-cache
env:
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ORG_GRADLE_PROJECT_BASE64_ENCODED_ASCII_ARMORED_SIGNING_KEY }}
26 changes: 16 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ bindings-examples-format: ## Format all bindings examples
define build_binding
cargo build -p iota-sdk-ffi --lib --release; \
case "$$(uname -s)" in \
Darwin) LIB_EXT=".dylib" ;; \
Linux) LIB_EXT=".so" ;; \
MINGW*|MSYS*|CYGWIN*|Windows_NT) LIB_EXT=".dll" ;; \
Darwin) LIB_PREFIX="lib"; LIB_EXT=".dylib" ;; \
Linux) LIB_PREFIX="lib"; LIB_EXT=".so" ;; \
MINGW*|MSYS*|CYGWIN*|Windows_NT) LIB_PREFIX=""; LIB_EXT=".dll" ;; \
*) echo "Unsupported platform"; exit 1 ;; \
esac;
endef
Expand All @@ -116,21 +116,27 @@ endef
go: ## Build Go bindings
@printf "Building Go bindings...\n"
@$(build_binding) \
uniffi-bindgen-go --library target/release/libiota_sdk_ffi$${LIB_EXT} --out-dir bindings/go --no-format || exit $$?
LIB_NAME="$${LIB_PREFIX}iota_sdk_ffi$${LIB_EXT}"; \
uniffi-bindgen-go --library target/release/$${LIB_NAME} --out-dir bindings/go --no-format || exit $$?

.PHONY: kotlin
kotlin: ## Build Kotlin bindings
@printf "Building Kotlin bindings...\n"
@$(build_binding) \
cargo run --bin iota_sdk_bindings -- generate --library "target/release/libiota_sdk_ffi$${LIB_EXT}" --language kotlin --out-dir bindings/kotlin/lib --no-format -c bindings/kotlin/uniffi.toml || exit $$?; \
cp target/release/libiota_sdk_ffi$${LIB_EXT} bindings/kotlin/lib/
printf "Built library with LIB_PREFIX=$${LIB_PREFIX}, LIB_EXT=$${LIB_EXT}\n"; \
LIB_NAME="$${LIB_PREFIX}iota_sdk_ffi$${LIB_EXT}"; \
printf "Checking if library exists: target/release/$${LIB_NAME}\n"; \
test -f "target/release/$${LIB_NAME}" || (echo "Library not found!" && exit 1); \
cargo run --bin iota_sdk_bindings -- generate --library "target/release/$${LIB_NAME}" --language kotlin --out-dir bindings/kotlin/lib --no-format -c bindings/kotlin/uniffi.toml || exit $$?; \
cp target/release/$${LIB_NAME} bindings/kotlin/lib/

.PHONY: python
python: ## Build Python bindings
@printf "Building Python bindings...\n"
@$(build_binding) \
cargo run --bin iota_sdk_bindings -- generate --library "target/release/libiota_sdk_ffi$${LIB_EXT}" --language python --out-dir bindings/python/lib --no-format || exit $$?; \
cp target/release/libiota_sdk_ffi$${LIB_EXT} bindings/python/lib/
LIB_NAME="$${LIB_PREFIX}iota_sdk_ffi$${LIB_EXT}"; \
cargo run --bin iota_sdk_bindings -- generate --library "target/release/$${LIB_NAME}" --language python --out-dir bindings/python/lib --no-format || exit $$?; \
cp target/release/$${LIB_NAME} bindings/python/lib/

.PHONY: go-example
go-example: ## Run a specific Go example. Usage: make go-example example
Expand Down Expand Up @@ -176,13 +182,13 @@ kotlin-examples: ## Run all Kotlin bindings examples
.PHONY: kotlin-examples-format-check
kotlin-examples-format-check: ## Check format of all Kotlin bindings examples
cd bindings/kotlin; \
./gradlew KtfmtCheck || exit $$?; \
./gradlew KtfmtCheck --no-configuration-cache || exit $$?; \
cd -

.PHONY: kotlin-examples-format
kotlin-examples-format: ## Format all Kotlin bindings examples
cd bindings/kotlin; \
./gradlew KtfmtFormat; \
./gradlew KtfmtFormat --no-configuration-cache; \
cd -

.PHONY: python-example
Expand Down
43 changes: 43 additions & 0 deletions bindings/kotlin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,46 @@ make kotlin
```sh
make kotlin-example chain_id
```

## Publishing to Maven Central

### Publishing

For snapshot releases, set the version in `build.gradle.kts` to end with `-SNAPSHOT`.

#### Dry Run Testing

To test the publishing process without actually publishing to Maven Central:

1. Go to the Actions tab in GitHub
2. Select "Publish to Maven Central" workflow
3. Click "Run workflow"
4. Check "Dry run - publish to local Maven repo only"
5. Optionally specify a version
6. Run the workflow

This will build all artifacts, sign them, and publish to your local Maven repository (`~/.m2/repository`) for verification, without uploading to Maven Central.

**Note:** The dry run uses the same GPG signing secrets as real publishing, but skips the Sonatype upload step.

#### Local Testing

You can also test the publishing process locally (signing is optional):

**Complete local testing script:**

```bash
#!/bin/bash
cd bindings/kotlin

# Test publish to Maven Local (no GPG required)
./gradlew clean publishToMavenLocal --info

# Verify the results
echo "Published artifacts:"
find ~/.m2/repository/org/iota -name "*.jar" -o -name "*.pom" | head -5

echo "JAR contents check:"
jar -tf ~/.m2/repository/org/iota/iota-sdk-jvm/1.0-SNAPSHOT/iota-sdk-jvm-1.0-SNAPSHOT.jar | grep -E "(libiota_sdk_ffi|iota_sdk)" | wc -l
echo "files found (should be > 1)"
```
48 changes: 45 additions & 3 deletions bindings/kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import com.ncorti.ktfmt.gradle.tasks.*
import java.util.Base64

plugins {
kotlin("jvm") version "1.9.24"
kotlin("plugin.serialization") version "1.9.24"
id("com.ncorti.ktfmt.gradle") version "0.25.0"
id("com.vanniktech.maven.publish") version "0.30.0"
application
signing
}

group = "org.iota"

version = "1.0-SNAPSHOT"
version = "0.0.1-alpha.1"

repositories { mavenCentral() }

Expand Down Expand Up @@ -85,8 +88,6 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
"-Xno-receiver-assertions",
// Add these flags to help with recursive type issues
"-Xtype-enhancement-improvements-strict-mode=false",
"-Xskip-runtime-version-check",
"-Xlenient-function-type-parameter-checks",
)
allWarningsAsErrors = false
suppressWarnings = true
Expand All @@ -105,3 +106,44 @@ tasks.register("compileWithErrors") {
}
}
}

mavenPublishing {
publishToMavenCentral(com.vanniktech.maven.publish.SonatypeHost.CENTRAL_PORTAL)
signAllPublications()

coordinates("org.iota", "iota-sdk", version.toString())

pom {
name.set("IOTA SDK Kotlin Bindings")
description.set("Kotlin bindings for the IOTA SDK")
url.set("https://github.com/iotaledger/iota-rust-sdk")
licenses {
license {
name.set("Apache-2.0")
url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("iotaledger")
name.set("IOTA Foundation")
email.set("contact@iota.org")
}
}
scm {
connection.set("scm:git:git://github.com/iotaledger/iota-rust-sdk.git")
developerConnection.set("scm:git:ssh://github.com/iotaledger/iota-rust-sdk.git")
url.set("https://github.com/iotaledger/iota-rust-sdk")
}
}
}

signing {
val signingKeyEncoded = providers.environmentVariable("ORG_GRADLE_PROJECT_signingInMemoryKey")
val signingPassword =
providers.environmentVariable("ORG_GRADLE_PROJECT_signingInMemoryKeyPassword")
if (signingKeyEncoded.isPresent && signingPassword.isPresent) {
val signingKey = String(Base64.getDecoder().decode(signingKeyEncoded.get()))
useInMemoryPgpKeys(signingKey, signingPassword.get())
}
}
2 changes: 1 addition & 1 deletion bindings/kotlin/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
5 changes: 1 addition & 4 deletions bindings/kotlin/gradlew
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh

#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac

CLASSPATH="\\\"\\\""


# Determine the Java command to use to start the JVM.
Expand Down Expand Up @@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )

JAVACMD=$( cygpath --unix "$JAVACMD" )

Expand Down Expand Up @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"

Expand Down
3 changes: 1 addition & 2 deletions bindings/kotlin/gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,10 @@ goto fail
:execute
@rem Setup the command line

set CLASSPATH=


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*

:end
@rem End local scope for the variables with windows NT shell
Expand Down
2 changes: 1 addition & 1 deletion bindings/kotlin/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
rootProject.name = "iota-sdk-jvm"
rootProject.name = "iota-sdk"

include(":lib")

Expand Down
Loading