Skip to content

feat: port Linux v4l2loopback backend to Rust core - #5

Merged
chris-piekarski merged 8 commits into
mainfrom
feat/rust-linux-backend
Jun 4, 2026
Merged

feat: port Linux v4l2loopback backend to Rust core#5
chris-piekarski merged 8 commits into
mainfrom
feat/rust-linux-backend

Conversation

@chris-piekarski

@chris-piekarski chris-piekarski commented Jun 4, 2026

Copy link
Copy Markdown

Summary

  • Introduces a Rust workspace (pyvirtualcam-core, pyvirtualcam-py) with shared pixel-format conversion (libyuv), a native Rust camera API, and PyO3 bindings for the Linux v4l2loopback backend.
  • Keeps the public Python API unchanged (pyvirtualcam.Camera, backends, pixel formats); macOS and Windows still use the existing C++/ObjC++ extensions.
  • Adds mock backend contract tests for CI without a real virtual camera device, plus architecture docs (AGENTS.md, crate README, Rust example).

Design notes

  • Linux wheels now build via setuptools-rust; CI installs Rust on Linux only.
  • V4L2 ABI layout is explicitly tested in Rust (linux.rs) after fixing a struct-size mismatch found on aarch64 (GB8).
  • libyuv NEON is disabled on aarch64 to avoid toolchain issues on older embedded GCC/binutils.

Upstream divergence (intentionally deferred)

This fork is based on upstream letmaik/pyvirtualcam but does not include these recent upstream commits yet:

Reason: this fork maintains Python 3.8 support and a Linux/macOS-only CI matrix. We can cherry-pick mypy/py.typed in a follow-up if desired.

Out of scope for this PR

  • Rust ports of macOS/Windows backends
  • Removing the legacy C++ Linux backend sources (pyvirtualcam/native_linux_v4l2loopback/)
  • Full device integration tests in Linux CI (requires v4l2loopback kernel module)

Test plan

  • cargo fmt --check, cargo clippy --workspace --locked --all-targets -- -D warnings
  • cargo test --workspace --locked
  • pytest test/test_backend_contract.py test/test_util.py
  • sphinx-build -b html docs dist-docs
  • python setup.py bdist_wheel (x86_64 Linux)
  • GB8 remote validation (aarch64, Python 3.8, real /dev/video0 smoke test)
  • GitHub Actions CI on this branch (build matrix + Linux mock tests)
  • macOS wheel build/test (unchanged backend, should be unaffected)

Made with Cursor

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a Rust-based v4l2loopback backend for Linux with improved performance and maintainability
    • Added native Rust API and examples for direct backend access
    • Added multi-device support for Linux virtual camera streams
  • Changed

    • Linux builds now require a Rust toolchain (installable via rustup)
    • CI publishes wheels for Linux and macOS only; Windows support remains unchanged
  • Documentation

    • Expanded README with backend status, build instructions, and additional examples
    • Added architecture and development guidance

Introduce a Rust workspace with shared format conversion and a PyO3
Linux backend while keeping the Python API unchanged. Enable mock tests
in Linux CI and document the migration for reviewers.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 90bb74b0-d4e4-492e-93bf-2408779313e3

📥 Commits

Reviewing files that changed from the base of the PR and between 046bdd9 and 658d668.

📒 Files selected for processing (5)
  • .github/lux-ci-config.json
  • .github/scripts/build-macos.sh
  • .github/workflows/ci.yml
  • .github/workflows/luxie-tronic.yml
  • setup.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/lux-ci-config.json
  • .github/workflows/luxie-tronic.yml
  • setup.py

📝 Walkthrough

Walkthrough

This PR introduces a Rust workspace (pyvirtualcam-core and pyvirtualcam-py crates) implementing a cross-platform virtual camera abstraction with a complete Linux v4l2loopback backend. The changes include Rust core types/error handling, libyuv C++ bindings for pixel conversion, Linux device management via V4L2 ioctls, PyO3 Python bindings, test infrastructure updates, documentation, and build/CI configuration.

