diff --git a/Cargo.lock b/Cargo.lock index e5275a2bd62dc2e1b11c740e1b4d379be08042b5..5f933af61d1b13c186e6c72a10b1a5daa42651fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3385,7 +3385,6 @@ dependencies = [ "reqwest 0.11.27", "reqwest_client", "rpc", - "rustc-demangle", "scrypt", "sea-orm", "semantic_version", diff --git a/Cargo.toml b/Cargo.toml index 5063bcc1d1c20895375bc56e59a31ec74ca7627e..4c12d819014e9cb04d9c08038ebb10940fd8f54e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -620,7 +620,6 @@ runtimelib = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804 "async-dispatcher-runtime", ] } rust-embed = { version = "8.4", features = ["include-exclude"] } -rustc-demangle = "0.1.23" rustc-hash = "2.1.0" rustls = { version = "0.23.26" } rustls-platform-verifier = "0.5.0" diff --git a/crates/collab/.env.toml b/crates/collab/.env.toml index 168e14415430e838d75c5dc0a5b92952ee53439d..325beb28fc0b8c9f60344d92b282723d3369aa28 100644 --- a/crates/collab/.env.toml +++ b/crates/collab/.env.toml @@ -20,7 +20,5 @@ LLM_DATABASE_MAX_CONNECTIONS = 5 LLM_API_SECRET = "llm-secret" OPENAI_API_KEY = "llm-secret" -# SLACK_PANICS_WEBHOOK = "" - # RUST_LOG=info # LOG_JSON=true diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 2d431cdf8ce3046d684dc6022dc29682416c0c9a..d95b318b0e791b532a340bda94d945fb7c9485c1 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -46,7 +46,6 @@ rand.workspace = true reqwest = { version = "0.11", features = ["json"] } reqwest_client.workspace = true rpc.workspace = true -rustc-demangle.workspace = true scrypt = "0.11" sea-orm = { version = "1.1.0-rc.1", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] } semantic_version.workspace = true diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index 1476e5890283c62cee3563a327fcdd5ee84842e7..751decf5ff5c2b8745f403e1f64cec8fd4a260cb 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -214,11 +214,6 @@ spec: secretKeyRef: name: blob-store key: bucket - - name: SLACK_PANICS_WEBHOOK - valueFrom: - secretKeyRef: - name: slack - key: panics_webhook - name: COMPLETE_WITH_LANGUAGE_MODEL_RATE_LIMIT_PER_HOUR value: "1000" - name: SUPERMAVEN_ADMIN_API_KEY diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 0cc7e2b2e93969ba7b8942838e4afcee251a20d9..d2432d828df899ff9551f5a7da9c37ea4ade8346 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -1,8 +1,6 @@ pub mod contributors; pub mod events; pub mod extensions; -pub mod ips_file; -pub mod slack; use crate::{AppState, Error, Result, auth, db::UserId, rpc}; use anyhow::Context as _; diff --git a/crates/collab/src/api/events.rs b/crates/collab/src/api/events.rs index da78a980693bec2243d872092a4f373698958b7a..0c1419144d40b1f6ab0f3806b5bd4edf165111fa 100644 --- a/crates/collab/src/api/events.rs +++ b/crates/collab/src/api/events.rs @@ -1,33 +1,28 @@ -use super::ips_file::IpsFile; use crate::api::CloudflareIpCountryHeader; -use crate::{AppState, Error, Result, api::slack}; +use crate::{AppState, Error, Result}; use anyhow::anyhow; -use aws_sdk_s3::primitives::ByteStream; use axum::{ Extension, Router, TypedHeader, body::Bytes, headers::Header, - http::{HeaderMap, HeaderName, StatusCode}, + http::{HeaderName, StatusCode}, routing::post, }; use chrono::Duration; -use semantic_version::SemanticVersion; use serde::{Deserialize, Serialize}; use serde_json::json; use sha2::{Digest, Sha256}; use std::sync::{Arc, OnceLock}; -use telemetry_events::{Event, EventRequestBody, Panic}; +use telemetry_events::{Event, EventRequestBody}; use util::ResultExt; use uuid::Uuid; -const CRASH_REPORTS_BUCKET: &str = "zed-crash-reports"; - pub fn router() -> Router { Router::new() .route("/telemetry/events", post(post_events)) - .route("/telemetry/crashes", post(post_crash)) + .route("/telemetry/crashes", post(post_panic)) .route("/telemetry/panics", post(post_panic)) - .route("/telemetry/hangs", post(post_hang)) + .route("/telemetry/hangs", post(post_panic)) } pub struct ZedChecksumHeader(Vec); @@ -58,437 +53,12 @@ impl Header for ZedChecksumHeader { } } -pub async fn post_crash( - Extension(app): Extension>, - headers: HeaderMap, - body: Bytes, -) -> Result<()> { - let report = IpsFile::parse(&body)?; - let version_threshold = SemanticVersion::new(0, 123, 0); - - let bundle_id = &report.header.bundle_id; - let app_version = &report.app_version(); - - if bundle_id == "dev.zed.Zed-Dev" { - log::error!("Crash uploads from {} are ignored.", bundle_id); - return Ok(()); - } - - if app_version.is_none() || app_version.unwrap() < version_threshold { - log::error!( - "Crash uploads from {} are ignored.", - report.header.app_version - ); - return Ok(()); - } - let app_version = app_version.unwrap(); - - if let Some(blob_store_client) = app.blob_store_client.as_ref() { - let response = blob_store_client - .head_object() - .bucket(CRASH_REPORTS_BUCKET) - .key(report.header.incident_id.clone() + ".ips") - .send() - .await; - - if response.is_ok() { - log::info!("We've already uploaded this crash"); - return Ok(()); - } - - blob_store_client - .put_object() - .bucket(CRASH_REPORTS_BUCKET) - .key(report.header.incident_id.clone() + ".ips") - .acl(aws_sdk_s3::types::ObjectCannedAcl::PublicRead) - .body(ByteStream::from(body.to_vec())) - .send() - .await - .map_err(|e| log::error!("Failed to upload crash: {}", e)) - .ok(); - } - - let recent_panic_on: Option = headers - .get("x-zed-panicked-on") - .and_then(|h| h.to_str().ok()) - .and_then(|s| s.parse().ok()); - - let installation_id = headers - .get("x-zed-installation-id") - .and_then(|h| h.to_str().ok()) - .map(|s| s.to_string()) - .unwrap_or_default(); - - let mut recent_panic = None; - - if let Some(recent_panic_on) = recent_panic_on { - let crashed_at = match report.timestamp() { - Ok(t) => Some(t), - Err(e) => { - log::error!("Can't parse {}: {}", report.header.timestamp, e); - None - } - }; - if crashed_at.is_some_and(|t| (t.timestamp_millis() - recent_panic_on).abs() <= 30000) { - recent_panic = headers.get("x-zed-panic").and_then(|h| h.to_str().ok()); - } - } - - let description = report.description(recent_panic); - let summary = report.backtrace_summary(); - - tracing::error!( - service = "client", - version = %report.header.app_version, - os_version = %report.header.os_version, - bundle_id = %report.header.bundle_id, - incident_id = %report.header.incident_id, - installation_id = %installation_id, - description = %description, - backtrace = %summary, - "crash report" - ); - - if let Some(kinesis_client) = app.kinesis_client.clone() - && let Some(stream) = app.config.kinesis_stream.clone() - { - let properties = json!({ - "app_version": report.header.app_version, - "os_version": report.header.os_version, - "os_name": "macOS", - "bundle_id": report.header.bundle_id, - "incident_id": report.header.incident_id, - "installation_id": installation_id, - "description": description, - "backtrace": summary, - }); - let row = SnowflakeRow::new( - "Crash Reported", - None, - false, - Some(installation_id), - properties, - ); - let data = serde_json::to_vec(&row)?; - kinesis_client - .put_record() - .stream_name(stream) - .partition_key(row.insert_id.unwrap_or_default()) - .data(data.into()) - .send() - .await - .log_err(); - } - - if let Some(slack_panics_webhook) = app.config.slack_panics_webhook.clone() { - let payload = slack::WebhookBody::new(|w| { - w.add_section(|s| s.text(slack::Text::markdown(description))) - .add_section(|s| { - s.add_field(slack::Text::markdown(format!( - "*Version:*\n{} ({})", - bundle_id, app_version - ))) - .add_field({ - let hostname = app.config.blob_store_url.clone().unwrap_or_default(); - let hostname = hostname.strip_prefix("https://").unwrap_or_else(|| { - hostname.strip_prefix("http://").unwrap_or_default() - }); - - slack::Text::markdown(format!( - "*Incident:*\n", - CRASH_REPORTS_BUCKET, - hostname, - report.header.incident_id, - report - .header - .incident_id - .chars() - .take(8) - .collect::(), - )) - }) - }) - .add_rich_text(|r| r.add_preformatted(|p| p.add_text(summary))) - }); - let payload_json = serde_json::to_string(&payload).map_err(|err| { - log::error!("Failed to serialize payload to JSON: {err}"); - Error::Internal(anyhow!(err)) - })?; - - reqwest::Client::new() - .post(slack_panics_webhook) - .header("Content-Type", "application/json") - .body(payload_json) - .send() - .await - .map_err(|err| { - log::error!("Failed to send payload to Slack: {err}"); - Error::Internal(anyhow!(err)) - })?; - } - - Ok(()) -} - -pub async fn post_hang( - Extension(app): Extension>, - TypedHeader(ZedChecksumHeader(checksum)): TypedHeader, - body: Bytes, -) -> Result<()> { - let Some(expected) = calculate_json_checksum(app.clone(), &body) else { - return Err(Error::http( - StatusCode::INTERNAL_SERVER_ERROR, - "events not enabled".into(), - ))?; - }; - - if checksum != expected { - return Err(Error::http( - StatusCode::BAD_REQUEST, - "invalid checksum".into(), - ))?; - } - - let incident_id = Uuid::new_v4().to_string(); - - // dump JSON into S3 so we can get frame offsets if we need to. - if let Some(blob_store_client) = app.blob_store_client.as_ref() { - blob_store_client - .put_object() - .bucket(CRASH_REPORTS_BUCKET) - .key(incident_id.clone() + ".hang.json") - .acl(aws_sdk_s3::types::ObjectCannedAcl::PublicRead) - .body(ByteStream::from(body.to_vec())) - .send() - .await - .map_err(|e| log::error!("Failed to upload crash: {}", e)) - .ok(); - } - - let report: telemetry_events::HangReport = serde_json::from_slice(&body).map_err(|err| { - log::error!("can't parse report json: {err}"); - Error::Internal(anyhow!(err)) - })?; - - let mut backtrace = "Possible hang detected on main thread:".to_string(); - let unknown = "".to_string(); - for frame in report.backtrace.iter() { - backtrace.push_str(&format!("\n{}", frame.symbols.first().unwrap_or(&unknown))); - } - - tracing::error!( - service = "client", - version = %report.app_version.unwrap_or_default().to_string(), - os_name = %report.os_name, - os_version = report.os_version.unwrap_or_default(), - incident_id = %incident_id, - installation_id = %report.installation_id.unwrap_or_default(), - backtrace = %backtrace, - "hang report"); - - Ok(()) -} - -pub async fn post_panic( - Extension(app): Extension>, - TypedHeader(ZedChecksumHeader(checksum)): TypedHeader, - body: Bytes, -) -> Result<()> { - let Some(expected) = calculate_json_checksum(app.clone(), &body) else { - return Err(Error::http( - StatusCode::INTERNAL_SERVER_ERROR, - "events not enabled".into(), - ))?; - }; - - if checksum != expected { - return Err(Error::http( - StatusCode::BAD_REQUEST, - "invalid checksum".into(), - ))?; - } - - let report: telemetry_events::PanicRequest = serde_json::from_slice(&body) - .map_err(|_| Error::http(StatusCode::BAD_REQUEST, "invalid json".into()))?; - let incident_id = uuid::Uuid::new_v4().to_string(); - let panic = report.panic; - - if panic.os_name == "Linux" && panic.os_version == Some("1.0.0".to_string()) { - return Err(Error::http( - StatusCode::BAD_REQUEST, - "invalid os version".into(), - ))?; - } - - if let Some(blob_store_client) = app.blob_store_client.as_ref() { - let response = blob_store_client - .head_object() - .bucket(CRASH_REPORTS_BUCKET) - .key(incident_id.clone() + ".json") - .send() - .await; - - if response.is_ok() { - log::info!("We've already uploaded this crash"); - return Ok(()); - } - - blob_store_client - .put_object() - .bucket(CRASH_REPORTS_BUCKET) - .key(incident_id.clone() + ".json") - .acl(aws_sdk_s3::types::ObjectCannedAcl::PublicRead) - .body(ByteStream::from(body.to_vec())) - .send() - .await - .map_err(|e| log::error!("Failed to upload crash: {}", e)) - .ok(); - } - - let backtrace = panic.backtrace.join("\n"); - - tracing::error!( - service = "client", - version = %panic.app_version, - os_name = %panic.os_name, - os_version = %panic.os_version.clone().unwrap_or_default(), - incident_id = %incident_id, - installation_id = %panic.installation_id.clone().unwrap_or_default(), - description = %panic.payload, - backtrace = %backtrace, - "panic report" - ); - - if let Some(kinesis_client) = app.kinesis_client.clone() - && let Some(stream) = app.config.kinesis_stream.clone() - { - let properties = json!({ - "app_version": panic.app_version, - "os_name": panic.os_name, - "os_version": panic.os_version, - "incident_id": incident_id, - "installation_id": panic.installation_id, - "description": panic.payload, - "backtrace": backtrace, - }); - let row = SnowflakeRow::new( - "Panic Reported", - None, - false, - panic.installation_id.clone(), - properties, - ); - let data = serde_json::to_vec(&row)?; - kinesis_client - .put_record() - .stream_name(stream) - .partition_key(row.insert_id.unwrap_or_default()) - .data(data.into()) - .send() - .await - .log_err(); - } - - if !report_to_slack(&panic) { - return Ok(()); - } - - if let Some(slack_panics_webhook) = app.config.slack_panics_webhook.clone() { - let backtrace = if panic.backtrace.len() > 25 { - let total = panic.backtrace.len(); - format!( - "{}\n and {} more", - panic - .backtrace - .iter() - .take(20) - .cloned() - .collect::>() - .join("\n"), - total - 20 - ) - } else { - panic.backtrace.join("\n") - }; - let backtrace_with_summary = panic.payload + "\n" + &backtrace; - - let version = if panic.release_channel == "nightly" - && !panic.app_version.contains("remote-server") - && let Some(sha) = panic.app_commit_sha - { - format!("Zed Nightly {}", sha.chars().take(7).collect::()) - } else { - panic.app_version - }; - - let payload = slack::WebhookBody::new(|w| { - w.add_section(|s| s.text(slack::Text::markdown("Panic request".to_string()))) - .add_section(|s| { - s.add_field(slack::Text::markdown(format!("*Version:*\n {version} ",))) - .add_field({ - let hostname = app.config.blob_store_url.clone().unwrap_or_default(); - let hostname = hostname.strip_prefix("https://").unwrap_or_else(|| { - hostname.strip_prefix("http://").unwrap_or_default() - }); - - slack::Text::markdown(format!( - "*{} {}:*\n", - panic.os_name, - panic.os_version.unwrap_or_default(), - CRASH_REPORTS_BUCKET, - hostname, - incident_id, - incident_id.chars().take(8).collect::(), - )) - }) - }) - .add_rich_text(|r| r.add_preformatted(|p| p.add_text(backtrace_with_summary))) - }); - let payload_json = serde_json::to_string(&payload).map_err(|err| { - log::error!("Failed to serialize payload to JSON: {err}"); - Error::Internal(anyhow!(err)) - })?; - - reqwest::Client::new() - .post(slack_panics_webhook) - .header("Content-Type", "application/json") - .body(payload_json) - .send() - .await - .map_err(|err| { - log::error!("Failed to send payload to Slack: {err}"); - Error::Internal(anyhow!(err)) - })?; - } - +pub async fn post_panic() -> Result<()> { + // as of v0.201.x crash/panic reporting is now done via Sentry. + // The endpoint returns OK to avoid spurious errors for old clients. Ok(()) } -fn report_to_slack(panic: &Panic) -> bool { - // Panics on macOS should make their way to Slack as a crash report, - // so we don't need to send them a second time via this channel. - if panic.os_name == "macOS" { - return false; - } - - if panic.payload.contains("ERROR_SURFACE_LOST_KHR") { - return false; - } - - if panic.payload.contains("ERROR_INITIALIZATION_FAILED") { - return false; - } - - if panic - .payload - .contains("GPU has crashed, and no debug information is available") - { - return false; - } - - true -} - pub async fn post_events( Extension(app): Extension>, TypedHeader(ZedChecksumHeader(checksum)): TypedHeader, diff --git a/crates/collab/src/api/ips_file.rs b/crates/collab/src/api/ips_file.rs deleted file mode 100644 index 583582d555428cb52e48123fb7447ff74865c1ee..0000000000000000000000000000000000000000 --- a/crates/collab/src/api/ips_file.rs +++ /dev/null @@ -1,346 +0,0 @@ -use anyhow::Context as _; -use collections::HashMap; - -use semantic_version::SemanticVersion; -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -#[derive(Debug)] -pub struct IpsFile { - pub header: Header, - pub body: Body, -} - -impl IpsFile { - pub fn parse(bytes: &[u8]) -> anyhow::Result { - let mut split = bytes.splitn(2, |&b| b == b'\n'); - let header_bytes = split.next().context("No header found")?; - let header: Header = serde_json::from_slice(header_bytes).context("parsing header")?; - - let body_bytes = split.next().context("No body found")?; - - let body: Body = serde_json::from_slice(body_bytes).context("parsing body")?; - Ok(IpsFile { header, body }) - } - - pub fn faulting_thread(&self) -> Option<&Thread> { - self.body.threads.get(self.body.faulting_thread? as usize) - } - - pub fn app_version(&self) -> Option { - self.header.app_version.parse().ok() - } - - pub fn timestamp(&self) -> anyhow::Result> { - chrono::DateTime::parse_from_str(&self.header.timestamp, "%Y-%m-%d %H:%M:%S%.f %#z") - .map_err(|e| anyhow::anyhow!(e)) - } - - pub fn description(&self, panic: Option<&str>) -> String { - let mut desc = if self.body.termination.indicator == "Abort trap: 6" { - match panic { - Some(panic_message) => format!("Panic `{}`", panic_message), - None => "Crash `Abort trap: 6` (possible panic)".into(), - } - } else if let Some(msg) = &self.body.exception.message { - format!("Exception `{}`", msg) - } else { - format!("Crash `{}`", self.body.termination.indicator) - }; - if let Some(thread) = self.faulting_thread() { - if let Some(queue) = thread.queue.as_ref() { - desc += &format!( - " on thread {} ({})", - self.body.faulting_thread.unwrap_or_default(), - queue - ); - } else { - desc += &format!( - " on thread {} ({})", - self.body.faulting_thread.unwrap_or_default(), - thread.name.clone().unwrap_or_default() - ); - } - } - desc - } - - pub fn backtrace_summary(&self) -> String { - if let Some(thread) = self.faulting_thread() { - let mut frames = thread - .frames - .iter() - .filter_map(|frame| { - if let Some(name) = &frame.symbol { - if self.is_ignorable_frame(name) { - return None; - } - Some(format!("{:#}", rustc_demangle::demangle(name))) - } else if let Some(image) = self.body.used_images.get(frame.image_index) { - Some(image.name.clone().unwrap_or("".into())) - } else { - Some("".into()) - } - }) - .collect::>(); - - let total = frames.len(); - if total > 21 { - frames = frames.into_iter().take(20).collect(); - frames.push(format!(" and {} more...", total - 20)) - } - frames.join("\n") - } else { - "".into() - } - } - - fn is_ignorable_frame(&self, symbol: &String) -> bool { - [ - "pthread_kill", - "panic", - "backtrace", - "rust_begin_unwind", - "abort", - ] - .iter() - .any(|s| symbol.contains(s)) - } -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(default)] -pub struct Header { - pub app_name: String, - pub timestamp: String, - pub app_version: String, - pub slice_uuid: String, - pub build_version: String, - pub platform: i64, - #[serde(rename = "bundleID", default)] - pub bundle_id: String, - pub share_with_app_devs: i64, - pub is_first_party: i64, - pub bug_type: String, - pub os_version: String, - pub roots_installed: i64, - pub name: String, - pub incident_id: String, -} -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct Body { - pub uptime: i64, - pub proc_role: String, - pub version: i64, - #[serde(rename = "userID")] - pub user_id: i64, - pub deploy_version: i64, - pub model_code: String, - #[serde(rename = "coalitionID")] - pub coalition_id: i64, - pub os_version: OsVersion, - pub capture_time: String, - pub code_signing_monitor: i64, - pub incident: String, - pub pid: i64, - pub translated: bool, - pub cpu_type: String, - #[serde(rename = "roots_installed")] - pub roots_installed: i64, - #[serde(rename = "bug_type")] - pub bug_type: String, - pub proc_launch: String, - pub proc_start_abs_time: i64, - pub proc_exit_abs_time: i64, - pub proc_name: String, - pub proc_path: String, - pub bundle_info: BundleInfo, - pub store_info: StoreInfo, - pub parent_proc: String, - pub parent_pid: i64, - pub coalition_name: String, - pub crash_reporter_key: String, - #[serde(rename = "codeSigningID")] - pub code_signing_id: String, - #[serde(rename = "codeSigningTeamID")] - pub code_signing_team_id: String, - pub code_signing_flags: i64, - pub code_signing_validation_category: i64, - pub code_signing_trust_level: i64, - pub instruction_byte_stream: InstructionByteStream, - pub sip: String, - pub exception: Exception, - pub termination: Termination, - pub asi: Asi, - pub ext_mods: ExtMods, - pub faulting_thread: Option, - pub threads: Vec, - pub used_images: Vec, - pub shared_cache: SharedCache, - pub vm_summary: String, - pub legacy_info: LegacyInfo, - pub log_writing_signature: String, - pub trial_info: TrialInfo, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct OsVersion { - pub train: String, - pub build: String, - pub release_type: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct BundleInfo { - #[serde(rename = "CFBundleShortVersionString")] - pub cfbundle_short_version_string: String, - #[serde(rename = "CFBundleVersion")] - pub cfbundle_version: String, - #[serde(rename = "CFBundleIdentifier")] - pub cfbundle_identifier: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct StoreInfo { - pub device_identifier_for_vendor: String, - pub third_party: bool, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct InstructionByteStream { - #[serde(rename = "beforePC")] - pub before_pc: String, - #[serde(rename = "atPC")] - pub at_pc: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct Exception { - pub codes: String, - pub raw_codes: Vec, - #[serde(rename = "type")] - pub type_field: String, - pub subtype: Option, - pub signal: String, - pub port: Option, - pub guard_id: Option, - pub message: Option, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct Termination { - pub flags: i64, - pub code: i64, - pub namespace: String, - pub indicator: String, - pub by_proc: String, - pub by_pid: i64, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct Asi { - #[serde(rename = "libsystem_c.dylib")] - pub libsystem_c_dylib: Vec, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct ExtMods { - pub caller: ExtMod, - pub system: ExtMod, - pub targeted: ExtMod, - pub warnings: i64, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct ExtMod { - #[serde(rename = "thread_create")] - pub thread_create: i64, - #[serde(rename = "thread_set_state")] - pub thread_set_state: i64, - #[serde(rename = "task_for_pid")] - pub task_for_pid: i64, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct Thread { - pub thread_state: HashMap, - pub id: i64, - pub triggered: Option, - pub name: Option, - pub queue: Option, - pub frames: Vec, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct Frame { - pub image_offset: i64, - pub symbol: Option, - pub symbol_location: Option, - pub image_index: usize, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct UsedImage { - pub source: String, - pub arch: Option, - pub base: i64, - #[serde(rename = "CFBundleShortVersionString")] - pub cfbundle_short_version_string: Option, - #[serde(rename = "CFBundleIdentifier")] - pub cfbundle_identifier: Option, - pub size: i64, - pub uuid: String, - pub path: Option, - pub name: Option, - #[serde(rename = "CFBundleVersion")] - pub cfbundle_version: Option, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct SharedCache { - pub base: i64, - pub size: i64, - pub uuid: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct LegacyInfo { - pub thread_triggered: ThreadTriggered, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct ThreadTriggered { - pub name: String, - pub queue: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct TrialInfo { - pub rollouts: Vec, - pub experiments: Vec, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct Rollout { - pub rollout_id: String, - pub factor_pack_ids: HashMap, - pub deployment_id: i64, -} diff --git a/crates/collab/src/api/slack.rs b/crates/collab/src/api/slack.rs deleted file mode 100644 index 2f4234b165ed2bced89671e555ff57ecf054a6b1..0000000000000000000000000000000000000000 --- a/crates/collab/src/api/slack.rs +++ /dev/null @@ -1,144 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// https://api.slack.com/reference/messaging/payload -#[derive(Default, Clone, Serialize, Deserialize)] -pub struct WebhookBody { - text: String, - #[serde(skip_serializing_if = "Vec::is_empty")] - blocks: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - thread_ts: Option, - #[serde(skip_serializing_if = "Option::is_none")] - mrkdwn: Option, -} - -impl WebhookBody { - pub fn new(f: impl FnOnce(Self) -> Self) -> Self { - f(Self::default()) - } - - pub fn add_section(mut self, build: impl FnOnce(Section) -> Section) -> Self { - self.blocks.push(Block::Section(build(Section::default()))); - self - } - - pub fn add_rich_text(mut self, build: impl FnOnce(RichText) -> RichText) -> Self { - self.blocks - .push(Block::RichText(build(RichText::default()))); - self - } -} - -#[derive(Clone, Serialize, Deserialize)] -#[serde(tag = "type")] -/// https://api.slack.com/reference/block-kit/blocks -pub enum Block { - #[serde(rename = "section")] - Section(Section), - #[serde(rename = "rich_text")] - RichText(RichText), - // .... etc. -} - -/// https://api.slack.com/reference/block-kit/blocks#section -#[derive(Default, Clone, Serialize, Deserialize)] -pub struct Section { - #[serde(skip_serializing_if = "Option::is_none")] - text: Option, - #[serde(skip_serializing_if = "Vec::is_empty")] - fields: Vec, - // fields, accessories... -} - -impl Section { - pub fn text(mut self, text: Text) -> Self { - self.text = Some(text); - self - } - - pub fn add_field(mut self, field: Text) -> Self { - self.fields.push(field); - self - } -} - -/// https://api.slack.com/reference/block-kit/composition-objects#text -#[derive(Clone, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum Text { - #[serde(rename = "plain_text")] - PlainText { text: String, emoji: bool }, - #[serde(rename = "mrkdwn")] - Markdown { text: String, verbatim: bool }, -} - -impl Text { - pub fn plain(s: String) -> Self { - Self::PlainText { - text: s, - emoji: true, - } - } - - pub fn markdown(s: String) -> Self { - Self::Markdown { - text: s, - verbatim: false, - } - } -} - -#[derive(Default, Clone, Serialize, Deserialize)] -pub struct RichText { - elements: Vec, -} - -impl RichText { - pub fn new(f: impl FnOnce(Self) -> Self) -> Self { - f(Self::default()) - } - - pub fn add_preformatted( - mut self, - build: impl FnOnce(RichTextPreformatted) -> RichTextPreformatted, - ) -> Self { - self.elements.push(RichTextObject::Preformatted(build( - RichTextPreformatted::default(), - ))); - self - } -} - -/// https://api.slack.com/reference/block-kit/blocks#rich_text -#[derive(Clone, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum RichTextObject { - #[serde(rename = "rich_text_preformatted")] - Preformatted(RichTextPreformatted), - // etc. -} - -/// https://api.slack.com/reference/block-kit/blocks#rich_text_preformatted -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct RichTextPreformatted { - #[serde(skip_serializing_if = "Vec::is_empty")] - elements: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - border: Option, -} - -impl RichTextPreformatted { - pub fn add_text(mut self, text: String) -> Self { - self.elements.push(RichTextElement::Text { text }); - self - } -} - -/// https://api.slack.com/reference/block-kit/blocks#element-types -#[derive(Clone, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum RichTextElement { - #[serde(rename = "text")] - Text { text: String }, - // etc. -} diff --git a/crates/collab/src/lib.rs b/crates/collab/src/lib.rs index f1de0cdc7ff79cd25c8ef7b0b2b21d9e0b45d332..14573e94b0b535b1644510e28dfc906b1a2c420e 100644 --- a/crates/collab/src/lib.rs +++ b/crates/collab/src/lib.rs @@ -153,7 +153,6 @@ pub struct Config { pub prediction_api_key: Option>, pub prediction_model: Option>, pub zed_client_checksum_seed: Option, - pub slack_panics_webhook: Option, pub auto_join_channel_id: Option, pub supermaven_admin_api_key: Option>, } @@ -204,7 +203,6 @@ impl Config { prediction_api_key: None, prediction_model: None, zed_client_checksum_seed: None, - slack_panics_webhook: None, auto_join_channel_id: None, migrations_path: None, seed_path: None, diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index 5e99cc192ad080c1a79913c79fbbaae9d8b6d951..fef931c0d8f3f5e8a6a731b4756cad1644b27a8f 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -599,7 +599,6 @@ impl TestServer { prediction_api_key: None, prediction_model: None, zed_client_checksum_seed: None, - slack_panics_webhook: None, auto_join_channel_id: None, migrations_path: None, seed_path: None,