diff --git a/Cargo.lock b/Cargo.lock index 61d1f2385b342059a900d8bb50cd1b1a71bf6a6f..2154d4e01ddb38cb6580d0d03ff1b628550c8ec0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2039,7 +2039,6 @@ dependencies = [ "release_channel", "reqwest", "rpc", - "rustc-demangle", "scrypt", "sea-orm", "semver", diff --git a/Cargo.toml b/Cargo.toml index a1e5b30c88d8ab0e06660540274c1efdf2acfc61..2757fed05de24bf9a83fddc9e98bd3d2f6b624c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -269,7 +269,6 @@ tree-sitter-gomod = { git = "https://github.com/camdencheek/tree-sitter-go-mod" tree-sitter-gowork = { git = "https://github.com/d1y/tree-sitter-go-work" } tree-sitter-haskell = { git = "https://github.com/tree-sitter/tree-sitter-haskell", rev = "8a99848fc734f9c4ea523b3f2a07df133cbbcec2" } tree-sitter-hcl = { git = "https://github.com/MichaHoffmann/tree-sitter-hcl", rev = "v1.1.0" } -rustc-demangle = "0.1.23" tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "2e1348c3cf2c9323e87c2744796cf3f3868aa82a" } tree-sitter-html = "0.19.0" tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" } diff --git a/crates/collab/.env.toml b/crates/collab/.env.toml index b244e83eb2607c00c99a6eb43a70384499e02599..40c037e0534a4298c5e4c5809de8f168c8ef4790 100644 --- a/crates/collab/.env.toml +++ b/crates/collab/.env.toml @@ -19,7 +19,5 @@ ZED_CLIENT_CHECKSUM_SEED = "development-checksum-seed" # CLICKHOUSE_PASSWORD = "" # CLICKHOUSE_DATABASE = "default" -# SLACK_PANICS_WEBHOOK = "" - # RUST_LOG=info # LOG_JSON=true diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 43bebe3e969d0428909b67740b6ac6bddfc66818..eccd7d9c96e5003b10883da328ec5aefd25cca19 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -7,11 +7,15 @@ version = "0.44.0" publish = false license = "AGPL-3.0-or-later" +[features] +seed-support = ["reqwest"] + [[bin]] name = "collab" [[bin]] name = "seed" +required-features = ["seed-support"] [dependencies] anyhow.workspace = true @@ -37,7 +41,7 @@ parking_lot.workspace = true prometheus = "0.13" prost.workspace = true rand.workspace = true -reqwest = { version = "0.11", features = ["json"] } +reqwest = { version = "0.11", features = ["json"], optional = true } rpc.workspace = true scrypt = "0.7" sea-orm = { version = "0.12.x", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] } @@ -47,7 +51,6 @@ serde_derive.workspace = true serde_json.workspace = true sha2.workspace = true sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "json", "time", "uuid", "any"] } -rustc-demangle.workspace = true telemetry_events.workspace = true text.workspace = true time.workspace = true diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index 4915c6c97c54e51b4084d59d96d1a0154bc4271b..e3738ddffe35abd88798275fa2d8927dc84acab3 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -156,11 +156,6 @@ spec: secretKeyRef: name: clickhouse key: database - - name: SLACK_PANICS_WEBHOOK - valueFrom: - secretKeyRef: - name: slack - key: panics_webhook - name: INVITE_LINK_PREFIX value: ${INVITE_LINK_PREFIX} - name: RUST_BACKTRACE diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 2d42beccc8f7dfe43767313263f8e54eecd7bc32..62dc5f0dfded9a72302a81618bc4811a4ccee0d4 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -1,7 +1,5 @@ pub mod events; pub mod extensions; -pub mod ips_file; -pub mod slack; use crate::{ auth, @@ -23,6 +21,7 @@ use chrono::SecondsFormat; use serde::{Deserialize, Serialize}; use std::sync::Arc; use tower::ServiceBuilder; +use tracing::instrument; pub use extensions::fetch_extensions_from_blob_store_periodically; @@ -30,6 +29,7 @@ pub fn routes(rpc_server: Option>, state: Arc) -> Rou Router::new() .route("/user", get(get_authenticated_user)) .route("/users/:id/access_tokens", post(create_access_token)) + .route("/panic", post(trace_panic)) .route("/rpc_server_snapshot", get(get_rpc_server_snapshot)) .route("/contributors", get(get_contributors).post(add_contributor)) .route("/contributor", get(check_is_contributor)) @@ -120,6 +120,20 @@ struct CreateUserResponse { metrics_id: String, } +#[derive(Debug, Deserialize)] +struct Panic { + version: String, + release_channel: String, + backtrace_hash: String, + text: String, +} + +#[instrument(skip(panic))] +async fn trace_panic(panic: Json) -> Result<()> { + tracing::error!(version = %panic.version, release_channel = %panic.release_channel, backtrace_hash = %panic.backtrace_hash, text = %panic.text, "panic report"); + Ok(()) +} + async fn get_rpc_server_snapshot( Extension(rpc_server): Extension>>, ) -> Result { diff --git a/crates/collab/src/api/events.rs b/crates/collab/src/api/events.rs index 24b4f7d58300f9575b15b495a93b5aa4d2eb398a..1b1a58fb55ed7cf5be1a5bb074cafb73ddd78d43 100644 --- a/crates/collab/src/api/events.rs +++ b/crates/collab/src/api/events.rs @@ -1,28 +1,22 @@ use std::sync::Arc; use anyhow::{anyhow, Context}; -use aws_sdk_s3::primitives::ByteStream; use axum::{ body::Bytes, headers::Header, http::HeaderName, routing::post, Extension, Router, TypedHeader, }; use hyper::StatusCode; -use hyper::{HeaderMap, StatusCode}; +use lazy_static::lazy_static; use serde::{Serialize, Serializer}; use sha2::{Digest, Sha256}; use telemetry_events::{ ActionEvent, AppEvent, AssistantEvent, CallEvent, CopilotEvent, CpuEvent, EditEvent, EditorEvent, Event, EventRequestBody, EventWrapper, MemoryEvent, SettingEvent, }; -use util::SemanticVersion; -use crate::{api::slack, AppState, Error, Result}; - -use super::ips_file::IpsFile; +use crate::{AppState, Error, Result}; pub fn router() -> Router { - Router::new() - .route("/telemetry/events", post(post_events)) - .route("/telemetry/crashes", post(post_crash)) + Router::new().route("/telemetry/events", post(post_events)) } lazy_static! { @@ -83,140 +77,6 @@ impl Header for CloudflareIpCountryHeader { } } -pub async fn post_crash( - Extension(app): Extension>, - body: Bytes, - headers: HeaderMap, -) -> Result<()> { - static CRASH_REPORTS_BUCKET: &str = "zed-crash-reports"; - - 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 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, - description = %description, - backtrace = %summary, - "crash report"); - - 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_events( Extension(app): Extension>, TypedHeader(ZedChecksumHeader(checksum)): TypedHeader, @@ -242,7 +102,7 @@ pub async fn post_events( summer.update(&body); summer.update(checksum_seed); - if &checksum != &summer.finalize()[..] { + if &checksum[..] != &summer.finalize()[..] { return Err(Error::Http( StatusCode::BAD_REQUEST, "invalid checksum".into(), diff --git a/crates/collab/src/api/ips_file.rs b/crates/collab/src/api/ips_file.rs deleted file mode 100644 index 6f1b4cd8f40be779eaccfaf097649745c54a1f5d..0000000000000000000000000000000000000000 --- a/crates/collab/src/api/ips_file.rs +++ /dev/null @@ -1,352 +0,0 @@ -use collections::HashMap; - -use serde_derive::Deserialize; -use serde_derive::Serialize; -use serde_json::Value; -use util::SemanticVersion; - -#[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() - .ok_or_else(|| anyhow::anyhow!("No header found"))?; - let header: Header = serde_json::from_slice(header_bytes) - .map_err(|e| anyhow::anyhow!("Failed to parse header: {}", e))?; - - let body_bytes = split - .next() - .ok_or_else(|| anyhow::anyhow!("No body found"))?; - - let body: Body = serde_json::from_slice(body_bytes) - .map_err(|e| anyhow::anyhow!("Failed to parse body: {}", e))?; - 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).into(), - 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 f45aba43519ade804a1d3863727f736f3d054b92..355768a3701b81c1734cfef88c7231425a5da1b7 100644 --- a/crates/collab/src/lib.rs +++ b/crates/collab/src/lib.rs @@ -127,7 +127,6 @@ pub struct Config { pub blob_store_bucket: Option, pub zed_environment: Arc, pub zed_client_checksum_seed: Option, - pub slack_panics_webhook: Option, } impl Config { diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index 515fef74c33f4b0a7579172dec8c4618e816eb08..35fee85ad24c5157059b53844f79feaa663af253 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -507,7 +507,6 @@ impl TestServer { clickhouse_password: None, clickhouse_database: None, zed_client_checksum_seed: None, - slack_panics_webhook: None, }, }) } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index bfdd0047690a650f313f9facb20f36900651a492..c509a8d0c12ca2465b50afae83224b529b8e8e4c 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -807,7 +807,7 @@ async fn upload_previous_crashes( .unwrap_or("zed-2024-01-17-221900.ips".to_string()); // don't upload old crash reports from before we had this. let mut uploaded = last_uploaded.clone(); - let crash_report_url = http.build_zed_api_url("/telemetry/crashes"); + let crash_report_url = http.build_url("/api/crash"); for dir in [&*CRASHES_DIR, &*CRASHES_RETIRED_DIR] { let mut children = smol::fs::read_dir(&dir).await?; diff --git a/script/seed-db b/script/seed-db index d3ee89e4f097ec3f129d7194b5ad2603e0e0c091..5079e0195539536a071bc4bd05e5d7a0853eedd8 100755 --- a/script/seed-db +++ b/script/seed-db @@ -1,4 +1,4 @@ #!/bin/bash set -e -cargo run --quiet --package=collab --bin seed -- $@ +cargo run --quiet --package=collab --features seed-support --bin seed -- $@