Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
214 commits
Select commit Hold shift + click to select a range
0da1c31
bcrypt, cffi, argon2, freetype, pillow are working
FeodorFitsner Sep 10, 2024
4626a07
Try Python 3.12 with beeware layout
FeodorFitsner Sep 11, 2024
7dcb8f7
Add Python framework to cargo linker args
FeodorFitsner Sep 11, 2024
4d43a16
All packages can be built for iOS, except "blis"
FeodorFitsner Sep 11, 2024
51b07e7
Explicit _PYTHON_SYSCONFIGDATA_NAME, cleanup
FeodorFitsner Sep 11, 2024
0180453
Blis 1.0.0, numpy 2.1.1
FeodorFitsner Sep 12, 2024
ba11885
websockets 13.0.1
FeodorFitsner Sep 12, 2024
6157eaf
cryptography 43.0.1
FeodorFitsner Sep 12, 2024
33f00c6
pydantic-core 2.23.3
FeodorFitsner Sep 12, 2024
864952b
contourpy 1.3.0, matplotlib 3.9.2, yarl 1.11.1
FeodorFitsner Sep 12, 2024
2346280
Try building iOS packages
FeodorFitsner Sep 13, 2024
d9f671c
Build a few iOS packages only
FeodorFitsner Sep 13, 2024
b99f03e
Fix archive path
FeodorFitsner Sep 13, 2024
6fcc4ae
Build more iOS packages
FeodorFitsner Sep 13, 2024
554869c
Fix bcrypt build
FeodorFitsner Sep 13, 2024
4406cdb
Fix numpy build
FeodorFitsner Sep 13, 2024
dba4122
kiwisolver 1.4.7
FeodorFitsner Sep 13, 2024
584e289
Meta cleanup, build all iOS packages
FeodorFitsner Sep 13, 2024
b9e6b66
Fix pillow recipe
FeodorFitsner Sep 13, 2024
5a980d8
Python3.12 with Android (#7)
FeodorFitsner Sep 16, 2024
4d1f80b
Re-build iOS and Android on Python 3.12.6
FeodorFitsner Sep 16, 2024
ae123ff
Try publishing something simple
FeodorFitsner Sep 25, 2024
f961e88
Fix install archives
FeodorFitsner Sep 25, 2024
680cc47
Re-build/re-publish all packages to a new feed
FeodorFitsner Sep 25, 2024
7c9cbbf
Use flet-build repo
FeodorFitsner Sep 27, 2024
aada3c0
build Android: contourpy
FeodorFitsner Sep 27, 2024
d6e03ad
Build MarkupSafe
FeodorFitsner Oct 7, 2024
6d25e73
Android fixes and new packages (#9)
FeodorFitsner Oct 29, 2024
47cfc11
Re-build all packages for iOS and Android
FeodorFitsner Oct 29, 2024
4e5338a
Add flet-libcpp-shared to contourpy deps
FeodorFitsner Nov 5, 2024
c5b4c32
Build contourpy for iOS
FeodorFitsner Nov 5, 2024
fe4ede3
Re-build contourpy for iOS
FeodorFitsner Nov 5, 2024
b7089a5
Re-build contourpy for iOS, again
FeodorFitsner Nov 5, 2024
4f3dc1a
contourpy: do not refer flet-libcpp-shared for iOS
FeodorFitsner Nov 5, 2024
a83cd39
Do not build flet-libcpp-shared for iOS
FeodorFitsner Nov 5, 2024
4da9f09
Fix libjpeg. Re-build pillow and matplotlib
FeodorFitsner Nov 6, 2024
c21f8ad
matplotlib requires numpy
FeodorFitsner Nov 6, 2024
6a4c0f3
Lxml (#11)
FeodorFitsner Nov 11, 2024
fe8c710
Grpcio for iOS and Android (#12)
FeodorFitsner Nov 13, 2024
73ae9f6
google-crc32c for iOS and Android (#13)
FeodorFitsner Nov 25, 2024
96c97a5
protobuf for iOS and Android (#14)
FeodorFitsner Nov 25, 2024
36837f5
shapely, fiona, pyogrio, GDAL, geopandas (#15)
FeodorFitsner Nov 28, 2024
c062d22
pendulum (#16)
FeodorFitsner Nov 28, 2024
5619a2f
pycryptodome, pycryptodomex (#17)
FeodorFitsner Nov 28, 2024
594bc1b
msgspec, msgpack for iOS and Android (#18)
FeodorFitsner Nov 28, 2024
377f925
PyYAML for iOS and Android (#19)
FeodorFitsner Nov 28, 2024
ada198d
re-build shapely
FeodorFitsner Nov 28, 2024
d2b9227
Re-build flet-libgeos with libgeos.so
FeodorFitsner Nov 28, 2024
ac4c2db
Fix shapely for iOS. Re-publish. (#20)
FeodorFitsner Nov 29, 2024
93b4e15
Jq for iOS and Android (#21)
FeodorFitsner Dec 19, 2024
3a22935
`pyjnius` and `pyobjus` (#22)
FeodorFitsner Dec 28, 2024
7065352
regex for iOS and Android (#23)
FeodorFitsner Dec 28, 2024
daeaa40
zstandard for iOS and Android (#24)
FeodorFitsner Dec 28, 2024
af8b4c6
ruamel.yaml.clib for iOS and Android (#25)
FeodorFitsner Dec 28, 2024
3dfdca1
pyxirr for iOS and Android (#26)
FeodorFitsner Dec 29, 2024
3fa3887
Pycryptodome fix (#27)
FeodorFitsner Dec 31, 2024
08b2521
libsodium, libopaque (#28)
FeodorFitsner Jan 5, 2025
a40e869
pynacl for iOS and Android (#29)
FeodorFitsner Jan 6, 2025
ee3fe3d
sqlalchemy for iOS and Android (#30)
FeodorFitsner Jan 6, 2025
9716178
pysodium, opaque (#31)
FeodorFitsner Jan 17, 2025
1a0dad0
Numpy and other Meson-built packages fix for iOS (#32)
FeodorFitsner Feb 17, 2025
77a2a54
greenlet:3.1.1 for iOS and Android (#33)
FeodorFitsner Feb 17, 2025
54cc83a
pandas 2.0.3 for iOS and Android (#34)
FeodorFitsner Feb 17, 2025
fcefe6d
pydantic-core 2.29.0
FeodorFitsner Feb 17, 2025
e619c87
Pillow 11.1.0 (#35)
FeodorFitsner Feb 18, 2025
1435c0d
tiktoken:0.9.0 for iOS and Android (#36)
FeodorFitsner Feb 20, 2025
96ed169
Tokenizers for iOS and Android (#37)
FeodorFitsner Feb 20, 2025
3c92c34
zope.interface 7.2 for iOS and Android (#38)
FeodorFitsner Feb 23, 2025
1640a39
bitarray 3.6.1
FeodorFitsner Aug 19, 2025
2b16b20
Add apt update step to AppVeyor install
FeodorFitsner Aug 19, 2025
562c2d4
pydantic-core 2.33.2 (#41)
FeodorFitsner Feb 9, 2026
3eec84c
jiter, rpds-py for iOS and Android (#42)
FeodorFitsner Feb 9, 2026
85556bf
Merge branch 'python3.12' of https://github.com/flet-dev/mobile-forge…
FeodorFitsner Feb 9, 2026
de20798
pymongo for iOS and Android (#43)
FeodorFitsner Feb 9, 2026
86c5b30
Add Mobile Forge CI workflow
FeodorFitsner Feb 10, 2026
0c807d3
Export MOBILE_FORGE support path env vars
FeodorFitsner Feb 10, 2026
fdbfa32
Support platform-config sysconfig and iOS handling
FeodorFitsner Feb 11, 2026
9d72351
Bump Python version in mobile CI to 3.12.12
FeodorFitsner Feb 11, 2026
8930fdf
Use PYTHON_SHORT_VERSION for setup-python
FeodorFitsner Feb 11, 2026
97c0803
Update python-build-standalone URL to 20260203
FeodorFitsner Feb 11, 2026
4cf1c1f
GHA for Python 3.12 (#46)
FeodorFitsner Mar 29, 2026
cf7a8e9
Add 16KB ELF alignment check for Android .so files in fix_wheel
FeodorFitsner Mar 29, 2026
c437240
Skip 16KB alignment check for 32-bit ELFs
FeodorFitsner Mar 29, 2026
0476971
Update libxml2 source URL to GNOME HTTPS
FeodorFitsner Mar 29, 2026
e922af1
Fix flet-libcrc32c builds
FeodorFitsner Mar 29, 2026
49313c6
Use HTTPS GNOME source for libxslt
FeodorFitsner Mar 29, 2026
946ed83
Use manual workflow_dispatch trigger
FeodorFitsner Mar 29, 2026
f1e8d8c
Disable pkg-config in flet-libcurl build
FeodorFitsner Mar 29, 2026
d88482c
Remove Mobile Forge CI workflow
FeodorFitsner Mar 29, 2026
ae7c6f7
Disable Python bindings for Android build
FeodorFitsner Mar 29, 2026
63e3f5b
Disable pkg-config via environment variable
FeodorFitsner Mar 29, 2026
f601dc9
Skip sys.prefix dirs when cross-compiling
FeodorFitsner Mar 29, 2026
3feee10
Skip sys.prefix paths when cross-compiling
FeodorFitsner Mar 29, 2026
1a9ea90
Strip host /usr/ include/lib paths from Pillow cross-compilation
FeodorFitsner Mar 29, 2026
83d75c8
Relax host dependency pins to minimum versions
FeodorFitsner Mar 30, 2026
6c5b8fb
Pin Cython <3.1 in pyjnius build requirements
FeodorFitsner Mar 30, 2026
9c10612
Move Android NDK env setup before templating
FeodorFitsner Mar 30, 2026
dadd962
Fix grpcio OpenSSL include path for Android cross-compilation
FeodorFitsner Mar 30, 2026
d1d358e
Fix grpcio OpenSSL discovery for Android via OPENSSL_ROOT_DIR
FeodorFitsner Mar 31, 2026
16a5fb2
Fix grpcio patch: restore ZLIB_INCLUDE exclusion for Android
FeodorFitsner Mar 31, 2026
42284b1
Add 16KB page alignment to opencv-python CMake build
FeodorFitsner Mar 31, 2026
b54a4ea
Add CMAKE_MODULE_LINKER_FLAGS for opencv-python 16KB alignment
FeodorFitsner Mar 31, 2026
62c8f33
Fix host dependency version handling in wheel METADATA
FeodorFitsner Apr 1, 2026
cbcb145
Bump libxml2/libxslt/lxml; add recipe-bump skill (#48)
FeodorFitsner May 5, 2026
826fb19
Coolprop (#49)
FeodorFitsner May 26, 2026
d8bce1d
rasterio 1.5.0 (#50)
FeodorFitsner May 27, 2026
10b57d7
Add libcpp-shared host dependency for rasterio
FeodorFitsner May 27, 2026
07f6f1b
initial commit
ndonkoHenri May 28, 2026
e5f6310
migrate to using uv
ndonkoHenri May 28, 2026
86d35ff
add Rust setup and target configuration for mobile builds
ndonkoHenri May 28, 2026
0e6dfc2
enhance CI workflow to detect changed recipes and add publish option
ndonkoHenri May 28, 2026
885598e
primp 1.3.1 (#51)
FeodorFitsner May 28, 2026
7c5331c
update build-wheels workflow to remove additional support-tree depend…
ndonkoHenri May 29, 2026
059226c
ujson 5.12.1 (#52)
ndonkoHenri May 29, 2026
7dd9bf7
Improve CI workflow (#53)
ndonkoHenri May 30, 2026
04a2116
initial commit (#56)
ndonkoHenri May 30, 2026
0074a91
recipe: selectolax 0.4.10 (#55)
ndonkoHenri May 30, 2026
711ac73
recipe: biopython 1.87 (#54)
ndonkoHenri May 30, 2026
013ba3b
add recipe-tester app skeleton for testing recipes on mobile CI
ndonkoHenri May 30, 2026
f188b69
add wait_for_console.sh — CI helper to poll device console.log
ndonkoHenri May 30, 2026
81635ef
extend build-wheels.yml with Android mobile test lane (Phase 1)
ndonkoHenri May 30, 2026
3f32ca1
also watch build-wheels.yml itself for shared-runner change detection
ndonkoHenri May 30, 2026
d55c0a1
pass --yes to flet build apk in the test lane
ndonkoHenri May 30, 2026
932b897
make Android test script dash-compatible (action uses /usr/bin/sh)
ndonkoHenri May 30, 2026
b112759
move emulator-step logic into .ci/run_android_test.sh
ndonkoHenri May 30, 2026
3b0b0a0
bump AVD api-level 24 → 28 (Flet app shell needs ImageDecoder API)
ndonkoHenri May 30, 2026
712a6cd
fix bcrypt test signature — drop leftover unittest `self` arg
ndonkoHenri May 30, 2026
5dde361
numpy: declare Android flet-libcpp-shared host dep
ndonkoHenri May 30, 2026
870c4f6
pillow: rewrite test_pillow.py for current Pillow API
ndonkoHenri May 30, 2026
b088037
improvements
ndonkoHenri May 30, 2026
138adf1
copy wheels to dist-test/ with build tag 9999 for the test lane
ndonkoHenri May 30, 2026
a1bc446
expand smoke set to 6 — add cryptography + bitarray
ndonkoHenri May 31, 2026
0243e59
add Phase 3 iOS test lane (non-blocking, macos-26)
ndonkoHenri May 31, 2026
1ac87d1
free disk space on Ubuntu runners before build (Gradle ENOSPC fix)
ndonkoHenri May 31, 2026
96439aa
fix cryptography on Android: symlink libpython3.so + abi3 wheel rename
ndonkoHenri May 31, 2026
aabff4d
cryptography Android: linker-script forwarding + recipe test fixes
ndonkoHenri May 31, 2026
88b17d0
revert detect-tests stub-skip — TBD placeholders must fail CI
ndonkoHenri May 31, 2026
8844d98
add tests for 38 untested recipes + fix CI libpython3.so linker script
ndonkoHenri May 31, 2026
48238c4
improve ci
ndonkoHenri May 31, 2026
54e164d
batch fix CI failures surfaced by run 26710544608
ndonkoHenri May 31, 2026
f19d298
two more fixes from run 26710544608
ndonkoHenri May 31, 2026
9fce34a
shapely + matplotlib + opencv-python: declare libcpp on Android (work…
ndonkoHenri May 31, 2026
c326c77
fix pycryptodome/pycryptodomex on Android + pyobjus py3 cython patch
ndonkoHenri May 31, 2026
f83c3c6
reword libcpp-on-numpy-users comments — stale wheel, not pipeline bug
ndonkoHenri May 31, 2026
8cb55ea
workflow platforms filter
ndonkoHenri May 31, 2026
f6167e4
patch pycryptodome/pycryptodomex setup.py + fix gdal/opaque tests
ndonkoHenri May 31, 2026
5ea91e1
Add standalone `read_platforms.py` script + integrate with CI matrix …
ndonkoHenri May 31, 2026
ce6e0d1
Add descriptions to `meta-schema.yaml` fields
ndonkoHenri May 31, 2026
80c4594
drop rerun_all_tests input; add 'ALL' packages-field expansion
ndonkoHenri May 31, 2026
9e13a4c
drop SMOKE_SET — pydantic-core is the only fallback now
ndonkoHenri May 31, 2026
2c5a0af
opaque test: correct tuple-unpack order for the OPAQUE roundtrip
ndonkoHenri May 31, 2026
7fbd70b
rename ci workflow
ndonkoHenri May 31, 2026
0be07e0
show version + build number in matrix job names
ndonkoHenri May 31, 2026
7ea0cde
fix more recipes
ndonkoHenri May 31, 2026
49e5764
replace uv-sync conditional with uvx — drop FLET_RUNTIME_DEPS list
ndonkoHenri May 31, 2026
58f79a0
cleanup: drop plan/phase comments + sync local-dev docs to uvx flow
ndonkoHenri May 31, 2026
d0ab770
drop 6× EXIT sentinel loop — single emit is enough
ndonkoHenri May 31, 2026
e755b45
drop plan-doc references from shipped files; remove the plan doc
ndonkoHenri May 31, 2026
c724c24
annotate pytest exit codes in the GH Step Summary
ndonkoHenri May 31, 2026
a56896a
flet-libpyjni: gate to Android — no JNI runtime on iOS
ndonkoHenri May 31, 2026
6b85f9e
grpcio: patch vendored zlib's zutil.h to fix iOS build
ndonkoHenri May 31, 2026
9781535
drop continue-on-error from iOS lane — fail loud, don't hide
ndonkoHenri May 31, 2026
d58f16e
Pre-install Flutter's pinned NDK for Android jobs to prevent intermit…
ndonkoHenri May 31, 2026
bbea477
test pyjnius
ndonkoHenri May 31, 2026
6e00403
Install forge NDK for Android builds
ndonkoHenri May 31, 2026
6dcd46c
Revert "Install forge NDK for Android builds"
ndonkoHenri May 31, 2026
3d6af99
improve ndk installs
ndonkoHenri May 31, 2026
8e55654
improve ndk installs
ndonkoHenri May 31, 2026
e281468
ndk: band-aid for old python-build tarball + mark cleanups
ndonkoHenri May 31, 2026
d63b785
pandas: bump meson-python for mobile support, add meson-wrapper.py fo…
ndonkoHenri May 31, 2026
0a7ce1d
Probe pyyaml C extension via _yaml directly, not CSafeDumper
ndonkoHenri May 31, 2026
24d8a20
fiona iOS: link libproj directly to resolve _geod_init
ndonkoHenri May 31, 2026
5d32d07
pyyaml: depend on new flet-libyaml recipe so the _yaml C extension ac…
ndonkoHenri May 31, 2026
c15ab9a
fiona iOS: pull the rest of libgdal's static deps into _env.so
ndonkoHenri Jun 1, 2026
0aacca9
ruamel.yaml.clib: accept iOS .fwork suffix in compiled-ext probe
ndonkoHenri Jun 1, 2026
4c4bb6d
fiona iOS: add libpsl to GDAL_LIBS — libcurl's Public Suffix List
ndonkoHenri Jun 1, 2026
c01497c
fiona iOS: skip GeoJSON write-read test, proj.db gap
ndonkoHenri Jun 1, 2026
ff2a379
gdal iOS: same static-cascade band-aid as fiona
ndonkoHenri Jun 1, 2026
c519660
pyogrio iOS: same static-cascade band-aid as fiona
ndonkoHenri Jun 1, 2026
0ac6f58
pyproj iOS: same static-cascade band-aid (libproj layer)
ndonkoHenri Jun 1, 2026
ba30fa7
pillow Android: surface NDK sysroot + opt tree via CPATH/LIBRARY_PATH
ndonkoHenri Jun 1, 2026
2d97d89
pillow: tighten cross-compile /usr/ filter to exact host paths
ndonkoHenri Jun 1, 2026
2306a90
recipes: reframe defensive libcpp deps as numpy workaround (CLEANUP-A…
ndonkoHenri Jun 1, 2026
77bb029
tests: import + np.fft canaries for libcpp-affected recipes
ndonkoHenri Jun 1, 2026
4ea248b
tests: import canaries for iOS static-cascade fixes
ndonkoHenri Jun 1, 2026
56d59c6
tests: import canaries for missing-install_requires fixes
ndonkoHenri Jun 1, 2026
c64a1f5
pyyaml test: explicit `from yaml import CSafeDumper, CSafeLoader`
ndonkoHenri Jun 1, 2026
aeae5c9
recipe: selectolax 0.4.10, orjson 3.11.9, biopython 1.87
ndonkoHenri May 30, 2026
a347bf3
Merge origin/python3.12 into test-recipes-on-mobile
ndonkoHenri Jun 1, 2026
64529e7
Preserve upstream wheel Python/ABI tag in fix_wheel (#61)
FeodorFitsner Jun 1, 2026
e09628a
Drop python-build#5 band-aids — tarball now ships the fixes natively
ndonkoHenri Jun 1, 2026
37108c0
Make `meta.yaml` the source of truth for `build.number` (#59)
ndonkoHenri Jun 1, 2026
13f4958
fix(numpy): add Android `flet-libcpp-shared` host dep (#58)
ndonkoHenri Jun 1, 2026
2f6ce8f
fix(`pyyaml`): bundle the `_yaml` C accelerator via new `flet-libyaml…
ndonkoHenri Jun 1, 2026
1d84f99
recipe: pyxirr 0.10.8 (#57)
ndonkoHenri Jun 1, 2026
d335aa2
Merge origin/python3.12 into test-recipes-on-mobile
ndonkoHenri Jun 1, 2026
72c0cab
recipes: drop defensive libcpp deps now that #58 + numpy republish la…
ndonkoHenri Jun 1, 2026
545463a
numpy: drop dead `sdk == 'iOS'` clause from longdouble_format condition
ndonkoHenri Jun 1, 2026
5c10d0c
pillow: drop the dead outer `if sdk != 'android'` wrapper
ndonkoHenri Jun 1, 2026
9fcb5da
recipes: bump build.number on the 14 wheel-affecting changes
ndonkoHenri Jun 1, 2026
49687cd
forge: inherit os.environ in cross_kwargs instead of starting from {}
ndonkoHenri Jun 1, 2026
186e7f2
ci: bump GHA actions off Node.js 20 ahead of the June 16 deadline
ndonkoHenri Jun 1, 2026
2b16616
ci: support tests/ subdir layout for recipe tests
ndonkoHenri Jun 1, 2026
bb82b50
recipes: add smoke tests for biopython, coolprop, primp, rasterio
ndonkoHenri Jun 1, 2026
26c58b8
blis: fix test_import_blis — explicitly import blis.py submodule
ndonkoHenri Jun 2, 2026
6b89d81
Update publish condition in build-wheels workflow to allow publishing…
ndonkoHenri Jun 2, 2026
4bd5e61
bump flet build apk verbosity to -vv
ndonkoHenri Jun 2, 2026
ead27aa
Revert "Preserve upstream wheel Python/ABI tag in fix_wheel (#61)"
ndonkoHenri Jun 2, 2026
2ebffe1
fix "rasterio" recipe on iOS
ndonkoHenri Jun 3, 2026
3eb426c
Merge remote-tracking branch 'refs/remotes/origin/main' into test-rec…
ndonkoHenri Jun 3, 2026
b52c3b9
Update branch condition in publish step of build-wheels workflow
ndonkoHenri Jun 3, 2026
a4f4ff9
Add package version handling and runtime variable references
ndonkoHenri Jun 3, 2026
f5c92c3
Add support for fetching python-build artifacts from Actions runs
ndonkoHenri Jun 3, 2026
ea1e05d
Remove publish workflow_dispatch input
ndonkoHenri Jun 3, 2026
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
35 changes: 35 additions & 0 deletions .ci/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,38 @@ function publish_to_pypi() {
curl -F package=@$wheel https://$GEMFURY_TOKEN@push.fury.io/flet/
done
}

# Resolve which python-build tarball source to use.
# When PYTHON_BUILD_RUN_ID is set (workflow_dispatch input), fetch the
# named tarball from that python-build Actions run's artifacts; otherwise
# download from the canonical v<version> release URL.
#
# Args:
# $1 artifact_platform — "android" | "darwin" (matches the python-build artifact name)
# $2 tarball — e.g. python-android-mobile-forge-3.12.tar.gz
# $3 extract_dir — local dir to extract the tarball into
#
# Caller env:
# PYTHON_SHORT_VERSION — e.g. 3.12 (required)
# PYTHON_BUILD_RUN_ID — empty for release URL; non-empty for `gh run download`
# GH_TOKEN — needed only when PYTHON_BUILD_RUN_ID is set
# RUNNER_TEMP — GitHub Actions runner temp dir (only used in override path)
function fetch_python_build_tarball() {
local artifact_platform="$1"
local tarball="$2"
local extract_dir="$3"
if [[ -n "${PYTHON_BUILD_RUN_ID:-}" ]]; then
echo "Fetching $tarball from python-build run $PYTHON_BUILD_RUN_ID"
local stage="$RUNNER_TEMP/python-build-artifact"
rm -rf "$stage"
mkdir -p "$stage"
gh run download "$PYTHON_BUILD_RUN_ID" \
--repo flet-dev/python-build \
--name "python-${artifact_platform}-${PYTHON_SHORT_VERSION}" \
--dir "$stage"
tar -xzf "$stage/$tarball" -C "$extract_dir"
else
curl -#OL "https://github.com/flet-dev/python-build/releases/download/v${PYTHON_SHORT_VERSION}/$tarball"
tar -xzf "$tarball" -C "$extract_dir"
fi
}
121 changes: 73 additions & 48 deletions .ci/install_ndk.sh
Original file line number Diff line number Diff line change
@@ -1,51 +1,76 @@
if [[ -z "${NDK_HOME-}" ]]; then
NDK_HOME=$HOME/ndk/$NDK_VERSION
echo "NDK_HOME environment variable is not set."
if [ ! -d $NDK_HOME ]; then
echo "Installing NDK $NDK_VERSION to $NDK_HOME"
mkdir -p downloads

if [ $(uname) = "Darwin" ]; then
seven_zip=downloads/7zip/7zz
if ! test -f $seven_zip; then
echo "Installing 7-zip"
mkdir -p $(dirname $seven_zip)
cd $(dirname $seven_zip)
curl -#OL https://www.7-zip.org/a/7z2301-mac.tar.xz
tar -xf 7z2301-mac.tar.xz
cd -
fi

ndk_dmg=android-ndk-$NDK_VERSION-darwin.dmg
if ! test -f downloads/$ndk_dmg; then
echo ">>> Downloading $ndk_dmg"
curl -#L -o downloads/$ndk_dmg https://dl.google.com/android/repository/$ndk_dmg
fi

cd downloads
$seven_zip x $ndk_dmg
mkdir -p $(dirname $NDK_HOME)
mv Android\ NDK\ */AndroidNDK*.app/Contents/NDK $NDK_HOME
rm -rf Android\ NDK\ *
cd -
else
ndk_zip=android-ndk-$NDK_VERSION-linux.zip
if ! test -f downloads/$ndk_zip; then
echo ">>> Downloading $ndk_zip"
curl -#L -o downloads/$ndk_zip https://dl.google.com/android/repository/$ndk_zip
fi
cd downloads
unzip -oq $ndk_zip
mkdir -p $(dirname $NDK_HOME)
mv android-ndk-$NDK_VERSION $NDK_HOME
cd -
echo "NDK installed to $NDK_HOME"
fi
else
echo "NDK $NDK_VERSION is already installed in $NDK_HOME"
#!/usr/bin/env bash
# Install an Android NDK component via sdkmanager.
#
# Usage:
# .ci/install_ndk.sh # uses $NDK_VERSION
# .ci/install_ndk.sh 27.3.13750724 # explicit component version
# .ci/install_ndk.sh r27d # release letter (resolved via Google's manifest)
#
# Installs into $ANDROID_HOME/ndk/<component-version>/ — sdkmanager's
# standard layout, which is also where AGP looks for Gradle builds.
# When SOURCED (`. install_ndk.sh ...`), exports NDK_HOME pointing at
# the resulting install path; forge reads that env var.
#
# Idempotent: if the NDK is already installed, the sdkmanager call is skipped.
#
# Requires `sdkmanager` from the Android SDK cmdline-tools. On CI both
# the Ubuntu and macOS runner images ship it; locally install Android
# Studio or the standalone cmdline-tools.

set -eu

version="${1:-${NDK_VERSION:-}}"
if [ -z "$version" ]; then
echo "usage: $0 <ndk-version> (or set NDK_VERSION)" >&2
exit 2
fi

# Resolve release-letter form (e.g. "r27d") to the component version
# (e.g. "27.3.13750724") via Google's repository manifest. Skipped when
# the input is already in component form. Uses awk to track the most-recent
# `path="ndk;<version>"` attribute.
if [[ "$version" =~ ^r[0-9]+[a-z]*$ ]]; then
letter="$version"
version=$(curl -sfL https://dl.google.com/android/repository/repository2-3.xml \
| awk -v zip="android-ndk-${letter}-linux.zip" '
match($0, /path="ndk;[0-9.]+"/) {
current = substr($0, RSTART+10, RLENGTH-11)
}
!found && index($0, zip) { print current; found=1 }
')
if [ -z "$version" ]; then
echo "Could not resolve NDK release letter '$letter' to a component version." >&2
echo "Check it exists at https://dl.google.com/android/repository/repository2-3.xml" >&2
exit 4
fi
else
echo "NDK home: $NDK_HOME"
echo "Resolved $letter → $version"
fi

: "${ANDROID_HOME:?ANDROID_HOME must be set (Android SDK location)}"

sdkmanager="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager"
if [ ! -x "$sdkmanager" ]; then
echo "sdkmanager not found at $sdkmanager" >&2
echo " Install Android Studio or the standalone cmdline-tools first." >&2
exit 3
fi

install_dir="$ANDROID_HOME/ndk/$version"

# Idempotency check: any host-triplet clang under the install dir means
# it's already installed and usable.
if ! find "$install_dir/toolchains/llvm/prebuilt"/*/bin/aarch64-linux-android*-clang 2>/dev/null | grep -q .; then
echo "Installing NDK $version via sdkmanager…"
yes | "$sdkmanager" --licenses > /dev/null
"$sdkmanager" --install "ndk;$version"
fi

export NDK_HOME
echo "NDK $version installed at $install_dir"
export NDK_HOME="$install_dir"

# When run as a GH Actions step (not sourced — the export above doesn't
# persist across steps), write NDK_HOME to $GITHUB_ENV so downstream
# steps inherit it. Harmless to no-op when $GITHUB_ENV is unset (local).
if [ -n "${GITHUB_ENV:-}" ]; then
echo "NDK_HOME=$install_dir" >> "$GITHUB_ENV"
fi
78 changes: 78 additions & 0 deletions .ci/read_meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# /// script
# requires-python = ">=3.10"
# dependencies = ["pyyaml", "jinja2"]
# ///
"""Read fields from a recipe's meta.yaml and print them as one
tab-separated line:

<version>\t<build_number>\t<space-joined-platforms>

Examples:

2.2.2\t0\t # numpy (no platforms, no build override)
1.6.1\t0\tandroid # pyjnius
1.2.3\t0\tios # pyobjus
8.11.0\t1\t # flet-libcurl (uses Jinja `{% set %}`)

Used by the build-wheels.yml matrix step to (a) skip per-recipe
(platform, pkg) combinations that the recipe opts out of, and (b)
include the version + build number in each job's display name.

A standalone PEP 723 script rather than an inline here-doc in the
workflow — testable in isolation, declares its own pyyaml/jinja2 deps
so the runner doesn't need them preinstalled.

meta.yaml is a Jinja template (forge renders it before YAML-parsing).
We render it the same way, with a generic SDK context — the fields we
read here are platform-independent, so any plausible render values
work. Picking `sdk='android'` is arbitrary and convenient.

On any failure (file missing, template invalid, YAML invalid,
schema-shape unexpected) we print a blank-but-tab-aligned line so the
bash caller's `IFS=$'\\t' read -r ver build platforms` doesn't blow up
— the caller treats empty fields as "unknown, fall back to whatever
the package spec or workflow defaults already say."""

import sys

import jinja2
import yaml


def main(path: str) -> int:
version = ""
build_number = ""
platforms = ""
try:
with open(path) as f:
tpl = f.read()
rendered = jinja2.Template(tpl).render(
sdk="android",
sdk_version=24,
arch="arm64-v8a",
version=None,
py_version=(3, 12, 12),
)
meta = yaml.safe_load(rendered) or {}
pkg = meta.get("package") or {}
if "version" in pkg:
version = str(pkg["version"])
plat = pkg.get("platforms")
if plat:
platforms = " ".join(plat)
# build.number defaults to 0 in the schema, but raw meta.yaml may
# omit it. Match the schema default rather than treating it as
# unknown.
build = (meta.get("build") or {}).get("number", 0)
build_number = str(build)
except Exception:
pass
print(f"{version}\t{build_number}\t{platforms}")
return 0


if __name__ == "__main__":
if len(sys.argv) != 2:
print("usage: read_meta.py <recipe-meta.yaml-path>", file=sys.stderr)
sys.exit(2)
sys.exit(main(sys.argv[1]))
35 changes: 35 additions & 0 deletions .ci/run_android_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Run inside reactivecircus/android-emulator-runner@v2's script: field.
# That action runs each script line through `sh -c` separately, which breaks
# multi-line bash constructs (functions, traps, if blocks). So the whole
# logic lives in this dedicated script file with its own bash shebang;
# the workflow's `script:` field just invokes this file as a one-liner.
#
# Side effects:
# - installs + launches the recipe-tester APK at
# tests/recipe-tester/build/apk/recipe-tester.apk
# - delegates the poll-and-parse to .ci/wait_for_console.sh
# - on failure, dumps `adb logcat -d` and a screencap into the workspace
# root so the workflow's upload-artifact step can pick them up
# (the AVD is alive at trap-time; the post-action phase has already
# killed it by the time the workflow's failure-conditional steps run)

set -eux

cleanup() {
rc=$?
if [ "$rc" -ne 0 ]; then
adb logcat -d > logcat-on-failure.txt 2>/dev/null || true
adb exec-out screencap -p > screen-on-failure.png 2>/dev/null || true
fi
return $rc
}
trap cleanup EXIT

adb install -r tests/recipe-tester/build/apk/recipe-tester.apk
adb logcat -c
adb shell monkey -p com.flet.recipe_tester -c android.intent.category.LAUNCHER 1

# 15min hard cap on the device-side run; recipe tests should finish in
# <2min, the extra slack absorbs AVD slowness.
TIMEOUT=900 .ci/wait_for_console.sh android
63 changes: 63 additions & 0 deletions .ci/run_ios_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Mirror of .ci/run_android_test.sh for the iOS Simulator lane.
#
# Boots an iPhone simulator if none is running, installs the recipe-tester
# .app, launches it, then delegates the EXIT-sentinel poll to
# .ci/wait_for_console.sh ios.
#
# Side effects:
# - on failure, dumps `xcrun simctl spawn booted log show --last 10m`
# into syslog-on-failure.txt and a screencap into screen-on-failure.png
# so the workflow's upload-artifact step can pick them up. The simulator
# is still booted at trap-time (the workflow doesn't shut it down
# itself), so these calls work.
#
# Why a separate script (not inline `run:`): same reason as the Android
# helper — keep the dash/bash quirks inside a file with a proper bash
# shebang so multi-line constructs survive whatever shell the action
# eventually picks. (Less critical here since the iOS step is a plain
# `run: bash` rather than an action's split-by-line script:, but it's
# consistent and keeps the diff focused.)

set -eux

APP=tests/recipe-tester/build/ios-simulator/recipe-tester.app
IOS_BUNDLE=${IOS_BUNDLE:-com.flet.recipe-tester}

cleanup() {
rc=$?
if [ "$rc" -ne 0 ]; then
xcrun simctl spawn booted log show --last 10m > syslog-on-failure.txt 2>/dev/null || true
xcrun simctl io booted screenshot screen-on-failure.png 2>/dev/null || true
fi
return $rc
}
trap cleanup EXIT

# Pick + boot a simulator if none is currently booted. Robust to the
# specific device names changing between macos image versions: grab the
# first available iPhone in any iOS runtime.
if ! xcrun simctl list devices booted | grep -q "Booted"; then
UDID=$(xcrun simctl list devices available -j \
| jq -r '.devices | to_entries[]
| select(.key | contains("iOS"))
| .value[]
| select(.isAvailable == true and (.name | startswith("iPhone")))
| .udid' \
| head -1)
if [ -z "$UDID" ]; then
echo "::error::no iPhone simulator available on this runner"
xcrun simctl list devices available
exit 3
fi
echo "Booting simulator $UDID"
xcrun simctl boot "$UDID"
fi
xcrun simctl bootstatus booted -b

xcrun simctl install booted "$APP"
xcrun simctl launch booted "$IOS_BUNDLE"

# Same 15-min device-side cap as Android. Tests should finish in <2min;
# the slack absorbs cold-boot + first-launch Python init overhead.
TIMEOUT=900 .ci/wait_for_console.sh ios
Loading
Loading