Skip to content
Merged
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
71 changes: 71 additions & 0 deletions .github/workflows/mobile-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Build-check for the mobile FFI artifacts — proves the iOS xcframework and the
# Android jniLibs still cross-compile, and the FFI host round-trip still passes.
# Non-publishing: it never cuts a release (that's mobile-release.yml, on a tag).
#
# Path-filtered to the crates that actually change the binary, so unrelated edits
# (docs, the wasm SDK, node/cli) don't spin up Rust cross-compiles.
name: mobile-ci

on:
pull_request:
paths:
- 'core/**'
- 'ffi/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- '.github/workflows/mobile-ci.yml'
push:
branches: [main]
paths:
- 'core/**'
- 'ffi/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- '.github/workflows/mobile-ci.yml'

# rust-toolchain.toml pins the channel (1.83.0); the action just installs it + adds
# the cross targets. Builds run --locked so CI uses the audited Cargo.lock set.
jobs:
host-test:
name: FFI host round-trip
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.83.0
- uses: Swatinem/rust-cache@v2
- name: cargo test -p warden-ffi
run: cargo test --locked -p warden-ffi

build-ios:
name: iOS xcframework (build check)
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.83.0
with:
targets: aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios
- uses: Swatinem/rust-cache@v2
- name: build-mobile.sh ios
run: ffi/build-mobile.sh ios

build-android:
name: Android jniLibs (build check)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.83.0
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android,i686-linux-android
- uses: Swatinem/rust-cache@v2
- name: Install cargo-ndk
# Pinned: cargo-ndk 4.x requires rustc 1.86, but the toolchain is pinned at
# 1.83 (rust-toolchain.toml). 3.5.4 is the last line that builds on 1.83.
# Pinning the tool also keeps the cross-compile reproducible.
run: cargo install cargo-ndk@3.5.4 --locked
- name: build-mobile.sh android
# ubuntu-latest ships an NDK; cargo-ndk reads ANDROID_NDK_HOME. The runner exposes
# the path as the shell var $ANDROID_NDK_LATEST_HOME (NOT the ${{ env }} expression
# context), so set it inline — pins the build to a known NDK for reproducibility.
run: ANDROID_NDK_HOME="$ANDROID_NDK_LATEST_HOME" ffi/build-mobile.sh android
93 changes: 93 additions & 0 deletions .github/workflows/mobile-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Publish prebuilt mobile FFI binaries as GitHub Release assets.
#
# Trigger: a version tag push (`v*`). The binary version is thereby kept in lockstep
# with the `warden_ffi` pub.dev version — cut both from the same tag (e.g. v0.1.0-dev.1).
# Not per-merge: most main merges don't change the binary, and auto-tagging would
# decouple the artifact from the published Dart binding version.
#
# workflow_dispatch runs the builds without releasing (the release job is tag-gated),
# so you can smoke-test artifact production on a branch before tagging.
#
# Produces, attached to the Release for the tag:
# WardenFfi.xcframework.zip (iOS device + simulator)
# warden-ffi-android-jniLibs.zip (Android: jniLibs/<abi>/libwarden_ffi.so, 4 ABIs)
# SHA256SUMS
name: mobile-release

on:
push:
tags: ['v*']
workflow_dispatch:

permissions:
contents: write # create/update the Release and upload assets

jobs:
build-ios:
name: Build iOS xcframework
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.83.0
with:
targets: aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios
- uses: Swatinem/rust-cache@v2
- name: Cross-compile (release, --locked)
run: ffi/build-mobile.sh ios
- name: Zip xcframework
# ditto preserves the framework bundle layout/symlinks better than `zip`.
run: ditto -c -k --keepParent dist/mobile/ios/WardenFfi.xcframework WardenFfi.xcframework.zip
- uses: actions/upload-artifact@v4
with:
name: ios
path: WardenFfi.xcframework.zip
if-no-files-found: error

build-android:
name: Build Android jniLibs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.83.0
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android,i686-linux-android
- uses: Swatinem/rust-cache@v2
- name: Install cargo-ndk
# Pinned: cargo-ndk 4.x requires rustc 1.86, but the toolchain is pinned at
# 1.83 (rust-toolchain.toml). 3.5.4 is the last line that builds on 1.83.
# Pinning the tool also keeps the cross-compile reproducible.
run: cargo install cargo-ndk@3.5.4 --locked
- name: Cross-compile (release, --locked)
# The runner exposes the NDK path as the shell var $ANDROID_NDK_LATEST_HOME (NOT the
# ${{ env }} expression context), so set ANDROID_NDK_HOME inline — pins the build to a
# known NDK for reproducibility.
run: ANDROID_NDK_HOME="$ANDROID_NDK_LATEST_HOME" ffi/build-mobile.sh android
- name: Zip jniLibs
working-directory: dist/mobile/android/app/src/main
run: zip -r "$GITHUB_WORKSPACE/warden-ffi-android-jniLibs.zip" jniLibs
- uses: actions/upload-artifact@v4
with:
name: android
path: warden-ffi-android-jniLibs.zip
if-no-files-found: error

