Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
81805af
build(desktop): package reproducible GPUI sidecars on every platform
richiemcilroy Aug 26, 2026
6592476
build(desktop): reject stale sidecars and mismatched Tauri runtimes
richiemcilroy Aug 26, 2026
014e25e
fix(windows): stop camera leaks and hidden tray polling
richiemcilroy Aug 26, 2026
a20d27c
fix(windows): preserve monitor DPI and cross-target framework linking
richiemcilroy Aug 26, 2026
6ee0f40
perf(editor): decode bounded music and audio sample ranges
richiemcilroy Aug 26, 2026
31adcd2
perf(rendering): bound decoder caches and harden media seeking
richiemcilroy Aug 26, 2026
1eca5dc
perf(rendering): reuse GPU surfaces and trim pooled readbacks
richiemcilroy Aug 26, 2026
6cadc33
perf(import): reuse decoded frames in classic and native editors
richiemcilroy Aug 26, 2026
0d88356
fix(macos): stabilize capture target discovery and screen startup
richiemcilroy Aug 26, 2026
b9eb838
perf(linux): safely enable verified NVIDIA hardware encoding
richiemcilroy Aug 26, 2026
5c3e034
fix(linux): preserve PipeWire system audio and microphone buffering
richiemcilroy Aug 26, 2026
e390c42
fix(recording): finalize static captures and stream instant segments
richiemcilroy Aug 26, 2026
e5057b9
perf(camera): reuse segmentation buffers and portable blur resources
richiemcilroy Aug 26, 2026
917f794
fix(linux): capture Wayland screenshots without leaking other windows
richiemcilroy Aug 26, 2026
42484de
fix(linux): safely deliver screenshot and automation notifications
richiemcilroy Aug 26, 2026
8ff0035
perf(gpui): keep complex editor playback and scrubbing responsive
richiemcilroy Aug 26, 2026
1f943c6
fix(gpui): preserve target privacy and responsive recording controls
richiemcilroy Aug 26, 2026
780d01a
fix(gpui): refresh permissions and restore onboarding behavior
richiemcilroy Aug 26, 2026
3ad51d3
feat(gpui): synchronize account profile, settings, and tray state
richiemcilroy Aug 26, 2026
a50880f
fix(gpui): recover interrupted recordings and guard background work
richiemcilroy Aug 26, 2026
9f4f925
fix(recording): require auth and stream instant uploads
richiemcilroy Aug 26, 2026
0fd1fb0
feat(gpui): harden single-instance, update, and classic-app handoffs
richiemcilroy Aug 26, 2026
95d24f6
fix(desktop): prevent unsafe updates and restarts during active work
richiemcilroy Aug 26, 2026
95f7443
fix(desktop): preserve clipboard, deep links, and app switching
richiemcilroy Aug 26, 2026
4aebcf2
test(desktop): measure real display-transport frame cadence
richiemcilroy Aug 26, 2026
a7eab0a
test(ci): validate desktop builds and signed release contents
richiemcilroy Aug 26, 2026
23db783
fix(extension): recover camera preview when ICE gathering stalls
richiemcilroy Aug 26, 2026
0b8dc5a
chore(release): integrate upstream camera fixes
richiemcilroy Aug 26, 2026
050ceca
style(gpui): remove redundant Metal shader comments
richiemcilroy Aug 26, 2026
b4d9987
fix(ci): validate mobile dependencies against installed SDK
richiemcilroy Aug 26, 2026
e73a263
fix(ci): provide a virtual display for Linux GPUI tests
richiemcilroy Aug 26, 2026
27d588d
fix(ci): keep Xvfb alive between test display connections
richiemcilroy Aug 26, 2026
727ac5a
Fix 1x clip mute rendering across segments
richiemcilroy Aug 26, 2026
a32c559
test(ci): run editor audio regressions on every platform
richiemcilroy Aug 26, 2026
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
3 changes: 3 additions & 0 deletions .github/actions/install-desktop-deps/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,8 @@ runs:
libxfixes-dev \
libwayland-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
libva-dev \
xvfb \
xauth \
patchelf
75 changes: 72 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
name: Detect Changes
runs-on: ubuntu-latest
outputs:
desktop: ${{ steps.filter.outputs.desktop }}
rust: ${{ steps.filter.outputs.rust }}
tauri-plugins: ${{ steps.filter.outputs.tauri-plugins }}
branch: ${{ steps.branch.outputs.branch }}
Expand All @@ -31,11 +32,30 @@ jobs:
with:
filters: |
desktop:
- '.github/actions/install-desktop-deps/**'
- '.github/workflows/ci.yml'
- 'apps/cli/**'
- 'apps/desktop/**'
- 'apps/desktop-gpui/**'
- 'crates/**'
- 'packages/database/**'
- 'packages/ui-solid/**'
- 'packages/utils/**'
- 'packages/web-api-contract/**'
- 'scripts/build-desktop-binaries*'
- 'scripts/build-gpui-binary.sh'
- 'scripts/prepare-gpui-dependency.sh'
- 'scripts/run-gpui-build*'
- 'scripts/sync-desktop-versions.mjs'
- 'scripts/verify-gpui-release-inputs.mjs'
- 'Cargo.toml'
- 'Cargo.lock'
- 'pnpm-lock.yaml'
rust:
- '.cargo/**'
- '.github/**'
- 'apps/cli/**'
- 'apps/desktop-gpui/**'
- 'crates/**'
- 'apps/desktop/src-tauri/**'
- 'Cargo.toml'
Expand Down Expand Up @@ -73,9 +93,13 @@ jobs:
- uses: ./.github/actions/setup-js

