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
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,26 @@ jobs:
run: cargo clippy --workspace --all-targets --target $env:TARGET -- -D warnings
- name: Check (workspace)
run: cargo check --workspace --all-targets --target $env:TARGET
- name: Test (buzz-dev-mcp)
# The Windows-only bash resolver lives in buzz-dev-mcp; its unit tests
# only gate if this crate is tested ON Windows.
run: cargo test -p buzz-dev-mcp --target $env:TARGET
# The bundled-bash staging (PortableGit download, SFX extract, mingw64 drop)
# runs only in release.yml on tag — so without this step the agent's only
# Windows transport would ship UNEXERCISED until a tagged release hits users.
# Stage the tree and spawn the staged bash on a real coreutils pipeline: this
# gates the SFX `-o` POSIX-path extraction, that bash.exe still spawns after
# mingw64/ is dropped, and that the lazily-loaded MSYS DLL closure (msys-2.0.dll,
# coreutils) survives — the exact behaviors unconfirmable off a non-Windows host.
- name: Smoke-test bundled bash staging
shell: bash
run: |
set -euo pipefail
stage_dir="$RUNNER_TEMP/git-bash"
scripts/stage-windows-bash.sh "$stage_dir"
out=$("$stage_dir/usr/bin/bash.exe" -c 'echo hello | tr a-z A-Z')
[[ "$out" == "HELLO" ]] || { echo "staged bash pipeline failed: got '$out'" >&2; exit 1; }
echo "staged bash spawned and ran a coreutils pipeline"
- name: Check (Tauri crate)
run: cargo check --manifest-path desktop/src-tauri/Cargo.toml --target $env:TARGET
env:
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.

9 changes: 9 additions & 0 deletions crates/buzz-dev-mcp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,12 @@ image = { version = "0.25", default-features = false, features = ["jpeg", "png",

[target.'cfg(unix)'.dependencies]
nix = { version = "0.31", default-features = false, features = ["signal", "process"] }

# Windows Job Object APIs for the shell tool's timeout kill path: terminating a
# job kills the bash child AND every MSYS grandchild it forked, the Windows
# analogue of the Unix killpg above. windows-sys 0.61 is already workspace-
# resident (pulled transitively), so this adds no new crate. Win32_Security is
# required because CreateJobObjectW takes a SECURITY_ATTRIBUTES parameter;
# Win32_System_Threading supplies IO_COUNTERS inside the extended-limit struct.
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Security", "Win32_System_JobObjects", "Win32_System_Threading"] }
3 changes: 2 additions & 1 deletion crates/buzz-dev-mcp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![forbid(unsafe_code)]
#![cfg_attr(not(windows), forbid(unsafe_code))]
#![cfg_attr(windows, deny(unsafe_code))]
use rmcp::{
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
model::{CallToolResult, ServerCapabilities, ServerInfo},
Expand Down
11 changes: 9 additions & 2 deletions crates/buzz-dev-mcp/src/read_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,24 @@ mod tests {
#[test]
fn read_allows_absolute_path() {
let dir = tempdir().expect("tempdir");
// A real file in a SECOND tempdir, genuinely outside the workspace
// root — proves absolute paths beyond workdir resolve, without a
// Unix-only system path like /etc/hosts (which is C:\etc\hosts on
// Windows and does not exist).
let outside = tempdir().expect("tempdir");
let target = outside.path().join("outside.txt");
fs::write(&target, b"localhost").expect("write");
let state = make_state(dir.path());
let p = ReadFileParams {
path: "/etc/hosts".into(),
path: target.display().to_string(),
offset: None,
limit: None,
workdir: Some(dir.path().display().to_string()),
};
let out = run(&state, p).expect("ok");
assert!(
out.contains("localhost"),
"expected /etc/hosts content, got: {out}"
"expected out-of-workspace file content, got: {out}"
);
}

Expand Down
Loading