Hive
Preview permalink serves the latest build’s .ipa instead of the pinned build
GitHub issue · Open
What happened?
When I open a specific preview’s permalink page (/{account}/{project}/previews/{preview_id}), the page metadata (git commit SHA, branch, created date, marketing version) is correctly pinned to that preview. However, the device artifact served for that preview is not the binary that was uploaded for it — it returns the latest preview’s .ipa for the same app + marketing version (CFBundleShortVersionString) + track.
As a result it’s impossible to share/install a specific historical build via its permalink: scanning the QR / tapping Install (the itms-services OTA flow) on an older preview installs the newest build instead.
Concrete evidence from our setup (values anonymized):
- Preview page metadata: commit
A1B2C3D, branchdevelop, trackqa, created 28 May, version 1.0.0. - The
manifest.plistandapp.ipaURLs are correctly preview-scoped (they contain thepreview_id) and return HTTP 200 withnum_redirects: 0— no redirect to a “latest” path:…/previews/{preview_id}/manifest.plist→content-type: application/xml, and itssoftware-packageasset URL points to…/previews/{preview_id}/app.ipa.…/previews/{preview_id}/app.ipa→ 200, ~98 MB binary.
- But the downloaded
.ipa(Payload/<App>.app/Info.plist) containsCFBundleVersion = 260601.401.2— a 1 June build — and every file inside the.ipais dated 06-01, i.e. a build created later than the preview the permalink belongs to (28 May, commitA1B2C3D).
Our CFBundleVersion is unique per build, composed as YYMMDD.<ci_run_number><run_attempt>.<track-suffix> (we follow the docs recommendation to derive the build version from a CI run number). So this is not a duplicate-CFBundleVersion situation — the two previews have different, strictly increasing build versions, yet the older permalink serves the newer binary.
All our previews share the same CFBundleShortVersionString (1.0.0) and the same track (qa), which appears to be what the .ipa endpoint resolves on (it seems to ignore the preview_id/CFBundleVersion and return the latest matching build).
This looks related to the documented branch/track “latest preview” resolution (see #7737, #8281), but here it is leaking into a specific preview’s permalink artifact, not just the /latest link or the dashboard/desktop-app “latest” entry points — so the page metadata and the served binary disagree.
How do we reproduce it?
- On a given track (e.g.
qa) with a fixed marketing version (e.g.1.0.0), runtuist share App.ipa --track qafrom commit A. Note the preview URLP_A. - Later, from commit B (same marketing version, same track, a different/greater unique
CFBundleVersion), runtuist share App.ipa --track qa. This creates previewP_B. - Open
P_A’s page — its metadata correctly shows commit A / date A. - Download
P_A’s artifact: open…/previews/{P_A}/app.ipadirectly (or tap Install / scan the QR onP_A’s page). - Inspect
Payload/<App>.app/Info.plist(CFBundleVersion) of the downloaded.ipa.
Expected: the .ipa is build A’s exact binary, matching P_A’s metadata / CFBundleVersion.
Actual: the .ipa is build B’s binary (the latest preview for app + marketing version + track), while P_A‘s page still shows build A’s metadata.
Error log
No error — HTTP 200 and correct-looking preview-scoped URLs (manifest.plist/app.ipa both contain the preview_id, no redirect), but the binary content corresponds to a different, later preview.
macOS version
15.x (server-side / tuist.dev behavior; reproduced by downloading the artifact with curl as well)
Tuist version
CLI 4.194.1; server: tuist.dev (SaaS), observed 2026-06-02
Xcode version
16.x (iOS device ad-hoc .ipa)
No GitHub comments yet.