- name: Check Expo dependencies
env:
EXPO_OFFLINE: "1"
run: pnpm --dir apps/mobile exec expo install --check

- name: Run Expo Doctor
env:
EXPO_OFFLINE: "1"
working-directory: apps/mobile
run: pnpm dlx expo-doctor@1.20.1

Expand Down Expand Up @@ -195,6 +219,8 @@ jobs:
runner: macos-latest
- target: x86_64-pc-windows-msvc
runner: windows-2022
- target: x86_64-unknown-linux-gnu
runner: ubuntu-24.04
runs-on: ${{ matrix.settings.runner }}
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
Expand All @@ -203,6 +229,10 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Linux desktop dependencies
if: ${{ runner.os == 'Linux' }}
uses: ./.github/actions/install-desktop-deps

- name: Rust setup
uses: dtolnay/rust-toolchain@1.88.0
with:
Expand Down Expand Up @@ -240,10 +270,43 @@ jobs:
shell: bash
run: ./scripts/build-desktop-binaries.sh ${{ matrix.settings.target }}

- name: Test Linux recording and encoder regressions
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
cargo test --locked -p cap-recording --lib
cargo test --locked -p cap-enc-ffmpeg
env:
LD_LIBRARY_PATH: ${{ format('{0}/target/native-deps/lib:{0}/target/debug:{0}/target/{1}/debug', github.workspace, matrix.settings.target) }}

- name: Build GPUI desktop
shell: bash
run: ./scripts/build-gpui-binary.sh debug ${{ matrix.settings.target }}

- name: Test GPUI desktop
shell: bash
run: |
if [[ "$RUNNER_OS" == "Windows" ]]; then
export PATH="$(cygpath "$GITHUB_WORKSPACE/target/debug"):$PATH"
elif [[ "$RUNNER_OS" == "macOS" ]]; then
frameworks="$GITHUB_WORKSPACE/apps/desktop-gpui/target/${{ matrix.settings.target }}/debug/Frameworks"
mkdir -p "$frameworks"
ln -sfn "$GITHUB_WORKSPACE/target/Frameworks/Spacedrive.framework" "$frameworks/Spacedrive.framework"
elif [[ "$RUNNER_OS" == "Linux" ]]; then
export LD_LIBRARY_PATH="$GITHUB_WORKSPACE/target/native-deps/lib:$GITHUB_WORKSPACE/target/debug:$GITHUB_WORKSPACE/target/${{ matrix.settings.target }}/debug${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
fi
if [[ "$RUNNER_OS" == "Linux" ]]; then
xvfb-run -a --server-args="-screen 0 1920x1080x24 -noreset" \
cargo +1.95.0 test --manifest-path apps/desktop-gpui/Cargo.toml --locked --target ${{ matrix.settings.target }} --bin cap-gpui
else
cargo +1.95.0 test --manifest-path apps/desktop-gpui/Cargo.toml --locked --target ${{ matrix.settings.target }} --bin cap-gpui
fi

- name: Build app
working-directory: apps/desktop
run: pnpm tauri build --debug --target ${{ matrix.settings.target }} --no-bundle
env:
LD_LIBRARY_PATH: ${{ runner.os == 'Linux' && format('{0}/target/native-deps/lib:{0}/target/debug:{0}/target/{1}/debug', github.workspace, matrix.settings.target) || '' }}
RUST_TARGET_TRIPLE: ${{ matrix.settings.target }}

tauri-plugins:
Expand Down Expand Up @@ -279,6 +342,8 @@ jobs:
- target: x86_64-pc-windows-msvc
runner: windows-2022
runs-on: ${{ matrix.settings.runner }}
env:
RUST_TARGET_TRIPLE: ${{ matrix.settings.target }}
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -300,12 +365,16 @@ jobs:

- run: node scripts/setup.js

- name: Build desktop binaries
shell: bash
run: ./scripts/build-desktop-binaries.sh ${{ matrix.settings.target }}

- name: Build debug
run: cargo build --all
run: cargo build --all --target ${{ matrix.settings.target }}