release:
name: Publish Release assets
needs: [build-ios, build-android]
runs-on: ubuntu-latest
# Only publish on a real tag push; workflow_dispatch stops after the builds above.
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Checksums
run: sha256sum WardenFfi.xcframework.zip warden-ffi-android-jniLibs.zip > SHA256SUMS
- name: Upload to Release
uses: softprops/action-gh-release@v2
with:
files: |
WardenFfi.xcframework.zip
warden-ffi-android-jniLibs.zip
SHA256SUMS
fail_on_unmatched_files: true
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ docker compose up --build

A consuming app embeds one of two bindings over the same Rust core (Maktub's **Veil** layer is one such consumer):

- **Flutter / Dart** — the `warden_ffi` binding is published on **[pub.dev](https://pub.dev/packages/warden_ffi)**. The native library it wraps is built locally by `ffi/build-mobile.sh [ios|android|all] [--out <dir>]` (iOS `xcframework` + Android `jniLibs`; output git-ignored, `--out` writes straight into a consumer's tree — no monorepo-path assumption).
- **Flutter / Dart** — the `warden_ffi` binding is published on **[pub.dev](https://pub.dev/packages/warden_ffi)**. The native library it wraps is published as **GitHub Release assets** (`WardenFfi.xcframework.zip` + `warden-ffi-android-jniLibs.zip`, built by the `mobile-release` workflow on each `v*` tag), so consumers download a prebuilt binary instead of needing a Rust cross-compile toolchain. Building from source is still available for warden developers via `ffi/build-mobile.sh [ios|android|all] [--out <dir>]`. See [`ffi/README.md`](ffi/README.md) for both paths. _(A turn-key `warden_ffi_flutter` plugin that pulls the release binary automatically is tracked in [#4](https://github.com/bytesbrains/warden/issues/4).)_
- **TypeScript / JavaScript** — build the wasm bindings with `wasm-pack build` in `wasm/`. (Today these are consumed bundled inside a host SDK rather than published as a standalone npm package.)

## Standalone by design
Expand Down
19 changes: 18 additions & 1 deletion ffi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,24 @@ The release profile keeps `panic = "unwind"` (see `warden/Cargo.toml`): the boun
`catch_unwind` guard only works while panics unwind. **Never build the mobile libs with
`panic = "abort"`** — a panic would kill the host app instead of returning `{"ok":false,…}`.

### Mobile (cross-compile)
### Mobile (prebuilt — recommended for consumers)

Each `v*` tag publishes the cross-compiled binaries as **GitHub Release assets** via the
`mobile-release` workflow, so a consuming app does **not** need a Rust/iOS/Android cross toolchain
— download and unpack the matching version:

| asset | unpack into the consuming app |
|---|---|
| `WardenFfi.xcframework.zip` | `ios/WardenFfi.xcframework` (then the one-time Xcode wiring below) |
| `warden-ffi-android-jniLibs.zip` | `android/app/src/main/` (yields `jniLibs/<abi>/libwarden_ffi.so`) |
| `SHA256SUMS` | verify the two zips before unpacking |

Pick the release whose version matches the `warden_ffi` pub.dev version you depend on — both are cut
from the same tag, so they're always compatible. A consuming-app redeploy is irrelevant (the gate
crypto bakes in no on-chain addresses). _(A `warden_ffi_flutter` plugin that does this download
automatically at build time is tracked in [#4](https://github.com/bytesbrains/warden/issues/4).)_

### Mobile (build from source — for warden developers)

One script builds both platforms. Artifacts default to `dist/mobile` inside this repo (git-ignored
— regenerate from source; never commit a prebuilt binary). Pass `--out <dir>` to write into a
Expand Down
8 changes: 6 additions & 2 deletions ffi/dart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,17 @@ boundary catches panics and returns a structured error).
## Bring your own native library

This package is the **binding only** — it does not ship a native binary (they're large and
platform-specific). Build it from the [`warden`](https://github.com/bytesbrains/warden) repo:
platform-specific). For **mobile**, download the prebuilt binary for your version from the
[warden Releases](https://github.com/bytesbrains/warden/releases) (`WardenFfi.xcframework.zip` +
`warden-ffi-android-jniLibs.zip`) — no Rust toolchain needed; pick the release matching this
package's version. For **desktop/tests**, or to build mobile from source, build from the
[`warden`](https://github.com/bytesbrains/warden) repo:

```sh
# Desktop / tests — a host dylib:
cargo build -p warden-ffi # → target/debug/libwarden_ffi.{dylib,so}

# Mobile — iOS xcframework + Android jniLibs, written into your app tree:
# Mobile from source — iOS xcframework + Android jniLibs, written into your app tree:
ffi/build-mobile.sh all --out /path/to/your-app/mobile
```

Expand Down
Loading