Hive Hive
Sign in

Preview permalink serves the latest build’s .ipa instead of the pinned build

GitHub issue · Open

Metadata
Source
tuist/tuist #11029
Updated
Jun 11, 2026
Domains
Distribution
Details

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, branch develop, track qa, created 28 May, version 1.0.0.
  • The manifest.plist and app.ipa URLs are correctly preview-scoped (they contain the preview_id) and return HTTP 200 with num_redirects: 0 — no redirect to a “latest” path:
    • …/previews/{preview_id}/manifest.plistcontent-type: application/xml, and its software-package asset 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) contains CFBundleVersion = 260601.401.2 — a 1 June build — and every file inside the .ipa is dated 06-01, i.e. a build created later than the preview the permalink belongs to (28 May, commit A1B2C3D).

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?

  1. On a given track (e.g. qa) with a fixed marketing version (e.g. 1.0.0), run tuist share App.ipa --track qa from commit A. Note the preview URL P_A.
  2. Later, from commit B (same marketing version, same track, a different/greater unique CFBundleVersion), run tuist share App.ipa --track qa. This creates preview P_B.
  3. Open P_A’s page — its metadata correctly shows commit A / date A.
  4. Download P_A’s artifact: open …/previews/{P_A}/app.ipa directly (or tap Install / scan the QR on P_A’s page).
  5. 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)

Comments

No GitHub comments yet.