Skip to content
Open
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: 19 additions & 1 deletion src/error_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Error stacktrace compression module.
//!
//! Detects stacktraces from 5 languages (Node.js, Python, Rust, Go, Java)
//! Detects stacktraces from 6 languages (Node.js, Python, Rust, Go, Java, Swift)
//! and compresses them by removing framework frames, keeping only user code.
//! Used as a post-processor after command-specific modules run.

Expand Down Expand Up @@ -41,6 +41,14 @@ lazy_static! {
// Extract function name and location from various frame formats
static ref NODE_EXTRACT_RE: Regex = Regex::new(r"^\s+at\s+(?:(.+?)\s+\()?(.+):(\d+):\d+\)?").unwrap();
static ref JAVA_EXTRACT_RE: Regex = Regex::new(r"^\s+at\s+([\w.$]+)\(([\w.]+):(\d+)\)").unwrap();

// Swift crash report detection
static ref SWIFT_CRASH_FRAME_RE: Regex = Regex::new(
r"^\d+\s+\S+\s+0x[0-9a-fA-F]+\s+.+"
).unwrap();
static ref SWIFT_CRASH_HEADER_RE: Regex = Regex::new(
r"^Thread \d+( Crashed)?:"
).unwrap();
}

#[derive(Debug, PartialEq)]
Expand All @@ -50,6 +58,7 @@ enum Language {
Rust,
Go,
Java,
Swift,
}

/// Detect the language of a stacktrace from the input text.
Expand All @@ -64,6 +73,14 @@ fn detect_language(input: &str) -> Option<Language> {
if GO_GOROUTINE_RE.is_match(line) {
return Some(Language::Go);
}
// Swift crash reports: "Thread N Crashed:" followed by dylib frames
if SWIFT_CRASH_HEADER_RE.is_match(line) {
// Verify there are actual Swift-style crash frames nearby
let has_crash_frames = input.lines().any(|l| SWIFT_CRASH_FRAME_RE.is_match(l));
if has_crash_frames {
return Some(Language::Swift);
}
}
}

// Node.js vs Java: both use "at " prefix. Distinguish by frame format.
Expand Down Expand Up @@ -98,6 +115,7 @@ pub fn compress_errors(input: &str) -> String {
Some(Language::Rust) => compress_rust(&deduped),
Some(Language::Go) => compress_go(&deduped),
Some(Language::Java) => compress_java(&deduped),
Some(Language::Swift) => crate::swift_cmd::compress_swift_crash(&deduped),
None => deduped,
}
}
Expand Down
39 changes: 39 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ mod ruff_cmd;
mod runner;
mod session_cmd;
mod summary;
mod swift_cmd;
mod tee;
mod telemetry;
mod toml_filter;
Expand Down Expand Up @@ -508,6 +509,12 @@ enum Commands {
command: CargoCommands,
},

/// Swift/Xcode commands with compact output (collapse compile lines, compress crash traces)
Swift {
#[command(subcommand)]
command: SwiftCommands,
},

/// npm run with filtered output (strip boilerplate)
Npm {
/// npm run arguments (script name + options)
Expand Down Expand Up @@ -978,6 +985,25 @@ enum CargoCommands {
Other(Vec<OsString>),
}

#[derive(Subcommand)]
enum SwiftCommands {
/// Build with compact output (collapse CompileSwift lines, keep errors)
Build {
/// Additional swift build arguments
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
},
/// Test with compact output (collapse compile noise, keep test results)
Test {
/// Additional swift test arguments
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
},
/// Passthrough: runs any unsupported swift subcommand directly
#[command(external_subcommand)]
Other(Vec<OsString>),
}

#[derive(Subcommand)]
enum DotnetCommands {
/// Build with compact output
Expand Down Expand Up @@ -1838,6 +1864,18 @@ fn main() -> Result<()> {
}
},

Commands::Swift { command } => match command {
SwiftCommands::Build { args } => {
swift_cmd::run(swift_cmd::SwiftCommand::Build, &args, cli.verbose)?;
}
SwiftCommands::Test { args } => {
swift_cmd::run(swift_cmd::SwiftCommand::Test, &args, cli.verbose)?;
}
SwiftCommands::Other(args) => {
swift_cmd::run_passthrough(&args, cli.verbose)?;
}
},

Commands::Npm { args } => {
npm_cmd::run(&args, cli.verbose, cli.skip_env)?;
}
Expand Down Expand Up @@ -2241,6 +2279,7 @@ fn is_operational_command(cmd: &Commands) -> bool {
| Commands::Prettier { .. }
| Commands::Playwright { .. }
| Commands::Cargo { .. }
| Commands::Swift { .. }
| Commands::Npm { .. }
| Commands::Npx { .. }
| Commands::Curl { .. }
Expand Down
2 changes: 2 additions & 0 deletions src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ pub fn run_err(command: &str, verbose: u8) -> Result<()> {
let compressed = crate::build_cmd::group_build_errors(&compressed);
// Post-process: compress Docker build logs
let compressed = crate::docker_cmd::compress_docker_log(&compressed);
// Post-process: compress Xcode build logs
let compressed = crate::swift_cmd::compress_xcode_log(&compressed);
summary.push_str(&compressed);
}

Expand Down
Loading