- name: Build release
run: cargo check --all --release
run: cargo check --all --release --target ${{ matrix.settings.target }}

- name: Run Clippy
if: ${{ matrix.settings.target == 'aarch64-apple-darwin' || matrix.settings.target == 'x86_64-pc-windows-msvc' }}
run: cargo clippy --workspace --all-features --locked -- -D warnings
run: cargo clippy --workspace --all-features --locked --target ${{ matrix.settings.target }} -- -D warnings
110 changes: 108 additions & 2 deletions .github/workflows/desktop-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,43 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
RUST_TARGET_TRIPLE: ${{ matrix.settings.target }}

- name: Verify macOS app contents and signing
if: ${{ runner.os == 'macOS' }}
shell: bash
run: |
set -euo pipefail
APP="target/${{ matrix.settings.target }}/release/bundle/macos/Cap.app"
for binary in Cap cap-muxer cap-exporter cap-cli cap-gpui; do
executable="$APP/Contents/MacOS/$binary"
if [[ ! -x "$executable" ]]; then
echo "::error::Bundled executable missing or not executable: $executable"
exit 1
fi
codesign --verify --strict "$executable"
done
GPUI_SIGNATURE="$(codesign -dv --verbose=4 "$APP/Contents/MacOS/cap-gpui" 2>&1)"
if ! grep -Eq 'flags=.*runtime' <<< "$GPUI_SIGNATURE"; then
echo "::error::Cap GPUI is not signed with the macOS hardened runtime."
exit 1
fi
if ! grep -Eq '^Authority=Developer ID Application:' <<< "$GPUI_SIGNATURE"; then
echo "::error::Cap GPUI is not signed with a Developer ID Application certificate."
exit 1
fi
codesign --verify --deep --strict "$APP"
spctl --assess --type execute "$APP"
xcrun stapler validate "$APP"
if [[ ! -s "$APP.tar.gz" || ! -s "$APP.tar.gz.sig" ]]; then
echo "::error::macOS updater artifact or production signature is missing."
exit 1
fi
DMG="$(find "target/${{ matrix.settings.target }}/release/bundle/dmg" -maxdepth 1 -type f -name '*.dmg' -print -quit)"
if [[ -z "$DMG" || ! -s "$DMG" ]]; then
echo "::error::macOS disk image is missing or empty."
exit 1
fi
codesign --verify "$DMG"

# Guards against the Linux packaging regressions:
# 1. Frontend missing: vinxi must produce apps/desktop/.output/public,
# which Tauri EMBEDS into the binary (it is NOT shipped as a loose
Expand Down Expand Up @@ -402,6 +439,22 @@ jobs:
WORK="$(mktemp -d)"
dpkg-deb -x "$DEB" "$WORK"

EXECUTABLES=()
for binary in Cap cap-muxer cap-exporter cap-cli cap-gpui; do
executable="$WORK/usr/bin/$binary"
if [[ ! -x "$executable" ]]; then
echo "::error::Bundled executable missing or not executable: /usr/bin/$binary"
exit 1
fi
EXECUTABLES+=("$executable")
done

GPUI_RPATH="$(objdump -p "$WORK/usr/bin/cap-gpui" | awk '/RPATH|RUNPATH/{print $2}')"
if [[ "$GPUI_RPATH" != *'$ORIGIN/../lib/cap'* ]]; then
echo "::error::The bundled GPUI executable cannot resolve shared libraries from /usr/lib/cap"
exit 1
fi

# Best-effort confirmation that the SPA actually got embedded into the
# binary (Tauri stores asset path keys as plaintext). Informational only.
if strings "$WORK/usr/bin/Cap" 2>/dev/null | grep -q "index.html"; then
Expand All @@ -412,7 +465,7 @@ jobs:

# (2) every FFmpeg soname the binary NEEDs is bundled
BIN="$WORK/usr/bin/Cap"
NEEDED="$(objdump -p "$BIN" | awk '/NEEDED/{print $2}' | grep -E '^lib(av|sw|postproc)' || true)"
NEEDED="$(for executable in "${EXECUTABLES[@]}"; do objdump -p "$executable"; done | awk '/NEEDED/{print $2}' | grep -E '^lib(av|sw|postproc)' | sort -u || true)"
echo "Binary NEEDs FFmpeg sonames:"; echo "${NEEDED:-(none)}"
echo "Bundled FFmpeg libs:"; ls -1 "$WORK/usr/lib/cap" 2>/dev/null || true
MISSING=0
Expand All @@ -434,12 +487,19 @@ jobs:
# in 0.5.2). Assert the curated set of such sonames the binary links.
DEPENDS="$(dpkg-deb -f "$DEB" Depends || true)"
echo "Declared Depends: ${DEPENDS:-(none)}"
NEEDED_ALL="$(objdump -p "$BIN" | awk '/NEEDED/{print $2}')"
if ! echo "$DEPENDS" | grep -Eq '(^|,[[:space:]]*)pulseaudio-utils([[:space:](,]|$)'; then
echo "::error::Linux system audio requires pulseaudio-utils (pactl) in .deb Depends."
exit 1
fi
NEEDED_ALL="$(for executable in "${EXECUTABLES[@]}"; do objdump -p "$executable"; done | awk '/NEEDED/{print $2}' | sort -u)"
# soname -> a Depends token that satisfies it. The alsa token is a
# substring so it matches both libasound2 and libasound2t64.
declare -A SONAME_DEP=(
[libpipewire-0.3.so.0]="libpipewire-0.3-0"
[libasound.so.2]="libasound2"
[libxkbcommon.so.0]="libxkbcommon0"
[libxkbcommon-x11.so.0]="libxkbcommon-x11-0"
[libssl.so.3]="libssl3"
)
DEP_MISSING=0
for so in "${!SONAME_DEP[@]}"; do
Expand All @@ -459,6 +519,33 @@ jobs:
fi
echo "Runtime dependency declarations OK"

- name: Verify Windows installer contents
if: ${{ runner.os == 'Windows' }}
shell: pwsh
run: |
$installers = Get-ChildItem -Path "target/${{ matrix.settings.target }}/release/bundle/nsis" -Filter *.exe
if (-not $installers) {
throw "No Windows NSIS installer was produced."
}
$sevenZip = Get-Command 7z -ErrorAction Stop
$requiredFiles = @(
"Cap.exe", "cap-muxer.exe", "cap-exporter.exe", "cap-cli.exe", "cap-gpui.exe",
"avcodec-61.dll", "avdevice-61.dll", "avfilter-10.dll", "avformat-61.dll",
"avutil-59.dll", "postproc-58.dll", "swresample-5.dll", "swscale-8.dll",
"dxcompiler.dll", "dxil.dll", "onnxruntime.dll", "onnxruntime_providers_shared.dll"
)
foreach ($installer in $installers) {
$contents = & $sevenZip.Source l -slt $installer.FullName
if ($LASTEXITCODE -ne 0) {
throw "Could not inspect Windows installer '$($installer.Name)'."
}
foreach ($file in $requiredFiles) {
if ($contents -notcontains "Path = $file") {
throw "Windows installer '$($installer.Name)' is missing bundled '$file'."
}
}
}

- name: Upload unsigned Windows installer
if: ${{ runner.os == 'Windows' }}
id: upload_unsigned_windows_installer
Expand Down Expand Up @@ -518,6 +605,21 @@ jobs:
Write-Host "Files in bundle directory after signing:"
Get-ChildItem -Path $bundleDir -Filter *.exe | ForEach-Object { Write-Host " - $($_.Name)" }

- name: Verify Windows installer Authenticode signature
if: ${{ runner.os == 'Windows' }}
shell: pwsh
run: |
$installers = Get-ChildItem -Path "target/${{ matrix.settings.target }}/release/bundle/nsis" -Filter *.exe
if (-not $installers) {
throw "No Windows installer exists after SignPath signing."
}
foreach ($installer in $installers) {
$signature = Get-AuthenticodeSignature -FilePath $installer.FullName
if ($signature.Status -ne "Valid") {
throw "Windows installer '$($installer.Name)' has invalid Authenticode status: $($signature.Status)."
}
}

- name: Re-sign Windows installer for Tauri updater
if: ${{ runner.os == 'Windows' }}
shell: bash
Expand All @@ -528,6 +630,10 @@ jobs:
echo "Re-signing $(basename "$exe") for Tauri updater..."
rm -f "${exe}.sig"
pnpm tauri signer sign -k "$TAURI_SIGNING_PRIVATE_KEY" -p "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" "$exe"
if [[ ! -s "${exe}.sig" ]]; then
echo "::error::Windows production updater signature missing for $exe"
exit 1
fi
done
echo "Signature files after re-signing:"
ls -la "$BUNDLE_DIR"/*.sig
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/sync-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ on:
- "crates/enc-mediafoundation/**"
- "crates/timestamp/**"
- "crates/rendering/**"
- "crates/editor/**"
- "crates/audio/**"
- "crates/media-info/**"
- ".github/workflows/sync-tests.yml"

Expand Down Expand Up @@ -119,6 +121,11 @@ jobs:
# looking identical to a pass in the CI log.
cargo test --locked -p cap-rendering -- --nocapture

- name: Editor audio playback and export regressions
shell: bash
run: |
cargo test --locked -p cap-editor --lib audio::tests::

# Real encoders + DASH muxer + remux/validation over full instant-mode
# scenarios: pause/resume excision, stall-recovery bursts with
# same-microsecond timestamps, segment assembly and A/V alignment.
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading