name: Release
on:
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., 0.8.16)'
required: true
type: string
env:
CARGO_TERM_COLOR: always
jobs:
prepare-release:
name: Prepare Release
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
version: ${{ steps.set-version.outputs.version }}
version_tag: ${{ steps.set-version.outputs.version_tag }}
steps:
- name: Checkout sources
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set version outputs
id: set-version
run: |
VERSION="${{ github.event.inputs.version }}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "version_tag=v${VERSION}" >> $GITHUB_OUTPUT
- name: Update Cargo.toml version
run: |
VERSION="${{ github.event.inputs.version }}"
sed -i "s/^version = .*/version = \"${VERSION}\"/" Cargo.toml
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Update Cargo.lock
run: cargo update -p cooklang-import
- name: Commit version bump
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Cargo.toml Cargo.lock
git commit -m "chore: bump version to ${{ github.event.inputs.version }}"
git push origin main
build-ios:
name: Build iOS
needs: prepare-release
runs-on: macos-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4
with:
ref: main
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-ios,aarch64-apple-ios-sim
- name: Build iOS targets
run: |
cargo build --release --target aarch64-apple-ios --features uniffi
cargo build --release --target aarch64-apple-ios-sim --features uniffi
- name: Generate Swift bindings
run: |
mkdir -p target/ios/swift
cargo run --features uniffi-cli --bin uniffi-bindgen -- generate \
--config uniffi.toml \
--library target/aarch64-apple-ios/release/libcooklang_import.a \
--language swift \
--out-dir target/ios/swift
- name: Create XCFramework
run: |
rm -rf target/ios/CooklangImportFFI.xcframework
rm -rf target/ios/ios-device target/ios/ios-simulator
mkdir -p target/ios/ios-device target/ios/ios-simulator
# Copy device library
cp target/aarch64-apple-ios/release/libcooklang_import.a target/ios/ios-device/
# Copy simulator library
cp target/aarch64-apple-ios-sim/release/libcooklang_import.a target/ios/ios-simulator/
# Create headers and module maps (must be in Headers/ directory)
mkdir -p target/ios/ios-device/Headers target/ios/ios-simulator/Headers
cp target/ios/swift/CooklangImportFFI.h target/ios/ios-device/Headers/
cp target/ios/swift/CooklangImportFFI.h target/ios/ios-simulator/Headers/
echo 'module CooklangImportFFI { header "CooklangImportFFI.h" export * }' > target/ios/ios-device/Headers/module.modulemap
echo 'module CooklangImportFFI { header "CooklangImportFFI.h" export * }' > target/ios/ios-simulator/Headers/module.modulemap
# Create XCFramework
xcodebuild -create-xcframework \
-library target/ios/ios-device/libcooklang_import.a \
-headers target/ios/ios-device/Headers \
-library target/ios/ios-simulator/libcooklang_import.a \
-headers target/ios/ios-simulator/Headers \
-output target/ios/CooklangImportFFI.xcframework
- name: Package iOS artifacts
run: |
cd target/ios
zip -r ../../CooklangImportFFI.xcframework.zip CooklangImportFFI.xcframework
mkdir -p CooklangImport/Sources/CooklangImport
cp swift/CooklangImport.swift CooklangImport/Sources/CooklangImport/
zip -r ../../CooklangImport-ios.zip CooklangImport CooklangImportFFI.xcframework swift
- name: Upload iOS artifacts
uses: actions/upload-artifact@v4
with:
name: ios-artifacts
path: |
CooklangImportFFI.xcframework.zip
CooklangImport-ios.zip
target/ios/swift/CooklangImport.swift
build-android:
name: Build Android
needs: prepare-release
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4
with:
ref: main
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
- name: Setup Android NDK
uses: android-actions/setup-android@v3
- name: Install NDK
run: |
sdkmanager --install "ndk;26.1.10909125"
echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/26.1.10909125" >> $GITHUB_ENV
- name: Install cargo-ndk
run: command -v cargo-ndk || cargo install cargo-ndk --locked
- name: Build uniffi-bindgen for host
run: cargo build --features uniffi-cli --bin uniffi-bindgen --release
- name: Build Android targets
run: |
cargo ndk --target aarch64-linux-android --platform 21 build --release --features uniffi
cargo ndk --target armv7-linux-androideabi --platform 21 build --release --features uniffi
cargo ndk --target x86_64-linux-android --platform 21 build --release --features uniffi
- name: Generate Kotlin bindings
run: |
rm -rf target/android/kotlin
mkdir -p target/android/kotlin
./target/release/uniffi-bindgen generate \
--config uniffi.toml \
--library target/aarch64-linux-android/release/libcooklang_import.so \
--language kotlin \
--out-dir target/android/kotlin
- name: Organize JNI libraries
run: |
mkdir -p target/android/jniLibs/{arm64-v8a,armeabi-v7a,x86_64}
cp target/aarch64-linux-android/release/libcooklang_import.so target/android/jniLibs/arm64-v8a/
cp target/armv7-linux-androideabi/release/libcooklang_import.so target/android/jniLibs/armeabi-v7a/
cp target/x86_64-linux-android/release/libcooklang_import.so target/android/jniLibs/x86_64/
- name: Create Android library module
run: |
VERSION="${{ needs.prepare-release.outputs.version }}"
mkdir -p target/android/cooklang-import-android/src/main/kotlin
mkdir -p target/android/cooklang-import-android/src/main/jniLibs
# Copy JNI libs
cp -R target/android/jniLibs/* target/android/cooklang-import-android/src/main/jniLibs/
# Copy Kotlin bindings
find target/android/kotlin -name "*.kt" -exec cp {} target/android/cooklang-import-android/src/main/kotlin/ \;
# Create build.gradle.kts
cat > target/android/cooklang-import-android/build.gradle.kts << 'EOF'
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "org.cooklang.cooklang_import"
compileSdk = 34
defaultConfig {
minSdk = 21
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
sourceSets {
getByName("main") {
jniLibs.srcDirs("src/main/jniLibs")
}
}
}
dependencies {
implementation("net.java.dev.jna:jna:5.14.0@aar")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}
EOF
# Create AndroidManifest.xml
mkdir -p target/android/cooklang-import-android/src/main
cat > target/android/cooklang-import-android/src/main/AndroidManifest.xml << 'EOF'
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
EOF
# Create proguard rules
cat > target/android/cooklang-import-android/proguard-rules.pro << 'EOF'
-keep class org.cooklang.** { *; }
-keep class com.sun.jna.** { *; }
-keepclassmembers class * extends com.sun.jna.** { public *; }
EOF
cat > target/android/cooklang-import-android/consumer-rules.pro << 'EOF'
-keep class org.cooklang.** { *; }
EOF
- name: Package Android artifacts
run: |
cd target/android
zip -r ../../cooklang-import-android.zip cooklang-import-android jniLibs kotlin
- name: Upload Android artifacts
uses: actions/upload-artifact@v4
with:
name: android-artifacts
path: cooklang-import-android.zip
update-package-swift:
name: Update Package.swift
needs: [prepare-release, build-ios]
runs-on: macos-latest
permissions:
contents: write
steps:
- name: Checkout sources
uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.GITHUB_TOKEN }}
- name: Download iOS artifacts
uses: actions/download-artifact@v4
with:
name: ios-artifacts
- name: Compute checksum
id: checksum
run: |
CHECKSUM=$(swift package compute-checksum CooklangImportFFI.xcframework.zip)
echo "checksum=${CHECKSUM}" >> $GITHUB_OUTPUT
echo "Computed checksum: ${CHECKSUM}"
- name: Update Package.swift
run: |
VERSION="${{ needs.prepare-release.outputs.version_tag }}"
CHECKSUM="${{ steps.checksum.outputs.checksum }}"
cat > Package.swift << EOF
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "CooklangImport",
platforms: [
.iOS(.v13),
.macOS(.v10_15)
],
products: [
.library(
name: "CooklangImport",
targets: ["CooklangImport", "CooklangImportFFI"]
),
],
targets: [
.target(
name: "CooklangImport",
dependencies: ["CooklangImportFFI"],
path: "Sources/CooklangImport"
),
.binaryTarget(
name: "CooklangImportFFI",
url: "https://github.com/cooklang/cooklang-import/releases/download/${VERSION}/CooklangImportFFI.xcframework.zip",
checksum: "${CHECKSUM}"
),
]
)
EOF
- name: Copy generated Swift bindings
run: |
mkdir -p Sources/CooklangImport
cp target/ios/swift/CooklangImport.swift Sources/CooklangImport/
- name: Commit Package.swift and Sources
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Package.swift Sources/
git commit -m "Release ${{ needs.prepare-release.outputs.version_tag }}"
git push origin main
create-release:
name: Create Tags and Release
needs: [prepare-release, build-ios, build-android, update-package-swift]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout sources
uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.GITHUB_TOKEN }}
- name: Download iOS artifacts
uses: actions/download-artifact@v4
with:
name: ios-artifacts
- name: Download Android artifacts
uses: actions/download-artifact@v4
with:
name: android-artifacts
- name: Create and push tags
run: |
VERSION="${{ needs.prepare-release.outputs.version }}"
VERSION_TAG="${{ needs.prepare-release.outputs.version_tag }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Create both tags at current commit
git tag "${VERSION_TAG}"
git tag "${VERSION}"
# Push tags
git push origin "${VERSION_TAG}" "${VERSION}"
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.prepare-release.outputs.version_tag }}
name: Release ${{ needs.prepare-release.outputs.version_tag }}
draft: false
prerelease: false
generate_release_notes: true
files: |
CooklangImportFFI.xcframework.zip
CooklangImport-ios.zip
cooklang-import-android.zip
body: |
## Mobile SDK Release ${{ needs.prepare-release.outputs.version_tag }}
### iOS (Swift Package Manager)
Add to your `Package.swift` or Xcode project:
```swift
.package(url: "https://github.com/cooklang/cooklang-import.git", from: "${{ needs.prepare-release.outputs.version_tag }}")
```
**Downloads:**
- `CooklangImportFFI.xcframework.zip` - XCFramework binary (used by Package.swift)
- `CooklangImport-ios.zip` - Full package with Swift sources for manual integration
### Android (GitHub Packages Maven)
Add to your `settings.gradle.kts`:
```kotlin
dependencyResolutionManagement {
repositories {
maven {
url = uri("https://maven.pkg.github.com/cooklang/cooklang-import")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
}
```
Add to your `build.gradle.kts`:
```kotlin
implementation("org.cooklang:cooklang-import:${{ needs.prepare-release.outputs.version_tag }}")
```
Or download `cooklang-import-android.zip` which contains:
- `cooklang-import-android/` - Android library module
- `jniLibs/` - Native libraries for all architectures
- `kotlin/` - Kotlin bindings source files
### Supported Architectures
**iOS:**
- arm64 (devices)
- arm64 (simulator, Apple Silicon)
**Android:**
- arm64-v8a
- armeabi-v7a
- x86_64
See the README for integration instructions.
publish-android:
name: Publish Android to GitHub Packages
needs: [prepare-release, build-android, create-release]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout sources
uses: actions/checkout@v4
with:
ref: main
- name: Download Android artifacts
uses: actions/download-artifact@v4
with:
name: android-artifacts
- name: Extract Android artifacts
run: |
mkdir -p target/android
unzip cooklang-import-android.zip -d target/android/
- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Configure Android library for publishing
run: |
VERSION="${{ needs.prepare-release.outputs.version }}"
ANDROID_DIR="target/android/cooklang-import-android"
# Create settings.gradle.kts with plugin management
cat > "${ANDROID_DIR}/settings.gradle.kts" << 'EOF'
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "cooklang-import-android"
EOF
# Create build.gradle.kts with publishing
cat > "${ANDROID_DIR}/build.gradle.kts" << EOF
plugins {
id("com.android.library") version "8.2.0"
id("org.jetbrains.kotlin.android") version "1.9.22"
id("maven-publish")
}
group = "org.cooklang"
version = "${VERSION}"
android {
namespace = "org.cooklang.cooklang_import"
compileSdk = 34
defaultConfig {
minSdk = 21
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
aarMetadata {
minCompileSdk = 21
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
sourceSets {
getByName("main") {
jniLibs.srcDirs("src/main/jniLibs")
}
}
publishing {
singleVariant("release") {
withSourcesJar()
}
}
}
dependencies {
implementation("net.java.dev.jna:jna:5.14.0@aar")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}
publishing {
publications {
register<MavenPublication>("release") {
groupId = "org.cooklang"
artifactId = "cooklang-import"
version = "${VERSION}"
afterEvaluate {
from(components["release"])
}
pom {
name.set("CooklangImport")
description.set("Android library for importing recipes into Cooklang format")
url.set("https://github.com/cooklang/cooklang-import")
licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
}
}
scm {
connection.set("scm:git:git://github.com/cooklang/cooklang-import.git")
developerConnection.set("scm:git:ssh://github.com/cooklang/cooklang-import.git")
url.set("https://github.com/cooklang/cooklang-import")
}
}
}
}
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/cooklang/cooklang-import")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
}
EOF
# Create gradle.properties
cat > "${ANDROID_DIR}/gradle.properties" << 'GRADLE_EOF'
android.useAndroidX=true
kotlin.code.style=official
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.nonTransitiveRClass=true
GRADLE_EOF
- name: Publish to GitHub Packages
working-directory: target/android/cooklang-import-android
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_ACTOR: ${{ github.actor }}
run: |
gradle wrapper --gradle-version 8.5
./gradlew publish --no-daemon