Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
167 changes: 167 additions & 0 deletions .github/workflows/picker-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
name: Picker smoke (real app)

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- "apps/desktop-gpui/src/target_overlay.rs"
- "apps/desktop-gpui/src/app_windows.rs"
- "apps/desktop-gpui/src/camera_window.rs"
- "apps/desktop-gpui/src/picker_probe.rs"
- "apps/desktop-gpui/src/platform.rs"
- "apps/desktop-gpui/src/platform/**"
- "scripts/picker-smoke/**"
- ".github/workflows/picker-smoke.yml"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Fork pull requests can queue three full-platform app builds by editing only the new smoke workflow

Self-referencing paths lets a fork queue 3 full app builds (60-min timeout) by editing only CI/harness files.

Gate workflow/harness-only triggers to same-repo PRs (as perf-budget.yml does) or drop those paths from the fork-triggerable filters.

AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.

<file name=".github/workflows/picker-smoke.yml">
<violation number="1" location=".github/workflows/picker-smoke.yml:14">
<priority>P2</priority>
<title>Fork pull requests can queue three full-platform app builds by editing only the new smoke workflow</title>
<evidence>The new workflow runs on pull_request (opened, synchronize, reopened, ready_for_review) and lists both its own file (`.github/workflows/picker-smoke.yml`, added line 14) and `scripts/picker-smoke/**` (line 13) in its paths filters. A fork contributor can therefore trigger the `smoke` matrix by editing only CI/harness files, without touching product code. That matrix builds the whole `cap-gpui` app on `blacksmith-6vcpu-macos-15`, `windows-2022`, and `blacksmith-8vcpu-ubuntu-2404` with `timeout-minutes: 60` (added lines 27-43). `concurrency.cancel-in-progress: true` only cancels overlapping runs, so successive `synchronize` pushes each get a fresh three-platform job. The repository already fixed this exact pattern in `perf-budget.yml` by gating self-file-triggered work on same-repository PRs, and an equivalent finding on `instant-reliability.yml` was accepted for fixing. This adds a new, cheap, repeatable route for untrusted forks to consume shared macOS/Windows/Linux runner capacity.</evidence>
<recommendation>Do not let a PR that changes only the smoke workflow or `scripts/picker-smoke/**` queue the full three-platform matrix for forks. Mirror the `perf-budget.yml` same-repo guard (e.g. `github.event.pull_request.head.repo.full_name == github.repository`) for workflow/harness-only changes, or remove the workflow's own path and the harness path from the fork-triggerable filters. Optionally lower the 60-minute timeout and keep full validation for internal PRs or `workflow_dispatch`.</recommendation>
</violation>
</file>

workflow_dispatch:

permissions:
contents: read
pull-requests: read

concurrency:
group: picker-smoke-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
# A pull request from a fork that changes only this workflow or the smoke's
# scripts does not queue the three-runner matrix; 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"
app='^apps/desktop-gpui/src/'
if grep -qE "$app" "$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 picker smoke." | tee -a "$GITHUB_STEP_SUMMARY"
fi

smoke:
name: Picker smoke (${{ matrix.settings.name }})
needs: scope
if: needs.scope.outputs.run == 'true'
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
settings:
- name: macOS
slug: macos
target: aarch64-apple-darwin
runner: blacksmith-6vcpu-macos-15
- name: Windows
slug: windows
target: x86_64-pc-windows-msvc
runner: windows-2022
- name: Linux X11
slug: linux-x11
target: x86_64-unknown-linux-gnu
runner: blacksmith-8vcpu-ubuntu-2404
runs-on: ${{ matrix.settings.runner }}
env:
CARGO_PROFILE_DEV_DEBUG: "0"
steps:
- name: Checkout repository
uses: actions/checkout@v4

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

- name: Install a window manager and a test window
if: ${{ runner.os == 'Linux' }}
run: sudo apt-get install -y openbox xterm

- name: Rust setup
uses: dtolnay/rust-toolchain@1.95.0
with:
targets: ${{ matrix.settings.target }}

- uses: ./.github/actions/setup-rust-cache
with:
target: ${{ matrix.settings.target }}
prefix-key: v1-rust-gpui
save-cache: "false"
workspaces: apps/desktop-gpui -> target

- name: Run setup
shell: bash
run: node scripts/setup.js
env:
RUST_TARGET_TRIPLE: ${{ matrix.settings.target }}

- name: Prepare GPUI dependency
shell: bash
run: |
node scripts/sync-desktop-versions.mjs
./scripts/prepare-gpui-dependency.sh

- name: Build the app
shell: bash
run: |
cargo +1.95.0 build --manifest-path apps/desktop-gpui/Cargo.toml --locked \
--target ${{ matrix.settings.target }} --bin cap-gpui
if [[ "$RUNNER_OS" == "macOS" ]]; then
for frameworks in "$GITHUB_WORKSPACE/apps/desktop-gpui/target/${{ matrix.settings.target }}/debug/Frameworks" \
"$GITHUB_WORKSPACE/apps/desktop-gpui/target/${{ matrix.settings.target }}/Frameworks"; do
mkdir -p "$frameworks"
ln -sfn "$GITHUB_WORKSPACE/target/Frameworks/Spacedrive.framework" "$frameworks/Spacedrive.framework"
done
fi

- name: Picker probe
shell: bash
run: |
app="$GITHUB_WORKSPACE/apps/desktop-gpui/target/${{ matrix.settings.target }}/debug/cap-gpui"
out="$RUNNER_TEMP/picker-smoke"
case "$RUNNER_OS" in
macOS)
caffeinate -u -t 2
open -a TextEdit
sleep 2
scripts/picker-smoke/run.sh "$app" "$out"
;;
Windows)
export PATH="$(cygpath "$GITHUB_WORKSPACE/target/debug"):$PATH"
notepad.exe &
sleep 2
scripts/picker-smoke/run.sh "$app.exe" "$out"
;;
Linux)
export LD_LIBRARY_PATH="$GITHUB_WORKSPACE/target/native-deps/lib:$GITHUB_WORKSPACE/target/debug${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
Xvfb :99 -screen 0 1920x1080x24 -noreset &
export DISPLAY=:99
sleep 1
openbox &
sleep 1
xterm -geometry 100x30+300+200 &
sleep 1
scripts/picker-smoke/run.sh "$app" "$out"
;;
esac

- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: picker-smoke-${{ matrix.settings.slug }}
path: ${{ runner.temp }}/picker-smoke
retention-days: 7
38 changes: 29 additions & 9 deletions apps/desktop-gpui/src/app_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2638,11 +2638,7 @@ fn open_overlay(
window.refresh();
})
.ok();
if let Some(camera) = cx.update(visible_camera_frame)
&& let Err(error) = camera.raise()
{
tracing::warn!(%error, "could not raise the camera above the target picker");
}
raise_camera_above_overlays(cx);
})
.detach();
}
Expand Down Expand Up @@ -4368,15 +4364,35 @@ pub(crate) fn studio_camera_snapshot(
.ok()
}

fn visible_camera_frame(cx: &mut App) -> Option<CameraFrame> {
/// Keep the camera bubble above the picker overlays. Not filtered on
/// `window_is_visible` off macOS: Windows' camera is shown asynchronously and
/// X11 has no visibility probe, and neither raise path shows a hidden window.
/// Lift the camera preview back over the picker overlays. The raise runs
/// outside any gpui update: ordering a window front calls straight back into
/// gpui, which drops those callbacks while the app is borrowed.
pub(crate) fn raise_camera_above_overlays(cx: &mut gpui::AsyncApp) {
let Some(camera) = cx.update(camera_to_raise) else {
return;
};
if let Err(error) = camera.raise() {
tracing::warn!(%error, "could not raise the camera above the target picker");
}
}

fn camera_to_raise(cx: &mut App) -> Option<CameraFrame> {
if camera_preview_is_inline(cx) {
return None;
}
#[cfg(target_os = "linux")]
if clean_capture_active(cx) {
return None;
}
camera_frame(cx).filter(|camera| camera.visible)
let camera = camera_frame(cx)?;
#[cfg(target_os = "macos")]
if !camera.visible {
return None;
}
Some(camera)
}

fn camera_snapshot(cx: &mut App) -> Option<CameraSnapshot> {
Expand All @@ -4396,9 +4412,13 @@ impl CameraFrame {
fn raise(&self) -> anyhow::Result<()> {
match &self.native {
#[cfg(target_os = "macos")]
CameraNativeFrame::Mac { native, .. } => platform::order_front_native(native),
CameraNativeFrame::Mac { native, .. } => {
platform::raise_panel_native(native, platform::recording_controls_level())
}
#[cfg(target_os = "windows")]
CameraNativeFrame::Windows(native) => platform::order_front_native(native),
CameraNativeFrame::Windows(native) => {
platform::raise_panel_native(native, platform::recording_controls_level())
}
#[cfg(target_os = "linux")]
CameraNativeFrame::X11 { window_id, .. } => {
use x11rb::connection::Connection as _;
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop-gpui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ mod permissions_ui;
#[cfg(test)]
mod picker_benchmark;
#[cfg(debug_assertions)]
mod picker_probe;
#[cfg(debug_assertions)]
mod picker_ui_benchmark;
mod platform;
mod presets;
Expand Down Expand Up @@ -806,6 +808,8 @@ fn main() {
// synthetic clicks are dropped.
#[cfg(debug_assertions)]
picker_ui_benchmark::run(window_handle, cx);
#[cfg(debug_assertions)]
picker_probe::run(window_handle, cx);
if let Ok(auto) = std::env::var("CAP_GPUI_AUTO_RECORD")
&& let Some((mode, secs)) = parse_auto_record(&auto)
{
Expand Down
Loading
Loading