Changes

Rust Core and Python Bindings for Virtual Camera

Layer / File(s) Summary
Build system, packaging, CI, and automation setup
Cargo.toml, pyproject.toml, setup.py, .github/scripts/build-*.sh, .github/workflows/ci.yml, .gitignore, .codex/luxie-tronic-policy.json, .github/lux-ci-config.json, .github/workflows/luxie-tronic.yml
Workspace and setuptools-rust configuration, conditional Rust extension registration on Linux, build script Python dependency updates (setuptools-rust with version constraints, PYO3_USE_ABI3_FORWARD_COMPATIBILITY for Python 3.13), shell script Rust bootstrap, Docker image/macOS runner tagging for reproducibility, ignore rules for Rust build artifacts and automation files.
Core Rust contracts, error model, and format primitives
crates/pyvirtualcam-core/Cargo.toml, crates/pyvirtualcam-core/src/error.rs, crates/pyvirtualcam-core/src/fourcc.rs, crates/pyvirtualcam-core/src/formats.rs, crates/pyvirtualcam-core/src/lib.rs
Error enum with InvalidArgument/Runtime/Io variants, FourCC type alias and byte-packing encoder, pixel-format enum with canonical-FourCC/frame-size methods, and crate-level module exports.
Vendored libyuv build and conversion bindings
crates/pyvirtualcam-core/build.rs, crates/pyvirtualcam-core/cpp/libyuv_wrapper.cpp, crates/pyvirtualcam-core/src/convert.rs
Rust build script compiling bundled libyuv sources with C++17 and NEON handling, C-ABI wrapper functions for canonical FourCC and RGB/BGR-to-I420 conversions, and public Rust conversion functions with size validation and buffer checking.
Linux v4l2loopback backend implementation
crates/pyvirtualcam-core/src/linux.rs
Device auto-discovery/open/validation via ioctl, process-global ACTIVE_DEVICES tracking, frame send with I420 conversion, Drop cleanup, hand-written V4L2 ABI structs/unions, ioctl helpers, and comprehensive unit tests for struct sizes/ioctl constants.
Camera abstraction, Rust example, and PyO3 module
crates/pyvirtualcam-core/src/camera.rs, crates/pyvirtualcam-core/examples/simple.rs, crates/pyvirtualcam-py/Cargo.toml, crates/pyvirtualcam-py/src/lib.rs
CameraBuilder with pixel-format/device configuration, Camera enum (Linux-only) with send/close/device/native_format methods, simple Rust example with color gradient frames, PyO3 Camera class binding with keyword-only constructor, NumPy frame handling, device/FourCC accessors, and error mapping to Python exceptions.
Documentation and test flow updates
crates/pyvirtualcam-core/README.md, AGENTS.md, CHANGELOG.md, README.md, docs/conf.py, docs/index.rst, examples/README.md, test/test_backend_contract.py, test/test_camera.py, .github/scripts/test-linux.sh
Core crate architecture/migration/ABI documentation, AGENTS guidance, changelog entries for Rust workspace and Linux backend, README examples/Rust status section, GitHub docs link update, implementation notes, backend contract tests with MockBackend fixture, camera test backend-name alignment (v4l2loopback), and CI pytest scope narrowing.
CI Docker image and runner version pinning
.github/workflows/ci.yml
Pins manylinux2014 x86_64, manylinux2014 aarch64 (Python 3.8), and manylinux_2_28 aarch64 (Python 3.9+) to tagged dates; macOS-13 replaced with macos-15-intel for x86_64 builds.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 A Rust core takes shape with V4L2 grace,
Pixel frames dance through libyuv's embrace,
PyO3 binds it all to Python's call,
Linux loopback devices answer the ball—
From camera builder to tests, we're complete! 📹✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: port Linux v4l2loopback backend to Rust core' directly and accurately describes the primary change: porting the Linux v4l2loopback backend from C++/ObjC++ to Rust, with new Rust core crates and PyO3 bindings.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rust-linux-backend
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/rust-linux-backend

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

