diff --git a/src/serious_python/CHANGELOG.md b/src/serious_python/CHANGELOG.md index c083548c..c915ba73 100644 --- a/src/serious_python/CHANGELOG.md +++ b/src/serious_python/CHANGELOG.md @@ -1,3 +1,11 @@ +## 4.5.0 + +* **iOS/macOS:** the pre-built `Python`, `dart_bridge` and stdlib extension XCFrameworks are now staged byte-for-byte and never modified. `SERIOUS_PYTHON_BUNDLE_ID` now namespaces only the frameworks built from *your* app's wheels; the pre-built ones keep the stable `dev.flet.*` identifiers their publisher assigned. +* **This corrects 4.4.2.** Rewriting those identifiers was a hypothesis for the `ITMS-91065: Missing signature` App Store rejection (flet-dev/flet#6724) and it was wrong — the rejection reports on the publisher's signature of each XCFramework, which Xcode records in the IPA separately from your app's own signature, and the identifier rewrite destroyed it. See `serious_python_darwin` for the full explanation. +* **iOS:** use the **Swift Package Manager** packaging path for App Store submissions. The CocoaPods path copies inner `.framework` bundles and cannot produce the SDK-origin signature receipts Apple's scan looks for; it now warns during the build. +* New `SERIOUS_PYTHON_VERIFY_PROVIDER_SIGNATURES` (`warn` default / `require` / `off`) and `SERIOUS_PYTHON_EXPECTED_TEAM_ID` env vars verify those publisher signatures during packaging. Set `require` for release builds. +* Bundled python-build snapshot re-pinned to **20260729** (`dart_bridge` **1.6.1 → 1.7.0**). No Python version moved — **3.12.13 / 3.13.14 / 3.14.6** and Pyodide are unchanged from 20260727 — but every Apple XCFramework in that release is now signed by the Flet publishing team with a secure timestamp. See `serious_python_darwin` 4.5.0. + ## 4.4.2 * **iOS:** new `SERIOUS_PYTHON_BUNDLE_ID` env var namespaces the generated frameworks' bundle identifiers under your app (`com.example.myapp.-ssl`) instead of a shared `org.python.*` default identical in every app built with serious_python. `flet build` sets it for you; set it yourself for a manual two-step build, in both places you set `SERIOUS_PYTHON_SITE_PACKAGES`. diff --git a/src/serious_python/README.md b/src/serious_python/README.md index f591832e..163732f4 100644 --- a/src/serious_python/README.md +++ b/src/serious_python/README.md @@ -198,17 +198,22 @@ On **iOS**, also set `SERIOUS_PYTHON_BUNDLE_ID` to your app's bundle identifier: export SERIOUS_PYTHON_BUNDLE_ID=com.example.myapp ``` -Each Python C-extension ships as its own embedded framework, and this namespaces -their `CFBundleIdentifier`s under your app (`com.example.myapp.-ssl`) instead of a -shared `org.python.*` default that would be identical in every app built with -serious_python — matching what CPython's own iOS support does. A framework's bundle -identifier also becomes its code signing identifier, which Apple's App Store scan -appears to key on for the third-party SDK requirements. Set it in **both** places +Each native extension in your app's own dependencies ships as its own embedded +framework, and this namespaces their `CFBundleIdentifier`s under your app +(`com.example.myapp.-numpy-core-multiarray`) instead of a shared `org.python.*` +default — matching what CPython's own iOS support does. Set it in **both** places `SERIOUS_PYTHON_SITE_PACKAGES` is set (the `package` command and the later `flutter build`), since the CocoaPods `prepare_command` re-runs the darwin sync. If unset — or if the value isn't a valid bundle identifier — the `org.python.*` defaults are kept and a warning is printed. `flet build` sets this for you. +This applies **only** to frameworks built here from your wheels. `Python.xcframework`, +`dart_bridge.xcframework`, and the stdlib extension frameworks arrive pre-built and +**signed by their publisher**, with stable `dev.flet.python.*` / `dev.flet.dartbridge` +identifiers assigned upstream. serious_python stages those byte-for-byte and never +rewrites anything inside them — see [SDK-origin signatures](#sdk-origin-signatures) +below. + ## Python app structure By default, embedded Python program is run in a separate thread, to avoid UI blocking. Your Flutter app is not supposed to directly call Python functions or modules, but instead it should communicate via some API provided by a Python app, such as: REST API, sockets, SQLite database, files, etc. @@ -266,6 +271,50 @@ The on-disk layout differs per platform, mostly because each OS has different ru The CPython runtime, stdlib, and (on iOS) native extensions are bundled into `serious_python_darwin.framework` as resources. On **iOS**, the App Store forbids loose `.dylib`s, so every native extension `.so` is repackaged into a signed `.framework` inside an `.xcframework`, with a `.fwork` text marker left at the module's import path; CPython's `AppleFrameworkLoader` reads the marker and loads the framework binary. On **macOS**, native extensions stay as plain `.so`, merged into universal (`arm64`+`x86_64`) binaries at package time. `PYTHONHOME` is the framework's resource path; `sys.path` includes `/site-packages`, `/stdlib`, and `/stdlib/lib-dynload`. +#### SDK-origin signatures + +Xcode records, for every `.xcframework` your app links against, whether **the +publisher** signed it and whether that signature carried a secure timestamp. The +result is written into the IPA as `Signatures/.xcframework-ios.signature`, +and Apple's App Store scan reports a missing one as `ITMS-91065: Missing signature`. + +This is **separate from your app's own signature.** Xcode re-signs every embedded +framework with your Apple Distribution identity at embed time and again at +`exportArchive`; that has no effect on the SDK-origin receipt. Equally, editing a +single file inside an `.xcframework` — even one `Info.plist` key — invalidates the +publisher's signature and turns the receipt back into `signed = false`. + +So serious_python treats `Python.xcframework`, `dart_bridge.xcframework`, and the +stdlib extension frameworks as **immutable** once downloaded. They are copied +verbatim at every staging step, and each step re-checks a digest manifest recorded +at extraction time, so a reintroduced mutation fails the build instead of surfacing +as an App Store rejection weeks later. + +`SERIOUS_PYTHON_VERIFY_PROVIDER_SIGNATURES` controls what happens when a publisher +signature is missing or invalid: + +| value | behaviour | +| :--- | :--- | +| `warn` (default) | report and continue — lets you pin an older, pre-signing `python-build` / `dart-bridge` release | +| `require` | fail the build; use this for App Store submissions | +| `off` | skip the signature checks | + +Set `SERIOUS_PYTHON_EXPECTED_TEAM_ID` to additionally require a specific Apple +Team ID on those signatures. The digest-manifest checks always run and are always +fatal — they cover this package's own behaviour, not the artifacts you pinned. + +#### iOS packaging path: use SwiftPM + +The **Swift Package Manager** path declares the publisher's `.xcframework`s as +`binaryTarget`s, which is what makes Xcode emit the `Signatures/` receipts above. + +The CocoaPods path copies the *inner* `.framework` bundles out of the stdlib +xcframeworks in a `Pods-Runner-frameworks.sh` script phase, which discards the +outer-XCFramework provenance even when the source is correctly signed. It still +produces a working app, but it cannot produce complete SDK-origin receipts. **Use +the SwiftPM path for App Store submissions** until that path is replaced with real +vendored XCFramework declarations. + ### Linux / Windows specifics The CPython runtime (`libpython3.so` + `libpython.so` on Linux; `python3.dll` + `python.dll` on Windows), `libdart_bridge`, the stdlib, and native modules are copied next to your app's executable at build time. `PYTHONHOME` is the executable's directory. On Windows, extension modules (`.pyd`) and their dependent DLLs live in `/DLLs/`, which is added to `sys.path`. diff --git a/src/serious_python/lib/src/python_versions.dart b/src/serious_python/lib/src/python_versions.dart index a4f33077..8110879d 100644 --- a/src/serious_python/lib/src/python_versions.dart +++ b/src/serious_python/lib/src/python_versions.dart @@ -1,5 +1,5 @@ // GENERATED by `dart run serious_python:gen_version_tables` from python-build's -// manifest.json (release 20260727). Do not edit by hand — edit python-build's +// manifest.json (release 20260729). Do not edit by hand — edit python-build's // manifest.json, cut a release, bump `pythonReleaseDate`, and regenerate. const pythonVersionEnvironmentVariable = "SERIOUS_PYTHON_VERSION"; @@ -10,8 +10,8 @@ const pyodideVersionEnvironmentVariable = "SERIOUS_PYTHON_PYODIDE_VERSION"; const dartBridgeVersionEnvironmentVariable = "DART_BRIDGE_VERSION"; /// python-build release the bundled runtimes come from (YYYYMMDD). -const pythonReleaseDate = "20260727"; -const dartBridgeVersion = "1.6.1"; +const pythonReleaseDate = "20260729"; +const dartBridgeVersion = "1.7.0"; const defaultPythonVersion = "3.14"; class PythonRelease { diff --git a/src/serious_python/pubspec.yaml b/src/serious_python/pubspec.yaml index cd2a1beb..77722da3 100644 --- a/src/serious_python/pubspec.yaml +++ b/src/serious_python/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python description: A cross-platform plugin for adding embedded Python runtime to your Flutter apps. homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.4.2 +version: 4.5.0 platforms: ios: diff --git a/src/serious_python_android/CHANGELOG.md b/src/serious_python_android/CHANGELOG.md index 4b5dd18d..22d1d50e 100644 --- a/src/serious_python_android/CHANGELOG.md +++ b/src/serious_python_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.5.0 + +* Re-pins the bundled python-build snapshot to **20260729** and `dart_bridge` to **1.7.0**. Both exist to provider-sign the Apple XCFrameworks (see `serious_python_darwin` 4.5.0). Nothing about Android changed: no Python version moved from 20260727 (**3.12.13 / 3.13.14 / 3.14.6**), and `dart_bridge`'s Android `.so`s are byte-identical to 1.6.1 — verified, not assumed. + ## 4.4.2 * Re-pins the bundled python-build snapshot to **20260727**. Its only functional change is to the iOS frameworks' `Info.plist` (see `serious_python_darwin` 4.4.2); no versions moved and the Android runtime is unchanged from 20260726. diff --git a/src/serious_python_android/android/build.gradle.kts b/src/serious_python_android/android/build.gradle.kts index a4f813ac..4ba1d4a4 100644 --- a/src/serious_python_android/android/build.gradle.kts +++ b/src/serious_python_android/android/build.gradle.kts @@ -21,7 +21,7 @@ buildscript { } group = "com.flet.serious_python_android" -version = "4.4.2" +version = "4.5.0" rootProject.allprojects { repositories { diff --git a/src/serious_python_android/android/python_versions.properties b/src/serious_python_android/android/python_versions.properties index 34e8f7f0..ea1da9cf 100644 --- a/src/serious_python_android/android/python_versions.properties +++ b/src/serious_python_android/android/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260727). Do not edit by hand. +# python-build manifest.json (release 20260729). Do not edit by hand. default_python_version=3.14 -dart_bridge_version=1.6.1 -python_build_release_date=20260727 +dart_bridge_version=1.7.0 +python_build_release_date=20260729 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_android/pubspec.yaml b/src/serious_python_android/pubspec.yaml index 7ceee06c..8f8007af 100644 --- a/src/serious_python_android/pubspec.yaml +++ b/src/serious_python_android/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_android description: Android implementation of the serious_python plugin homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.4.2 +version: 4.5.0 environment: sdk: ">=3.0.0 <4.0.0" diff --git a/src/serious_python_darwin/CHANGELOG.md b/src/serious_python_darwin/CHANGELOG.md index 3d13f548..31f74ca2 100644 --- a/src/serious_python_darwin/CHANGELOG.md +++ b/src/serious_python_darwin/CHANGELOG.md @@ -1,3 +1,12 @@ +## 4.5.0 + +* **iOS/macOS: provider-built XCFrameworks are now staged byte-for-byte, and their SDK-origin signatures are verified.** `Python.xcframework`, `dart_bridge.xcframework`, and every stdlib extension framework arrive pre-built from flet-dev/python-build and flet-dev/dart-bridge. serious_python no longer rewrites anything inside them: the `SERIOUS_PYTHON_BUNDLE_ID` pass now applies **only** to frameworks built here from the app's own wheels, which are generated in a separate directory, mutated there, and merged with the untouched provider artifacts afterwards. `reconcile_framework_install_names` (which rewrites Mach-O headers and re-signs ad-hoc) likewise never reaches a provider binary. +* **This retracts the reasoning in 4.4.2.** That entry claimed "a signature applied at the source can't matter", on the grounds that Xcode's `codesign -f` at embed and `exportArchive` overwrites it. The overwrite is real but the conclusion was wrong: Xcode *separately* records the state of each `.xcframework` as its publisher shipped it, and writes that into the IPA as `Signatures/.xcframework-ios.signature` with `signed` and `isSecureTimestamp` flags. `ITMS-91065: Missing signature` reports on **those receipts**, not on the embedded copy. Namespacing `CFBundleIdentifier` under the host app was therefore not a fix, and was actively counterproductive — the plist edit invalidated the very signature the receipt reports on. The 4.4.2 identifier rewrite is reverted for provider frameworks and kept for locally built ones, where it is harmless and matches CPython's convention. +* **New: provider integrity is checked at every staging step.** A SHA-256 manifest of the provider XCFrameworks is recorded when they are extracted, and re-checked after site-package synchronization and after SwiftPM staging. Any file that changed fails the build. `SERIOUS_PYTHON_VERIFY_PROVIDER_SIGNATURES` (`warn` default / `require` / `off`) additionally checks that each provider XCFramework carries a real, securely timestamped publisher signature; `SERIOUS_PYTHON_EXPECTED_TEAM_ID` pins the expected Apple Team ID. The default is `warn` so that pinning an older, pre-signing `python-build` or `dart-bridge` release still builds — use `require` for App Store submissions. +* **iOS: use the Swift Package Manager path for App Store submissions.** The CocoaPods path installs the stdlib and site-package extensions by copying the *inner* `.framework` bundles in a `Pods-Runner-frameworks.sh` script phase; Xcode writes SDK-origin receipts only for xcframeworks it consumes as declared binary dependencies, so that path cannot produce complete receipts no matter how the source is signed. It now emits an Xcode `warning:` saying so. The SwiftPM path declares them as `binaryTarget`s and is unaffected. +* Provider identifiers are now assigned upstream and are stable: `dev.flet.python.runtime` for the Python runtime, `dev.flet.python.` for each stdlib extension, `dev.flet.dartbridge` for the bridge. They no longer depend on the consuming app, which is what allows one publisher signature to cover every app. +* Re-pins the bundled python-build snapshot to **20260729** (`dart_bridge` **1.6.1 → 1.7.0**). No Python version moved — **3.12.13 / 3.13.14 / 3.14.6** and Pyodide are unchanged from 20260727. What did change is that every Apple XCFramework in that release, and `dart_bridge.xcframework` in 1.7.0, is now signed by the Flet publishing team's Apple Distribution identity with a secure timestamp: verified on the published artifacts as `Authority=Apple Distribution: Appveyor Systems Inc. (GXXRQJK434)`, a real `Timestamp=`, and an outer `_CodeSignature/CodeResources` — 56 XCFrameworks in `python-ios-dart-3.14.6`, plus the macOS and mobile-forge runtimes. See flet-dev/python-build#37 and flet-dev/dart-bridge#13. + ## 4.4.2 * **iOS: framework bundle identifiers are now namespaced under the host app.** Every Python C-extension ships as its own embedded framework, and each carried a fixed `org.python.` `CFBundleIdentifier` — `org.python.ssl`, `org.python.hashlib`, … — byte-identical in every app ever built with serious_python. Set `SERIOUS_PYTHON_BUNDLE_ID` to the app's bundle identifier and they become `com.example.myapp.-ssl` instead, matching what CPython's own iOS support does when it converts a `.so` into a framework. This matters because a framework's bundle identifier also becomes its **code signing identifier**, which is the one field that survives the `codesign -f` Xcode applies at embed and again at `exportArchive`. `Python.framework` is covered too — it was `org.python.python` in every app; nothing resolves it by identifier (the string appears in no shipped binary and there are no `CFBundleGetBundleWithIdentifier` lookups), so the rename is inert at runtime. `dart_bridge.framework` is deliberately left at `dev.flet.dartbridge`, which is already a vendor-owned identifier rather than a shared placeholder. Unset, or set to something that isn't a valid bundle identifier, leaves the old defaults in place and prints a warning. `flet build` supplies it from your app's configured bundle id (flet-dev/flet#6731). diff --git a/src/serious_python_darwin/darwin/bundle-python-frameworks-ios.sh b/src/serious_python_darwin/darwin/bundle-python-frameworks-ios.sh index 3c9766f0..04d0dd61 100755 --- a/src/serious_python_darwin/darwin/bundle-python-frameworks-ios.sh +++ b/src/serious_python_darwin/darwin/bundle-python-frameworks-ios.sh @@ -1,5 +1,16 @@ echo "Bundle Python xcframeworks" +# This path installs the stdlib / site-package extensions by copying the INNER +# .framework bundles out of each .xcframework (see pods-runner-frameworks-addon.sh). +# Xcode only writes an SDK-origin receipt — Signatures/.xcframework-ios.signature +# in the IPA — for xcframeworks it consumes as a declared binary dependency, so +# frameworks installed this way arrive with no provenance record regardless of how +# well the source xcframework was signed. That is what Apple reports as +# `ITMS-91065: Missing signature`. +# +# `warning:` is the prefix Xcode surfaces in the build log and issue navigator. +echo "warning: serious_python: the CocoaPods iOS path copies inner .framework bundles and cannot produce SDK-origin XCFramework signature receipts. Use the Swift Package Manager path for App Store submissions." + # modify Flutter packaging script to include user frameworks if [ -n "$PODS_TARGET_SRCROOT" ]; then echo "modify Flutter packaging script to include user frameworks: $PODS_TARGET_SRCROOT" diff --git a/src/serious_python_darwin/darwin/prepare_ios.sh b/src/serious_python_darwin/darwin/prepare_ios.sh index 49902602..02f571ae 100755 --- a/src/serious_python_darwin/darwin/prepare_ios.sh +++ b/src/serious_python_darwin/darwin/prepare_ios.sh @@ -63,3 +63,15 @@ if [ ! -d "$dist/xcframeworks/dart_bridge.xcframework" ] || [ "$(cat "$db_marker unzip -q "$dart_bridge_path" -d "$dist/xcframeworks/" echo "$dart_bridge_version" > "$db_marker" fi + +# ---- provider provenance --------------------------------------------------- +# Both provider archives are now extracted. Verify their signatures at the point +# of arrival (so a bad download is caught here, not in an App Store rejection), +# and record a digest manifest of everything they contain. Every later staging +# step re-checks that manifest: the provider bundles must reach the app exactly +# as their publisher signed them, because editing any file inside one destroys +# the SDK-origin signature that the IPA's Signatures/ receipts report on. +. "$script_dir/xcframework_verify.sh" +spv_verify_provider "$dist/xcframeworks" "$dist/python-xcframeworks" || exit 1 +spv_manifest_record "$dist/.provider-manifests/xcframeworks.sha256" "$dist/xcframeworks" +spv_manifest_record "$dist/.provider-manifests/python-xcframeworks.sha256" "$dist/python-xcframeworks" diff --git a/src/serious_python_darwin/darwin/prepare_macos.sh b/src/serious_python_darwin/darwin/prepare_macos.sh index cd0f516b..ea4ebf50 100755 --- a/src/serious_python_darwin/darwin/prepare_macos.sh +++ b/src/serious_python_darwin/darwin/prepare_macos.sh @@ -76,3 +76,16 @@ if [ ! -d "$dist/xcframeworks/dart_bridge.xcframework" ] || [ "$(cat "$db_marker unzip -q "$dart_bridge_path" -d "$dist/xcframeworks/" echo "$dart_bridge_version" > "$db_marker" fi + +# ---- provider provenance --------------------------------------------------- +# Runs last, and in particular AFTER the Python.app removal above. That removal +# is a legacy safety net -- python-build has excluded Python.app from the shipped +# framework since the exclude list gained it, so on any current artifact it +# matches nothing -- but it does delete a file from inside a provider bundle, so +# it has to happen before the signature check and the digest snapshot rather +# than after. Nothing downstream of this point may modify these bundles: editing +# any file inside a signed XCFramework destroys the SDK-origin signature that the +# IPA's Signatures/ receipts report on. +. "$script_dir/xcframework_verify.sh" +spv_verify_provider "$dist/xcframeworks" || exit 1 +spv_manifest_record "$dist/.provider-manifests/xcframeworks.sha256" "$dist/xcframeworks" diff --git a/src/serious_python_darwin/darwin/python_versions.properties b/src/serious_python_darwin/darwin/python_versions.properties index 34e8f7f0..ea1da9cf 100644 --- a/src/serious_python_darwin/darwin/python_versions.properties +++ b/src/serious_python_darwin/darwin/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260727). Do not edit by hand. +# python-build manifest.json (release 20260729). Do not edit by hand. default_python_version=3.14 -dart_bridge_version=1.6.1 -python_build_release_date=20260727 +dart_bridge_version=1.7.0 +python_build_release_date=20260729 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_darwin/darwin/serious_python_darwin.podspec b/src/serious_python_darwin/darwin/serious_python_darwin.podspec index a2a3caf0..5bf40acf 100644 --- a/src/serious_python_darwin/darwin/serious_python_darwin.podspec +++ b/src/serious_python_darwin/darwin/serious_python_darwin.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'serious_python_darwin' - s.version = '4.4.2' + s.version = '4.5.0' s.summary = 'A cross-platform plugin for adding embedded Python runtime to your Flutter apps.' s.description = <<-DESC A cross-platform plugin for adding embedded Python runtime to your Flutter apps. diff --git a/src/serious_python_darwin/darwin/stage_spm.sh b/src/serious_python_darwin/darwin/stage_spm.sh index 0ebba398..06e9296f 100755 --- a/src/serious_python_darwin/darwin/stage_spm.sh +++ b/src/serious_python_darwin/darwin/stage_spm.sh @@ -18,6 +18,9 @@ res="$pkg/Sources/serious_python_darwin/Resources" [ -d "$dist" ] || { echo "stage_spm: $dist not found" >&2; exit 1; } +# shellcheck source=src/serious_python_darwin/darwin/xcframework_verify.sh +. "$script_dir/xcframework_verify.sh" + # 1. Python runtime (dynamic framework -> embedded). Platform-specific path so a # single shared manifest can carry both via platform-conditional binaryTargets. rm -rf "$pkg/Python-$platform.xcframework" @@ -44,6 +47,41 @@ for tree in stdlib site-packages app; do [ -d "$dist/$tree" ] && rsync -a --exclude '.pod' "$dist/$tree/" "$dest/" done +# 4b. Provenance gate. This is the last place serious_python touches the provider +# XCFrameworks before Xcode consumes them as binaryTargets and writes the +# IPA's Signatures/ receipts, so it is the last chance to catch a mutation. +# The staged copies are compared against the digest manifest recorded at +# extraction time; `cp -R` preserves content, symlinks, and _CodeSignature, +# so an identical manifest here means the SDK-origin signature survived. +# +# Python-.xcframework is a renamed copy, so it is checked against +# the manifest entries for its source name rather than by path. +# +# Everything in this block is redirected to stderr: this script's stdout is +# the SP_NATIVE_SET key, which prepare_spm.sh captures verbatim. +{ + python_manifest="$dist/.provider-manifests/Python.xcframework.sha256" + spv_manifest_record "$python_manifest" "$dist/xcframeworks/Python.xcframework" + spv_manifest_check "$python_manifest" "$pkg/Python-$platform.xcframework" \ + "staged Python-$platform.xcframework" + + dart_bridge_manifest="$dist/.provider-manifests/dart_bridge.xcframework.sha256" + spv_manifest_record "$dart_bridge_manifest" "$dist/xcframeworks/dart_bridge.xcframework" + spv_manifest_check "$dart_bridge_manifest" "$pkg/dart_bridge.xcframework" \ + "staged dart_bridge.xcframework" + + if [ "$platform" = "ios" ] && [ -d "$pkg/extra-xcframeworks" ]; then + # extra-xcframeworks mixes provider stdlib frameworks with frameworks + # built locally from the app's own wheels. spv_manifest_check ignores + # files the manifest doesn't name, so this validates exactly the + # provider subset. + spv_manifest_check "$dist/.provider-manifests/python-xcframeworks.sha256" \ + "$pkg/extra-xcframeworks" "staged extra-xcframeworks (provider subset)" + fi + + spv_verify_provider "$pkg/Python-$platform.xcframework" "$pkg/dart_bridge.xcframework" +} >&2 + # 5. Cache-bust key: platform + Python version + the staged native/resource set # (path+size). Changes whenever requirements, app, or Python version change. key_paths=("Python-$platform.xcframework" "Sources/serious_python_darwin/Resources") diff --git a/src/serious_python_darwin/darwin/sync_site_packages.sh b/src/serious_python_darwin/darwin/sync_site_packages.sh index d08bad36..ed065943 100755 --- a/src/serious_python_darwin/darwin/sync_site_packages.sh +++ b/src/serious_python_darwin/darwin/sync_site_packages.sh @@ -1,5 +1,9 @@ script_dir=$(cd "$(dirname "$0")" && pwd -P) +# Provider-signature + provider-integrity checks (see xcframework_verify.sh). +# Sourced unconditionally so both the iOS and macOS branches can use it. +source $script_dir/xcframework_verify.sh + # App sources are arch- and platform-independent; stage them as a bare `app/` # resource bundle into BOTH dist trees, regardless of whether site-packages # exist (an app may have no pip dependencies). The per-target resource bundle @@ -21,14 +25,15 @@ if [[ -n "$SERIOUS_PYTHON_SITE_PACKAGES" && -d "$SERIOUS_PYTHON_SITE_PACKAGES" ] echo "Sync iOS xcframeworks and site-packages" dist=$script_dir/dist_ios - # app xcframeworks - rm -rf $dist/site-xcframeworks - mkdir -p $dist/site-xcframeworks - cp -R $dist/python-xcframeworks/* $dist/site-xcframeworks - source $script_dir/xcframework_utils.sh tmp_dir=$(mktemp -d) + # Locally generated frameworks are built and mutated HERE, in their own + # directory, never alongside the provider-built ones. Every app-specific + # rewrite below (bundle ids, install names, ad-hoc re-sign) is confined + # to this tree; the provider artifacts are merged in afterwards, + # byte-for-byte, and are never opened for writing. + local_xcframeworks=$(mktemp -d) cp -R $SERIOUS_PYTHON_SITE_PACKAGES/* $tmp_dir @@ -45,47 +50,75 @@ if [[ -n "$SERIOUS_PYTHON_SITE_PACKAGES" && -d "$SERIOUS_PYTHON_SITE_PACKAGES" ] $dylib_relative_path \ "$_sp_ext" \ "Frameworks/serious_python_darwin.framework/python.bundle/site-packages" \ - $dist/site-xcframeworks + $local_xcframeworks done done - # Namespace the frameworks' CFBundleIdentifiers under the host app's - # bundle id (see xcframework_utils.sh). Runs before the reconcile pass - # below, whose ad-hoc re-sign reseals the modified Info.plists. Skipped - # when the caller doesn't supply a bundle id, leaving the org.python.* - # defaults in place. + # Namespace the LOCALLY BUILT frameworks' CFBundleIdentifiers under the + # host app's bundle id (see xcframework_utils.sh). These are created + # moments ago from this app's own wheels; nobody has signed them, so + # editing their plists invalidates nothing. + # + # This deliberately no longer touches Python.xcframework or the stdlib + # extension frameworks. Those arrive already signed by their provider, + # and a plist rewrite destroys that signature -- which is what made the + # IPA's Signatures/ receipts report `signed = false` and produced + # ITMS-91065. They now carry stable `dev.flet.python.*` identifiers + # assigned upstream in python-build, so there is nothing left to fix + # here anyway. if [[ -n "${SERIOUS_PYTHON_BUNDLE_ID:-}" ]]; then echo "Namespacing framework bundle identifiers under $SERIOUS_PYTHON_BUNDLE_ID" - rewrite_framework_bundle_ids "$dist/site-xcframeworks" "$SERIOUS_PYTHON_BUNDLE_ID" || exit 1 - # Python.xcframework is staged straight out of dist/xcframeworks by - # stage_spm.sh (and referenced there by the podspec), so it never - # passes through site-xcframeworks above and kept a shared - # `org.python.python`. Nothing resolves it by identifier -- the string - # appears in no binary and there are no CFBundleGetBundleWithIdentifier - # lookups -- so renaming it is inert at runtime. dart_bridge is left - # alone: `dev.flet.dartbridge` is already a vendor-owned identifier. - rewrite_framework_bundle_ids "$dist/xcframeworks" "$SERIOUS_PYTHON_BUNDLE_ID" "dart_bridge" || exit 1 + rewrite_framework_bundle_ids "$local_xcframeworks" "$SERIOUS_PYTHON_BUNDLE_ID" || exit 1 fi # After every .so/.dylib is framework-ized, reconcile the Mach-O # install names so the interdependent @rpath refs point at the new # framework paths (serious-python #223). Without this, dyld cannot # resolve e.g. @rpath/libarrow.dylib at launch and the app crashes - # before Python starts. Exclude the python/stdlib xcframeworks copied - # in above -- they are already correct. + # before Python starts. # A reconcile failure (e.g. a Mach-O with no header space to grow a load # command, or a signing error) would otherwise ship an app that crashes # at launch -- abort the build instead. sync_site_packages.sh has no # set -e; prepare_spm.sh runs it under set -euo pipefail, so a non-zero # exit here fails `flet build` loudly. - reconcile_framework_install_names "$dist/site-xcframeworks" "$dist/python-xcframeworks" || exit 1 + # + # The exclude argument still names the provider stdlib directory. It is + # now belt and braces -- $local_xcframeworks holds only locally built + # frameworks -- but it keeps the guarantee explicit: this pass rewrites + # Mach-O headers and re-signs ad-hoc, and must never reach a provider + # binary. + reconcile_framework_install_names "$local_xcframeworks" "$dist/python-xcframeworks" || exit 1 + + # Assemble the staging directory: provider frameworks first, copied + # verbatim, then the locally built ones merged over the top (a name + # collision resolves to the local build, as it always has). The rm -rf + # in the loop is what makes that an overwrite rather than a `cp -R` + # merge, which would leave the provider's files behind inside it. + rm -rf $dist/site-xcframeworks + mkdir -p $dist/site-xcframeworks + cp -R $dist/python-xcframeworks/* $dist/site-xcframeworks + for _local_xcf in "$local_xcframeworks"/*.xcframework; do + [ -d "$_local_xcf" ] || continue + rm -rf "$dist/site-xcframeworks/$(basename "$_local_xcf")" + cp -R "$_local_xcf" "$dist/site-xcframeworks/" + done rm -rf $dist/site-packages mkdir -p $dist/site-packages cp -R $tmp_dir/${archs[0]}/* $dist/site-packages # cleanup - rm -rf "${tmp_dir}" >/dev/null + rm -rf "${tmp_dir}" "${local_xcframeworks}" >/dev/null + + # The provider artifacts must be bit-identical to what was extracted in + # prepare_ios.sh, both where they live and in the copies just staged. + spv_manifest_check "$dist/.provider-manifests/xcframeworks.sha256" \ + "$dist/xcframeworks" "dist_ios/xcframeworks" || exit 1 + spv_manifest_check "$dist/.provider-manifests/python-xcframeworks.sha256" \ + "$dist/python-xcframeworks" "dist_ios/python-xcframeworks" || exit 1 + spv_manifest_check "$dist/.provider-manifests/python-xcframeworks.sha256" \ + "$dist/site-xcframeworks" "dist_ios/site-xcframeworks (provider subset)" || exit 1 + spv_verify_provider "$dist/xcframeworks" "$dist/python-xcframeworks" || exit 1 else @@ -103,5 +136,12 @@ if [[ -n "$SERIOUS_PYTHON_SITE_PACKAGES" && -d "$SERIOUS_PYTHON_SITE_PACKAGES" ] # file. .pod is only needed by package_command.dart at packaging # time to invoke this sync script; it does not belong in the bundle. rsync -av --delete --exclude '.pod' "$SERIOUS_PYTHON_SITE_PACKAGES/" "$dist/site-packages/" + + # macOS has no framework-ization step -- its .so's load flat from the + # resource tree -- so nothing here should ever touch the provider + # xcframeworks. Assert it rather than assume it. + spv_manifest_check "$dist/.provider-manifests/xcframeworks.sha256" \ + "$dist/xcframeworks" "dist_macos/xcframeworks" || exit 1 + spv_verify_provider "$dist/xcframeworks" || exit 1 fi fi diff --git a/src/serious_python_darwin/darwin/xcframework_utils.sh b/src/serious_python_darwin/darwin/xcframework_utils.sh index dd5ea203..95c4bbde 100644 --- a/src/serious_python_darwin/darwin/xcframework_utils.sh +++ b/src/serious_python_darwin/darwin/xcframework_utils.sh @@ -106,33 +106,41 @@ create_xcframework_from_dylibs() { rm -rf "${dylib_tmp_dir}" >/dev/null } -# Namespace every generated framework's CFBundleIdentifier under the host app's +# Namespace LOCALLY BUILT frameworks' CFBundleIdentifiers under the host app's # bundle id, mirroring what CPython's own iOS support does when it converts a # .so into a framework (Platforms/Apple/testbed/Python.xcframework/build/utils.sh): # # FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr "_" "-") # -# The frameworks are built long before the app's bundle id is known -- python-build -# has no idea what app its stdlib will end up in -- so they carry a placeholder -# `org.python.` until this pass. That placeholder is byte-identical in every -# app that ships serious_python, and it also becomes the framework's CODE SIGNING -# identifier, which is the one field that survives Xcode's `codesign -f` at embed -# and again at exportArchive. A globally-shared identifier on a framework Apple -# fingerprints as a listed third-party SDK is the leading explanation for the -# ITMS-91065 rejection in flet-dev/flet#6724. +# SCOPE -- read this before adding a call site. +# This may only be pointed at frameworks create_xcframework_from_dylibs built +# moments earlier from the app's own site-packages. It must NEVER be pointed at a +# provider-built artifact (Python.xcframework, dart_bridge.xcframework, or the +# stdlib extension frameworks from python-build). +# +# It used to be. The reasoning was that a globally-shared `org.python.` +# identifier on a framework Apple fingerprints as a third-party SDK explained the +# ITMS-91065 rejection in flet-dev/flet#6724. That hypothesis was wrong, and the +# fix was actively harmful: rewriting an Info.plist inside a provider XCFramework +# invalidates the provider's signature, and it is precisely that SDK-origin +# signature which ITMS-91065 is reporting missing. Xcode records it in the IPA's +# top-level Signatures/ receipts, separately from -- and unaffected by -- the app's +# own signature applied at embed and exportArchive. +# +# Provider frameworks now arrive with stable, provider-owned `dev.flet.python.*` +# identifiers assigned upstream in python-build (dart_bridge has always used +# `dev.flet.dartbridge`), so there is nothing here left to fix. # # The leading hyphen this produces for underscore-prefixed modules (`_ssl` -> # `.-ssl`) is deliberate: it is what keeps `_ssl` distinct from `ssl`, and it # is the exact form CPython and BeeWare ship. # # Must run BEFORE reconcile_framework_install_names, whose ad-hoc re-sign reseals -# the modified Info.plists. The stdlib xcframeworks copied in from python-build are -# unsigned at this point, so editing their plists invalidates nothing. +# the modified Info.plists. # -# $3 is an optional space-separated list of framework names to leave alone -- -# used for artifacts that already carry a vendor-owned reverse-DNS identifier -# (dart_bridge is `dev.flet.dartbridge`), which is the shape we are aiming for -# rather than the problem we are fixing. +# $3 is an optional space-separated list of framework names to leave alone. No +# current caller passes it; it is kept because the guarantee it encodes -- some +# names are off limits -- is cheaper to keep than to re-derive. rewrite_framework_bundle_ids() { local xcframeworks_dir=$1 local bundle_id=${2%.} diff --git a/src/serious_python_darwin/darwin/xcframework_verify.sh b/src/serious_python_darwin/darwin/xcframework_verify.sh new file mode 100644 index 00000000..34d76162 --- /dev/null +++ b/src/serious_python_darwin/darwin/xcframework_verify.sh @@ -0,0 +1,206 @@ +#!/usr/bin/env bash +# +# Consumer-side integrity checks for the XCFrameworks serious_python does NOT +# build: Python.xcframework and the stdlib extension frameworks from +# flet-dev/python-build, and dart_bridge.xcframework from flet-dev/dart-bridge. +# +# WHY +# Xcode writes an SDK-origin receipt for every XCFramework an app links +# against into the IPA's `Signatures/` directory, reporting whether the +# XCFramework AS SHIPPED BY ITS PROVIDER was signed and carried a secure +# timestamp. Signing the app does not fill that in, and any edit to a file +# inside a signed XCFramework destroys it — which is exactly what this plugin +# used to do when it rewrote CFBundleIdentifier in the provider bundles. +# +# So this file exists to answer two questions at each staging step: +# 1. does the provider signature still verify? (spv_verify_provider) +# 2. did anything inside the provider bundles change? (spv_manifest_*) +# +# Question 2 matters even while the upstream artifacts are still unsigned: the +# digest manifest catches a reintroduced mutation immediately, rather than in +# an App Store rejection three releases later. +# +# MODE — SERIOUS_PYTHON_VERIFY_PROVIDER_SIGNATURES +# warn (default) report problems, do not fail the build +# require a missing or invalid provider signature fails the build +# off skip signature checks entirely +# +# The default is `warn` because a consumer may pin an older, pre-signing +# python-build / dart-bridge release through SERIOUS_PYTHON_BUILD_DATE or +# DART_BRIDGE_VERSION, and that must keep building. serious_python's own CI +# sets `require`. +# +# Digest-manifest checks are NOT gated by this: they cover code in this repo, +# which is always expected to leave provider bundles untouched. +# +# SERIOUS_PYTHON_EXPECTED_TEAM_ID +# When set, the provider signature's TeamIdentifier must match it. +# +# Written to POSIX sh so the podspec's prepare_command (run through /bin/sh) can +# source it as-is. + +spv_mode() { echo "${SERIOUS_PYTHON_VERIFY_PROVIDER_SIGNATURES:-warn}"; } + +spv_note() { echo "provider-verify: $*"; } + +# Report one problem. +# +# In `require` mode every problem is printed and the caller fails. In `warn` mode +# only the first few are printed and the rest are counted: a build against a +# pre-signing python-build release has ~57 unsigned xcframeworks, and 57 +# identical lines in every `flet build` teaches people to ignore the message. +# The summary line at the end always reports the true total. +spv_bad() { + _spv_problems=$((${_spv_problems:-0} + 1)) + if [ "$(spv_mode)" = "require" ] || [ "$_spv_problems" -le 3 ]; then + echo "provider-verify: $*" >&2 + elif [ "$_spv_problems" -eq 4 ]; then + echo "provider-verify: ... further problems suppressed;" \ + "set SERIOUS_PYTHON_VERIFY_PROVIDER_SIGNATURES=require to see them all" >&2 + fi + # Written as an `if` rather than `[ ] && return 1` so a caller running under + # `set -e` is not aborted by the false test in `warn` mode. + if [ "$(spv_mode)" = "require" ]; then + return 1 + fi + return 0 +} + +# Emit the xcframework paths a root refers to. A root is either an +# `*.xcframework` itself or a directory containing some. +spv_each() { + case "$1" in + *.xcframework) + if [ -d "$1" ]; then printf '%s\n' "$1"; fi + ;; + *) + for _spv_x in "$1"/*.xcframework; do + if [ -d "$_spv_x" ]; then printf '%s\n' "$_spv_x"; fi + done + ;; + esac + return 0 +} + +# Verify the provider signature on every xcframework under the given roots. +# +# Roots must name PROVIDER artifacts only. Frameworks this plugin generates from +# the app's own site-packages are built locally, carry an ad-hoc signature, and +# are correctly excluded — requiring a provider identity on them would be wrong. +spv_verify_provider() { + _spv_mode=$(spv_mode) + if [ "$_spv_mode" = "off" ]; then + return 0 + fi + + _spv_status=0 + _spv_count=0 + _spv_problems=0 + for _spv_root in "$@"; do + [ -e "$_spv_root" ] || continue + for _spv_xcf in $(spv_each "$_spv_root"); do + _spv_count=$((_spv_count + 1)) + + if [ ! -f "$_spv_xcf/_CodeSignature/CodeResources" ]; then + spv_bad "$_spv_xcf: no provider signature (unsigned XCFramework);" \ + "its IPA receipt will report signed = false" || _spv_status=1 + continue + fi + + if ! _spv_out=$(codesign --verify --strict --verbose=4 "$_spv_xcf" 2>&1); then + spv_bad "$_spv_xcf: provider signature does not verify: $_spv_out" || _spv_status=1 + continue + fi + + _spv_info=$(codesign -dvvv "$_spv_xcf" 2>&1) || _spv_info="" + + if printf '%s\n' "$_spv_info" | grep -q '^Signature=adhoc'; then + spv_bad "$_spv_xcf: ad-hoc signature, not a provider signature" || _spv_status=1 + continue + fi + + if ! printf '%s\n' "$_spv_info" | grep -q '^Timestamp='; then + spv_bad "$_spv_xcf: no secure timestamp;" \ + "its IPA receipt will report isSecureTimestamp = false" || _spv_status=1 + continue + fi + + if [ -n "${SERIOUS_PYTHON_EXPECTED_TEAM_ID:-}" ]; then + _spv_team=$(printf '%s\n' "$_spv_info" | sed -n 's/^TeamIdentifier=//p' | head -1) + if [ "$_spv_team" != "$SERIOUS_PYTHON_EXPECTED_TEAM_ID" ]; then + spv_bad "$_spv_xcf: TeamIdentifier '$_spv_team' !=" \ + "expected '$SERIOUS_PYTHON_EXPECTED_TEAM_ID'" || _spv_status=1 + continue + fi + fi + done + done + + if [ "$_spv_count" -eq 0 ]; then + # An empty tree must never read as "everything passed". + spv_bad "no *.xcframework found under: $*" || _spv_status=1 + elif [ "$_spv_problems" -gt 0 ]; then + spv_note "$_spv_problems of $_spv_count provider xcframework(s) lack a usable" \ + "SDK-origin signature; the resulting IPA will report them as unsigned" + else + spv_note "provider signatures verified on $_spv_count xcframework(s)" + fi + return $_spv_status +} + +# Record a digest manifest of everything under $2, keyed on paths relative to +# it, so the same manifest can be checked against a copy staged elsewhere. +# +# Symlinks are recorded by target, not followed: the macOS Python.framework is a +# versioned bundle whose symlinks are part of what codesign seals. +spv_manifest_record() { + _spv_out=$1 + _spv_root=$2 + [ -d "$_spv_root" ] || return 0 + + mkdir -p "$(dirname "$_spv_out")" + { + ( cd "$_spv_root" && find . -type f -exec shasum -a 256 {} + ) + ( cd "$_spv_root" && find . -type l -exec sh -c \ + 'for l; do printf "symlink:%s %s\n" "$(readlink "$l")" "$l"; done' _ {} + ) + } | LC_ALL=C sort > "$_spv_out" + spv_note "recorded $(wc -l < "$_spv_out" | tr -d ' ') digests for $_spv_root" +} + +# Check that every path recorded in $1 still exists under $2 with the same +# content. Extra files under $2 are ignored, so one manifest of the provider +# stdlib frameworks also validates the merged directory they are staged into +# alongside locally generated ones. +# +# Always fatal. Nothing in this repo is allowed to modify a provider bundle, so +# a mismatch is a bug here, not a property of whatever upstream release is +# pinned. +spv_manifest_check() { + _spv_manifest=$1 + _spv_root=$2 + _spv_label=${3:-$_spv_root} + + [ -f "$_spv_manifest" ] || { spv_note "no manifest at $_spv_manifest; skipping check"; return 0; } + [ -d "$_spv_root" ] || { echo "provider-verify: $_spv_root missing" >&2; return 1; } + + _spv_actual=$(mktemp) + { + ( cd "$_spv_root" && find . -type f -exec shasum -a 256 {} + ) + ( cd "$_spv_root" && find . -type l -exec sh -c \ + 'for l; do printf "symlink:%s %s\n" "$(readlink "$l")" "$l"; done' _ {} + ) + } | LC_ALL=C sort > "$_spv_actual" + + # Only the recorded lines have to be present; that is what makes this work + # against a directory holding additional, locally built frameworks. + _spv_missing=$(LC_ALL=C comm -23 "$_spv_manifest" "$_spv_actual") + rm -f "$_spv_actual" + + if [ -n "$_spv_missing" ]; then + echo "provider-verify: provider artifacts changed during staging ($_spv_label):" >&2 + printf '%s\n' "$_spv_missing" | head -20 >&2 || true + echo "provider-verify: editing anything inside a provider XCFramework destroys its" \ + "SDK-origin signature; stage it byte-for-byte instead" >&2 + return 1 + fi + spv_note "provider artifacts unchanged ($_spv_label)" +} diff --git a/src/serious_python_darwin/pubspec.yaml b/src/serious_python_darwin/pubspec.yaml index 7b49b96e..8b036be9 100644 --- a/src/serious_python_darwin/pubspec.yaml +++ b/src/serious_python_darwin/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_darwin description: iOS and macOS implementations of the serious_python plugin homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.4.2 +version: 4.5.0 environment: # The Swift Package Manager build path needs Flutter 3.44 / Dart 3.11 (the diff --git a/src/serious_python_linux/CHANGELOG.md b/src/serious_python_linux/CHANGELOG.md index b905dfba..0953aca4 100644 --- a/src/serious_python_linux/CHANGELOG.md +++ b/src/serious_python_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.5.0 + +* Re-pins the bundled python-build snapshot to **20260729** and `dart_bridge` to **1.7.0**. Both exist to provider-sign the Apple XCFrameworks (see `serious_python_darwin` 4.5.0). Nothing about Linux changed: no Python version moved from 20260727 (**3.12.13 / 3.13.14 / 3.14.6**), and `libdart_bridge-linux-*.so` is byte-identical to 1.6.1 — verified, not assumed. + ## 4.4.2 * Re-pins the bundled python-build snapshot to **20260727**. Its only functional change is to the iOS frameworks' `Info.plist` (see `serious_python_darwin` 4.4.2); no versions moved and the Linux runtime is unchanged from 20260726. diff --git a/src/serious_python_linux/linux/python_versions.properties b/src/serious_python_linux/linux/python_versions.properties index 34e8f7f0..ea1da9cf 100644 --- a/src/serious_python_linux/linux/python_versions.properties +++ b/src/serious_python_linux/linux/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260727). Do not edit by hand. +# python-build manifest.json (release 20260729). Do not edit by hand. default_python_version=3.14 -dart_bridge_version=1.6.1 -python_build_release_date=20260727 +dart_bridge_version=1.7.0 +python_build_release_date=20260729 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_linux/pubspec.yaml b/src/serious_python_linux/pubspec.yaml index 6eceec74..925b8a2a 100644 --- a/src/serious_python_linux/pubspec.yaml +++ b/src/serious_python_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_linux description: Linux implementations of the serious_python plugin homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.4.2 +version: 4.5.0 environment: sdk: '>=3.1.3 <4.0.0' diff --git a/src/serious_python_platform_interface/CHANGELOG.md b/src/serious_python_platform_interface/CHANGELOG.md index 0288708c..c6e13e09 100644 --- a/src/serious_python_platform_interface/CHANGELOG.md +++ b/src/serious_python_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.5.0 + +* Version bump aligning with the `serious_python_*` 4.5.0 release. + ## 4.4.2 * Version bump aligning with the `serious_python_*` 4.4.2 release. diff --git a/src/serious_python_platform_interface/pubspec.yaml b/src/serious_python_platform_interface/pubspec.yaml index d6f77f41..3ae8281a 100644 --- a/src/serious_python_platform_interface/pubspec.yaml +++ b/src/serious_python_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_platform_interface description: A common platform interface for the serious_python plugin. homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.4.2 +version: 4.5.0 environment: sdk: ">=3.0.0 <4.0.0" diff --git a/src/serious_python_windows/CHANGELOG.md b/src/serious_python_windows/CHANGELOG.md index 93a8faff..aee2da86 100644 --- a/src/serious_python_windows/CHANGELOG.md +++ b/src/serious_python_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.5.0 + +* Re-pins the bundled python-build snapshot to **20260729** and `dart_bridge` to **1.7.0**. Both exist to provider-sign the Apple XCFrameworks (see `serious_python_darwin` 4.5.0); no Python version moved from 20260727 (**3.12.13 / 3.13.14 / 3.14.6**). `dart_bridge.dll` is rebuilt rather than byte-identical — MSVC stamps a build timestamp and PDB GUID into every DLL — but no Windows source changed between 1.6.1 and 1.7.0. + ## 4.4.2 * Re-pins the bundled python-build snapshot to **20260727**. Its only functional change is to the iOS frameworks' `Info.plist` (see `serious_python_darwin` 4.4.2); no versions moved and the Windows runtime is unchanged from 20260726. diff --git a/src/serious_python_windows/pubspec.yaml b/src/serious_python_windows/pubspec.yaml index 1c99abad..40191744 100644 --- a/src/serious_python_windows/pubspec.yaml +++ b/src/serious_python_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_windows description: Windows implementations of the serious_python plugin homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.4.2 +version: 4.5.0 environment: sdk: '>=3.1.3 <4.0.0' diff --git a/src/serious_python_windows/windows/python_versions.properties b/src/serious_python_windows/windows/python_versions.properties index 34e8f7f0..ea1da9cf 100644 --- a/src/serious_python_windows/windows/python_versions.properties +++ b/src/serious_python_windows/windows/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260727). Do not edit by hand. +# python-build manifest.json (release 20260729). Do not edit by hand. default_python_version=3.14 -dart_bridge_version=1.6.1 -python_build_release_date=20260727 +dart_bridge_version=1.7.0 +python_build_release_date=20260729 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14