diff --git a/.github/workflows/mobile-ci.yml b/.github/workflows/mobile-ci.yml new file mode 100644 index 0000000..bbfef14 --- /dev/null +++ b/.github/workflows/mobile-ci.yml @@ -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 diff --git a/.github/workflows/mobile-release.yml b/.github/workflows/mobile-release.yml new file mode 100644 index 0000000..b87a1f6 --- /dev/null +++ b/.github/workflows/mobile-release.yml @@ -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//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 diff --git a/README.md b/README.md index 933076a..65da854 100644 --- a/README.md +++ b/README.md @@ -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 ]` (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 ]`. 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 diff --git a/ffi/README.md b/ffi/README.md index 877f615..bbf2330 100644 --- a/ffi/README.md +++ b/ffi/README.md @@ -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//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 ` to write into a diff --git a/ffi/dart/README.md b/ffi/dart/README.md index 16138c3..17b5bb1 100644 --- a/ffi/dart/README.md +++ b/ffi/dart/README.md @@ -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 ```