Automate the Dart/Flutter release for livekit-uniffi - #1323
Conversation
…ests The upstream rev pinned by #1183 predates data tracks, so the bindgen emitted analysis errors (Bytes vs NativeType) for the multi-crate surface and the Dart tests have been disabled since #1034. Pin the fork rev that fixes multi-crate, custom-type and name-collision codegen; the pin is temporary until upstream merges the fixes or a livekit fork exists, see the Cargo.toml comment. Verified locally: cargo make dart-package passes all 5 FFI tests across the FFI boundary.
Split the dart-package flow by cargo-make profile: dev builds keep the embedded host dylib and publish_to: none, release builds omit the dylib (consumers use the hook's download mode) and clear publish_to so pub.dev accepts the package. The publish_to guard fails closed, an unknown profile still renders none. A dart-clean task starts every build from scratch so a release build over a previous dev tree cannot ship the stale host dylib, which the hook would prefer over download on every platform. Also add the metadata pub.dev requires or scores: LICENSE, README, CHANGELOG, repository/homepage/issue_tracker pubspec fields, real version constraints for code_assets/hooks (wide ranges, flutter_test pins meta exactly so carets break older stable channels), and an analysis_options.yaml so unused-import lints in generated code do not fail publish validation. Verified: both profiles build, all 5 FFI tests pass on the dev package, dart pub publish --dry-run exits 0 on the release package, and a release build over a dev tree contains no dylib.
Changeset ✓This PR includes a changeset covering all affected packages:
|
Wire the existing uniffi-cdylib.yml into uniffi-packages.yml so every livekit-uniffi release carries the build-<triple>.zip archives (and sha256 sidecars) the Dart build hook downloads at consumer build time. The assets attach to the already-published release, so the knope assets marker stays off (a draft-based flow stranded releases before, #1256). Since this activates the previously dormant uniffi-cdylib.yml on every release, also bind its tag name through env instead of template expanding it into the upload script: tag names may contain shell metacharacters (Actions script injection). The changeset cuts the release that carries the first assets.
pub.dev's automated publishing only accepts workflows triggered by a push of a tag matching the configured pattern, so this cannot be a job in the release-event-triggered uniffi-packages.yml. The workflow builds the release-profile package (cached, before the asset wait), stages it outside the work tree (pub archives from git's file listing and the generated packages/ tree is gitignored), refuses to publish a package containing a local native library, gates on every cdylib zip having its sha256 sidecar, and fast-fails on mis-pointed tags or a missing release. Tag names are env-bound, never template-expanded into scripts, since the job holds id-token: write for pub.dev token minting. PUBLISHING IS NOT ENABLED YET (PUBLISH_ENABLED=false): every run stops at dart pub publish --dry-run until the uniffi-dart pin moves off the personal fork, a livekit.io publisher admin has done the first manual publish, and automated publishing is configured on pub.dev. See the header for the full enablement steps.
10fb002 to
c343755
Compare
| crate with [UniFFI](https://mozilla.github.io/uniffi-rs/) and | ||
| [uniffi-dart](https://github.com/Uniffi-Dart/uniffi-dart). | ||
|
|
||
| This is a low-level package. It is consumed by |
There was a problem hiding this comment.
Nice callout! We should make sure this is included in all the READMEs for the package repos (outside the scope of this PR).
| # Flip to "true" once the enablement steps in the header are done. Real | ||
| # publishing additionally requires a tag-push trigger; workflow_dispatch | ||
| # runs always stop at the dry run (pub.dev rejects their OIDC tokens). | ||
| PUBLISH_ENABLED: "false" |
There was a problem hiding this comment.
question: Might it make sense to turn this into a workflow input like some of the other workflows do?
| - name: Setup Dart | ||
| uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 # v1.7.2 | ||
| with: | ||
| sdk: stable |
There was a problem hiding this comment.
question: Should this pin to a specific release?
| run: | | ||
| expected=12 | ||
| missing=0 | ||
| while true; do |
There was a problem hiding this comment.
Based on my fails with swift it's not a good idea to poll, maybe there's another way to trigger it after dylib update? Have you tested that e2e?
There was a problem hiding this comment.
🟥 Release dispatch enables command injection
A crafted tag_name reaches the shell through TAG before validation. The job can execute commands with the workflow's write-enabled GitHub token.
(Refers to this code)
Was this helpful? React with 👍 or 👎 to provide feedback.
| permissions: | ||
| # The upload step attaches assets to the release with the workflow token. | ||
| contents: write | ||
| uses: ./.github/workflows/uniffi-cdylib.yml |
There was a problem hiding this comment.
🔴 Manual recovery builds wrong revision
On a manual rerun for an older tag, cdylib checks out the selected workflow ref instead. It can replace release libraries with another version.
Prompt for agents
The cdylib reusable workflow is invoked with a resolved release tag, but its checkout step does not use that input. Release-event runs inherit the release ref, while workflow_dispatch runs inherit the ref selected in the dispatch UI, often main. Update .github/workflows/uniffi-cdylib.yml so every matrix build checks out inputs.tag_name, matching the existing Swift and Android reusable workflows. Preserve submodule checkout behavior and ensure the manual recovery procedure builds the tagged source before uploading with --clobber.
Was this helpful? React with 👍 or 👎 to provide feedback.
The multi-crate codegen fixes (Uniffi-Dart/uniffi-dart#150, #154, #156) are all merged upstream, so the temporary pin to the contributor fork is no longer needed. Pin the current upstream main rev, as no release tag has been cut since v0.2.1. Upstream codegen now emits ignore_for_file hints and drops unused imports, but it introduces unused locals in async callback trampolines, so add unused_local_variable to the generated-code lint ignores.
Before you submit your PR
Make sure the following is true before submitting your PR:
PR description
Automates the Dart/Flutter half of the livekit-uniffi release (CLT-2872), the release-side prerequisite for livekit/client-sdk-flutter#1160. One commit per component:
publish_to, and gain LICENSE/README/CHANGELOG and real version constraints. Fails closed for unknown profiles, and adart-cleantask keeps stale host dylibs out of release packages.uniffi-cdylib.ymlintouniffi-packages.ymlso releases carry thebuild-<triple>.zipassets the Dart build hook downloads. Its tag name is now env-bound (Actions script injection, newly on the release path).livekit_uniffito pub.dev via OIDC (same mechanism as client-sdk-flutter'spublish.yaml). It stages the package outside the work tree (packages/is gitignored and pub archives from git's file listing) and gates on every zip having its sha256 sidecar.Publishing stays disabled (
PUBLISH_ENABLED=false, every run stops at--dry-run) until the pin moves off the personal fork, the first manual publish creates the package, and pub.dev automated publishing is configured. Runbook in the workflow header.Follow-ups kept out: a stacked PR with two pre-existing CI cleanups, the fork-pin decision, and the non-atomic asset re-upload window.
Breaking changes
None. Dev-profile
cargo make dart-packagebehaves as before.MSRV
No changes.
Testing
dart pub publish --dry-runexits 0 with zero warnings and no dylib in the package; a release build over a previous dev tree stays clean.Async
No async code added; the crate surface is untouched.
🤖 Generated with Claude Code