Skip to content

Commit b01798a

Browse files
helenkwokclaude
andcommitted
feat(14-08): rewrite sync script to pull from GitHub Release + seed manifest
sync-litertlm-swift.sh rewritten (D-34, D-35): - Takes a tag arg (e.g. v0.7.3+rewrap.1) - Fetches rewrap-manifest.json FIRST (trust anchor, small payload) - Validates schema_version=1 + >=2 xcframeworks entries - For EACH xcframework: fetches zip, verifies SHA-256 against manifest, unzips into ios/Frameworks/; fails loudly on any mismatch - Copies manifest to ios/Frameworks/rewrap-manifest.json (source control) - Bumps package.json to 0.2.0-litertlm.<upstream>.r<iter> (D-35) - Appends CHANGELOG.md entry ios/Frameworks/.gitignore: *.xcframework — binaries gitignored (large platform binaries) !rewrap-manifest.json — manifest explicitly NOT gitignored (trust anchor) ios/Frameworks/rewrap-manifest.json: Placeholder manifest seeded (real values populated by first sync run). schema_version=1, dual xcframework entries, all sha256=0 until real sync. CHANGELOG.md: documents the seam change for this plan. Phase 14 D-31,D-34,D-35 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0304087 commit b01798a

4 files changed

Lines changed: 162 additions & 59 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## 0.2.0-dev.1 — Phase 14 (in progress, 2026-05-15)
44

5+
- **Changed:** `scripts/sync-litertlm-swift.sh` rewritten — now fetches rewrapped LiteRTLM-Swift from `helenkwok/LiteRTLM-Swift`'s GitHub Release by tag, verifies SHA-256 against `rewrap-manifest.json`, places artifacts under `ios/Frameworks/`. Manual sync per Phase 14 D-34. Run `make sync TAG=v<upstream>+rewrap.<n>`.
6+
- **Added:** `ios/Frameworks/rewrap-manifest.json` — trust anchor (source-controlled JSON, populated by sync script). xcframework binaries remain gitignored.
7+
- **Added:** `ExpoLitertLm.podspec` now reads `vendored_frameworks` and `s.version` from `ios/Frameworks/rewrap-manifest.json` via Ruby `JSON.parse` at install time. Phase 14 D-31 single source of truth.
8+
- **Added:** Layer A/B/C verification gates (`scripts/verify-consumption.sh`, `make verify`). Layer A = pod install from fresh consumer; Layer B = grep gate for raw xcframework refs; Layer C = manifest-driven podspec consistency.
9+
- **Added:** `.github/workflows/verify-consumption.yml` — CI mirrors fork's three-layer gate on macos-latest.
10+
511
- **BREAKING:** iOS path migrated from `MediaPipeTasksGenAI` default-dep to vendored `LiteRTLM-Swift` (`CLiteRTLM.xcframework` + `GemmaModelConstraintProvider.xcframework`). Consumers must run `pod install` after upgrade.
612
- **BREAKING:** `ExpoLitertLm.podspec` no longer carries `static_framework = true` — see CocoaPods issue [#11948](https://github.com/CocoaPods/CocoaPods/issues/11948) + offlineaid Phase 14 CONTEXT D-21. Static linkage of a dylib-containing xcframework crashes the host app at launch.
713
- **BREAKING:** MediaPipe `.task` support moves to opt-in `MediaPipeFallback` subspec; default install does not include `MediaPipeTasksGenAI` on iOS. Consumers who need it: `pod 'ExpoLitertLm', :subspecs => ['Core', 'MediaPipeFallback']` in their Podfile.

ios/Frameworks/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# LiteRTLM xcframework binaries are gitignored — they are large platform binaries
2+
# fetched at sync time via scripts/sync-litertlm-swift.sh.
3+
# Use `make sync TAG=<tag>` to populate this directory.
4+
*.xcframework
5+
6+
# The manifest is the trust anchor and MUST be checked into source control.
7+
# It is populated by scripts/sync-litertlm-swift.sh after SHA-256 verification.
8+
!rewrap-manifest.json
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"schema_version": 1,
3+
"upstream_version": "v0.7.3",
4+
"rewrap_iteration": 1,
5+
"tag": "v0.7.3+rewrap.1",
6+
"fork_sha": "pending",
7+
"rewrap_script_sha": "pending",
8+
"built_at": "2026-05-16T00:00:00Z",
9+
"xcframeworks": [
10+
{
11+
"name": "LiteRTLM-rewrapped.xcframework",
12+
"swift_target_name": "LiteRTLMBinary",
13+
"zip_filename": "LiteRTLM-rewrapped-v0.7.3+rewrap.1.xcframework.zip",
14+
"zip_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
15+
"release_asset_url": "https://github.com/helenkwok/LiteRTLM-Swift/releases/download/v0.7.3+rewrap.1/LiteRTLM-rewrapped-v0.7.3+rewrap.1.xcframework.zip",
16+
"install_names": [
17+
"@rpath/CLiteRTLM.framework/CLiteRTLM",
18+
"@rpath/LiteRTLM.framework/LiteRTLM"
19+
]
20+
},
21+
{
22+
"name": "GemmaModelConstraintProvider.xcframework",
23+
"swift_target_name": "GemmaModelConstraintProviderBinary",
24+
"zip_filename": "GemmaModelConstraintProvider-v0.7.3+rewrap.1.xcframework.zip",
25+
"zip_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
26+
"release_asset_url": "https://github.com/helenkwok/LiteRTLM-Swift/releases/download/v0.7.3+rewrap.1/GemmaModelConstraintProvider-v0.7.3+rewrap.1.xcframework.zip",
27+
"install_names": [
28+
"@rpath/GemmaModelConstraintProvider.framework/GemmaModelConstraintProvider"
29+
]
30+
}
31+
]
32+
}

scripts/sync-litertlm-swift.sh

Lines changed: 116 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,131 @@
11
#!/usr/bin/env bash
22
# sync-litertlm-swift.sh
3-
# Manual sync from helenkwok/LiteRTLM-Swift fork into vendored copies under
4-
# expo-litert-lm/ios/. Pulls the source xcframework + Swift sources, runs the
5-
# fork's scripts/rewrap-xcframework.sh, and copies the rewrapped outputs into
6-
# place. Version bumps and CHANGELOG entries are manual per offlineaid D-16.
3+
# Fetches rewrapped LiteRTLM-Swift xcframeworks from the fork's GitHub Release
4+
# by tag, verifies SHA-256 against rewrap-manifest.json, places artifacts
5+
# under ios/Frameworks/, bumps package.json version, copies manifest.
6+
#
7+
# Manual sync only — this script does NOT auto-commit. Operator reviews and
8+
# commits explicitly: ios/Frameworks/rewrap-manifest.json + package.json +
9+
# CHANGELOG.md. Do NOT commit the .xcframework binaries (gitignored).
10+
#
11+
# Requires: gh CLI authenticated as a user with read access to helenkwok/LiteRTLM-Swift
12+
# Requires: jq, shasum (macOS), npm (for version bump), unzip
713
#
814
# Usage:
9-
# scripts/sync-litertlm-swift.sh [REF] # clone fork at REF (default: main)
10-
# LITERTLM_SWIFT_LOCAL_PATH=/path scripts/sync-litertlm-swift.sh # use local fork checkout
15+
# scripts/sync-litertlm-swift.sh <tag>
16+
# e.g. scripts/sync-litertlm-swift.sh v0.7.3+rewrap.1
17+
#
18+
# Phase 14 D-34, D-35
1119

1220
set -euo pipefail
1321

14-
REF="${1:-main}"
22+
TAG="${1:-}"
23+
[ -n "$TAG" ] || { echo "usage: $0 <tag> (e.g. v0.7.3+rewrap.1)" >&2; exit 1; }
24+
25+
REPO="helenkwok/LiteRTLM-Swift"
26+
DEST="ios/Frameworks"
1527
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
16-
PKG_VERSION="$(node -p "require('${ROOT}/package.json').version")"
17-
export LITERTLM_VERSION="${PKG_VERSION%-*}" # strip any -dev.N suffix for the xcframework Info.plist
18-
export LITERTLM_BUILD="1"
28+
cd "$ROOT"
29+
30+
echo "==> Syncing LiteRTLM-Swift $TAG from $REPO into $DEST/"
31+
32+
# 1. Fetch manifest FIRST — it is the trust anchor for ALL xcframeworks.
33+
# Paying manifest bandwidth before xcframework bandwidth allows SHA verification
34+
# before downloading the (large) xcframework zips.
35+
tmpdir=$(mktemp -d)
36+
trap "rm -rf $tmpdir" EXIT
37+
38+
echo " Fetching rewrap-manifest.json..."
39+
gh release download "$TAG" --repo "$REPO" --pattern 'rewrap-manifest.json' --dir "$tmpdir"
40+
manifest="$tmpdir/rewrap-manifest.json"
1941

20-
echo "==> Sync helenkwok/LiteRTLM-Swift -> expo-litert-lm/ios/ (ref=${REF}, version=${LITERTLM_VERSION})"
42+
schema_version=$(jq -r .schema_version "$manifest")
43+
[ "$schema_version" = "1" ] || {
44+
echo "FAIL: unexpected manifest schema_version=$schema_version (expected 1)" >&2
45+
exit 1
46+
}
2147

22-
# 1. Resolve fork source (clone or local path)
23-
if [ -n "${LITERTLM_SWIFT_LOCAL_PATH:-}" ]; then
24-
FORK_DIR="$LITERTLM_SWIFT_LOCAL_PATH"
25-
if [ ! -d "$FORK_DIR" ]; then
26-
echo "ERROR: LITERTLM_SWIFT_LOCAL_PATH=$FORK_DIR does not exist" >&2
48+
count=$(jq '.xcframeworks | length' "$manifest")
49+
[ "$count" -ge 2 ] || {
50+
echo "FAIL: manifest.xcframeworks has $count entries, expected >=2 (LiteRTLM-rewrapped + GemmaModelConstraintProvider)" >&2
51+
exit 1
52+
}
53+
54+
echo " Manifest OK: schema_version=$schema_version, $count xcframeworks"
55+
56+
# 2. For EACH xcframework entry: fetch zip, verify SHA-256, unzip into DEST.
57+
mkdir -p "$DEST"
58+
59+
jq -c '.xcframeworks[]' "$manifest" | while read -r entry; do
60+
name=$(echo "$entry" | jq -r .name)
61+
zip_filename=$(echo "$entry" | jq -r .zip_filename)
62+
expected_sha=$(echo "$entry" | jq -r .zip_sha256)
63+
64+
# Validate expected sha is 64-hex (guards against manifest with placeholder)
65+
if ! [[ "$expected_sha" =~ ^[0-9a-f]{64}$ ]]; then
66+
echo "FAIL: manifest entry '$name' has non-hex zip_sha256: '$expected_sha'" >&2
2767
exit 1
2868
fi
29-
echo "==> Using local fork: $FORK_DIR"
30-
CLEANUP=""
31-
else
32-
TMP="$(mktemp -d)"
33-
FORK_DIR="$TMP/fork"
34-
echo "==> Cloning helenkwok/LiteRTLM-Swift @ ${REF} into $FORK_DIR"
35-
git clone --depth 1 --branch "$REF" \
36-
https://github.com/helenkwok/LiteRTLM-Swift.git "$FORK_DIR"
37-
CLEANUP="$TMP"
38-
fi
39-
40-
# 2. Run the fork's rewrap script (idempotent — emits sibling -rewrapped + GMCP xcframeworks)
41-
(cd "$FORK_DIR" && ./scripts/rewrap-xcframework.sh Frameworks/LiteRTLM.xcframework >/dev/null)
42-
43-
REWRAPPED="$FORK_DIR/Frameworks/LiteRTLM-rewrapped.xcframework"
44-
GMCP="$FORK_DIR/Frameworks/GemmaModelConstraintProvider.xcframework"
45-
46-
[ -d "$REWRAPPED" ] || { echo "ERROR: rewrap did not produce $REWRAPPED" >&2; exit 1; }
47-
[ -d "$GMCP" ] || { echo "ERROR: rewrap did not produce $GMCP" >&2; exit 1; }
48-
49-
# 3. Stage vendored copies
50-
DEST_FW="$ROOT/ios/Frameworks"
51-
DEST_SRC="$ROOT/ios/Sources"
52-
mkdir -p "$DEST_FW" "$DEST_SRC"
53-
54-
# Drop the -rewrapped suffix so podspec references stay clean (D-15 — vendored
55-
# copy is authoritative).
56-
rm -rf "$DEST_FW/CLiteRTLM.xcframework" "$DEST_FW/GemmaModelConstraintProvider.xcframework"
57-
cp -R "$REWRAPPED" "$DEST_FW/CLiteRTLM.xcframework"
58-
cp -R "$GMCP" "$DEST_FW/GemmaModelConstraintProvider.xcframework"
59-
60-
rm -rf "$DEST_SRC/LiteRTLMSwift"
61-
cp -R "$FORK_DIR/Sources/LiteRTLMSwift" "$DEST_SRC/LiteRTLMSwift"
62-
63-
# 4. Tree summary
64-
echo ""
65-
echo "==> Vendored copy summary:"
66-
find "$DEST_FW" "$DEST_SRC" -maxdepth 3 -type d | sed "s|$ROOT/||"
67-
echo ""
68-
du -sh "$DEST_FW"/*.xcframework "$DEST_SRC/LiteRTLMSwift" 2>/dev/null
6969

70-
# 5. Cleanup temp clone
71-
[ -n "$CLEANUP" ] && rm -rf "$CLEANUP"
70+
echo " Fetching $zip_filename..."
71+
gh release download "$TAG" --repo "$REPO" --pattern "$zip_filename" --dir "$tmpdir"
72+
73+
actual_sha=$(shasum -a 256 "$tmpdir/$zip_filename" | awk '{print $1}')
74+
if [ "$actual_sha" != "$expected_sha" ]; then
75+
echo "FAIL: sha256 mismatch for $name" >&2
76+
echo " expected: $expected_sha" >&2
77+
echo " actual: $actual_sha" >&2
78+
exit 1
79+
fi
80+
81+
echo "$name sha256 verified (${expected_sha:0:12}...)"
82+
83+
# Remove existing xcframework if present, then unzip
84+
rm -rf "$DEST/$name"
85+
unzip -q "$tmpdir/$zip_filename" -d "$DEST"
86+
87+
if [ ! -d "$DEST/$name" ]; then
88+
echo "FAIL: expected $DEST/$name after unzip of $zip_filename" >&2
89+
echo " Contents of $DEST after unzip:" >&2
90+
ls "$DEST/" >&2
91+
exit 1
92+
fi
7293

94+
echo "$name installed at $DEST/$name"
95+
done
96+
97+
# 3. Copy manifest into source-controlled location (single SoT on Pod side).
98+
# The manifest is the trust anchor and MUST be checked into source control.
99+
# The xcframework binaries are gitignored.
100+
cp "$manifest" "$DEST/rewrap-manifest.json"
101+
echo " ✓ rewrap-manifest.json written to $DEST/rewrap-manifest.json"
102+
103+
# 4. Bump package.json version (per D-35, D-16).
104+
upstream_ver=$(jq -r .upstream_version "$manifest")
105+
rewrap_iter=$(jq -r .rewrap_iteration "$manifest")
106+
# e.g. 0.2.0-litertlm.0.7.3.r1
107+
new_pkg_ver="0.2.0-litertlm.${upstream_ver#v}.r${rewrap_iter}"
108+
echo " Bumping package.json version to $new_pkg_ver..."
109+
npm version "$new_pkg_ver" --no-git-tag-version
110+
111+
# 5. Append CHANGELOG entry
112+
first_sha=$(jq -r '.xcframeworks[0].zip_sha256' "$manifest")
113+
date_str=$(date -u +"%Y-%m-%d")
114+
echo "${date_str} — synced rewrapped LiteRTLM-Swift $TAG ($count xcframeworks, first sha256: ${first_sha:0:12}...)" >> CHANGELOG.md
115+
echo " ✓ CHANGELOG.md updated"
116+
117+
echo ""
118+
echo "==> Sync complete."
119+
echo ""
120+
echo "Files to COMMIT (manual review first):"
121+
echo " $DEST/rewrap-manifest.json (trust anchor — MUST be committed)"
122+
echo " package.json (version bumped to $new_pkg_ver)"
123+
echo " CHANGELOG.md (sync entry added)"
124+
echo ""
125+
echo "Files to NOT COMMIT (gitignored binaries):"
126+
jq -r '.xcframeworks[].name' "$manifest" | while read -r xcf; do
127+
echo " $DEST/$xcf"
128+
done
73129
echo ""
74-
echo "REMINDER: bump package.json version + add CHANGELOG entry before publishing — see offlineaid CONTEXT D-16."
130+
echo "Suggested commit message:"
131+
echo " chore(sync): rewrapped LiteRTLM-Swift $TAG ($count xcframeworks)"

0 commit comments

Comments
 (0)