Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changeset/dart_hook_code_assets_2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
livekit-uniffi: patch
---

# Allow code_assets 2.x in the Dart package and stop re-running its build hook

The `livekit_uniffi` Dart package capped `code_assets` below 2.0.0, which has since
shipped. Its only breaking change (equality on `OS` and `Architecture`) does not
affect the hook, and the cap would make the package unresolvable next to any
dependency that already requires 2.x. The constraint now allows it, and the hook
was verified against `code_assets 2.0.0` / `hooks 2.2.0`.

The hook also registered the downloaded library as a dependency. Dependencies are
inputs, so the hooks runner saw a file modified during the build and re-ran the
hook, and the download, once on every fresh build. The registration is removed.

The hook also wrote every target's library to the same shared path. A universal
macOS build runs the hook once per architecture and then merges the results with
`lipo`, which failed because the second download had overwritten the first. Each
target now gets its own subdirectory.
19 changes: 13 additions & 6 deletions livekit-uniffi/support/dart/hook/build.dart.tera
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,22 @@ Future<Uri> _resolveLibrary(
return local.uri;
}

// Download mode: fetch the prebuilt library for the target. The hooks-runner
// already caches hook results across builds, so there's no manual cache check.
// Download mode: fetch the prebuilt library for the target. The hooks runner
// caches hook results across builds, so there is no manual cache check. The
// downloaded file is an output, not an input, so it is deliberately not
// registered as a dependency: doing so made the runner see a file modified
// during the build and rerun the hook (and the download) once per fresh build.
//
// The shared directory is one per package, not per target, and a universal
// macOS build runs this hook once per architecture before lipo merges the
// results. Keep each target in its own subdirectory, or the second download
// overwrites the first and lipo is handed the same architecture twice.
final triple = _targetTriple(os, arch, iosSdk);
final outDir = Directory.fromUri(
input.outputDirectoryShared.resolve('$_cdylibName/'),
input.outputDirectoryShared.resolve('$_cdylibName/$triple/'),
);
final libFile = File.fromUri(outDir.uri.resolve(libName));

final triple = _targetTriple(os, arch, iosSdk);
// Asset naming matches the existing node downloader convention so one set of
// release assets serves both: `<base>/v<version>/build-<triple>.zip`.
final zipUrl = Uri.parse('$_downloadBase/v$_version/build-$triple.zip');
Expand All @@ -89,7 +97,7 @@ Future<Uri> _resolveLibrary(
for (final entry in ZipDecoder().decodeBytes(bytes)) {
if (!entry.isFile) continue;
// Guard against zip-slip: `resolve` normalizes `../` and absolute paths, so
// an entry that lands outside `base` is a malicious name reject it. The
// an entry that lands outside `base` is a malicious name, so reject it. The
// SHA-256 check proves integrity against tampering in transit, not that a
// compromised host can't serve a matching-checksum malicious archive.
final entryUri = base.resolve(entry.name);
Expand All @@ -104,7 +112,6 @@ Future<Uri> _resolveLibrary(
if (!await libFile.exists()) {
throw Exception('$libName not found in $zipUrl');
}
output.dependencies.add(libFile.uri);
return libFile.uri;
}

Expand Down
16 changes: 10 additions & 6 deletions livekit-uniffi/support/dart/pubspec.yaml.tera
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ environment:
dependencies:
ffi: ^2.1.0
# Native Assets build-hook support. pub.dev requires bounded constraints
# (`any` fails publish validation), but these must stay wide: the resolvable
# version is coupled to the consumer's Dart SDK via a shared `meta` pin
# (Flutter 3.44 back-solves to code_assets 1.0.0 / hooks 1.0.2, while a bare
# Dart 3.12 resolves 1.2.1 / 2.1.0). Carets here break `flutter pub get`
# for consumers on older stable channels.
code_assets: ">=1.0.0 <2.0.0"
# (`any` fails publish validation), but these must stay wide. The resolvable
# version is coupled to the consumer's Dart SDK: older hooks releases pin
# `meta`, which Flutter's flutter_test pins exactly, so Flutter 3.44
# back-solves to code_assets 1.0.0 / hooks 1.0.2 while a bare Dart 3.12
# resolves newer ones. Carets here break `flutter pub get` for consumers on
# older stable channels, and a tight upper bound makes this package
# unresolvable next to any package that already requires the next major.
# The hook uses only the stable core of both APIs (target config, CodeAsset,
# DynamicLoadingBundled), which is unchanged across these majors.
code_assets: ">=1.0.0 <3.0.0"
hooks: ">=1.0.2 <3.0.0"
# Used by hook/build.dart to download and verify prebuilt libraries.
archive: ^4.0.0
Expand Down
Loading