Repository navigation
588 lines (525 loc) · 26.8 KB
/
Copy pathlinux-desktop-reliability.yml
File metadata and controls
588 lines (525 loc) · 26.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
name: Linux desktop reliability
# The built GPUI app on real Linux desktops: Ubuntu 26.04 with GNOME 50 and
# Arch with Hyprland, each with its real portals and tray host, in systemd
# containers (scripts/linux-desktop-harness). Each case starts a Display
# recording the way a person does (overlay Start, approve the shortcut or use
# the tray, approve the screen share), stops it, and checks it finished.
on:
pull_request:
paths:
- "apps/desktop-gpui/**"
- "crates/recording/**"
- "crates/camera/**"
- "crates/camera-ffmpeg/**"
- "crates/utils/**"
- "crates/scap-targets/**"
- "scripts/linux-desktop-harness/**"
- "scripts/prepare-gpui-dependency.sh"
- "scripts/finalize-linux-appimage.mjs"
- ".github/workflows/linux-desktop-reliability.yml"
workflow_dispatch:
permissions:
contents: read
pull-requests: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# The package only supplies the desktop file and runtime dependencies; the
# binary under test is this commit's build.
CAP_DEB_URL: https://cdn.crabnebula.app/asset/01M5CHFS77QM2HSZ69KT784SAV
CAP_DEB_SHA256: bb03dddc5268f64d3e6eb4c11534462e5b7a8a5c1c7bb5bca5f5bdaa99d11c56
# The 0.6.1 AppImage, whose libraries the NixOS check lays out both ways.
CAP_APPIMAGE_URL: https://cdn.crabnebula.app/asset/01M5CHFYYW4JXX4M9SPZ1NF6HM
CAP_APPIMAGE_SHA256: dd579d76b91173a2e620034c407e63ec3ae4ae5420540de80f5fededb7519b97
jobs:
# A pull request from a fork that changes only this workflow or the harness
# does not queue the desktop jobs; one that also changes the app does, and
# so does any change from this repository.
scope:
name: Scope
runs-on: blacksmith-2vcpu-ubuntu-2404
timeout-minutes: 5
outputs:
run: ${{ steps.scope.outputs.run }}
steps:
- name: Scope
id: scope
env:
GH_TOKEN: ${{ github.token }}
EVENT: ${{ github.event_name }}
PR: ${{ github.event.pull_request.number }}
SAME_REPO: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
run: |
set -euo pipefail
if [[ "$EVENT" != pull_request || "$SAME_REPO" == true ]]; then
echo "run=true" >> "$GITHUB_OUTPUT"; exit 0
fi
gh api "repos/$GITHUB_REPOSITORY/pulls/$PR/files" --paginate --jq '.[].filename' > "$RUNNER_TEMP/changed.txt"
if grep -qE '^(apps/desktop-gpui/|crates/(recording|camera|camera-ffmpeg|utils|scap-targets)/|scripts/(prepare-gpui-dependency\.sh|finalize-linux-appimage\.mjs)$)' "$RUNNER_TEMP/changed.txt"; then
echo "run=true" >> "$GITHUB_OUTPUT"
else
echo "run=false" >> "$GITHUB_OUTPUT"
echo "A fork pull request that changes no app code does not run the Linux desktop suite." | tee -a "$GITHUB_STEP_SUMMARY"
fi
desktops:
name: Record on GNOME
needs: scope
if: needs.scope.outputs.run == 'true'
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 90
env:
CARGO_PROFILE_DEV_DEBUG: "0"
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Linux desktop dependencies
uses: ./.github/actions/install-desktop-deps
- name: Rust setup
uses: dtolnay/rust-toolchain@1.95.0
with:
targets: x86_64-unknown-linux-gnu
- uses: ./.github/actions/setup-rust-cache
with:
target: x86_64-unknown-linux-gnu
prefix-key: v1-rust-gpui
save-cache: "false"
workspaces: apps/desktop-gpui -> target
- name: Run setup
run: node scripts/setup.js
env:
RUST_TARGET_TRIPLE: x86_64-unknown-linux-gnu
- name: Prepare GPUI dependency
run: |
node scripts/sync-desktop-versions.mjs
./scripts/prepare-gpui-dependency.sh
- name: Build cap-gpui
run: cargo +1.95.0 build --manifest-path apps/desktop-gpui/Cargo.toml --locked --bin cap-gpui
# niri 26.04 shares screencasts only as DMA-BUFs with a mandatory
# modifier (GitHub #2941). niri itself refuses software rendering, so a
# producer that negotiates and shares frames the way it does stands in.
- name: Capture from a niri-style DMA-BUF screencast
env:
LD_LIBRARY_PATH: ${{ github.workspace }}/target/native-deps/lib
run: |
sudo apt-get install -y -qq pipewire wireplumber libpipewire-0.3-dev dbus >/dev/null
RUSTUP_TOOLCHAIN=1.95.0 bash scripts/linux-desktop-harness/niri-screencast/run.sh
- name: Confirmation dialog without zenity (X11 input)
run: |
sudo apt-get install -y -qq xdotool xvfb >/dev/null
bash scripts/linux-desktop-harness/dialog-x11.sh \
apps/desktop-gpui/target/debug/cap-gpui target/native-deps/lib
- name: X11 display, area and window recordings, audio and a screenshot
run: |
sudo apt-get install -y -qq x11-utils pulseaudio pulseaudio-utils libasound2-plugins ffmpeg >/dev/null
bash scripts/linux-desktop-harness/x11-capture.sh \
apps/desktop-gpui/target/debug/cap-gpui target/native-deps/lib "$RUNNER_TEMP/out/x11-capture"
# A systemd user environment keeps WAYLAND_DISPLAY after a Wayland
# session ends.
- name: X11 launch with a stale WAYLAND_DISPLAY
run: |
bash scripts/linux-desktop-harness/x11-stale-wayland.sh \
apps/desktop-gpui/target/debug/cap-gpui target/native-deps/lib "$RUNNER_TEMP/out/x11-stale-wayland"
- name: Fetch the Cap package for desktop integration
run: |
curl --fail --location --retry 3 -o "$RUNNER_TEMP/cap.deb" "$CAP_DEB_URL"
echo "$CAP_DEB_SHA256 $RUNNER_TEMP/cap.deb" | sha256sum --check
- name: GNOME 50 (shortcut approval, screen share dialog)
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome" LAUNCH=dock APPROVE_SHORTCUT_AFTER=6
- name: GNOME 50 tray fallback (no app id, AppIndicator icon and menu)
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-tray" LAUNCH=terminal TRAY_CLICK=pointer GNOME_UNSAFE=1 \
CAP_GPUI_PORTAL_APP_ID=0
# A terminal launch has no launcher unit for the portals to name Cap
# after; Cap registers its own app id so GlobalShortcuts still works.
- name: GNOME 50 shortcut from a terminal launch
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-terminal" LAUNCH=terminal APPROVE_SHORTCUT_AFTER=6
cat "$RUNNER_TEMP/out/gnome-terminal/result.txt"
grep -qx attempt1_control=shortcut "$RUNNER_TEMP/out/gnome-terminal/result.txt"
grep -qx app_id_required=0 "$RUNNER_TEMP/out/gnome-terminal/result.txt"
# Every shell launch names itself to the portals, including the ones
# that have just written the desktop entry the portal looks the id up in.
- name: GNOME 50 shell launches register Cap's app id
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
SCENARIO: register
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-register" ROUNDS=4
cat "$RUNNER_TEMP/out/gnome-register/result.txt"
for line in fresh_registered=4 again_registered=4 entry_written=yes panics=0; do
grep -qx "$line" "$RUNNER_TEMP/out/gnome-register/result.txt"
done
# Fedora's GNOME without the AppIndicator extension, launched where the
# portal grants no shortcut: no tray and no Stop shortcut. Cap records
# with its controls visible instead of refusing to start.
- name: GNOME 50 without a tray or a shortcut records with visible controls
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-no-control" LAUNCH=terminal GNOME_TRAY=0 CAP_GPUI_PORTAL_APP_ID=0 \
GNOME_UNSAFE=1
cat "$RUNNER_TEMP/out/gnome-no-control/result.txt"
for line in attempt1_control=visible visible_controls_shown=yes attempt1_finished=yes blocked=0 \
restoration_unconfirmed=0 panics=0; do
grep -qx "$line" "$RUNNER_TEMP/out/gnome-no-control/result.txt"
done
- name: GNOME 50 settings window closes from its own button
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
SCENARIO: settings-close
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-settings" GNOME_UNSAFE=1
cat "$RUNNER_TEMP/out/gnome-settings/result.txt"
grep -qx settings_closed=yes "$RUNNER_TEMP/out/gnome-settings/result.txt"
- name: GNOME 50 relaunch from the app grid reaches the running Cap
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
SCENARIO: reopen
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-reopen"
cat "$RUNNER_TEMP/out/gnome-reopen/result.txt"
grep -qx reopen_forwarded=yes "$RUNNER_TEMP/out/gnome-reopen/result.txt"
- name: GNOME 50 relaunch brings Cap to the front over its settings
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
SCENARIO: raise
run: |
for window in main onboarding; do
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-raise-$window" GNOME_UNSAFE=1 RAISE_WINDOW=$window
cat "$RUNNER_TEMP/out/gnome-raise-$window/result.txt"
grep -qx main_raised=yes "$RUNNER_TEMP/out/gnome-raise-$window/result.txt"
done
- name: GNOME 50 editor background image from the portal file chooser
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
SCENARIO: pick-background
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-pick-background" GNOME_UNSAFE=1 LAUNCH=dock APPROVE_SHORTCUT_AFTER=6
cat "$RUNNER_TEMP/out/gnome-pick-background/result.txt"
for line in recorded=yes editor_opened=yes chooser_opened=yes background_chosen=yes cap_alive=yes; do
grep -qx "$line" "$RUNNER_TEMP/out/gnome-pick-background/result.txt"
done
# GNOME's default wallpaper is JPEG XL.
- name: GNOME 50 editor background from the desktop wallpaper
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
SCENARIO: wallpaper
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-wallpaper" LAUNCH=dock APPROVE_SHORTCUT_AFTER=6
cat "$RUNNER_TEMP/out/gnome-wallpaper/result.txt"
for line in recorded=yes expected_exists=yes editor_opened=yes wallpaper_found=yes wallpaper_imported=yes \
cap_alive=yes; do
grep -qx "$line" "$RUNNER_TEMP/out/gnome-wallpaper/result.txt"
done
# GNOME asks once whether Cap may take screenshots, and only while Cap
# has focus.
- name: GNOME 50 screenshot, allowed when the portal asks
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
SCENARIO: screenshot
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-screenshot" LAUNCH=dock GNOME_UNSAFE=1
cat "$RUNNER_TEMP/out/gnome-screenshot/result.txt" "$RUNNER_TEMP/out/gnome-screenshot/actions.log"
for line in screenshot_taken=yes screenshot_size=yes capture_failed=0 cap_alive=yes panics=0; do
grep -qx "$line" "$RUNNER_TEMP/out/gnome-screenshot/result.txt"
done
- name: GNOME 50 screenshot from a terminal launch (Cap names itself so.cap.Cap)
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
SCENARIO: screenshot
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-screenshot-terminal" LAUNCH=terminal PRELAUNCH=1 GNOME_UNSAFE=1
cat "$RUNNER_TEMP/out/gnome-screenshot-terminal/result.txt" "$RUNNER_TEMP/out/gnome-screenshot-terminal/actions.log"
for line in screenshot_taken=yes screenshot_size=yes capture_failed=0 cap_alive=yes panics=0; do
grep -qx "$line" "$RUNNER_TEMP/out/gnome-screenshot-terminal/result.txt"
done
- name: GNOME 50 area recording (portal stream cropped to the area)
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-area" LAUNCH=dock APPROVE_SHORTCUT_AFTER=6 AREA=200,150,800,450
cat "$RUNNER_TEMP/out/gnome-area/result.txt"
for line in attempt1_finished=yes area_video=yes panics=0; do
grep -qx "$line" "$RUNNER_TEMP/out/gnome-area/result.txt"
done
- name: GNOME 50 editor export to MP4 and GIF
env:
DEV_BIN: ${{ github.workspace }}/apps/desktop-gpui/target/debug/cap-gpui
DEV_LIBS: ${{ github.workspace }}/target/native-deps/lib
SCENARIO: export
run: |
sudo -E bash scripts/linux-desktop-harness/run-desktop.sh gnome "$RUNNER_TEMP/cap.deb" \
"$RUNNER_TEMP/out/gnome-export" LAUNCH=dock APPROVE_SHORTCUT_AFTER=6
cat "$RUNNER_TEMP/out/gnome-export/result.txt"
for line in recorded=yes mp4_exported=yes mp4_has_video=yes mp4_cap_alive=yes gif_exported=yes \
gif_has_video=yes gif_cap_alive=yes; do
grep -qx "$line" "$RUNNER_TEMP/out/gnome-export/result.txt"
done
- name: Check results
run: |
bash scripts/linux-desktop-harness/check-results.sh \
"$RUNNER_TEMP/out/gnome" "$RUNNER_TEMP/out/gnome-tray" "$RUNNER_TEMP/out/gnome-terminal"
- name: Package the build for the Hyprland job
run: |
mkdir -p "$RUNNER_TEMP/build/libs"
cp apps/desktop-gpui/target/debug/cap-gpui "$RUNNER_TEMP/build/cap-gpui"
strip --strip-debug "$RUNNER_TEMP/build/cap-gpui"
cp -a target/native-deps/lib/. "$RUNNER_TEMP/build/libs/"
- uses: actions/upload-artifact@v4
with:
name: cap-gpui-linux
path: ${{ runner.temp }}/build
retention-days: 1
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: linux-desktop-reliability-gnome
path: ${{ runner.temp }}/out
if-no-files-found: ignore
hyprland:
name: Record on Hyprland
needs: desktops
# vkms, the virtual display Hyprland runs on, is in the hosted runner's
# extra kernel modules; Blacksmith's kernel does not ship it.
runs-on: ubuntu-24.04
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: cap-gpui-linux
path: ${{ runner.temp }}/build
- name: Virtual display devices
run: |
# Newer runner kernels ship vkms in the base modules package and
# have no linux-modules-extra package at all.
if ! sudo modprobe vkms 2>/dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq "linux-modules-extra-$(uname -r)"
sudo modprobe vkms
fi
ls -la /dev/dri
- name: Hyprland (portal refuses host apps, tray Stop control)
run: |
chmod +x "$RUNNER_TEMP/build/cap-gpui"
sudo -E env DEV_BIN="$RUNNER_TEMP/build/cap-gpui" DEV_LIBS="$RUNNER_TEMP/build/libs" \
bash scripts/linux-desktop-harness/run-desktop.sh hyprland "$RUNNER_TEMP/build/cap-gpui" \
"$RUNNER_TEMP/out/hyprland" LAUNCH=dock
# A GPU without a Vulkan driver renders through wgpu's GL backend, whose
# surfaces have no Mailbox present mode. Hiding the Vulkan drivers puts
# Cap there; showing its hidden windows again must not panic.
- name: Hyprland on OpenGL (no Vulkan driver)
run: |
sudo -E env DEV_BIN="$RUNNER_TEMP/build/cap-gpui" DEV_LIBS="$RUNNER_TEMP/build/libs" \
bash scripts/linux-desktop-harness/run-desktop.sh hyprland "$RUNNER_TEMP/build/cap-gpui" \
"$RUNNER_TEMP/out/hyprland-gl" LAUNCH=dock VK_ICD_FILENAMES=/nonexistent.json
grep -q 'Selected GPU adapter: .*(Gl)' "$RUNNER_TEMP/out/hyprland-gl/cap.log"
- name: Hyprland screenshot
env:
SCENARIO: screenshot
run: |
sudo -E env DEV_BIN="$RUNNER_TEMP/build/cap-gpui" DEV_LIBS="$RUNNER_TEMP/build/libs" \
bash scripts/linux-desktop-harness/run-desktop.sh hyprland "$RUNNER_TEMP/build/cap-gpui" \
"$RUNNER_TEMP/out/hyprland-screenshot" LAUNCH=dock
cat "$RUNNER_TEMP/out/hyprland-screenshot/result.txt"
for line in screenshot_taken=yes screenshot_size=yes capture_failed=0 cap_alive=yes panics=0; do
grep -qx "$line" "$RUNNER_TEMP/out/hyprland-screenshot/result.txt"
done
# The hosted runner can build v4l2loopback; Blacksmith's kernel cannot.
- name: Install Linux desktop dependencies
uses: ./.github/actions/install-desktop-deps
- name: X11 recording with a camera
run: |
sudo apt-get install -y -qq "linux-headers-$(uname -r)" v4l2loopback-dkms xvfb xdotool ffmpeg dbus >/dev/null
chmod +x "$RUNNER_TEMP/build/cap-gpui"
# Each format Cap's Linux camera path converts: packed YUYV, NV12 and
# MJPEG (decoded before use).
for format in yuyv422 nv12 mjpeg; do
dir="$RUNNER_TEMP/out/x11-camera-$format"
sudo install -d -o "$(id -u)" -g "$(id -g)" "$dir"
CAMERA_FORMAT=$format bash scripts/linux-desktop-harness/x11-camera.sh \
"$RUNNER_TEMP/build/cap-gpui" "$RUNNER_TEMP/build/libs" "$dir"
done
- name: Hyprland area recording
run: |
sudo -E env DEV_BIN="$RUNNER_TEMP/build/cap-gpui" DEV_LIBS="$RUNNER_TEMP/build/libs" \
bash scripts/linux-desktop-harness/run-desktop.sh hyprland "$RUNNER_TEMP/build/cap-gpui" \
"$RUNNER_TEMP/out/hyprland-area" LAUNCH=dock AREA=200,150,800,450
cat "$RUNNER_TEMP/out/hyprland-area/result.txt"
for line in attempt1_finished=yes area_video=yes panics=0; do
grep -qx "$line" "$RUNNER_TEMP/out/hyprland-area/result.txt"
done
- name: Hyprland clipboard (Ctrl+V into and Ctrl+C out of a text field)
env:
SCENARIO: clipboard
run: |
sudo -E env DEV_BIN="$RUNNER_TEMP/build/cap-gpui" DEV_LIBS="$RUNNER_TEMP/build/libs" \
bash scripts/linux-desktop-harness/run-desktop.sh hyprland "$RUNNER_TEMP/build/cap-gpui" \
"$RUNNER_TEMP/out/hyprland-clipboard"
for key in typing_reached_field paste_reached_field copy_reached_clipboard; do
grep -qx "$key=yes" "$RUNNER_TEMP/out/hyprland-clipboard/result.txt"
done
# Omarchy draws its wallpaper with swaybg.
- name: Hyprland editor background from the swaybg wallpaper
env:
SCENARIO: wallpaper
run: |
sudo -E env DEV_BIN="$RUNNER_TEMP/build/cap-gpui" DEV_LIBS="$RUNNER_TEMP/build/libs" \
bash scripts/linux-desktop-harness/run-desktop.sh hyprland "$RUNNER_TEMP/build/cap-gpui" \
"$RUNNER_TEMP/out/hyprland-wallpaper" LAUNCH=dock WALLPAPER=swaybg
cat "$RUNNER_TEMP/out/hyprland-wallpaper/result.txt"
for line in recorded=yes editor_opened=yes wallpaper_found=yes wallpaper_imported=yes cap_alive=yes; do
grep -qx "$line" "$RUNNER_TEMP/out/hyprland-wallpaper/result.txt"
done
- name: Check results
run: |
bash scripts/linux-desktop-harness/check-results.sh \
"$RUNNER_TEMP/out/hyprland" "$RUNNER_TEMP/out/hyprland-gl"
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: linux-desktop-reliability-hyprland
path: ${{ runner.temp }}/out
if-no-files-found: ignore
appimage-nixos:
name: AppImage screen capture on NixOS
needs: scope
if: needs.scope.outputs.run == 'true'
# NixOS users run the AppImage through appimage-run. The PipeWire plugins
# that load into Cap there link NixOS's FFmpeg, which needs a newer libva
# than the one 0.6.1 bundled ahead of the host's (GitHub #2941). This
# connects a capture stream through the real launchers, with the AppDir as
# 0.6.1 shipped it and as scripts/finalize-linux-appimage.mjs lays it out.
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Fetch the 0.6.1 AppImage
run: |
curl --fail --location --retry 3 -o "$RUNNER_TEMP/Cap.AppImage" "$CAP_APPIMAGE_URL"
echo "$CAP_APPIMAGE_SHA256 $RUNNER_TEMP/Cap.AppImage" | sha256sum --check
- name: Connect a capture stream in NixOS appimage-run
run: |
sudo bash scripts/linux-desktop-harness/appimage-pipewire/run.sh \
"$RUNNER_TEMP/Cap.AppImage" "$RUNNER_TEMP/out/appimage-nixos"
- name: Check results
run: |
set -euo pipefail
result="$RUNNER_TEMP/out/appimage-nixos/result.txt"
cat "$result"
# The shipped layout is the control: it must still reproduce the
# failure, or the check proves nothing.
grep -qx "shipped_connects=no" "$result"
grep -qx "fixed_connects=yes" "$result"
# Programs Cap starts (xdg-open, file managers) no longer inherit
# the bundle's libraries.
grep -qx "shipped_children_get_bundle_libraries=yes" "$result"
grep -qx "fixed_children_get_bundle_libraries=no" "$result"
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: linux-desktop-reliability-appimage-nixos
path: ${{ runner.temp }}/out
if-no-files-found: ignore
niri:
name: Record on niri
needs: desktops
# niri 26.04 shares screencasts only as DMA-BUFs with a mandatory
# modifier (GitHub #2941). It refuses software rendering, so this builds
# the release with only that check removed and runs it on vkms.
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: cap-gpui-linux
path: ${{ runner.temp }}/build
- name: Virtual display devices
run: |
# Newer runner kernels ship vkms in the base modules package and
# have no linux-modules-extra package at all.
if ! sudo modprobe vkms 2>/dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq "linux-modules-extra-$(uname -r)"
sudo modprobe vkms
fi
ls -la /dev/dri
- name: Build niri
run: |
cd scripts/linux-desktop-harness
sudo docker build -q -f arch.Dockerfile -t cap-harness-arch . >/dev/null
sudo docker build -q -f niri-software.Dockerfile -t cap-harness-arch-niri . >/dev/null
# The runner's Hyper-V display (card1, Virtual-1) is a second output; with
# two, the screen-share dialog waits for a pick the harness never makes.
- name: niri (DMA-BUF screencast, tray Stop control)
run: |
chmod +x "$RUNNER_TEMP/build/cap-gpui"
sudo -E env DEV_BIN="$RUNNER_TEMP/build/cap-gpui" DEV_LIBS="$RUNNER_TEMP/build/libs" \
HARNESS_IMAGE=cap-harness-arch-niri \
bash scripts/linux-desktop-harness/run-desktop.sh niri "$RUNNER_TEMP/build/cap-gpui" \
"$RUNNER_TEMP/out/niri" LAUNCH=dock NIRI_RENDER_DEVICE=vkms NIRI_OUTPUTS_OFF=Virtual-1 \
GSK_RENDERER=cairo
- name: niri clipboard (Ctrl+V into and Ctrl+C out of a text field)
env:
SCENARIO: clipboard
run: |
sudo -E env DEV_BIN="$RUNNER_TEMP/build/cap-gpui" DEV_LIBS="$RUNNER_TEMP/build/libs" \
HARNESS_IMAGE=cap-harness-arch-niri \
bash scripts/linux-desktop-harness/run-desktop.sh niri "$RUNNER_TEMP/build/cap-gpui" \
"$RUNNER_TEMP/out/niri-clipboard" NIRI_RENDER_DEVICE=vkms NIRI_OUTPUTS_OFF=Virtual-1 \
GSK_RENDERER=cairo
for key in typing_reached_field paste_reached_field copy_reached_clipboard; do
grep -qx "$key=yes" "$RUNNER_TEMP/out/niri-clipboard/result.txt"
done
- name: Check results
run: |
bash scripts/linux-desktop-harness/check-results.sh "$RUNNER_TEMP/out/niri"
grep -q "negotiated DMA-BUF frames" "$RUNNER_TEMP/out/niri/cap.log"
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: linux-desktop-reliability-niri
path: ${{ runner.temp }}/out
if-no-files-found: ignore