@chris-piekarski

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

chris-piekarski and others added 2 commits June 3, 2026 23:31
Enable org-standard luxie-tronic reviews on pull requests, with a
pyvirtualcam-specific policy covering Rust and Python paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@chris-piekarski

Copy link
Copy Markdown
Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (2)
crates/pyvirtualcam-core/src/error.rs (1)

43-43: ⚡ Quick win

Preserve std::error::Error source chaining for Error::Io

Line 43: impl std::error::Error for Error {} does not expose source for the Io variant, so callers lose causal chain inspection.

Suggested fix
-impl std::error::Error for Error {}
+impl std::error::Error for Error {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match self {
+            Self::Io { source, .. } => Some(source),
+            _ => None,
+        }
+    }
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/pyvirtualcam-core/src/error.rs` at line 43, The impl of
std::error::Error for the Error enum currently doesn't expose a source for
Error::Io; update the impl of std::error::Error for Error to override
source(&self) -> Option<&(dyn std::error::Error + 'static)> (or the modern
source method signature) and return the inner io::Error as Some(&inner) when
matching Error::Io(...), otherwise return None; locate the Error enum and the
impl block for std::error::Error to add this match for the Io variant so callers
can inspect the causal chain.
crates/pyvirtualcam-core/src/linux.rs (1)

456-467: ⚡ Quick win

Layout tests verify size but not field offsets — add offset checks.

v4l2_struct_layout_matches_linux_headers only asserts total sizes, which is exactly why the V4l2Format offset mismatch (see Line 440) slips through. Add offset_of! assertions for the critical fields (V4l2Format::fmt, V4l2PixFormat::pixelformat) to guard the ABI against future edits.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/pyvirtualcam-core/src/linux.rs` around lines 456 - 467, The test
v4l2_struct_layout_matches_linux_headers currently only checks total sizes; add
offset assertions to ensure field layouts match the kernel headers: use
offset_of! to assert the offset of V4l2Format::fmt and
V4l2PixFormat::pixelformat (in the same test or a new one) against the expected
byte offsets from the Linux headers so ABI breaks like the V4l2Format mismatch
cannot slip through; update the test function
v4l2_struct_layout_matches_linux_headers (or add
v4l2_field_offsets_match_linux_headers) to include these offset_of! checks
alongside the existing size assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/build-linux.sh:
- Around line 30-34: The script currently unconditionally sources
"$HOME/.cargo/env" which can fail if rustup wasn't installed and the file
doesn't exist; update the post-install logic by checking for the file's
existence before sourcing it (i.e., after the existing cargo presence
check/installation block, wrap the source "$HOME/.cargo/env" call in a
conditional that tests -f "$HOME/.cargo/env" and only sources when present) so
the build continues when the image already provides cargo or the env file is
absent.

In `@crates/pyvirtualcam-core/cpp/libyuv_wrapper.cpp`:
- Around line 17-25: The chroma plane stride/offsets in pyvc_rgb_to_i420 and
pyvc_bgr_to_i420 are computed with floor halves causing incorrect U/V pointers
for odd dimensions; change half_width to (width + 1) / 2 and half_height to
(height_abs + 1) / 2, use chroma_width (half_width) as the U/V stride and
compute the V-plane pointer offset as i420 + width * height_abs + chroma_width *
chroma_height so the calls to libyuv::RAWToI420 pass the corrected chroma
strides and pointers consistent with libyuv’s (width+1)/2 rounding.

In `@crates/pyvirtualcam-core/src/convert.rs`:
- Around line 43-44: The current narrowing of width/height with `as i32` in
convert functions can wrap large u32 values; before casting in convert_to_i420
(and in rgb_to_i420/bgr_to_i420 callers that forward u32), validate that width
and height are <= i32::MAX and return a clear error (or clamp) if not, then cast
to i32 and call the extern pyvc_*_to_i420 APIs; also harden
PixelFormat::frame_size by doing checked multiplication (using checked_mul on
usize) and return an error on overflow so buffer-length checks don't rely on
unchecked width*height arithmetic.

