Build ARM64 iPhone IPA for signing with Sideloadly
iOS build and tests / simulator (push) Has been cancelled
iOS build and tests / simulator (push) Has been cancelled
This commit is contained in:
@@ -64,3 +64,21 @@ jobs:
|
||||
name: MusicBridge-simulator
|
||||
path: build/MusicBridge-simulator.zip
|
||||
retention-days: 7
|
||||
- name: Build iPhone IPA for local signing
|
||||
shell: bash
|
||||
run: bash tools/package_ipa.sh
|
||||
- name: Save iPhone IPA
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MusicBridge-iPhone
|
||||
path: build/MusicBridge-iPhone.ipa
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
- name: Save device build log
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ios-device-build-log
|
||||
path: build/device-build.log
|
||||
if-no-files-found: ignore
|
||||
retention-days: 7
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
mkdir -p build
|
||||
xcodebuild archive \
|
||||
-project MusicBridge.xcodeproj \
|
||||
-scheme MusicBridge \
|
||||
-configuration Release \
|
||||
-destination 'generic/platform=iOS' \
|
||||
-derivedDataPath build/DeviceDerivedData \
|
||||
-archivePath build/MusicBridge-iPhone.xcarchive \
|
||||
ARCHS=arm64 \
|
||||
CODE_SIGNING_ALLOWED=NO \
|
||||
CODE_SIGNING_REQUIRED=NO \
|
||||
| tee build/device-build.log
|
||||
|
||||
# A device archive packaged for re-signing by Sideloadly on the user's PC.
|
||||
# This is not an App Store export and contains no Apple credentials or provisioning profile.
|
||||
package_dir=$(mktemp -d "${TMPDIR:-/tmp}/musicbridge-ipa.XXXXXX")
|
||||
trap 'rm -rf -- "$package_dir"' EXIT
|
||||
mkdir -p "$package_dir/Payload"
|
||||
ditto build/MusicBridge-iPhone.xcarchive/Products/Applications/MusicBridge.app "$package_dir/Payload/MusicBridge.app"
|
||||
ditto -c -k --keepParent "$package_dir/Payload" build/MusicBridge-iPhone.ipa
|
||||
python3 tools/verify_ipa.py build/MusicBridge-iPhone.ipa
|
||||
@@ -0,0 +1,33 @@
|
||||
"""Verify a thin ARM64 iPhone IPA, independently of the macOS build environment."""
|
||||
import pathlib
|
||||
import plistlib
|
||||
import struct
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
path = pathlib.Path(sys.argv[1])
|
||||
with zipfile.ZipFile(path) as archive:
|
||||
assert archive.testzip() is None, "Corrupt ZIP member"
|
||||
prefix = "Payload/MusicBridge.app/"
|
||||
info = plistlib.loads(archive.read(prefix + "Info.plist"))
|
||||
assert info["CFBundleSupportedPlatforms"] == ["iPhoneOS"], "Not an iPhone device build"
|
||||
assert info["CFBundleIdentifier"] == "ru.yukinoki.musicbridge", "Unexpected bundle ID"
|
||||
assert info["MinimumOSVersion"] == "17.0", "Unexpected minimum iOS version"
|
||||
executable = info["CFBundleExecutable"]
|
||||
assert executable == "MusicBridge", "Unexpected executable"
|
||||
binary = archive.read(prefix + executable)
|
||||
magic, cpu = struct.unpack_from("<II", binary)
|
||||
assert magic == 0xFEEDFACF and cpu == 0x0100000C, "Expected ARM64 Mach-O executable"
|
||||
# ARM64 can also be a simulator build: check LC_BUILD_VERSION's platform field.
|
||||
command_count = struct.unpack_from("<I", binary, 16)[0]
|
||||
offset = 32
|
||||
platforms = []
|
||||
for _ in range(command_count):
|
||||
command, size = struct.unpack_from("<II", binary, offset)
|
||||
assert size >= 8 and offset + size <= len(binary), "Invalid Mach-O load command"
|
||||
if command == 0x32:
|
||||
platforms.append(struct.unpack_from("<I", binary, offset + 8)[0])
|
||||
offset += size
|
||||
assert platforms == [2], "Mach-O is not built for iOS devices"
|
||||
print(f"PASS: {path.name}: valid IPA, iPhoneOS ARM64, iOS 17+, ru.yukinoki.musicbridge.")
|
||||
print("Requires local signing with Sideloadly before installation on an iPhone.")
|
||||
Reference in New Issue
Block a user