Skip to content

The published binary fallback on main cannot match HEAD bindings #723

Description

@TarikGul

Package.swift on main falls back to a published xcframework when no local one
is staged. That fallback cannot hold the pairing the generated code enforces.

The binary comes from a release commit. The Swift bindings are gitignored and
generated from whatever commit you are on. UniFFI checks that pairing at
initialization, so unless the two come from the same commit the pairing is only
correct by luck.

Measured between main and the newest published asset, 0.13.1:

main tag 0.13.1
bindings_contract_version 30 30
checksum constants 62 62
..._hostcallbacks_auth_state_changed() 35488 50346

One value differs. That is enough, because the check is a single lazy global and
any mismatch returns apiChecksumMismatch for the whole library:

private let initializationResult: InitializationResult = {
    ...
    if (uniffi_truapi_server_checksum_method_hostcallbacks_auth_state_changed() != 35488) {
        return InitializationResult.apiChecksumMismatch
    }

So it is not one degraded callback with the rest working. Every use of
TrUAPIHost fails to initialize.

Who this reaches

Almost nobody, which is why it has gone unnoticed.

resolves the fallback
CI no, TRUAPI_USE_LOCAL_BINARY: "1"
the in-tree iOS host no, in-tree core and a staged binary since #679
a consumer pinning a semver tag no, it reads that tag's own manifest
a clean clone with bindings generated but no xcframework yes

swift package resolve also downloads the 74 MB asset before the build fails on
missing FFI headers, so a clean clone pays for it and cannot use it.

What is not wrong

The consumer tags are correct and should stay as they are. Each plain semver tag
carries generated sources and a manifest pointing at its own asset:

tag publishedBinaryURL FFI headers on the tag
0.7.0 0.7.0 present
0.12.0 0.12.0 present
0.13.1 0.13.1 present

Verified by resolving exact: "0.13.1" from a throwaway consumer package: it
fetched the 0.13.1 asset and the checksum validated. Bindings and binary on a
tag come from one commit, which is exactly why tags work. That property is the
reason to keep the fallback on tags and reconsider it only on main.

The bookkeeping problem underneath

release-ios.yml opens a pull request after each release to move main onto the
new asset. Those do not land:

#582 has no CI Status because a bot-authored pull request needs its workflows
approved, and the explicit workflow_dispatch run the release workflow triggers
does not attach as a pull request check. So main sits on 0.7.0.

Merging #582 is accurate bookkeeping and changes no behaviour: the asset and
checksum verify, and the one case that reads the fallback fails at init either
way. #556 is superseded and would walk main backwards if it merged later.

Decisions

  • Should main keep a published fallback at all, given it cannot be correct
    between releases?
  • If not, what does a clean clone get instead, given the build already
    requires generated bindings?
  • Close build(ios): publish host XCFramework 0.12.0 #556 as superseded, and decide whether to merge or close build(ios): publish host XCFramework 0.13.1 #582
  • Should CI exercise the published fallback at all, since
    TRUAPI_USE_LOCAL_BINARY: "1" means a wrong URL or checksum is invisible
    to every current job?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghost-workNeeds implementation in one or more host repos

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions