From bf7a47becd1e51168fbe363192cd5bdc5ceb8910 Mon Sep 17 00:00:00 2001 From: jwinterm Date: Tue, 27 Oct 2026 15:34:33 -0400 Subject: [PATCH] ci(macos): restore LibTorch.framework's symlinks before signing torch_dart's prebuilt tarball stores the framework's Versions/Current, top-level binary and Resources as copies of Versions/A, and codesign refuses the result as an ambiguous bundle, failing the app's signature. relink_framework.sh puts the three symlinks back, after checking that every copy it replaces matches Versions/A byte for byte. --- .github/workflows/build-macos.yml | 4 +++ scripts/macos/relink_framework.sh | 48 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100755 scripts/macos/relink_framework.sh diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index fda25d95b..b8b79bca4 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -169,6 +169,10 @@ jobs: tar -xzf torch_dart.tar.gz -C torch_dart rm torch_dart.tar.gz popd + # The tarball is packed on Linux with the framework's symlinks stored + # as copies, which codesign refuses as an ambiguous bundle. + scripts/macos/relink_framework.sh \ + scripts/torch_dart/macos/LibTorch.xcframework/macos-arm64_x86_64/LibTorch.framework - name: Fetch prebuilt reown_flutter run: | diff --git a/scripts/macos/relink_framework.sh b/scripts/macos/relink_framework.sh new file mode 100755 index 000000000..1ece9a3dc --- /dev/null +++ b/scripts/macos/relink_framework.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Restore the symlinks of a versioned macOS framework whose archive flattened them. +# +# Usage: relink_framework.sh +# +# torch_dart's prebuilt tarball is packed on Linux, and tar there stores +# Versions/Current, the top-level binary and Resources as full copies of +# Versions/A. codesign then calls the bundle "ambiguous (could be app or +# framework)" and refuses to sign the app around it. A versioned framework +# needs exactly: +# Versions/Current -> A +# -> Versions/Current/ +# Resources -> Versions/Current/Resources +# Every copy is compared with Versions/A before it is replaced, so this never +# throws away a file that differs. +set -euo pipefail + +[ $# -eq 1 ] || { echo "usage: $0 " >&2; exit 64; } +fw=${1%/} +name=$(basename "$fw" .framework) +[ -f "$fw/Versions/A/$name" ] || { + echo "FATAL: $fw has no Versions/A/$name; not a versioned framework" >&2; exit 1; } + +same() { # same : the copy exists and matches Versions/A + [ -L "$1" ] && return 1 + diff -r "$1" "$2" >/dev/null || { + echo "FATAL: $1 differs from $2; refusing to replace it with a symlink" >&2; exit 1; } +} + +relink() { # relink + if [ -L "$fw/$1" ]; then + [ "$(readlink "$fw/$1")" = "$2" ] || { + echo "FATAL: $fw/$1 points at $(readlink "$fw/$1"), not $2" >&2; exit 1; } + return + fi + rm -rf "${fw:?}/$1" + ln -s "$2" "$fw/$1" + echo "relinked $fw/$1 -> $2" +} + +[ -e "$fw/Versions/Current" ] && ! [ -L "$fw/Versions/Current" ] && same "$fw/Versions/Current" "$fw/Versions/A" +relink Versions/Current A +[ -e "$fw/$name" ] && ! [ -L "$fw/$name" ] && same "$fw/$name" "$fw/Versions/A/$name" +relink "$name" "Versions/Current/$name" +if [ -d "$fw/Versions/A/Resources" ]; then + [ -e "$fw/Resources" ] && ! [ -L "$fw/Resources" ] && same "$fw/Resources" "$fw/Versions/A/Resources" + relink Resources Versions/Current/Resources +fi