Skip to content

Conversation

@ladvoc
Copy link
Contributor

@ladvoc ladvoc commented Oct 16, 2025

Summary by CodeRabbit

  • New Features

    • Added a UniFFI-based native library exposing token generation/verification, unverified claim parsing, build/version info, and log-forwarding for host languages.
    • Included language bindings and runnable examples for Node.js and Python, plus Swift packaging templates.
  • Documentation

    • Added README and usage examples demonstrating binding usage, testing, and multi-platform build workflows.
  • Chores

    • Added workspace member, gitignore rules, packaging/build scripts, and license exclusion updates.

✏️ Tip: You can customize this high-level summary in your review settings.

@ladvoc ladvoc force-pushed the ladvoc/uniffi-trial branch from 48449a0 to 41bd64c Compare October 31, 2025 23:59
pub struct Claims {
pub exp: u64,
pub iss: String,
pub nbf: u64,
Copy link

@pblazej pblazej Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] can we use SystemTime instead?

@ladvoc ladvoc force-pushed the ladvoc/uniffi-trial branch from a028a03 to 3faab50 Compare November 20, 2025 05:08
@pblazej
Copy link

pblazej commented Nov 20, 2025

The cargo make thing works great for me!

I'd also commit (local) Package.swift to packages/swift if you don't mind - does not pollute the others.

cc @1egoman

@pblazej pblazej mentioned this pull request Nov 20, 2025
1 task
@pblazej pblazej requested review from 1egoman and davidliu November 20, 2025 14:29
@pblazej
Copy link

pblazej commented Dec 11, 2025

I'll try to complete the Swift distribution path in the meantime.

@pblazej pblazej marked this pull request as ready for review December 12, 2025 09:52
@pblazej pblazej changed the title [WIP] UniFFI trial UniFFI trial Dec 12, 2025
@pblazej
Copy link

pblazej commented Dec 12, 2025

Here's more or less everything we need for local distribution, the tricky part is:

[tasks.swift-generate-manifest.env]
SPM_URL = { value = "https://dummy.com" }
SPM_CHECKSUM = { value = "1234" }

Let's assume the following workflow:

  • create tag rust-sdks/livekit-uniffi@*
  • create a release here with the artifacts

optionally:

Alternatively, we can skip Package.swift generation for release whatsoever and just track it in the above repo (it won't change that often). It will also bypass the cocoapods legacy problem here.

@pblazej
Copy link

pblazej commented Dec 12, 2025

I made a manual pre-release here: https://github.com/livekit/livekit-uniffi-xcframework
Swift SDK support: livekit/client-sdk-swift#822

@ladvoc if you're able to finish the release pipeline here, that would be cool (artifact is OK as mentioned above).

--platforms maccatalyst \
--platforms visionos \
--platforms tvos \
--lib-type dynamic \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And one more thing @ladvoc static release is still ~40MB whereas dylib ~1.8MB.

Dynamic one won't pass AppStore validation (we'd need to wrap it in a Framework).

90426: Invalid Swift Support. The SwiftSupport folder is missing. Rebuild your app using the current public (GM) version of Xcode and resubmit it.

I'd rather go for stripping the static one further, any ideas?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iOS in --release profile:

Ar Archive - 47 MB

Copy link

@pblazej pblazej Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, looks like DEAD_CODE_STRIPPING does the job and the bundle size increase is negligible (0.1 MB), moved back to static 729bc10

It also passes 🍏 validation 👍

I'm still open to optimization ideas 😄

script = """
# Rename modules inside the xcframework to match the module name (fix for Swift >6.2)
# Workaroud for cargo-swift v0.10.0 not supporting ffi_module_name
find "${SPM_NAME}/Rust${SPM_NAME}.xcframework" -depth -type d -name "Rust${SPM_NAME}" -exec bash -c 'mv "$1" "${1%Rust'${SPM_NAME}'}livekit_uniffiFFI"' _ {} \; || true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to fix it here antoniusnaumann/cargo-swift#87

pblazej added a commit to livekit/client-sdk-swift that referenced this pull request Dec 18, 2025
Applies a patch to fix newer Swift versions 6.2+ failing when the module
name does not match folder name inside xcframework.

`cargo-swift` fix in progress,
livekit/rust-sdks#750 (comment)

tests
https://github.com/livekit/client-sdk-swift/actions/runs/20334177943/job/58416504807
@coderabbitai
Copy link

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

Adds a new workspace member livekit-uniffi providing UniFFI-based FFI bindings (access token generation/verification, log forwarding, build metadata), associated build/tooling/templates/tests, and small updates to livekit-api (unverified claims helper) and workspace/license configs.

Changes

Cohort / File(s) Summary
Workspace & Licensing
Cargo.toml, licenserc.toml
Added livekit-uniffi to workspace members; updated license excludes (add livekit-uniffi/packages, allow Makefile.toml).
livekit-api token helper & tests
livekit-api/src/access_token.rs, livekit-api/src/test_token.txt
Added Claims::from_unverified(&str) to decode JWTs without signature verification; removed test-only expiry bypass; updated test token.
UniFFI crate manifest & gitignore
livekit-uniffi/Cargo.toml, livekit-uniffi/.gitignore
New crate manifest with cdylib/staticlib/lib, dependencies, build-deps, release profile; ignore rules for generated/ and packages/.
UniFFI library root & bindgen
livekit-uniffi/src/lib.rs, livekit-uniffi/bindgen.rs
Library root exposing modules (access_token, log_forward, build_info) and uniffi scaffolding; added uniffi-bindgen binary entry.
Access token FFI module
livekit-uniffi/src/access_token.rs
New FFI-exposed types and functions: ApiCredentials, TokenOptions, Claims, token_generate, token_verify, token_claims_from_unverified, error enum, and conversions.
Log forwarding FFI module
livekit-uniffi/src/log_forward.rs
New log forwarding API: log_forward_bootstrap(LevelFilter), async log_forward_receive() -> Option<LogForwardEntry>, enums/record types, global logger using tokio mpsc and OnceCell.
Build metadata FFI
livekit-uniffi/src/build_info.rs
Added build_version() -> String returning crate version.
Bindings orchestration & tasks
livekit-uniffi/Makefile.toml
Large cargo-make task set for cross-platform builds, UniFFI bindgen, per-language packaging and release pipelines.
Language templates & tooling
livekit-uniffi/support/node/*, livekit-uniffi/support/swift/*
Added Node and Swift support templates/scripts: download-lib.ts.tera, package.json.tera, tsup.config.ts, tsconfig.json, Swift Package templates for packaging/linking xcframeworks.
Language test harnesses
livekit-uniffi/node_test/*, livekit-uniffi/python_test/main.py
Added Node test (index.ts, package.json, .gitignore) and Python test (main.py) to exercise bindings: build_version, token generation/verification, and log forwarding consumption.

Sequence Diagrams

sequenceDiagram
    participant Client as Client (Node/Python/Swift)
    participant UniFFI as UniFFI Binding Layer
    participant Rust as livekit-uniffi (Rust)
    participant LiveKitAPI as livekit-api

    Client->>UniFFI: token_generate(options, credentials)
    UniFFI->>Rust: call token_generate
    Rust->>LiveKitAPI: construct & sign token
    LiveKitAPI-->>Rust: return JWT
    Rust-->>UniFFI: return JWT
    UniFFI-->>Client: return JWT
Loading
sequenceDiagram
    participant Client as Client (Node/Python/Swift)
    participant UniFFI as UniFFI Binding Layer
    participant RustLogger as Rust Logger (log_forward)
    participant Channel as MPSC Channel

    Client->>UniFFI: log_forward_bootstrap(level)
    UniFFI->>RustLogger: init global logger
    RustLogger->>Channel: create sender/receiver
    Note over RustLogger,Channel: Rust emits logs -> sent to Channel
    Client->>UniFFI: log_forward_receive()
    UniFFI->>Channel: poll receiver
    Channel-->>UniFFI: Option<LogForwardEntry>
    UniFFI-->>Client: deliver entry or None
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 I hopped into bindings, small and spry,
Tokens I stitched, then sent them high,
From Rust burrow to Node and Swift,
Logs and claims in a tidy gift,
Hooray — a rabbit-built FFI tie!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'UniFFI trial' is vague and generic, using non-descriptive terminology that doesn't convey meaningful information about the changeset's scope or primary purpose. Use a more descriptive title that clearly indicates the main change, such as 'Add UniFFI integration for cross-platform FFI bindings' or 'Introduce livekit-uniffi crate with access token and logging support'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@livekit-uniffi/python_test/main.py`:
- Around line 44-52: The receive_log_messages loop using log_forward_receive can
block forever because the Rust channel may never close; update
receive_log_messages (or the caller) to avoid hanging by adding a timeout around
await log_forward_receive (e.g., with asyncio.wait_for) or by running the
receiver concurrently with the main token operations and cancelling it
afterwards; specifically modify the async function receive_log_messages and the
asyncio.run invocation so that log_forward_receive calls time out (handle
asyncio.TimeoutError and treat it as "no more logs") or run receive_log_messages
as a background task and cancel/join it after the token operations complete.

In `@livekit-uniffi/support/node/download-lib.ts.tera`:
- Around line 45-54: generateLibraryName currently switches on
'windows'/'macos'/'linux' but targetPlatform() returns Rust triple fragments
(e.g. 'pc-windows-msvc', 'apple-darwin', 'unknown-linux-gnu'), so no case
matches; update generateLibraryName to normalize or pattern-match the triples
returned by targetPlatform() (e.g. use startsWith/includes checks like
startsWith('pc-windows') or includes('darwin')/includes('linux') or map the
known triples to the three targets) and return the correct filename for each,
and add a default branch that throws/returns a clear error; reference
generateLibraryName and targetPlatform to locate and change the logic.
🧹 Nitpick comments (9)
livekit-uniffi/README.md (1)

19-21: Add language specifier to fenced code blocks.

The shell command blocks should specify a language for proper syntax highlighting and to satisfy markdown linting.

📝 Suggested fix
-```
+```bash
 cargo make swift-package

```diff
-```
+```bash
 cargo make node-package
</details>


Also applies to: 26-28

</blockquote></details>
<details>
<summary>livekit-uniffi/support/node/download-lib.ts.tera (1)</summary><blockquote>

`119-126`: **Consider adding type assertions for CLI arguments.**

The `parseArgs` values are typed as `string | boolean | undefined`, but `downloadLib` expects specific platform/arch types. Consider adding runtime validation or type assertions to catch invalid CLI inputs early.


<details>
<summary>♻️ Suggested improvement</summary>

```diff
+function assertValidPlatform(value: unknown): asserts value is TargetPlatform {
+  const valid = ['pc-windows-msvc', 'apple-darwin', 'unknown-linux-gnu'];
+  if (typeof value !== 'string' || !valid.includes(value)) {
+    throw new Error(`Invalid platform: ${value}`);
+  }
+}
+
+function assertValidArch(value: unknown): asserts value is TargetArch {
+  const valid = ['aarch64', 'x86_64'];
+  if (typeof value !== 'string' || !valid.includes(value)) {
+    throw new Error(`Invalid arch: ${value}`);
+  }
+}

 if (require.main === module) {
   // ...
   const { values } = parseArgs({ args: process.argv.slice(2), options });
+  if (values.platform) assertValidPlatform(values.platform);
+  if (values.arch) assertValidArch(values.arch);
   downloadLib(
     values.platform,
     // ...
livekit-uniffi/src/log_forward.rs (1)

102-102: Consider adding context to the unwrap() for future debugging.

While the unwrap() is currently safe (the receiver can't be dropped while the Logger exists in OnceCell), adding an expect() with a message would help if the design ever changes.

♻️ Suggested improvement
-        self.tx.send(record).unwrap();
+        self.tx.send(record).expect("Log receiver unexpectedly dropped");
livekit-uniffi/node_test/index.ts (1)

67-68: Handle async errors from main() to avoid silent failures.

Top-level promise rejections won’t be surfaced clearly; add a catch handler.

🛠️ Proposed fix
 if (require.main === module) {
-  main();
+  main().catch((err) => {
+    console.error(err);
+    process.exit(1);
+  });
 }
livekit-uniffi/node_test/package.json (1)

5-7: Consider cleaning up the placeholder test script.

The npm test script in this package is not currently invoked in CI or any automation (tests are run directly via npx tsx index.ts per the README). However, if someone manually runs npm test in this directory, it will unconditionally fail. Either replace it with "test": "tsx index.ts" or remove it entirely to avoid confusion.

livekit-uniffi/python_test/main.py (1)

21-21: Consider explicit imports instead of star import.

While star imports are acceptable for test files, using explicit imports would silence the static analysis warnings and make dependencies clearer:

from livekit_uniffi import (
    log_forward_bootstrap,
    log_forward_receive,
    LogForwardFilter,
    build_version,
    ApiCredentials,
    token_generate,
    token_verify,
    TokenOptions,
)
livekit-uniffi/Cargo.toml (1)

1-11: Consider reordering sections for conventional layout.

Placing [lib] before [package] is valid but unconventional. The typical Cargo.toml order is [package] first, followed by [lib], [[bin]], dependencies, and profiles. This is a minor style preference.

livekit-uniffi/support/node/package.json.tera (1)

12-17: Consider setting main to CJS for broader compatibility.

The main field points to the ESM entry (dist/index.mjs), but conventionally main should point to CommonJS for tools that don't support exports. Consider:

"main": "dist/index.js",
"module": "dist/index.mjs",

However, with engines.node >= 18 and proper exports field, this may not be necessary for your target audience.

livekit-uniffi/src/access_token.rs (1)

184-185: Address TODO: Remove test logging or the TODO comment.

The TODO indicates this debug log was added for testing log forwarding. Either remove the log statement if it's not needed in production, or remove the TODO comment if the logging should be kept.

Do you want me to help clean up these debug log statements across token_generate and token_verify?

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ec9971 and 3472606.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • livekit-uniffi/node_test/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • Cargo.toml
  • licenserc.toml
  • livekit-api/src/access_token.rs
  • livekit-api/src/test_token.txt
  • livekit-uniffi/.gitignore
  • livekit-uniffi/Cargo.toml
  • livekit-uniffi/Makefile.toml
  • livekit-uniffi/README.md
  • livekit-uniffi/bindgen.rs
  • livekit-uniffi/node_test/.gitignore
  • livekit-uniffi/node_test/index.ts
  • livekit-uniffi/node_test/package.json
  • livekit-uniffi/python_test/main.py
  • livekit-uniffi/src/access_token.rs
  • livekit-uniffi/src/build_info.rs
  • livekit-uniffi/src/lib.rs
  • livekit-uniffi/src/log_forward.rs
  • livekit-uniffi/support/node/download-lib.ts.tera
  • livekit-uniffi/support/node/package.json.tera
  • livekit-uniffi/support/node/tsconfig.json
  • livekit-uniffi/support/node/tsup.config.ts
  • livekit-uniffi/support/swift/Package.swift.tera
  • livekit-uniffi/support/swift/[email protected]
🧰 Additional context used
🧬 Code graph analysis (1)
livekit-uniffi/python_test/main.py (3)
livekit-uniffi/src/log_forward.rs (2)
  • log_forward_bootstrap (28-32)
  • log_forward_receive (41-44)
livekit-uniffi/src/build_info.rs (1)
  • build_version (17-19)
livekit-uniffi/src/access_token.rs (2)
  • token_generate (180-230)
  • token_verify (238-250)
🪛 Gitleaks (8.30.0)
livekit-api/src/test_token.txt

[high] 1-1: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

🪛 markdownlint-cli2 (0.18.1)
livekit-uniffi/README.md

19-19: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


26-26: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.14.13)
livekit-uniffi/python_test/main.py

21-21: from livekit_uniffi import * used; unable to detect undefined names

(F403)


25-25: log_forward_bootstrap may be undefined, or defined from star imports

(F405)


25-25: LogForwardFilter may be undefined, or defined from star imports

(F405)


28-28: build_version may be undefined, or defined from star imports

(F405)


30-30: ApiCredentials may be undefined, or defined from star imports

(F405)


30-30: Possible hardcoded password assigned to argument: "secret"

(S106)


32-32: token_generate may be undefined, or defined from star imports

(F405)


33-33: TokenOptions may be undefined, or defined from star imports

(F405)


38-38: token_verify may be undefined, or defined from star imports

(F405)


46-46: log_forward_receive may be undefined, or defined from star imports

(F405)

🔇 Additional comments (25)
livekit-uniffi/node_test/.gitignore (1)

1-1: LGTM!

Standard .gitignore entry for Node.js projects.

livekit-uniffi/.gitignore (1)

1-2: LGTM!

Correctly excludes generated artifacts and packaged bindings from version control.

livekit-api/src/test_token.txt (1)

1-1: Test token is appropriate for unit testing.

The Gitleaks warning is a false positive—this is a test fixture with dummy credentials (myapikey as issuer, identity as subject) and a far-future expiration date (year 2100). The token is used for testing Claims::from_unverified and related token parsing functionality.

licenserc.toml (1)

16-16: LGTM!

The negation pattern correctly ensures Makefile.toml is checked for license headers despite the *.toml exclusion. Excluding livekit-uniffi/packages appropriately skips generated artifacts.

Also applies to: 21-21

livekit-uniffi/src/build_info.rs (1)

15-19: LGTM!

Clean implementation using compile-time env! macro to expose the crate version through UniFFI. The #[uniffi::export] attribute correctly generates cross-language bindings.

Cargo.toml (1)

8-8: LGTM!

The new livekit-uniffi crate is properly added to the workspace members.

livekit-uniffi/Makefile.toml (2)

24-26: Placeholder download URL needs to be updated before release.

The CDYLIB_DOWNLOAD_BASE_URL points to an example URL. Ensure this is updated to the actual release artifact location before merging or enabling release builds.


293-299: sed -i '' syntax is acceptable for this macOS-only manual task.

The swift-package task is a manual developer workflow documented in the README for local Swift package generation, not part of CI automation. Since XCFramework/Swift package building is inherently macOS-focused, the BSD sed syntax sed -i '' is appropriate and doesn't require portability fixes.

If this task were automated in CI on multiple platforms, portability would be a concern. As-is, no action needed.

livekit-uniffi/support/swift/[email protected] (1)

1-41: LGTM!

The Swift 6.0 manifest template properly handles development vs release profiles with appropriate binaryTarget configurations. The visionOS platform support (.v2) is correctly included.

livekit-uniffi/support/swift/Package.swift.tera (1)

8-13: visionOS is missing from platform list, unlike the Swift 6.0 template.

The [email protected] includes .visionOS(.v2) but this template omits it. Since visionOS support was added in Swift 5.9, this may be intentional (to support older Xcode versions) or an oversight.

livekit-uniffi/src/log_forward.rs (2)

41-44: try_lock() may silently return None under lock contention.

If multiple consumers call log_forward_receive() concurrently and one holds the lock, others will return None immediately (interpreted as "forwarding ended"). This could cause premature termination of log polling loops in client code.

Consider whether this is the intended behavior, or if you want to use .lock().await to wait for the mutex (though this changes semantics for single-consumer scenarios).


27-32: LGTM!

The bootstrap function correctly handles idempotent initialization while allowing subsequent level changes, as discussed in prior review comments.

livekit-uniffi/bindgen.rs (1)

15-19: LGTM for the bindgen entrypoint.

livekit-uniffi/support/node/tsconfig.json (1)

2-13: LGTM for the TypeScript compiler settings.

livekit-uniffi/support/node/tsup.config.ts (1)

3-15: LGTM for the tsup build configuration.

livekit-uniffi/src/lib.rs (1)

15-26: LGTM for the module wiring.

livekit-uniffi/node_test/index.ts (1)

57-63: The review comment is incorrect. The concern about empty strings terminating the loop does not apply here.

The function logForwardReceive() returns Option<LogForwardEntry> (TypeScript: LogForwardEntry | null), not a string. The check if (!message) correctly tests whether the entire object is null to detect loop termination. Empty content in the message field inside LogForwardEntry has no effect on the loop condition, so the current code handles the intended termination signal correctly.

livekit-api/src/access_token.rs (2)

154-167: LGTM!

The from_unverified implementation correctly disables signature validation while retaining exp and nbf time validation, which is appropriate for inspecting token contents without the secret. The method name clearly communicates the security implications.


398-441: LGTM!

Comprehensive test coverage for from_unverified:

  • Parses pre-existing token with room configuration
  • Parses freshly generated token
  • Correctly handles malformed token with altered signature

Good validation of the intended behavior.

livekit-uniffi/Cargo.toml (1)

28-34: LGTM!

The release profile is well-configured for FFI distribution:

  • panic = "abort" prevents undefined behavior from unwinding across FFI boundaries
  • Size optimizations (opt-level = "z", lto, strip) are appropriate for distributable binaries
livekit-uniffi/support/node/package.json.tera (1)

34-44: Potential trailing comma issue in generated JSON.

The template iterates over devDependencies and adds a trailing comma after each entry (line 37), which is followed by hardcoded entries. However, if devDependencies is empty, the first hardcoded entry ("tsup") won't have a preceding comma separator from the loop, and the JSON will still be valid.

The current pattern works but is fragile. If devDependencies has entries, the trailing comma from line 37 correctly precedes "tsup". Consider explicitly handling the comma separator for clarity:

💡 Alternative approach
"devDependencies": {
    {% for package_name, package_version in devDependencies %}
      "{{package_name | addslashes}}": "{{package_version | addslashes}}",
    {%- endfor %}
    "tsup": "^8.4.0",
    ...

The current approach works because of the trailing comma in the loop, but ensure the template is tested with both empty and populated devDependencies.

livekit-uniffi/src/access_token.rs (4)

21-29: LGTM!

Correct use of #[uniffi::remote(Error)] to expose the existing AccessTokenError from livekit_api. The flat_error attribute is appropriate for cross-language error handling.


107-139: LGTM!

Using u64 for timestamps (exp, nbf) is appropriate for FFI - it's more portable across target languages than SystemTime. The usize as u64 conversion at lines 126 and 128 is safe on both 32-bit (widening) and 64-bit (no-op) platforms.


237-250: LGTM!

The verification logic correctly handles optional credentials and converts the result to the UniFFI-compatible Claims type. Note: the TODO on line 242-243 should also be addressed (same as noted for token_generate).


252-262: LGTM!

Excellent documentation with a clear security warning. The implementation correctly delegates to the API layer's from_unverified and converts to the UniFFI-compatible type.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +44 to +52
async def receive_log_messages():
while True:
message = await log_forward_receive()
if message is None:
print("Log forwarding ended")
break
print(f"Log from Rust: {message}")

asyncio.run(receive_log_messages())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Log receive loop may block indefinitely.

The async loop waits for log messages but has no mechanism to exit after the token operations complete. Unless the Rust log channel is explicitly closed, log_forward_receive() will block forever waiting for more messages, causing the test script to hang.

Consider adding a timeout or running the log receiver concurrently with the main operations:

💡 Suggested approach using asyncio.wait_for or concurrent execution
async def receive_log_messages():
    import asyncio
    try:
        while True:
            message = await asyncio.wait_for(log_forward_receive(), timeout=1.0)
            if message is None:
                print("Log forwarding ended")
                break
            print(f"Log from Rust: {message}")
    except asyncio.TimeoutError:
        print("No more log messages (timeout)")
🧰 Tools
🪛 Ruff (0.14.13)

46-46: log_forward_receive may be undefined, or defined from star imports

(F405)

🤖 Prompt for AI Agents
In `@livekit-uniffi/python_test/main.py` around lines 44 - 52, The
receive_log_messages loop using log_forward_receive can block forever because
the Rust channel may never close; update receive_log_messages (or the caller) to
avoid hanging by adding a timeout around await log_forward_receive (e.g., with
asyncio.wait_for) or by running the receiver concurrently with the main token
operations and cancelling it afterwards; specifically modify the async function
receive_log_messages and the asyncio.run invocation so that log_forward_receive
calls time out (handle asyncio.TimeoutError and treat it as "no more logs") or
run receive_log_messages as a background task and cancel/join it after the token
operations complete.

Comment on lines +45 to +54
function generateLibraryName(platform: TargetPlatform) {
switch (platform) {
case "windows":
return `${PACKAGE_NAME}.dll`;
case "macos":
return `lib${PACKAGE_NAME}.dylib`;
case "linux":
return `lib${PACKAGE_NAME}.so`;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Platform string mismatch will cause generateLibraryName to never match.

The targetPlatform() function returns Rust target triple fragments ('pc-windows-msvc', 'apple-darwin', 'unknown-linux-gnu'), but generateLibraryName() switches on different strings ('windows', 'macos', 'linux'). This will cause all cases to fall through and return undefined.

🐛 Proposed fix
 function generateLibraryName(platform: TargetPlatform) {
   switch (platform) {
-    case "windows":
+    case "pc-windows-msvc":
       return `${PACKAGE_NAME}.dll`;
-    case "macos":
+    case "apple-darwin":
       return `lib${PACKAGE_NAME}.dylib`;
-    case "linux":
+    case "unknown-linux-gnu":
       return `lib${PACKAGE_NAME}.so`;
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function generateLibraryName(platform: TargetPlatform) {
switch (platform) {
case "windows":
return `${PACKAGE_NAME}.dll`;
case "macos":
return `lib${PACKAGE_NAME}.dylib`;
case "linux":
return `lib${PACKAGE_NAME}.so`;
}
}
function generateLibraryName(platform: TargetPlatform) {
switch (platform) {
case "pc-windows-msvc":
return `${PACKAGE_NAME}.dll`;
case "apple-darwin":
return `lib${PACKAGE_NAME}.dylib`;
case "unknown-linux-gnu":
return `lib${PACKAGE_NAME}.so`;
}
}
🤖 Prompt for AI Agents
In `@livekit-uniffi/support/node/download-lib.ts.tera` around lines 45 - 54,
generateLibraryName currently switches on 'windows'/'macos'/'linux' but
targetPlatform() returns Rust triple fragments (e.g. 'pc-windows-msvc',
'apple-darwin', 'unknown-linux-gnu'), so no case matches; update
generateLibraryName to normalize or pattern-match the triples returned by
targetPlatform() (e.g. use startsWith/includes checks like
startsWith('pc-windows') or includes('darwin')/includes('linux') or map the
known triples to the three targets) and return the correct filename for each,
and add a default branch that throws/returns a clear error; reference
generateLibraryName and targetPlatform to locate and change the logic.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@livekit-uniffi/Makefile.toml`:
- Around line 25-27: The CDYLIB_DOWNLOAD_BASE_URL is set to the example release
URL and must be guarded so release builds don't embed a non-existent example
asset; update the Makefile.toml logic that sets CDYLIB_DOWNLOAD_BASE_URL (and
the downloader generation code referenced around the other block at the 226-236
region) to require an explicit environment variable (e.g.
CDYLIB_DOWNLOAD_BASE_URL or CDYLIB_DOWNLOAD_BASE_URL_RELEASE) or only use the
example value when a non-release flag is present (e.g. when PROFILE != "release"
or when a DEV_DOWNLOADS=true env is set); modify the downloader generation step
to fail fast with a clear error if the URL is unset in release mode, and
reference CDYLIB_DOWNLOAD_BASE_URL in both places to ensure they use the guarded
value.
- Around line 281-324: The task config currently hard-sets SPM_URL and
SPM_CHECKSUM in [tasks.swift-generate-manifest.env], which unconditionally
overrides CI-provided values; remove the unconditional env entries and instead
handle defaults and validation inside the swift-generate-manifest script: in the
script for the task (swift-generate-manifest) only assign a dummy default to
SPM_URL/SPM_CHECKSUM when the env var is empty (e.g. if [ -z "$SPM_URL" ]; then
SPM_URL="https://dummy.com"; fi), and after generating the manifests fail fast
in release CI (detect via CI flag or BUILD_TYPE) if the values are still the
dummy placeholders (exit 1 with an error message). Update references in the
script and keep the unique symbols SPM_URL, SPM_CHECKSUM, and task names
swift-generate-manifest and swift-move-to-packages to locate the changes.
- Around line 107-117: The locate-lib task constructs LIB_PATH using a LIB_NAME
that always includes a "lib" prefix which is incorrect on Windows; update the
locate-lib script to set LIB_NAME conditionally based on
CARGO_MAKE_RUST_TARGET_OS (or use CARGO_MAKE_RUST_TARGET_OS value): when the OS
is "windows" use "${CARGO_MAKE_CRATE_FS_NAME}.${LIB_EXT}" (no "lib" prefix) and
for other OSes use "lib${CARGO_MAKE_CRATE_FS_NAME}.${LIB_EXT}", then build
LIB_PATH as before and export LIB_PATH; adjust the script inside the locate-lib
duckscript block to branch on CARGO_MAKE_RUST_TARGET_OS and set LIB_NAME
accordingly so bindgen and downstream tasks find the correct DLL name on
Windows.
- Around line 185-224: The node packaging tasks node-move-code-into-src,
node-copy-support-files, and node-link-lib use the POSIX `@shell` runner with
mv/cp/ln which will fail on Windows; update these tasks to either switch their
script_runner to `@duckscript` and rewrite the commands using cross-platform
duckscript steps (matching how the build task uses `@duckscript`), or add explicit
platform conditions (e.g., condition = { platforms = ["linux", "macos"] }) to
each of node-move-code-into-src, node-copy-support-files, and node-link-lib so
they do not run on Windows; ensure node-generate-manifest and
node-generate-downloader remain unchanged unless they also need platform gating.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f388f1d and 035a88f.

📒 Files selected for processing (1)
  • livekit-uniffi/Makefile.toml
🔇 Additional comments (2)
livekit-uniffi/Makefile.toml (2)

34-103: Build matrix and tier‑3 handling look solid.

No issues found here.


146-157: Kotlin/Python bindgen task wiring looks good.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +25 to +27
# Base URL for downloading release builds of the cdylib.
# Currently, it is assumed the archives will be named "ffi-<platform>-<arch>.zip".
CDYLIB_DOWNLOAD_BASE_URL = "https://github.com/livekit/example/releases/download/example/build"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Prevent release Node packages from using the example download base URL.

If CDYLIB_DOWNLOAD_BASE_URL stays on the example value, release downloaders will point at a non‑existent asset. Add a release‑only guard (or require an explicit env var) before downloader generation.

🔧 Suggested fix
+[tasks.node-verify-download-url]
+private = true
+condition = { profiles = ["release"] }
+script_runner = "@shell"
+script = """
+if [ "${CDYLIB_DOWNLOAD_BASE_URL}" = "https://github.com/livekit/example/releases/download/example/build" ]; then
+  echo "CDYLIB_DOWNLOAD_BASE_URL must be set for release" >&2
+  exit 1
+fi
+"""
+
 [tasks.node-package-flow]
 private = true
 dependencies = [
     "node-bindgen",
     "node-move-code-into-src",
     "node-generate-manifest",
+    "node-verify-download-url",
     "node-generate-downloader",
     "node-copy-support-files",
     "node-format",
     "node-link-lib"
 ]

Also applies to: 226-236

🤖 Prompt for AI Agents
In `@livekit-uniffi/Makefile.toml` around lines 25 - 27, The
CDYLIB_DOWNLOAD_BASE_URL is set to the example release URL and must be guarded
so release builds don't embed a non-existent example asset; update the
Makefile.toml logic that sets CDYLIB_DOWNLOAD_BASE_URL (and the downloader
generation code referenced around the other block at the 226-236 region) to
require an explicit environment variable (e.g. CDYLIB_DOWNLOAD_BASE_URL or
CDYLIB_DOWNLOAD_BASE_URL_RELEASE) or only use the example value when a
non-release flag is present (e.g. when PROFILE != "release" or when a
DEV_DOWNLOADS=true env is set); modify the downloader generation step to fail
fast with a clear error if the URL is unset in release mode, and reference
CDYLIB_DOWNLOAD_BASE_URL in both places to ensure they use the guarded value.

Comment on lines +107 to +117
[tasks.locate-lib.env.LIB_EXT]
source = "${CARGO_MAKE_RUST_TARGET_OS}"
mapping = { linux = "so", macos = "dylib", windows = "dll" }

[tasks.locate-lib]
private = true
script_runner = "@duckscript"
script = """
path = join_path ${TARGET_DIR} ${TARGET} release/ ${LIB_NAME}.${LIB_EXT}
set_env LIB_PATH "${path}"
"""
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find and read the Makefile.toml to understand the full context
fd "Makefile.toml" --type f

Repository: livekit/rust-sdks

Length of output: 89


🏁 Script executed:

# Read the Makefile.toml to understand the full context
cat -n livekit-uniffi/Makefile.toml

Repository: livekit/rust-sdks

Length of output: 11552


🏁 Script executed:

# Let me verify Windows Rust cdylib naming conventions by checking Rust documentation
# and looking at how the crate is actually built
fd "Cargo.toml" livekit-uniffi --type f

Repository: livekit/rust-sdks

Length of output: 86


🏁 Script executed:

# Read the Cargo.toml to understand the crate configuration
cat -n livekit-uniffi/Cargo.toml

Repository: livekit/rust-sdks

Length of output: 1102


🏁 Script executed:

# Let me search for any existing usage or documentation about this issue in the codebase
rg "LIB_PATH" livekit-uniffi/

Repository: livekit/rust-sdks

Length of output: 294


🏁 Script executed:

# Let me verify the exact Rust cdylib naming convention with a quick check
web_search

Repository: livekit/rust-sdks

Length of output: 111


🌐 Web query:

Rust cdylib naming convention Windows Linux macOS

💡 Result:

Short answer — cdylib filenames follow the platform's native dynamic-library naming:

  • Linux / other ELF: lib.so. [1]
  • macOS: lib.dylib. [1]
  • Windows: .dll (with an optional import .lib for MSVC linking). [1][2]

Notes: cdylib is the crate type that emits a C-compatible dynamic library for those native formats; Cargo/rustc choose the platform-specific prefix/suffix automatically. [1][3]

Sources:
[1] Rust Reference — Linkage / crate types.
[2] Rustonomicon — FFI / calling Rust from C.
[3] RFC 1510 (cdylib description).


Fix Windows DLL naming in locate-lib task.

On Windows, Rust cdylib artifacts are named <crate>.dll without the lib prefix, but the current code unconditionally uses lib${CARGO_MAKE_CRATE_FS_NAME}, causing LIB_PATH to point to a non-existent file and breaking bindgen and all dependent tasks on Windows builds.

🔧 Suggested fix
 [tasks.locate-lib]
 private = true
 script_runner = "@duckscript"
 script = """
-path = join_path ${TARGET_DIR} ${TARGET} release/ ${LIB_NAME}.${LIB_EXT}
+if eq ${CARGO_MAKE_RUST_TARGET_OS} "windows"
+    lib_name = ${CARGO_MAKE_CRATE_FS_NAME}
+else
+    lib_name = "lib${CARGO_MAKE_CRATE_FS_NAME}"
+end
+path = join_path ${TARGET_DIR} ${TARGET} release/ ${lib_name}.${LIB_EXT}
 set_env LIB_PATH "${path}"
 """
🤖 Prompt for AI Agents
In `@livekit-uniffi/Makefile.toml` around lines 107 - 117, The locate-lib task
constructs LIB_PATH using a LIB_NAME that always includes a "lib" prefix which
is incorrect on Windows; update the locate-lib script to set LIB_NAME
conditionally based on CARGO_MAKE_RUST_TARGET_OS (or use
CARGO_MAKE_RUST_TARGET_OS value): when the OS is "windows" use
"${CARGO_MAKE_CRATE_FS_NAME}.${LIB_EXT}" (no "lib" prefix) and for other OSes
use "lib${CARGO_MAKE_CRATE_FS_NAME}.${LIB_EXT}", then build LIB_PATH as before
and export LIB_PATH; adjust the script inside the locate-lib duckscript block to
branch on CARGO_MAKE_RUST_TARGET_OS and set LIB_NAME accordingly so bindgen and
downstream tasks find the correct DLL name on Windows.

Comment on lines +185 to +224
[tasks.node-move-code-into-src]
private = true
script_runner = "@shell"
script = """
mkdir -p ${PACKAGES_DIR}/${LANG}/src
mv ${PACKAGES_DIR}/${LANG}/*.ts ${PACKAGES_DIR}/${LANG}/src
"""

[tasks.node-generate-manifest]
private = true
extend = "tera"
args = [
"--env",
"-t", "${SUPPORT_DIR}/${LANG}/package.json.tera",
"-o", "${PACKAGES_DIR}/${LANG}/package.json",
"${PACKAGES_DIR}/${LANG}/package.json"
# Provide original package.json as input
]

[tasks.node-copy-support-files]
private = true
script_runner = "@shell"
script = """
cp ${SUPPORT_DIR}/${LANG}/tsconfig.json ${PACKAGES_DIR}/${LANG}/tsconfig.json
cp ${SUPPORT_DIR}/${LANG}/tsup.config.ts ${PACKAGES_DIR}/${LANG}/tsup.config.ts
"""

[tasks.node-generate-downloader]
private = true
condition = { profiles = ["release"] }
extend = "tera"
args = ["--env-only", "-t", "${SUPPORT_DIR}/${LANG}/download-lib.ts.tera", "-o", "${PACKAGES_DIR}/${LANG}/download-lib.ts"]

[tasks.node-link-lib]
private = true
condition = { profiles = ["development"] }
script_runner = "@shell"
script = """
ln -sf ${LIB_PATH} ${PACKAGES_DIR}/${LANG}/src
"""
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | head -20

Repository: livekit/rust-sdks

Length of output: 513


🏁 Script executed:

find . -name "Makefile.toml" -o -name ".github" -type d | head -10

Repository: livekit/rust-sdks

Length of output: 101


🏁 Script executed:

# Check the full Makefile.toml to understand context better
wc -l livekit-uniffi/Makefile.toml

Repository: livekit/rust-sdks

Length of output: 93


🏁 Script executed:

cat -n livekit-uniffi/Makefile.toml | head -50

Repository: livekit/rust-sdks

Length of output: 2009


🏁 Script executed:

cat -n livekit-uniffi/Makefile.toml | sed -n '180,230p'

Repository: livekit/rust-sdks

Length of output: 1711


🏁 Script executed:

# Search for platform-specific conditions or duckscript usage in Makefile.toml
grep -n "condition\|duckscript\|win\|platform" livekit-uniffi/Makefile.toml

Repository: livekit/rust-sdks

Length of output: 1156


🏁 Script executed:

# Check if Windows CI is configured in workflows
cat .github/workflows/builds.yml

Repository: livekit/rust-sdks

Length of output: 4073


🏁 Script executed:

# Search for Node-related workflows or CI configuration
find .github/workflows -name "*.yml" -o -name "*.yaml" | xargs grep -l "node\|npm\|uniffi" 2>/dev/null

Repository: livekit/rust-sdks

Length of output: 45


🏁 Script executed:

# Check the ffi-builds workflow which might be related to Node packaging
cat .github/workflows/ffi-builds.yml | head -100

Repository: livekit/rust-sdks

Length of output: 3566


🏁 Script executed:

# Search for references to node-package task in Makefile
grep -n "node-package\|node-bindgen" livekit-uniffi/Makefile.toml

Repository: livekit/rust-sdks

Length of output: 199


🏁 Script executed:

# Check if there's any documentation about Windows support or platform handling
grep -n "windows\|win32\|Windows" livekit-uniffi/Makefile.toml

Repository: livekit/rust-sdks

Length of output: 413


🏁 Script executed:

# Check the node-package-flow task and its dependencies
cat -n livekit-uniffi/Makefile.toml | sed -n '226,280p'

Repository: livekit/rust-sdks

Length of output: 1906


🏁 Script executed:

# Look for any workflow that triggers node-package task
grep -r "node-package\|node.package" .github/workflows/

Repository: livekit/rust-sdks

Length of output: 43


🏁 Script executed:

# Check if there's a separate Node build/release workflow
ls -la .github/workflows/ | grep -i node

Repository: livekit/rust-sdks

Length of output: 43


🏁 Script executed:

# Search for any duckscript usage for file operations to understand patterns
grep -n "duckscript\|@shell" livekit-uniffi/Makefile.toml | head -20

Repository: livekit/rust-sdks

Length of output: 335


🏁 Script executed:

# Search all workflows for invocation of node-package or cargo make calls
grep -r "node-package\|cargo.*make\|cargo-make" .github/workflows/

Repository: livekit/rust-sdks

Length of output: 43


🏁 Script executed:

# Look at the full Makefile to understand when node-package is called
cat -n livekit-uniffi/Makefile.toml | sed -n '1,50p'

Repository: livekit/rust-sdks

Length of output: 2009


🏁 Script executed:

# Check release workflow which might build Node packages
cat .github/workflows/release.yml 2>/dev/null | head -100

Repository: livekit/rust-sdks

Length of output: 2825


🏁 Script executed:

# Look at CargoMake documentation or see how `@shell` behaves - check nearby tasks
cat -n livekit-uniffi/Makefile.toml | sed -n '280,310p'

Repository: livekit/rust-sdks

Length of output: 1588


🏁 Script executed:

# Check if there's any shell configuration or Windows handling in cargo-make config
cat livekit-uniffi/Makefile.toml | grep -i "shell\|windows\|@" | head -30

Repository: livekit/rust-sdks

Length of output: 763


🏁 Script executed:

# Look for any .cargo/config.toml that might set shell behavior
cat .cargo/config.toml 2>/dev/null

Repository: livekit/rust-sdks

Length of output: 497


🏁 Script executed:

# Check if swift-move-to-packages (which also uses `@shell` with POSIX commands) has platform conditions
cat -n livekit-uniffi/Makefile.toml | sed -n '294,320p'

Repository: livekit/rust-sdks

Length of output: 1244


Use duckscript or platform-specific conditions for file operations in Node packaging tasks.

The tasks node-move-code-into-src, node-copy-support-files, and node-link-lib (lines 185–224) use @shell runner with POSIX-only commands: mv, cp, and ln -sf. These will fail on Windows since the commands don't exist in cmd.exe or PowerShell.

The project explicitly supports Windows targets (x86_64-pc-windows-msvc, aarch64-pc-windows-msvc), and these tasks have no platform conditions to prevent execution on Windows. Consider converting to @duckscript (like the build task at line 41) or adding condition = { platforms = ["linux", "macos"] } if Node packaging is not intended for Windows.

🤖 Prompt for AI Agents
In `@livekit-uniffi/Makefile.toml` around lines 185 - 224, The node packaging
tasks node-move-code-into-src, node-copy-support-files, and node-link-lib use
the POSIX `@shell` runner with mv/cp/ln which will fail on Windows; update these
tasks to either switch their script_runner to `@duckscript` and rewrite the
commands using cross-platform duckscript steps (matching how the build task uses
`@duckscript`), or add explicit platform conditions (e.g., condition = { platforms
= ["linux", "macos"] }) to each of node-move-code-into-src,
node-copy-support-files, and node-link-lib so they do not run on Windows; ensure
node-generate-manifest and node-generate-downloader remain unchanged unless they
also need platform gating.

Comment on lines +281 to +324
[tasks.swift-generate-manifest.env]
SPM_URL = { value = "https://dummy.com" }
SPM_CHECKSUM = { value = "1234" }

[tasks.swift-generate-manifest]
private = true
install_crate = { crate_name = "tera-cli", binary = "tera", test_arg = "--version" }
script_runner = "@shell"
script = """
tera --env --file ${SUPPORT_DIR}/${LANG}/Package.swift.tera > ${SPM_NAME}/Package.swift
tera --env --file ${SUPPORT_DIR}/${LANG}/[email protected] > ${SPM_NAME}/[email protected]
"""

[tasks.swift-move-to-packages]
private = true
script_runner = "@shell"
script = """
# Patch the generated XCFramework and Swift sources to match the framework name
# This ensures compatibility with Swift 6.2+ which requires the module name to match the framework name

FFI_NAME="livekit_uniffiFFI"

# 1. Rename header files inside the XCFramework
find "${SPM_NAME}/${XCFRAMEWORK_NAME}.xcframework" -name "${FFI_NAME}.h" -exec bash -c 'mv "$1" "$(dirname "$1")/'"${XCFRAMEWORK_NAME}"'.h"' _ {} \\;

# 2. Patch the modulemaps inside the XCFramework
find "${SPM_NAME}/${XCFRAMEWORK_NAME}.xcframework" -name "module.modulemap" -exec sed -i '' "s/${FFI_NAME}/${XCFRAMEWORK_NAME}/g" {} +

# 3. Patch the generated Swift sources
find "${SPM_NAME}/Sources" -name "*.swift" -exec sed -i '' "s/${FFI_NAME}/${XCFRAMEWORK_NAME}/g" {} +

out_dir="${PACKAGES_DIR}/${LANG}"
rm -rf "${out_dir}"
mkdir -p "${out_dir}"
mv "${SPM_NAME}" "${out_dir}"
"""

[tasks.swift-package-flow]
private = true
dependencies = [
"swift-xcframework",
"swift-generate-manifest",
"swift-move-to-packages"
]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "Makefile.toml" -type f

Repository: livekit/rust-sdks

Length of output: 178


🏁 Script executed:

# Check if the file exists and look at the relevant sections
grep -n "SPM_URL\|SPM_CHECKSUM" -r . --include="Makefile.toml" -A 2 -B 2

Repository: livekit/rust-sdks

Length of output: 416


🏁 Script executed:

# Check for CI configuration to see if these values are set
find . -name "*.yml" -o -name "*.yaml" | head -20

Repository: livekit/rust-sdks

Length of output: 375


🏁 Script executed:

# Check the release workflow to see if SPM_URL and SPM_CHECKSUM are provided
cat -n ./.github/workflows/release.yml | head -100

Repository: livekit/rust-sdks

Length of output: 3525


🏁 Script executed:

# Check ffi-builds workflow
cat -n ./.github/workflows/ffi-builds.yml | head -150

Repository: livekit/rust-sdks

Length of output: 6582


🏁 Script executed:

# Get more context around the problematic lines in Makefile.toml
sed -n '275,330p' ./livekit-uniffi/Makefile.toml

Repository: livekit/rust-sdks

Length of output: 1841


🏁 Script executed:

# Search for where SPM_URL and SPM_CHECKSUM are used in the tera templates
grep -r "SPM_URL\|SPM_CHECKSUM" ./livekit-uniffi/support --include="*.tera" -A 2 -B 2

Repository: livekit/rust-sdks

Length of output: 1063


Hard-coded dummy SPM_URL and SPM_CHECKSUM values override CI-provided values.

The environment variables are unconditionally set in the task configuration, which causes the dummy values to override any CI-provided environment variables and generates invalid Package.swift manifests in release builds. Default only when unset and fail fast during release if dummy values persist.

🔧 Suggested fix
 [tasks.swift-generate-manifest.env]
-SPM_URL = { value = "https://dummy.com" }
-SPM_CHECKSUM = { value = "1234" }
+SPM_URL = { value = "https://dummy.com", condition = { env_not_set = ["SPM_URL"] } }
+SPM_CHECKSUM = { value = "1234", condition = { env_not_set = ["SPM_CHECKSUM"] } }
 
+[tasks.swift-verify-spm-env]
+private = true
+condition = { profiles = ["release"] }
+script_runner = "@shell"
+script = """
+if [ "${SPM_URL}" = "https://dummy.com" ] || [ "${SPM_CHECKSUM}" = "1234" ]; then
+  echo "SPM_URL/SPM_CHECKSUM must be set for release" >&2
+  exit 1
+fi
+"""
+
 [tasks.swift-package-flow]
 private = true
 dependencies = [
     "swift-xcframework",
+    "swift-verify-spm-env",
     "swift-generate-manifest",
     "swift-move-to-packages"
 ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[tasks.swift-generate-manifest.env]
SPM_URL = { value = "https://dummy.com" }
SPM_CHECKSUM = { value = "1234" }
[tasks.swift-generate-manifest]
private = true
install_crate = { crate_name = "tera-cli", binary = "tera", test_arg = "--version" }
script_runner = "@shell"
script = """
tera --env --file ${SUPPORT_DIR}/${LANG}/Package.swift.tera > ${SPM_NAME}/Package.swift
tera --env --file ${SUPPORT_DIR}/${LANG}/[email protected] > ${SPM_NAME}/[email protected]
"""
[tasks.swift-move-to-packages]
private = true
script_runner = "@shell"
script = """
# Patch the generated XCFramework and Swift sources to match the framework name
# This ensures compatibility with Swift 6.2+ which requires the module name to match the framework name
FFI_NAME="livekit_uniffiFFI"
# 1. Rename header files inside the XCFramework
find "${SPM_NAME}/${XCFRAMEWORK_NAME}.xcframework" -name "${FFI_NAME}.h" -exec bash -c 'mv "$1" "$(dirname "$1")/'"${XCFRAMEWORK_NAME}"'.h"' _ {} \\;
# 2. Patch the modulemaps inside the XCFramework
find "${SPM_NAME}/${XCFRAMEWORK_NAME}.xcframework" -name "module.modulemap" -exec sed -i '' "s/${FFI_NAME}/${XCFRAMEWORK_NAME}/g" {} +
# 3. Patch the generated Swift sources
find "${SPM_NAME}/Sources" -name "*.swift" -exec sed -i '' "s/${FFI_NAME}/${XCFRAMEWORK_NAME}/g" {} +
out_dir="${PACKAGES_DIR}/${LANG}"
rm -rf "${out_dir}"
mkdir -p "${out_dir}"
mv "${SPM_NAME}" "${out_dir}"
"""
[tasks.swift-package-flow]
private = true
dependencies = [
"swift-xcframework",
"swift-generate-manifest",
"swift-move-to-packages"
]
[tasks.swift-generate-manifest.env]
SPM_URL = { value = "https://dummy.com", condition = { env_not_set = ["SPM_URL"] } }
SPM_CHECKSUM = { value = "1234", condition = { env_not_set = ["SPM_CHECKSUM"] } }
[tasks.swift-generate-manifest]
private = true
install_crate = { crate_name = "tera-cli", binary = "tera", test_arg = "--version" }
script_runner = "@shell"
script = """
tera --env --file ${SUPPORT_DIR}/${LANG}/Package.swift.tera > ${SPM_NAME}/Package.swift
tera --env --file ${SUPPORT_DIR}/${LANG}/[email protected] > ${SPM_NAME}/[email protected]
"""
[tasks.swift-move-to-packages]
private = true
script_runner = "@shell"
script = """
# Patch the generated XCFramework and Swift sources to match the framework name
# This ensures compatibility with Swift 6.2+ which requires the module name to match the framework name
FFI_NAME="livekit_uniffiFFI"
# 1. Rename header files inside the XCFramework
find "${SPM_NAME}/${XCFRAMEWORK_NAME}.xcframework" -name "${FFI_NAME}.h" -exec bash -c 'mv "$1" "$(dirname "$1")/'"${XCFRAMEWORK_NAME}"'.h"' _ {} \\;
# 2. Patch the modulemaps inside the XCFramework
find "${SPM_NAME}/${XCFRAMEWORK_NAME}.xcframework" -name "module.modulemap" -exec sed -i '' "s/${FFI_NAME}/${XCFRAMEWORK_NAME}/g" {} +
# 3. Patch the generated Swift sources
find "${SPM_NAME}/Sources" -name "*.swift" -exec sed -i '' "s/${FFI_NAME}/${XCFRAMEWORK_NAME}/g" {} +
out_dir="${PACKAGES_DIR}/${LANG}"
rm -rf "${out_dir}"
mkdir -p "${out_dir}"
mv "${SPM_NAME}" "${out_dir}"
"""
[tasks.swift-verify-spm-env]
private = true
condition = { profiles = ["release"] }
script_runner = "@shell"
script = """
if [ "${SPM_URL}" = "https://dummy.com" ] || [ "${SPM_CHECKSUM}" = "1234" ]; then
echo "SPM_URL/SPM_CHECKSUM must be set for release" >&2
exit 1
fi
"""
[tasks.swift-package-flow]
private = true
dependencies = [
"swift-xcframework",
"swift-verify-spm-env",
"swift-generate-manifest",
"swift-move-to-packages"
]
🤖 Prompt for AI Agents
In `@livekit-uniffi/Makefile.toml` around lines 281 - 324, The task config
currently hard-sets SPM_URL and SPM_CHECKSUM in
[tasks.swift-generate-manifest.env], which unconditionally overrides CI-provided
values; remove the unconditional env entries and instead handle defaults and
validation inside the swift-generate-manifest script: in the script for the task
(swift-generate-manifest) only assign a dummy default to SPM_URL/SPM_CHECKSUM
when the env var is empty (e.g. if [ -z "$SPM_URL" ]; then
SPM_URL="https://dummy.com"; fi), and after generating the manifests fail fast
in release CI (detect via CI flag or BUILD_TYPE) if the values are still the
dummy placeholders (exit 1 with an error message). Update references in the
script and keep the unique symbols SPM_URL, SPM_CHECKSUM, and task names
swift-generate-manifest and swift-move-to-packages to locate the changes.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants