Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
94 changes: 73 additions & 21 deletions .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -218,28 +218,41 @@ jobs:
# The display churn regression creates its own virtual display above,
# so recording must start after that harness is ready.
if [ "$RECORD_VIDEO" = "true" ]; then
DEVLIST=$( ffmpeg -f avfoundation -list_devices true -i "" 2>&1 || true )
echo "Available devices:"
echo "$DEVLIST" | grep -E "AVFoundation|Capture screen"

SCREEN_INDEX=$( echo "$DEVLIST" | grep "Capture screen" | head -1 \
| sed 's/.*\[\([0-9]*\)\].*/\1/' )
SCREEN_INDEX="${SCREEN_INDEX:-0}"
echo "Using screen device index: $SCREEN_INDEX"

ffmpeg -f avfoundation -framerate 10 -capture_cursor 1 \
-i "${SCREEN_INDEX}:none" \
-c:v libx264 -preset ultrafast -pix_fmt yuv420p \
/tmp/test-recording-raw.mp4 </dev/null >/tmp/ffmpeg.log 2>&1 &
RECORD_PID=$!
if [ -x /usr/sbin/screencapture ]; then
RAW_RECORDING=/tmp/test-recording-raw.mov
/usr/sbin/screencapture -v -k -D 1 "$RAW_RECORDING" >/tmp/screencapture.log 2>&1 &
RECORD_PID=$!
echo "RECORD_BACKEND=screencapture" >> "$GITHUB_ENV"
echo "RECORD_RAW=$RAW_RECORDING" >> "$GITHUB_ENV"
echo "Started screencapture video recording"
else
DEVLIST=$( ffmpeg -f avfoundation -list_devices true -i "" 2>&1 || true )
echo "Available devices:"
echo "$DEVLIST" | grep -E "AVFoundation|Capture screen"

SCREEN_INDEX=$( echo "$DEVLIST" | grep "Capture screen" | head -1 \
| sed 's/.*\[\([0-9]*\)\].*/\1/' )
SCREEN_INDEX="${SCREEN_INDEX:-0}"
echo "Using screen device index: $SCREEN_INDEX"

RAW_RECORDING=/tmp/test-recording-raw.mp4
ffmpeg -f avfoundation -framerate 10 -capture_cursor 1 \
-i "${SCREEN_INDEX}:none" \
-c:v libx264 -preset ultrafast -pix_fmt yuv420p \
"$RAW_RECORDING" </dev/null >/tmp/ffmpeg.log 2>&1 &
RECORD_PID=$!
echo "RECORD_BACKEND=ffmpeg" >> "$GITHUB_ENV"
echo "RECORD_RAW=$RAW_RECORDING" >> "$GITHUB_ENV"
fi
echo "RECORD_PID=$RECORD_PID" >> "$GITHUB_ENV"
sleep 2

if kill -0 "$RECORD_PID" 2>/dev/null; then
echo "Recording started (PID $RECORD_PID)"
else
echo "::warning::ffmpeg screen recording failed to start"
cat /tmp/ffmpeg.log
echo "::warning::screen recording failed to start"
cat /tmp/screencapture.log 2>/dev/null || true
cat /tmp/ffmpeg.log 2>/dev/null || true
fi
fi

Expand Down Expand Up @@ -297,26 +310,65 @@ jobs:
fi
sleep 1
done
if kill -0 "$RECORD_PID" 2>/dev/null; then
kill -TERM "$RECORD_PID" 2>/dev/null || true
for i in $(seq 1 5); do
if ! kill -0 "$RECORD_PID" 2>/dev/null; then
echo "Recording terminated after ${i}s"
break
fi
sleep 1
done
fi
kill -9 "$RECORD_PID" 2>/dev/null || true

echo "=== raw recording ==="
ls -lh /tmp/test-recording-raw.mp4 2>/dev/null || { echo "No recording file"; exit 0; }
Comment thread
cursor[bot] marked this conversation as resolved.
RAW_RECORDING="${RECORD_RAW:-/tmp/test-recording-raw.mp4}"
ls -lh "$RAW_RECORDING" 2>/dev/null || {
echo "No recording file"
cat /tmp/screencapture.log 2>/dev/null || true
cat /tmp/ffmpeg.log 2>/dev/null || true
SCREENSHOT_ROOTS=("$HOME" "/Users/runner" /var/folders /tmp)
BEFORE_SCREENSHOT=$(find "${SCREENSHOT_ROOTS[@]}" -type f -name 'cmux-configured-menubar-demo-before.png' -print 2>/dev/null | head -1 || true)
OPEN_SCREENSHOT=$(find "${SCREENSHOT_ROOTS[@]}" -type f -name 'cmux-configured-menubar-demo-open.png' -print 2>/dev/null | head -1 || true)
find "${SCREENSHOT_ROOTS[@]}" -type f -name 'cmux-configured-menubar-demo-*.png' -print 2>/dev/null || true
if [ -n "$BEFORE_SCREENSHOT" ]; then
ls -lh "$BEFORE_SCREENSHOT"
fi
if [ -n "$OPEN_SCREENSHOT" ]; then
ls -lh "$OPEN_SCREENSHOT"
fi
if [ -f "$BEFORE_SCREENSHOT" ] && [ -f "$OPEN_SCREENSHOT" ]; then
echo "Creating fallback recording from UI test screenshots"
ffmpeg -y \
-loop 1 -t 1 -i "$BEFORE_SCREENSHOT" \
-loop 1 -t 4 -i "$OPEN_SCREENSHOT" \
-filter_complex "[0:v]fps=10,format=yuv420p[v0];[1:v]fps=10,format=yuv420p[v1];[v0][v1]concat=n=2:v=1:a=0[v]" \
-map "[v]" /tmp/test-recording.mp4 2>/tmp/fallback-recording.log
cat /tmp/fallback-recording.log 2>/dev/null || true
ls -lh /tmp/test-recording.mp4
exit 0
fi
exit 0
}