In `@crates/pyvirtualcam-core/src/formats.rs`:
- Around line 47-54: The I420/NV12 size math in frame_size undercounts when
width or height is odd; update the I420 | Nv12 arm in pub fn frame_size(self,
width: u32, height: u32) -> usize to compute chroma width and height with
ceiling division (chroma_w = (width as usize + 1) / 2, chroma_h = (height as
usize + 1) / 2), compute chroma_pixels = chroma_w * chroma_h, and return pixels
+ 2 * chroma_pixels (instead of pixels * 3 / 2). Also apply the same ceil-chroma
logic to any other places that compute chroma plane sizes (e.g., the
corresponding conversion/stride calculations referenced near the other match
arms) so all I420/NV12 code paths use (w+1)/2 and (h+1)/2 for chroma dimensions.

In `@crates/pyvirtualcam-core/src/linux.rs`:
- Around line 253-258: The TOCTOU bug: `try_open` only checks active_devices()
then releases the mutex while opening/configuring, allowing races with
concurrent V4l2LoopbackCamera::new; fix by making the check+reserve atomic under
the same mutex—either (A) modify try_open to lock active_devices(), check
contains(device_name) and insert a reservation before returning the fd, and
ensure any failure path (including subsequent configure_device errors) removes
that reservation, or (B) change V4l2LoopbackCamera::new to hold the
active_devices() guard across contains → insert → open → configure so the device
cannot be opened concurrently; reference active_devices()/ACTIVE_DEVICES,
try_open, and V4l2LoopbackCamera::new when applying the change and ensure
cleanup removes the inserted device on any error.

In `@crates/pyvirtualcam-py/src/lib.rs`:
- Around line 21-27: The constructor currently accepts an fps parameter but
ignores it when creating the backend (fps is not passed into
V4l2LoopbackCamera::new), so update the call site in the function that invokes
V4l2LoopbackCamera::new to either (A) forward the fps value into
V4l2LoopbackCamera::new (and update that constructor signature/implementation
accordingly) or (B) validate/reject unsupported fps values before constructing
the backend; specifically modify the function that calls parse_devices(...) and
V4l2LoopbackCamera::new(width, height, fourcc, devices) to include fps (e.g.
V4l2LoopbackCamera::new(width, height, fps, fourcc, devices) or validate fps and
return a PyErr via to_py_err) so pyvirtualcam.Camera semantics remain consistent
with PixelFormat/Backend/register_backend.

In `@setup.py`:
- Around line 182-184: Update the unpinned setuptools-rust requirement to a
Python-3.8-compatible bound across all build manifests and scripts: change the
setup_requires entry in setup.py (currently "pybind11>=2.6.0",
"setuptools-rust") to pin setuptools-rust (e.g., "setuptools-rust<1.11.0" or
"setuptools-rust==1.10.2"); apply the exact same constraint to pyproject.toml's
build-system.requires and to any CI install commands that call pip install
setuptools-rust (e.g., in .github/scripts/build-windows.ps1, build-macos.sh,
build-linux.sh) so all places use the same pinned version range.

---

Nitpick comments:
In `@crates/pyvirtualcam-core/src/error.rs`:
- Line 43: The impl of std::error::Error for the Error enum currently doesn't
expose a source for Error::Io; update the impl of std::error::Error for Error to
override source(&self) -> Option<&(dyn std::error::Error + 'static)> (or the
modern source method signature) and return the inner io::Error as Some(&inner)
when matching Error::Io(...), otherwise return None; locate the Error enum and
the impl block for std::error::Error to add this match for the Io variant so
callers can inspect the causal chain.

In `@crates/pyvirtualcam-core/src/linux.rs`:
- Around line 456-467: The test v4l2_struct_layout_matches_linux_headers
currently only checks total sizes; add offset assertions to ensure field layouts
match the kernel headers: use offset_of! to assert the offset of V4l2Format::fmt
and V4l2PixFormat::pixelformat (in the same test or a new one) against the
expected byte offsets from the Linux headers so ABI breaks like the V4l2Format
mismatch cannot slip through; update the test function
v4l2_struct_layout_matches_linux_headers (or add
v4l2_field_offsets_match_linux_headers) to include these offset_of! checks
alongside the existing size assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ff108083-f101-4925-8e7d-f829c7f44909

📥 Commits

Reviewing files that changed from the base of the PR and between 27f4bb3 and ecbab90.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (31)
  • .github/scripts/build-linux.sh
  • .github/scripts/build-macos.sh
  • .github/scripts/build-windows.ps1
  • .github/scripts/test-linux.sh
  • .gitignore
  • AGENTS.md
  • CHANGELOG.md
  • Cargo.toml
  • MANIFEST.in
  • README.md
  • crates/pyvirtualcam-core/Cargo.toml
  • crates/pyvirtualcam-core/README.md
  • crates/pyvirtualcam-core/build.rs
  • crates/pyvirtualcam-core/cpp/libyuv_wrapper.cpp
  • crates/pyvirtualcam-core/examples/simple.rs
  • crates/pyvirtualcam-core/src/camera.rs
  • crates/pyvirtualcam-core/src/convert.rs
  • crates/pyvirtualcam-core/src/error.rs
  • crates/pyvirtualcam-core/src/formats.rs
  • crates/pyvirtualcam-core/src/fourcc.rs
  • crates/pyvirtualcam-core/src/lib.rs
  • crates/pyvirtualcam-core/src/linux.rs
  • crates/pyvirtualcam-py/Cargo.toml
  • crates/pyvirtualcam-py/src/lib.rs
  • docs/conf.py
  • docs/index.rst
  • examples/README.md
  • pyproject.toml
  • setup.py
  • test/test_backend_contract.py
  • test/test_camera.py

Comment thread .github/scripts/build-linux.sh Outdated
Comment thread crates/pyvirtualcam-core/cpp/libyuv_wrapper.cpp Outdated
Comment thread crates/pyvirtualcam-core/src/convert.rs
Comment thread crates/pyvirtualcam-core/src/formats.rs Outdated
Comment thread crates/pyvirtualcam-core/src/linux.rs
Comment thread crates/pyvirtualcam-py/src/lib.rs Outdated
Comment thread setup.py Outdated
Fix I420 buffer sizing for odd dimensions, guard V4L2 device reservation,
validate conversion dimensions, pin setuptools-rust for Python 3.8, and
apply other review-driven hardening.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chris-piekarski

Copy link
Copy Markdown
Author

Replying to the two CodeRabbit nitpick items from the summary (no inline threads):

  • error.rs — Error::Io source chaining: Fixed in 69fa3c9. impl std::error::Error for Error now overrides source() and returns the inner io::Error for Error::Io.

  • linux.rs — V4L2 offset tests: Fixed in 69fa3c9. v4l2_struct_layout_matches_linux_headers now asserts offset_of!(V4l2Format, fmt) == 4 and offset_of!(V4l2PixFormat, pixelformat) == 8 in addition to struct sizes.

Pin manylinux2014 images that still ship cp38 for 3.8 matrix jobs and
enable PyO3 ABI3 forward compatibility when building cp313 wheels.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

37-37: ⚖️ Poor tradeoff

Pinning is inconsistent across the matrix.

Only the cp38 rows pin to the dated tag :2026.05.01-2; the cp39–cp313 rows still use floating manylinux2014_x86_64 / manylinux_2_28_aarch64 tags. Floating tags pull whatever the registry serves at build time, which undermines reproducibility and can cause non-cp38 jobs to drift while cp38 stays fixed. Consider pinning all rows to a consistent dated tag (or digest) for reproducible builds.

Also applies to: 74-74, 235-235, 272-272

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 37, The workflow uses a dated pinned image
only for the cp38 matrix row (docker-image:
quay.io/pypa/manylinux2014_x86_64:2026.05.01-2) while other matrix rows still
reference floating tags (manylinux2014_x86_64 / manylinux_2_28_aarch64); update
all matrix entries that currently use the unpinned images to the same dated tag
(or preferably to the corresponding image digest) so every docker-image entry is
consistently pinned (apply the same change for the other occurrences noted in
the matrix).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/build-macos.sh:
- Line 32: The macOS build installs setuptools-rust even though setup.py only
builds the RustExtension (RustExtension,
pyvirtualcam._native_linux_v4l2loopback) on Linux, which can force a Rust
toolchain on macOS; modify .github/scripts/build-macos.sh to remove
'setuptools-rust>=1.10.2,<1.11.0' from the pip install line and update setup.py
so that setuptools-rust is only declared/required (e.g. in setup_requires or
conditional imports) when platform.system() == 'Linux' and RustExtension will be
used, ensuring macOS builds do not attempt to pull or build the Rust toolchain.

In @.github/workflows/luxie-tronic.yml:
- Around line 5-33: The workflow uses pull_request_target with broad write
permissions and forwards all repo secrets via secrets: inherit to a mutable
reusable workflow ref (automation_ref/v1.6); update the generator config
(.github/lux-ci-config.json) to pin automation_ref to a full commit SHA
(immutable) instead of a tag, regenerate the workflow so the reusable-workflow
reference in luxie-tronic.yml points to that SHA, remove secrets: inherit and
instead pass only the minimal required secrets explicitly to the reusable
workflow, and tighten permissions (contents/pull-requests/issues) to least
privilege for the review job to eliminate secret leakage risk.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 37: The workflow uses a dated pinned image only for the cp38 matrix row
(docker-image: quay.io/pypa/manylinux2014_x86_64:2026.05.01-2) while other
matrix rows still reference floating tags (manylinux2014_x86_64 /
manylinux_2_28_aarch64); update all matrix entries that currently use the
unpinned images to the same dated tag (or preferably to the corresponding image
digest) so every docker-image entry is consistently pinned (apply the same
change for the other occurrences noted in the matrix).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 71f0da3f-56cd-4d7d-91ff-f60aade313eb

📥 Commits

Reviewing files that changed from the base of the PR and between ecbab90 and b74b92d.

📒 Files selected for processing (16)
  • .codex/luxie-tronic-policy.json
  • .github/lux-ci-config.json
  • .github/scripts/build-linux.sh
  • .github/scripts/build-macos.sh
  • .github/scripts/build-windows.ps1
  • .github/workflows/ci.yml
  • .github/workflows/luxie-tronic.yml
  • .gitignore
  • crates/pyvirtualcam-core/cpp/libyuv_wrapper.cpp
  • crates/pyvirtualcam-core/src/convert.rs
  • crates/pyvirtualcam-core/src/error.rs
  • crates/pyvirtualcam-core/src/formats.rs
  • crates/pyvirtualcam-core/src/linux.rs
  • crates/pyvirtualcam-py/src/lib.rs
  • pyproject.toml
  • setup.py
✅ Files skipped from review due to trivial changes (2)
  • pyproject.toml
  • .codex/luxie-tronic-policy.json
🚧 Files skipped from review as they are similar to previous changes (9)
  • .github/scripts/build-windows.ps1
  • crates/pyvirtualcam-core/src/convert.rs
  • .gitignore
  • crates/pyvirtualcam-core/src/error.rs
  • crates/pyvirtualcam-py/src/lib.rs
  • crates/pyvirtualcam-core/cpp/libyuv_wrapper.cpp
  • crates/pyvirtualcam-core/src/formats.rs
  • setup.py
  • crates/pyvirtualcam-core/src/linux.rs

Comment thread .github/scripts/build-macos.sh Outdated
Comment thread .github/workflows/luxie-tronic.yml Outdated
GitHub retired the macos-13 runner image in December 2025, which left
Intel matrix jobs queued indefinitely and blocked downstream test/docs.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/ci.yml (1)

43-70: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Pin all manylinux Docker images to fixed tags in ci.yml (avoid untagged quay.io/pypa/...).

build pins quay.io/pypa/manylinux2014_x86_64:2026.05.01-2 for Python 3.8, but uses untagged quay.io/pypa/manylinux2014_x86_64 for Python 3.9–3.13 (implicitly :latest, which can drift) at lines 43-70; same issue on ARM at lines 80-107 (manylinux_2_28_aarch64 is untagged for 3.9–3.13). The test job repeats this pattern at lines 242-269 and 279-306. Pin the remaining x86_64/aarch64 entries to the same fixed tag already used for Python 3.8 to keep CI reproducible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 43 - 70, The CI uses untagged
quay.io/pypa manylinux images for Python 3.9–3.13 (fields docker-image for
entries with python-version '3.9'..'3.13' and the ARM entries using
manylinux_2_28_aarch64), which can drift; update those docker-image values to
the fixed tag used for Python 3.8
(quay.io/pypa/manylinux2014_x86_64:2026.05.01-2 and the corresponding
manylinux_2_28_aarch64:2026.05.01-2) in the matrix entries for x86_64 and
aarch64 across the build and test job matrices so all python-version rows use
the pinned tag.
🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

109-109: 💤 Low value

Consider clarifying the retirement date format in the comment.

The comment references "2025-12-04" but mixes ISO date format with explanatory text. While the information is correct and helpful, consider making it more precise.

💬 Optional clarity improvement
-        # GitHub retired macos-13 on 2025-12-04; use macos-15-intel for x86_64.
+        # GitHub retired macos-13 (December 4, 2025); use macos-15-intel for x86_64.

Or with a reference:

-        # GitHub retired macos-13 on 2025-12-04; use macos-15-intel for x86_64.
+        # macos-13 was retired on 2025-12-04; macos-15-intel provides x86_64 support.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 109, Update the inline comment string
"GitHub retired macos-13 on 2025-12-04; use macos-15-intel for x86_64." to
clarify the retirement date format (e.g., "GitHub retired macos-13 on 2025-12-04
(YYYY-MM-DD) — Dec 4, 2025; use macos-15-intel for x86_64." or similar) so the
date is unambiguous while preserving the guidance about using macos-15-intel.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/ci.yml:
- Around line 43-70: The CI uses untagged quay.io/pypa manylinux images for
Python 3.9–3.13 (fields docker-image for entries with python-version
'3.9'..'3.13' and the ARM entries using manylinux_2_28_aarch64), which can
drift; update those docker-image values to the fixed tag used for Python 3.8
(quay.io/pypa/manylinux2014_x86_64:2026.05.01-2 and the corresponding
manylinux_2_28_aarch64:2026.05.01-2) in the matrix entries for x86_64 and
aarch64 across the build and test job matrices so all python-version rows use
the pinned tag.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 109: Update the inline comment string "GitHub retired macos-13 on
2025-12-04; use macos-15-intel for x86_64." to clarify the retirement date
format (e.g., "GitHub retired macos-13 on 2025-12-04 (YYYY-MM-DD) — Dec 4, 2025;
use macos-15-intel for x86_64." or similar) so the date is unambiguous while
preserving the guidance about using macos-15-intel.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5a9f18c8-ac85-4ce3-9d69-313b480bcf2d

📥 Commits

Reviewing files that changed from the base of the PR and between b74b92d and 046bdd9.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

Gate setuptools-rust to Linux-only builds, pin all manylinux matrix
images to dated tags, and harden luxie-tronic with SHA-pinned workflow
ref and explicit secret passing.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chris-piekarski

Copy link
Copy Markdown
Author

Addressed the outside-diff ci.yml manylinux pinning comment in 658d668: all x86_64 matrix rows now use manylinux2014_x86_64:2026.05.01-2 and all aarch64 3.9–3.13 rows use manylinux_2_28_aarch64:2026.05.01-2 in both build and test jobs.

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	.github/lux-ci-config.json
#	.github/workflows/luxie-tronic.yml
@chris-piekarski
chris-piekarski merged commit 59498c2 into main Jun 4, 2026
75 checks passed
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.

1 participant