From 8e60b7608c3f14224e24fa40b647d59aa5ecc3d5 Mon Sep 17 00:00:00 2001 From: Tom Brow Date: Sun, 18 Oct 2026 18:40:54 -0700 Subject: [PATCH] test(mobile): exercise native signing in linked iOS Runner Signed-off-by: Tom Brow --- .github/workflows/_ci-clients.yml | 7 ++++++ mobile/tool/native_signing_probe.dart | 8 +++++- scripts/test-mobile-linked-signing.sh | 35 +++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 scripts/test-mobile-linked-signing.sh diff --git a/.github/workflows/_ci-clients.yml b/.github/workflows/_ci-clients.yml index 0b23b4143a..dd75ea7ed0 100644 --- a/.github/workflows/_ci-clients.yml +++ b/.github/workflows/_ci-clients.yml @@ -248,11 +248,18 @@ jobs: tail -n 5 "$build_log" "${xcode[@]}" -parallel-testing-enabled NO -only-testing:RunnerTests \ test-without-building + /bin/bash scripts/test-mobile-linked-signing.sh "$device" - name: Build complete unsigned iOS release if: matrix.lane == 'release' run: | /bin/bash scripts/test-mobile-gateway-recipes.sh cd mobile && flutter build ios --release --no-codesign --no-pub + # Flutter does not support Release mode on the simulator. Check the + # actual device Release executable's exported symbols separately. + nm -gU build/ios/iphoneos/Buzz.app/Buzz > /tmp/buzz-release-symbols + for symbol in buzz_signer_create buzz_signer_sign buzz_signer_destroy; do + grep -Eq "[[:space:]]_${symbol}$" /tmp/buzz-release-symbols + done results: name: Results diff --git a/mobile/tool/native_signing_probe.dart b/mobile/tool/native_signing_probe.dart index ec497c9178..0c00c9bc33 100644 --- a/mobile/tool/native_signing_probe.dart +++ b/mobile/tool/native_signing_probe.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:nostr/nostr.dart' as nostr; import 'package:buzz/shared/relay/media_auth.dart'; @@ -7,6 +8,7 @@ import 'package:buzz/shared/crypto/event_signer.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); + if (!Platform.isIOS) throw StateError('This probe requires an iOS Runner'); final results = []; final key = '0' * 63 + '3'; final nsec = nostr.Nip19.encode(prefix: nostr.Nip19Prefix.nsec, data: key); @@ -86,12 +88,16 @@ Future main() async { '32 complete NIP98 headers: ${queryWatch.elapsedMicroseconds} us', ); for (final header in headers) { - nostr.Event.fromJson(utf8.decode(base64Decode(header.split(' ').last))); + final event = nostr.Event.fromJson( + utf8.decode(base64Decode(header.split(' ').last)), + ); + if (!event.isValid()) throw StateError('Invalid query proof'); } results.add('query signatures: PASS'); for (final result in results) { debugPrint('SIGNING_PROBE $result'); } + debugPrint('SIGNING_PROBE COMPLETE'); runApp( MaterialApp( home: Scaffold(body: SafeArea(child: Text(results.join('\n')))), diff --git a/scripts/test-mobile-linked-signing.sh b/scripts/test-mobile-linked-signing.sh new file mode 100644 index 0000000000..f02349161d --- /dev/null +++ b/scripts/test-mobile-linked-signing.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Run the Dart production signer through symbols exported by the actual Runner. +set -euo pipefail +cd "$(dirname "$0")/.." +. ./bin/activate-hermit +device=${1:?Pass a booted simulator UDID} +fixture=$(mktemp -d) +bundle=xyz.block.buzz.signing-probe +trap 'xcrun simctl terminate "$device" "$bundle" >/dev/null 2>&1 || true; rm -rf "$fixture"' EXIT +xcodebuild -workspace mobile/ios/Runner.xcworkspace -scheme Runner \ + -configuration Debug -sdk iphonesimulator -destination "id=$device" \ + -derivedDataPath "${BUZZ_SIGNING_DERIVED_DATA:-$fixture/build}" -jobs 2 \ + BUNDLE_IDENTIFIER="$bundle" CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES \ + FLUTTER_TARGET=tool/native_signing_probe.dart build > "$fixture/build.log" 2>&1 || { + cat "$fixture/build.log" + exit 1 + } +xcrun simctl install "$device" "${BUZZ_SIGNING_DERIVED_DATA:-$fixture/build}/Build/Products/Debug-iphonesimulator/Buzz.app" +launch=$(xcrun simctl launch --terminate-running-process "$device" "$bundle") +probe_pid=${launch##*: } +[[ "$probe_pid" =~ ^[0-9]+$ ]] || { echo "Unexpected launch result: $launch" >&2; exit 1; } +# Flutter's iOS logger writes to the unified log, not redirected stdout. +for ((attempt = 0; attempt < 90; attempt++)); do + xcrun simctl spawn "$device" log show --last 5m --style compact \ + --predicate "processIdentifier == $probe_pid AND eventMessage CONTAINS \"SIGNING_PROBE\"" \ + > "$fixture/probe.log" 2>/dev/null + if grep -q 'SIGNING_PROBE COMPLETE' "$fixture/probe.log"; then + cat "$fixture/probe.log" + exit 0 + fi + sleep 1 +done +cat "$fixture/probe.log" +echo 'Linked iOS signing probe did not complete' >&2 +exit 1