# Trim: detect first non-black frame and cut from there
BLACK_END=$(ffmpeg -i /tmp/test-recording-raw.mp4 \
BLACK_END=$(ffmpeg -i "$RAW_RECORDING" \
-vf "blackdetect=d=0.3:pic_th=0.95:pix_th=0.1" \
-an -f null - 2>&1 \
| grep "black_end" | tail -1 \
| sed 's/.*black_end:\([0-9.]*\).*/\1/' || true)

if [ -n "$BLACK_END" ] && [ "$BLACK_END" != "0" ]; then
echo "Trimming ${BLACK_END}s of black frames from start"
ffmpeg -y -i /tmp/test-recording-raw.mp4 -ss "$BLACK_END" \
ffmpeg -y -i "$RAW_RECORDING" -ss "$BLACK_END" \
-c:v libx264 -preset ultrafast -pix_fmt yuv420p \
/tmp/test-recording.mp4 2>/dev/null
else
echo "No black frames detected, using raw recording"
mv /tmp/test-recording-raw.mp4 /tmp/test-recording.mp4
echo "No black frames detected, transcoding raw recording"
ffmpeg -y -i "$RAW_RECORDING" \
-c:v libx264 -preset ultrafast -pix_fmt yuv420p \
/tmp/test-recording.mp4 2>/dev/null
fi

echo "=== final recording ==="
Expand Down
8 changes: 8 additions & 0 deletions GhosttyTabs.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
C0DE31390000000000000105 /* CMUXCLIErrorOutputRegressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE31390000000000000106 /* CMUXCLIErrorOutputRegressionTests.swift */; };
C0DE31390000000000000103 /* FilePreviewReviewFeedbackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE31390000000000000104 /* FilePreviewReviewFeedbackTests.swift */; };
C0DEF0A10000000000000001 /* CmuxConfigUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DEF0A10000000000000002 /* CmuxConfigUI.swift */; };
C0DEF0C10000000000000001 /* ConfiguredMenuBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DEF0C10000000000000002 /* ConfiguredMenuBarController.swift */; };
C0DEF0A30000000000000001 /* SidebarPortDisplayText.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DEF0A30000000000000002 /* SidebarPortDisplayText.swift */; };
C0DEF0B10000000000000001 /* JSONCParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DEF0B10000000000000002 /* JSONCParser.swift */; };
C0DEF0B10000000000000003 /* JSONCParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DEF0B10000000000000002 /* JSONCParser.swift */; };
Expand Down Expand Up @@ -318,6 +319,7 @@
B6BF3DC98DB1495E57900199 /* TabManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42092CDB2109E250F7F2A76E /* TabManagerUnitTests.swift */; };
B8F266236A1A3D9A45BD840F /* SidebarResizeUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818DBCD4AB69EB72573E8138 /* SidebarResizeUITests.swift */; };
C0DE34020000000000000005 /* HelpMenuUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE34020000000000000006 /* HelpMenuUITests.swift */; };
C0DE40520000000000000005 /* ConfiguredMenuBarDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE40520000000000000006 /* ConfiguredMenuBarDemoUITests.swift */; };
B8F266246A1A3D9A45BD840F /* SidebarHelpMenuUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F266256A1A3D9A45BD840F /* SidebarHelpMenuUITests.swift */; };
C0DE32470000000000000005 /* CommandPaletteIdentifierClipboardUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE32470000000000000006 /* CommandPaletteIdentifierClipboardUITests.swift */; };
B8F266266A1A3D9A45BD840F /* DisplayResolutionRegressionUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F266276A1A3D9A45BD840F /* DisplayResolutionRegressionUITests.swift */; };
Expand Down Expand Up @@ -812,6 +814,7 @@
B09C007F42697761B5F1A2AB /* OmnibarAndToolsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmnibarAndToolsTests.swift; sourceTree = "<group>"; };
B2E7294509CC42FE9191870E /* xterm-ghostty */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ghostty/terminfo/78/xterm-ghostty"; sourceTree = "<group>"; };
C0DE34020000000000000006 /* HelpMenuUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpMenuUITests.swift; sourceTree = "<group>"; };
C0DE40520000000000000006 /* ConfiguredMenuBarDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfiguredMenuBarDemoUITests.swift; sourceTree = "<group>"; };
B8F266256A1A3D9A45BD840F /* SidebarHelpMenuUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarHelpMenuUITests.swift; sourceTree = "<group>"; };
C0DE32470000000000000006 /* CommandPaletteIdentifierClipboardUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandPaletteIdentifierClipboardUITests.swift; sourceTree = "<group>"; };
B8F266276A1A3D9A45BD840F /* DisplayResolutionRegressionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayResolutionRegressionUITests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -856,6 +859,7 @@
C0DE31390000000000000106 /* CMUXCLIErrorOutputRegressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CMUXCLIErrorOutputRegressionTests.swift; sourceTree = "<group>"; };
C0DE31390000000000000104 /* FilePreviewReviewFeedbackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewReviewFeedbackTests.swift; sourceTree = "<group>"; };
C0DEF0A10000000000000002 /* CmuxConfigUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmuxConfigUI.swift; sourceTree = "<group>"; };
C0DEF0C10000000000000002 /* ConfiguredMenuBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfiguredMenuBarController.swift; sourceTree = "<group>"; };
C0DEF0A30000000000000002 /* SidebarPortDisplayText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarPortDisplayText.swift; sourceTree = "<group>"; };
C0DEF0A40000000000000002 /* CmuxConfigContextMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmuxConfigContextMenuTests.swift; sourceTree = "<group>"; };
A5001435A5001435A5001435 /* FilePreviewPDFThumbnailSidebarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewPDFThumbnailSidebarTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1041,6 +1045,7 @@
B9000016A1B2C3D4E5F60719 /* MultiWindowNotificationsUITests.swift */,
818DBCD4AB69EB72573E8138 /* SidebarResizeUITests.swift */,
C0DE34020000000000000006 /* HelpMenuUITests.swift */,
C0DE40520000000000000006 /* ConfiguredMenuBarDemoUITests.swift */,
B8F266256A1A3D9A45BD840F /* SidebarHelpMenuUITests.swift */,
C0DE32470000000000000006 /* CommandPaletteIdentifierClipboardUITests.swift */,
B8F266276A1A3D9A45BD840F /* DisplayResolutionRegressionUITests.swift */,
Expand Down Expand Up @@ -1293,6 +1298,7 @@
A5001651 /* CmuxConfig.swift */,
C10D00040000000000000004 /* CmuxSurfaceTabBarBuiltInAction.swift */,
C0DEF0A10000000000000002 /* CmuxConfigUI.swift */,
C0DEF0C10000000000000002 /* ConfiguredMenuBarController.swift */,
E30750000000000000000003 /* CmuxWorkspaceDefinition.swift */,
A5001653 /* CmuxConfigExecutor.swift */,
A5001655 /* CmuxActionTrust.swift */,
Expand Down Expand Up @@ -1933,6 +1939,7 @@
A5001650 /* CmuxConfig.swift in Sources */,
C10D00030000000000000003 /* CmuxSurfaceTabBarBuiltInAction.swift in Sources */,
C0DEF0A10000000000000001 /* CmuxConfigUI.swift in Sources */,
C0DEF0C10000000000000001 /* ConfiguredMenuBarController.swift in Sources */,
E30750000000000000000004 /* CmuxWorkspaceDefinition.swift in Sources */,
A5001652 /* CmuxConfigExecutor.swift in Sources */,
A5001654 /* CmuxActionTrust.swift in Sources */,
Expand Down Expand Up @@ -2027,6 +2034,7 @@
F0F0A001A1B2C3D4E5F60718 /* WorkspaceSidebarScrollUITests.swift in Sources */,
B8F266236A1A3D9A45BD840F /* SidebarResizeUITests.swift in Sources */,
C0DE34020000000000000005 /* HelpMenuUITests.swift in Sources */,
C0DE40520000000000000005 /* ConfiguredMenuBarDemoUITests.swift in Sources */,
B8F266246A1A3D9A45BD840F /* SidebarHelpMenuUITests.swift in Sources */,
C0DE32470000000000000005 /* CommandPaletteIdentifierClipboardUITests.swift in Sources */,
B8F266266A1A3D9A45BD840F /* DisplayResolutionRegressionUITests.swift in Sources */,
Expand Down
Loading