feat(apple/macos): App Sandbox + entitlements, wire Mac App Store TestFlight
ci / bench (push) Successful in 1m33s
apple / swift (push) Successful in 1m15s
ci / web (push) Successful in 31s
ci / docs-site (push) Successful in 30s
ci / rust (push) Successful in 2m5s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 3s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 18s
deb / build-publish (push) Successful in 2m1s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 5m5s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 5m7s
docker / deploy-docs (push) Successful in 17s
ci / bench (push) Successful in 1m33s
apple / swift (push) Successful in 1m15s
ci / web (push) Successful in 31s
ci / docs-site (push) Successful in 30s
ci / rust (push) Successful in 2m5s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 3s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 18s
deb / build-publish (push) Successful in 2m1s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 5m5s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 5m7s
docker / deploy-docs (push) Successful in 17s
The Mac App Store requires App Sandbox, which the macOS app didn't declare. App Sandbox is macOS-only (invalid on iOS/tvOS, fails upload validation), so the macOS target now uses a dedicated Config/Punktfunk-macOS.entitlements while iOS/tvOS keep the shared Config/Punktfunk.entitlements (unchanged). The single macOS app is sandboxed for BOTH channels — the Developer ID DMG is codesigned with the same file — so the local build equals what App Store users get. Entitlement set (verified against the code + Apple docs): - app-sandbox, network.client. - network.server: NOT optional despite the client being outbound-only — the sandbox gates the bind() syscall as network-bind, and quinn (quic.rs) + the raw-UDP plane (transport/udp.rs) both bind explicitly, so host->client datagrams never arrive without it (the classic QUIC-under-sandbox trap). - device.audio-input (mic uplink), device.bluetooth + device.usb (Xbox/DualSense controllers over BT/USB via GameController), keychain-access-groups (existing). Omitted: device.hid (undocumented), files.user-selected.* (no pickers), networking.multicast (Bonjour browse is exempt; requesting it breaks signing). CI (release.yml): add a macOS App Store archive+upload-to-TestFlight step mirroring the iOS lane (manual Apple Distribution signing + the 'Punktfunk macOS App Store Distribution' profile, app-store-connect/upload, installer-signed pkg), continue-on-error until the portal prereqs exist; point the Developer ID DMG codesign at the sandboxed entitlements. Docs (ci.md) + clients/apple README updated; the runner additionally needs the macOS platform on the App Store Connect record + the '3rd Party Mac Developer Installer' cert. Verified: signed Debug build embeds exactly the intended entitlements (codesign -d --entitlements), swift build green against the rebuilt xcframework. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,32 @@
|
||||
# Production Apple client builds — runs on the macos-arm64 runner (home-mac-mini-1).
|
||||
#
|
||||
# Tag v* (or workflow_dispatch):
|
||||
# macOS -> Developer ID signed + notarized + stapled .dmg, attached to a Gitea
|
||||
# release on tag pushes
|
||||
# macOS (Developer ID) -> sandboxed, signed, notarized + stapled .dmg, attached to a
|
||||
# Gitea release on tag pushes
|
||||
# macOS (App Store) -> archive + upload to TestFlight (App Store Connect)
|
||||
# iOS -> archive + upload straight to TestFlight (App Store Connect)
|
||||
# tvOS -> not built: the Rust core needs tier-3 targets (nightly -Zbuild-std)
|
||||
# macOS App Store/TestFlight -> deferred: needs App Sandbox entitlements first.
|
||||
# tvOS -> archive + upload to TestFlight (Rust core built from tier-3 targets,
|
||||
# nightly -Zbuild-std, in build-xcframework.sh)
|
||||
#
|
||||
# One App Store listing for all platforms (universal purchase): every target shares the
|
||||
# bundle ID io.unom.punktfunk.
|
||||
#
|
||||
# The macOS app is App-SANDBOXED for both channels (Config/Punktfunk-macOS.entitlements —
|
||||
# app-sandbox + network client/server + audio-input + bluetooth/usb device access; the
|
||||
# shared Config/Punktfunk.entitlements stays iOS/tvOS-only, where app-sandbox is invalid).
|
||||
# The Developer ID DMG is codesigned with the SAME macOS entitlements, so what we test
|
||||
# locally equals what App Store users get.
|
||||
#
|
||||
# macOS App Store prerequisites (one-time, Apple portal — NOT done by this workflow; the
|
||||
# step is continue-on-error until they exist):
|
||||
# * App Store Connect: add the macOS platform to the io.unom.punktfunk app record
|
||||
# (universal purchase).
|
||||
# * A "Punktfunk macOS App Store Distribution" provisioning profile installed on the
|
||||
# runner (under ~/Library/Developer/Xcode/UserData/Provisioning Profiles/).
|
||||
# * The "3rd Party Mac Developer Installer" (Mac Installer Distribution) certificate in
|
||||
# the runner's login keychain, in addition to "Apple Distribution" — the App Store
|
||||
# .pkg is installer-signed with it.
|
||||
#
|
||||
# Signing setup (NOT secret-based anymore): the runner is a LaunchAgent in the user's
|
||||
# logged-in Aqua session, so it uses the **login keychain** directly. Install the signing
|
||||
# identities there once via Xcode (Settings -> Accounts -> Manage Certificates): Developer
|
||||
@@ -103,7 +120,9 @@ jobs:
|
||||
# Archive UNSIGNED, then codesign with the Developer ID Application identity from the
|
||||
# login keychain. Unsigned archive sidesteps Xcode's keychain-access-groups
|
||||
# provisioning-profile gate; codesign just needs the (now valid) identity + the
|
||||
# team-prefixed entitlement, no profile. Bundle is a single static binary.
|
||||
# team-prefixed entitlements, no profile (App Sandbox + the network/device
|
||||
# capabilities are self-asserted for Developer ID — no profile entry needed).
|
||||
# Bundle is a single static binary.
|
||||
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild archive \
|
||||
-project "$PROJECT" -scheme Punktfunk \
|
||||
-destination 'generic/platform=macOS' \
|
||||
@@ -112,10 +131,11 @@ jobs:
|
||||
MARKETING_VERSION="$VERSION" CURRENT_PROJECT_VERSION="$BUILD_NUM" \
|
||||
CODE_SIGNING_ALLOWED=NO
|
||||
APP="$RUNNER_TEMP/Punktfunk-macos.xcarchive/Products/Applications/Punktfunk.app"
|
||||
# codesign won't expand $(AppIdentifierPrefix) — resolve it to the team prefix.
|
||||
# Sandboxed Developer ID: sign with the SAME macOS entitlements the App Store build
|
||||
# uses. codesign won't expand $(AppIdentifierPrefix) — resolve it to the team prefix.
|
||||
RESOLVED="$RUNNER_TEMP/macos.entitlements"
|
||||
sed "s/\$(AppIdentifierPrefix)/${TEAM_ID}./g" \
|
||||
clients/apple/Config/Punktfunk.entitlements > "$RESOLVED"
|
||||
clients/apple/Config/Punktfunk-macOS.entitlements > "$RESOLVED"
|
||||
codesign --force --options runtime --timestamp \
|
||||
--entitlements "$RESOLVED" \
|
||||
--sign "Developer ID Application" "$APP"
|
||||
@@ -152,6 +172,54 @@ jobs:
|
||||
-F "attachment=@$DMG" >/dev/null
|
||||
echo "attached Punktfunk-$VERSION.dmg to release $GITHUB_REF_NAME"
|
||||
|
||||
- name: macOS App Store — archive + upload to TestFlight
|
||||
if: gitea.event_name != 'workflow_dispatch' || inputs.testflight == 'true'
|
||||
# Best-effort until the App Store Connect record has the macOS platform + the
|
||||
# "Punktfunk macOS App Store Distribution" profile and the "3rd Party Mac Developer
|
||||
# Installer" cert are on the runner (see the header). The macOS app is sandboxed
|
||||
# (Config/Punktfunk-macOS.entitlements) — mandatory for the Mac App Store.
|
||||
continue-on-error: true
|
||||
run: |
|
||||
# Separate archive from the Developer ID one above: App Store needs a profile-signed
|
||||
# archive (manual signing), not the unsigned-then-codesign DMG path. Same App-Manager
|
||||
# ASC-key constraint as iOS/tvOS — MANUAL signing, NOT -allowProvisioningUpdates
|
||||
# (cloud signing the key can't do). Quit Xcode so it can't prune the dropped profile.
|
||||
osascript -e 'tell application "Xcode" to quit' >/dev/null 2>&1 || true
|
||||
pkill -x Xcode 2>/dev/null || true
|
||||
PROFILE="Punktfunk macOS App Store Distribution"
|
||||
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild archive \
|
||||
-project "$PROJECT" -scheme Punktfunk \
|
||||
-destination 'generic/platform=macOS' \
|
||||
-archivePath "$RUNNER_TEMP/Punktfunk-macos-appstore.xcarchive" \
|
||||
MARKETING_VERSION="$VERSION" CURRENT_PROJECT_VERSION="$BUILD_NUM" \
|
||||
CODE_SIGN_STYLE=Manual \
|
||||
CODE_SIGN_IDENTITY="Apple Distribution" \
|
||||
DEVELOPMENT_TEAM="$TEAM_ID" \
|
||||
PROVISIONING_PROFILE_SPECIFIER="$PROFILE"
|
||||
cat > "$RUNNER_TEMP/export-macos-appstore.plist" <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>method</key><string>app-store-connect</string>
|
||||
<key>destination</key><string>upload</string>
|
||||
<key>teamID</key><string>$TEAM_ID</string>
|
||||
<key>signingStyle</key><string>manual</string>
|
||||
<key>signingCertificate</key><string>Apple Distribution</string>
|
||||
<key>installerSigningCertificate</key><string>3rd Party Mac Developer Installer</string>
|
||||
<key>provisioningProfiles</key>
|
||||
<dict><key>io.unom.punktfunk</key><string>$PROFILE</string></dict>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild -exportArchive \
|
||||
-archivePath "$RUNNER_TEMP/Punktfunk-macos-appstore.xcarchive" \
|
||||
-exportOptionsPlist "$RUNNER_TEMP/export-macos-appstore.plist" \
|
||||
-exportPath "$RUNNER_TEMP/export-macos-appstore" \
|
||||
-authenticationKeyPath "$RUNNER_TEMP/asc.p8" \
|
||||
-authenticationKeyID "${{ secrets.ASC_API_KEY_ID }}" \
|
||||
-authenticationKeyIssuerID "${{ secrets.ASC_API_ISSUER_ID }}"
|
||||
|
||||
- name: iOS — archive + upload to TestFlight
|
||||
if: gitea.event_name != 'workflow_dispatch' || inputs.testflight == 'true'
|
||||
# Best-effort until the App Store Connect app record for io.unom.punktfunk exists.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- macOS-ONLY entitlements. App Sandbox is a macOS concept (iOS/tvOS are always
|
||||
sandboxed and REJECT this key at upload), so the macOS target points here while
|
||||
iOS/tvOS keep the shared Config/Punktfunk.entitlements. The single macOS app is
|
||||
sandboxed for BOTH channels — the Developer ID DMG is codesigned with this same
|
||||
file (App Sandbox is allowed, not just required, for Developer ID), so what we
|
||||
test locally (⌘R / DMG) is exactly what Mac App Store / TestFlight users get. -->
|
||||
|
||||
<!-- Required for Mac App Store / TestFlight distribution. -->
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
|
||||
<!-- Outbound QUIC control plane + raw-UDP data plane to the host, and NWBrowser mDNS
|
||||
discovery / NWConnection resolve. Every outbound socket (incl. the linked Rust
|
||||
core's UDP binds) needs this under the sandbox. -->
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
|
||||
<!-- NOT optional, despite the client being "outbound only": the App Sandbox gates the
|
||||
bind() syscall itself as a network-bind ("server") operation. quinn binds its QUIC
|
||||
endpoint socket (quic.rs Endpoint::client 0.0.0.0:0) and the raw-UDP data plane
|
||||
binds a local socket to receive host→client datagrams (transport/udp.rs); both fail
|
||||
with deny(1) network-bind / EPERM without this, so NO video/audio/rumble ever
|
||||
arrives. (The classic QUIC-on-quinn-under-sandbox trap.) -->
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
|
||||
<!-- Microphone uplink: SessionAudio installs an AVAudioEngine input tap → Opus → host
|
||||
virtual mic. TCC blocks AVAudioEngine input under the sandbox without this even with
|
||||
NSMicrophoneUsageDescription present. -->
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
|
||||
<!-- Game controllers over Bluetooth via the GameController framework
|
||||
(GCController.startWirelessControllerDiscovery — Xbox/DualSense). No CoreBluetooth in
|
||||
the app, so no NSBluetoothAlwaysUsageDescription is required, but the sandbox still
|
||||
gates GameController's BT HID access on this key. -->
|
||||
<key>com.apple.security.device.bluetooth</key>
|
||||
<true/>
|
||||
|
||||
<!-- Game controllers over USB + USB HID mouse/keyboard via the GameController framework.
|
||||
device.usb gates the IOHIDLibUserClient path the framework uses for wired devices
|
||||
(per Apple DTS); without it, plugged-in controllers deliver no input. Justify in App
|
||||
Review notes ("reads input from USB game controllers"). -->
|
||||
<key>com.apple.security.device.usb</key>
|
||||
<true/>
|
||||
|
||||
<!-- Keychain Sharing (unchanged from the shared file): a team-scoped access group so the
|
||||
punktfunk/1 client identity in the data-protection keychain is gated by the app's
|
||||
entitlement (team + bundle id), persisting across rebuilds with NO prompt — see
|
||||
ClientIdentityStore. $(AppIdentifierPrefix) expands to the team prefix at signing
|
||||
time (the Developer ID codesign step in release.yml resolves it via sed). -->
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)io.unom.punktfunk</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -355,7 +355,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = punktfunk_Logo;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Config/Punktfunk.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = Config/Punktfunk-macOS.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
@@ -389,7 +389,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = punktfunk_Logo;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Config/Punktfunk.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = Config/Punktfunk-macOS.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
|
||||
@@ -137,6 +137,17 @@ The app target **Punktfunk** wraps the same sources as the `swift run` shell
|
||||
catalog) and links `PunktfunkKit` from the local package. Generated Info.plist, ad-hoc
|
||||
signing, bundle id `io.unom.punktfunk`. Notes:
|
||||
|
||||
- **Entitlements (sandbox)**: the macOS target uses
|
||||
`Config/Punktfunk-macOS.entitlements`; iOS/tvOS use the shared
|
||||
`Config/Punktfunk.entitlements`. The macOS app is **App-Sandboxed** (mandatory for the Mac
|
||||
App Store/TestFlight, and used for the Developer ID DMG too so the local build matches what
|
||||
ships): `com.apple.security.app-sandbox`, `network.client` + **`network.server`** (the
|
||||
sandbox gates `bind()`; quinn + the raw-UDP plane both bind, so receive breaks without it),
|
||||
`device.audio-input` (mic), `device.bluetooth` + `device.usb` (GameController over BT/USB),
|
||||
and the existing `keychain-access-groups`. `app-sandbox` is macOS-only — keep it OUT of the
|
||||
shared iOS/tvOS file (it fails upload validation there). Verify a build is sandboxed with
|
||||
`codesign -d --entitlements :- <built .app>`. Heads-up: `device.usb` draws some App Review
|
||||
scrutiny — justify it in the review notes ("reads input from USB game controllers").
|
||||
- **App icon**: `App/Assets.xcassets` ships an empty `AppIcon` slot. For an Icon Composer
|
||||
`.icon`: add the file to the project (target Punktfunk), set it as the App Icon in the
|
||||
target's General tab, and delete the placeholder `AppIcon.appiconset`. Heads-up: CLI
|
||||
|
||||
@@ -13,7 +13,7 @@ CI runs on **Gitea Actions** (`git.unom.io`, org `unom`). Three workflows in
|
||||
| `ci.yml` | push to `main`, PRs | `ubuntu-24.04` | Rust workspace (fmt · clippy `-D warnings` · build · test · C-ABI harness · generated-header drift) inside the `punktfunk-rust-ci` image; `web/` and `docs-site/` build + typecheck in `oven/bun:1` |
|
||||
| `docker.yml` | push to `main`, `v*` tags, manual | `ubuntu-24.04` | Builds + pushes the three images below (`latest` + `sha-<short>` tags) |
|
||||
| `apple.yml` | push to `main`, PRs, manual | `macos-arm64` | Rust core → `PunktfunkCore.xcframework` → `swift build` + `swift test` in `clients/apple` |
|
||||
| `release.yml` | `v*` tags, manual | `macos-arm64` | Production Apple builds: Developer-ID-signed, notarized, stapled macOS `.dmg` attached to the Gitea release + iOS archive uploaded to TestFlight |
|
||||
| `release.yml` | `v*` tags, manual | `macos-arm64` | Production Apple builds: sandboxed macOS `.dmg` (Developer ID, notarized, stapled) attached to the Gitea release + macOS/iOS/tvOS archives uploaded to TestFlight |
|
||||
|
||||
## Dockerized pieces
|
||||
|
||||
@@ -60,20 +60,31 @@ ssh enricobuehler@192.168.1.135 GITEA_RUNNER_TOKEN=<token> bash -s \
|
||||
|
||||
`release.yml` produces the production client builds on the Mac runner. All three app
|
||||
targets share the bundle ID **`io.unom.punktfunk`** (one App Store listing, universal
|
||||
purchase — effectively unchangeable after first submission). Secrets:
|
||||
`DEVID_CERT_P12_B64`/`DEVID_CERT_PASSWORD` (Developer ID Application certificate, only
|
||||
creatable by the account holder) and `ASC_API_KEY_P8`/`ASC_API_KEY_ID`/`ASC_API_ISSUER_ID`
|
||||
(App Store Connect API key — notarization, TestFlight upload, automatic-signing profile
|
||||
fetch). Signing uses a per-run throwaway keychain; nothing persists on the runner.
|
||||
Per-platform state:
|
||||
purchase — effectively unchangeable after first submission). Signing is **not** secret-based:
|
||||
the runner uses its **login keychain** directly, so install the **Developer ID Application**,
|
||||
**Apple Distribution**, and (for the Mac App Store `.pkg`) **3rd Party Mac Developer
|
||||
Installer** identities once via Xcode, with the WWDR intermediate present so they show as
|
||||
valid. The only secrets are `ASC_API_KEY_P8`/`ASC_API_KEY_ID`/`ASC_API_ISSUER_ID` (App Store
|
||||
Connect API key — notarization + TestFlight upload). Per-platform state:
|
||||
|
||||
- **macOS** — Developer ID export → `notarytool` → stapled `.dmg` on the Gitea release.
|
||||
The Mac **App Store** lane is deferred: it requires App Sandbox entitlements
|
||||
(network client + Bonjour) the app doesn't declare yet.
|
||||
- **macOS (Developer ID)** — sandboxed app (`Config/Punktfunk-macOS.entitlements`) → export
|
||||
→ `notarytool` → stapled `.dmg` on the Gitea release.
|
||||
- **macOS (App Store)** — manual-signed archive (Apple Distribution + the *Punktfunk macOS
|
||||
App Store Distribution* profile) → upload to TestFlight. App Sandbox is **mandatory** here
|
||||
and is now declared (app-sandbox + network client/server + audio-input + bluetooth/usb).
|
||||
Prereqs (one-time, Apple portal): add the **macOS platform** to the App Store Connect app
|
||||
record (universal purchase), install the Mac App Store distribution profile + the installer
|
||||
cert above. `continue-on-error` until those exist.
|
||||
- **iOS** — archive + upload to TestFlight (`method: app-store-connect`,
|
||||
`destination: upload`). Crypto is declared exempt (`ITSAppUsesNonExemptEncryption`,
|
||||
`Config/Info.plist`) so builds don't stall on the compliance question.
|
||||
- **tvOS** — not built: the Rust core needs tier-3 targets (nightly `-Zbuild-std`).
|
||||
- **tvOS** — archive + upload to TestFlight (Rust core built from tier-3 targets, nightly
|
||||
`-Zbuild-std` via `build-xcframework.sh`).
|
||||
|
||||
Each macOS target uses its own entitlements: `Config/Punktfunk-macOS.entitlements` (App
|
||||
Sandbox is macOS-only) for the macOS app, and the shared `Config/Punktfunk.entitlements`
|
||||
(keychain-access-groups only) for iOS/tvOS — `com.apple.security.app-sandbox` is invalid on
|
||||
iOS/tvOS and would fail upload validation.
|
||||
|
||||
The runner needs a **release (non-beta) Xcode** — App Store processing rejects beta-SDK
|
||||
builds, and a beta is unusable for the Rust side too: a newer-than-OS ld emits dylibs the
|
||||
|
||||
Reference in New Issue
Block a user