UniFFI: Publish Swift packages#1078
Conversation
- Add uniffi-packages.yml mirroring ffi-builds.yml's draft-release lookup pattern; matrix-keyed on `package` (swift today, kotlin/python later) - Move podspec generation into the Makefile via tera template at support/swift/LiveKitUniFFI.podspec.tera; rendered alongside Package.swift - Add swift-zip-xcframework task (release profile only) that zips the xcframework and stashes the checksum; swift-generate-manifest now derives SPM_URL/SPM_CHECKSUM internally so callers only set SPM_VERSION - After `cargo make --profile release swift-package`, the output dir is publish-ready: Package.swift, Package@swift-6.2.swift, podspec, LICENSE, zipped xcframework, and Sources/<name>/livekit_uniffi.swift Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| with: | ||
| submodules: recursive | ||
|
|
||
| - uses: maxim-lobanov/setup-xcode@v1 |
There was a problem hiding this comment.
issue: Just FYI, before merging this, you should be sure to pin all third party action uses to specific hashes to prevent some flavors of supply chain attacks.
There was a problem hiding this comment.
good point, I think it should be applied elsewhere (sdk et al)
There was a problem hiding this comment.
Most livekit repos (at least in the web sphere) are doing this already, but yes if there are repos which are not pinning actions, definitely update those!
There was a problem hiding this comment.
Done: a37c961. Also pinned livekit/publish-xcframework-action to the fix commit from livekit/publish-xcframework-action#3 — will repin to the main-merged SHA once that lands.
| tag_name: | ||
| description: "Release tag (e.g., livekit-uniffi/v0.0.7). Required if no draft release exists." | ||
| required: false | ||
| type: string |
There was a problem hiding this comment.
question: I'm a bit confused why this is set up to work with draft releases - would it be better to use a similar kick off mechanism as the release.yml and tie into knope PR merges somehow?
There was a problem hiding this comment.
same pattern as ffi-builds.yml — knope creates the draft on the release-PR merge, this workflow watches main pushes and picks it up. wiring it into release.yml directly would mean baking swift-specific build/publish logic into the release orchestrator, which feels out of place.
| include: | ||
| - package: swift | ||
| runner: macos-26 | ||
| hosting-repo: livekit/livekit-uniffi-xcframework | ||
| # NOTE: spm-name must match SPM_NAME in livekit-uniffi/Makefile.toml. | ||
| spm-name: LiveKitUniFFI |
There was a problem hiding this comment.
thought: Realistically, I'm not sure that it's worth trying to parameterize this job for all platforms. In the ai-coustics plugin project, I found it worked a lot better to treat each platform as its own independent pipeline which handles its own builds / packaging concerns / etc. So I think my vote would be to rename this swift-build and get rid of the matrix here.
There was a problem hiding this comment.
looked into it: GH Actions can't pick uses: based on matrix.* dynamically, so "separate workflow + matrix" collapses to N hardcoded uses: jobs (basically your suggestion). matrix-of-one stays simpler for now; if kotlin/python end up with very divergent steps we revisit and split.
There was a problem hiding this comment.
Reconsidered — extracted to a reusable workflow (uniffi-swift.yml) called from a thin parent that handles tag resolution. No matrix; each future lang would be a hardcoded uses: job. Done: ac8941a
| # knope has produced a draft release for the livekit-uniffi package; if so the | ||
| # matching package job runs. Manual runs go through workflow_dispatch. | ||
| # | ||
| # NOTE: livekit-uniffi must be added to knope.toml with `assets = "marker"` |
There was a problem hiding this comment.
question: I don't see that added here. Did you want to add in a separate PR?
| matrix: | ||
| include: | ||
| - package: swift | ||
| runner: macos-26 |
There was a problem hiding this comment.
suggestion: How about using a large runner here to speed things up (e.g. macos-26-large)?
There was a problem hiding this comment.
-large is x86, -xlarge is arm (and paid), I don't have any exact numbers yet, may be worth trying.
| aarch64-apple-tvos \ | ||
| aarch64-apple-tvos-sim | ||
|
|
||
| - name: Install nightly + rust-src (visionOS tier-3 target) |
There was a problem hiding this comment.
comment: visionOS has been upgrade to tier II now.
There was a problem hiding this comment.
Correction: tvOS is still tier-3 and cargo-swift falls back to nightly+-Zbuild-std for any tier-3 arch, so I dropped tier-3 entries from the stable rustup target add list and put the nightly + rust-src install back. Verified end-to-end locally. Done: 6ff5f79
| - uses: cargo-bins/cargo-binstall@v1 | ||
| - run: cargo binstall cargo-make --no-confirm | ||
|
|
||
| - name: Install Protoc |
There was a problem hiding this comment.
question: Did you get an error without protoc installed? It shouldn't be needed here.
There was a problem hiding this comment.
you were right, no error without protoc. Done: a37c961
| xcframework-zip: ${{ env.OUTPUT_DIR }}/Rust${{ matrix.spm-name }}.xcframework.zip | ||
| version: ${{ needs.resolve-tag.outputs.version }} | ||
| hosting-repo: ${{ matrix.hosting-repo }} | ||
| files: | |
There was a problem hiding this comment.
question: Does this fail if any of the specified files are missing? To avoid similar incomplete release issues to what we've seen with libwebrtc in this repo, it probably should.
There was a problem hiding this comment.
addressed in livekit/publish-xcframework-action#3 — explicit test -f per source file + set -euo pipefail, no more silent partials. workflow pinned to that commit. Done: a37c961
|
Thanks for constructive fb, I'll revisit that in the meantime. @ladvoc are we still fine with creating some "draft" releases of the crate for e2e test? |
Adds a [packages.livekit-uniffi] entry so a normal knope release PR merge produces a draft release tagged 'livekit-uniffi/v<X.Y.Z>'. uniffi-packages.yml picks that draft up on the post-merge push to main and runs the Swift publish job.
- Remove nightly + rust-src install: visionOS is rustc Tier 2 now, no need for -Zbuild-std anymore. Add the visionos targets to the stable rustup target list instead. - Remove arduino/setup-protoc: the uniffi build doesn't invoke protoc. - Pin all third-party actions to immutable commit SHAs (incl. livekit/publish-xcframework-action at the fix commit from PR #3 in that repo). Repin to the main-merged SHA after PR #3 lands. Addresses #1078 comments on lines 84, 106, 115.
No changeset foundThis PR modifies the following packages but doesn't include a changeset: Directly changed:
Click here to create a changeset The link pre-populates a changeset file with If this change doesn't require a version bump, add the |
Splits the per-language pipeline out of uniffi-packages.yml: the parent keeps the trigger + tag-resolution and delegates to uniffi-swift.yml via workflow_call. Each language wrapper gets its own focused file (kotlin/python will slot in as new reusable workflows + new jobs in the parent) without conditional 'if: matrix.lang == …' gates. Note: GH Actions can't pick 'uses:' by matrix expression, so each language is a hardcoded job in the parent rather than a matrix entry. Addresses #1078 comment on uniffi-packages.yml:73.
Previous behavior used 'echo | while' which put the loop body in a subshell — a cp failure there could be swallowed by the pipe's exit semantics, producing a partial release. Switch to a heredoc-fed loop in the parent shell, add 'set -euo pipefail', and check each source file exists before copy so a typo or missing artifact fails the step loudly instead of half-publishing. Reported on livekit/rust-sdks#1078.
livekit/publish-xcframework-action#3 merged; repin from the branch SHA to the main commit.
…ntain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
cargo-make's `[tasks.X.env]` block overrides caller env unless gated with a `condition`. Without the guard, SPM_VERSION=0.0.0-test from the environment was being clobbered by the makefile's 0.0.0-dev default during `cargo make swift-package`. Mark both SPM_VERSION and SPM_HOSTING_REPO with `env_not_set` so they only apply when the caller hasn't set the variable.
ladvoc's comment that visionOS reached tier-2 is accurate, but tvOS remains tier-3 — and cargo-swift's fork falls back to `cargo +nightly -Zbuild-std` for any tier-3 arch in the build. Drop the tier-3 entries from the stable `rustup target add` list (they error on stable) and put the nightly + rust-src install back so the build doesn't fail on the tvOS slice. Verified locally: `SPM_VERSION=0.0.0-test cargo make --profile release swift-package` succeeds end-to-end (incl. xros-arm64 + tvos-arm64 slices in the produced xcframework).
The CodeQL autofix landed in #1078 added `permissions: contents: read` at the top level of uniffi-packages.yml, which then forbids any nested job (including the called workflow uniffi-swift.yml) from requesting higher permissions — workflow validation fails with: Error calling workflow ... The nested job 'build-and-publish' is requesting 'contents: write', but is only allowed 'contents: read'. Neither job actually needs write: `gh release list` in resolve-tag is read-only, and the publish step authenticates to the hosting repo with UNIFFI_XCFRAMEWORK_PAT (not GITHUB_TOKEN). Drop the elevation on both so the workflow can start.
Leverages https://github.com/livekit/publish-xcframework-action that seems to be working https://github.com/pblazej/test-xcframework-hosting/releases