From e1ebb273782d70c6173c233e1e3afa20fe8dded1 Mon Sep 17 00:00:00 2001 From: tkgstrator Date: Sat, 1 Aug 2026 07:39:49 +0000 Subject: [PATCH] feat(build_patched_ipa): --bundle-id-suffix flag Lets a patched IPA install alongside the original app instead of overwriting it, which is what a consumer needs when it wants the stock build and the patched build on the device at the same time. Ported from feat/bundle-id-suffix, which branched before #17-#19 and so never picked up the verify_sites overload resolution its consumer now depends on. Rebuilding the flag on top of develop keeps the two lines from drifting further apart. uv.lock rides along: develop bumped pyproject to 0.2.0 without regenerating the lock. Co-Authored-By: Claude Opus 4.7 --- tools/build_patched_ipa.sh | 48 +++++++++++++++++++++++++++++++------- uv.lock | 2 +- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/tools/build_patched_ipa.sh b/tools/build_patched_ipa.sh index 0782692..b1c7e91 100755 --- a/tools/build_patched_ipa.sh +++ b/tools/build_patched_ipa.sh @@ -30,7 +30,8 @@ # --framework \ # --dylib \ # --input \ -# [--output ] +# [--output ] \ +# [--bundle-id-suffix ] # # The script never distributes the input IPA itself — the operator # supplies one. @@ -50,6 +51,12 @@ Usage: build_patched_ipa.sh --recipe NAME --framework BASENAME --dylib PATH --in NOT distribute the IPA itself). --output IPA Optional; defaults to packages/ipa/-patched.ipa (e.g. Kiou-1.0.1.ipa -> packages/ipa/Kiou-1.0.1-patched.ipa). + --bundle-id-suffix SUFFIX + Optional; if non-empty, append "." to the + Info.plist CFBundleIdentifier so the patched IPA + installs alongside the original app instead of + overwriting it (e.g. "chinlan" turns + com.acme.app into com.acme.app.chinlan). EOF exit 64 } @@ -59,16 +66,18 @@ FRAMEWORK="" DYLIB_SRC="" INPUT_IPA="" OUTPUT_IPA="" +BUNDLE_ID_SUFFIX="" while [ $# -gt 0 ]; do case "$1" in - --recipe) RECIPE="$2"; shift 2;; - --framework) FRAMEWORK="$2"; shift 2;; - --dylib) DYLIB_SRC="$2"; shift 2;; - --input) INPUT_IPA="$2"; shift 2;; - --output) OUTPUT_IPA="$2"; shift 2;; - -h|--help) usage;; - *) echo "error: unknown argument: $1" >&2; usage;; + --recipe) RECIPE="$2"; shift 2;; + --framework) FRAMEWORK="$2"; shift 2;; + --dylib) DYLIB_SRC="$2"; shift 2;; + --input) INPUT_IPA="$2"; shift 2;; + --output) OUTPUT_IPA="$2"; shift 2;; + --bundle-id-suffix) BUNDLE_ID_SUFFIX="$2"; shift 2;; + -h|--help) usage;; + *) echo "error: unknown argument: $1" >&2; usage;; esac done @@ -243,6 +252,29 @@ echo "==> verifying LC_LOAD_DYLIB (recipe: $RECIPE)" echo "==> patching Info.plist (recipe: $RECIPE)" "$PY" -m tools.patch_plist --recipe "$RECIPE" "$INFO_PLIST" +# Optional CFBundleIdentifier rewrite so the patched IPA can live +# side-by-side with the original app instead of overwriting it. Skipped +# when --bundle-id-suffix is empty (the default), so the pipeline stays +# a no-op for the "overwrite the original" workflow. +if [ -n "$BUNDLE_ID_SUFFIX" ]; then + ORIGINAL_BUNDLE_ID="$("$PY" -c "import plistlib,sys; print(plistlib.load(open(sys.argv[1],'rb')).get('CFBundleIdentifier',''))" "$INFO_PLIST")" + if [ -z "$ORIGINAL_BUNDLE_ID" ]; then + echo "error: Info.plist has no CFBundleIdentifier to append suffix to" >&2 + exit 1 + fi + # Idempotent: if the suffix is already present, don't append it twice. + case "$ORIGINAL_BUNDLE_ID" in + *".$BUNDLE_ID_SUFFIX") + echo "==> CFBundleIdentifier already carries suffix .$BUNDLE_ID_SUFFIX — skipping rewrite" + ;; + *) + NEW_BUNDLE_ID="${ORIGINAL_BUNDLE_ID}.${BUNDLE_ID_SUFFIX}" + echo "==> rewriting CFBundleIdentifier: $ORIGINAL_BUNDLE_ID -> $NEW_BUNDLE_ID" + "$PY" -m tools.patch_plist --set "CFBundleIdentifier=${NEW_BUNDLE_ID}" "$INFO_PLIST" + ;; + esac +fi + # --------------------------------------------------------------------------- # 4. inject dylib # --------------------------------------------------------------------------- diff --git a/uv.lock b/uv.lock index edc1e26..835cd8d 100644 --- a/uv.lock +++ b/uv.lock @@ -22,7 +22,7 @@ wheels = [ [[package]] name = "ipa-patch-shared" -version = "0.1.3" +version = "0.2.0" source = { virtual = "." } dependencies = [ { name = "lief" },