Cargo.lock 🔗
@@ -2039,7 +2039,6 @@ dependencies = [
"release_channel",
"reqwest",
"rpc",
- "rustc-demangle",
"scrypt",
"sea-orm",
"semver",
Conrad Irwin created
This reverts commit c05f8154bed922c439749aee1f8519081f49d4cd.
Cargo.lock | 1
Cargo.toml | 1
crates/collab/.env.toml | 2
crates/collab/Cargo.toml | 7
crates/collab/k8s/collab.template.yml | 5
crates/collab/src/api.rs | 18 +
crates/collab/src/api/events.rs | 148 -----------
crates/collab/src/api/ips_file.rs | 352 ----------------------------
crates/collab/src/api/slack.rs | 144 -----------
crates/collab/src/lib.rs | 1
crates/collab/src/tests/test_server.rs | 1
crates/zed/src/main.rs | 2
script/seed-db | 2
13 files changed, 27 insertions(+), 657 deletions(-)
@@ -2039,7 +2039,6 @@ dependencies = [
"release_channel",
"reqwest",
"rpc",
- "rustc-demangle",
"scrypt",
"sea-orm",
"semver",
@@ -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" }
@@ -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
@@ -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
@@ -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
@@ -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<Arc<rpc::Server>>, state: Arc<AppState>) -> 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<Panic>) -> 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<Option<Arc<rpc::Server>>>,
) -> Result<ErasedJson> {
@@ -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<Arc<AppState>>,
- 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<i64> = 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<https://{}.{}/{}.ips|{}…>",
- CRASH_REPORTS_BUCKET,
- hostname,
- report.header.incident_id,
- report
- .header
- .incident_id
- .chars()
- .take(8)
- .collect::<String>(),
- ))
- })
- })
- .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<Arc<AppState>>,
TypedHeader(ZedChecksumHeader(checksum)): TypedHeader<ZedChecksumHeader>,
@@ -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(),
@@ -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<IpsFile> {
- 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<SemanticVersion> {
- self.header.app_version.parse().ok()
- }
-
- pub fn timestamp(&self) -> anyhow::Result<chrono::DateTime<chrono::FixedOffset>> {
- 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("<unknown-image>".into()))
- } else {
- Some("<unknown>".into())
- }
- })
- .collect::<Vec<_>>();
-
- 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 {
- "<no backtrace available>".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<i64>,
- pub threads: Vec<Thread>,
- pub used_images: Vec<UsedImage>,
- 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<i64>,
- #[serde(rename = "type")]
- pub type_field: String,
- pub subtype: Option<String>,
- pub signal: String,
- pub port: Option<i64>,
- pub guard_id: Option<i64>,
- pub message: Option<String>,
-}
-
-#[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<String>,
-}
-
-#[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<String, Value>,
- pub id: i64,
- pub triggered: Option<bool>,
- pub name: Option<String>,
- pub queue: Option<String>,
- pub frames: Vec<Frame>,
-}
-
-#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase", default)]
-pub struct Frame {
- pub image_offset: i64,
- pub symbol: Option<String>,
- pub symbol_location: Option<i64>,
- 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<String>,
- pub base: i64,
- #[serde(rename = "CFBundleShortVersionString")]
- pub cfbundle_short_version_string: Option<String>,
- #[serde(rename = "CFBundleIdentifier")]
- pub cfbundle_identifier: Option<String>,
- pub size: i64,
- pub uuid: String,
- pub path: Option<String>,
- pub name: Option<String>,
- #[serde(rename = "CFBundleVersion")]
- pub cfbundle_version: Option<String>,
-}
-
-#[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<Rollout>,
- pub experiments: Vec<Value>,
-}
-
-#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase", default)]
-pub struct Rollout {
- pub rollout_id: String,
- pub factor_pack_ids: HashMap<String, Value>,
- pub deployment_id: i64,
-}
@@ -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<Block>,
- #[serde(skip_serializing_if = "Option::is_none")]
- thread_ts: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- mrkdwn: Option<bool>,
-}
-
-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<Text>,
- #[serde(skip_serializing_if = "Vec::is_empty")]
- fields: Vec<Text>,
- // 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<RichTextObject>,
-}
-
-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<RichTextElement>,
- #[serde(skip_serializing_if = "Option::is_none")]
- border: Option<u8>,
-}
-
-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.
-}
@@ -127,7 +127,6 @@ pub struct Config {
pub blob_store_bucket: Option<String>,
pub zed_environment: Arc<str>,
pub zed_client_checksum_seed: Option<String>,
- pub slack_panics_webhook: Option<String>,
}
impl Config {
@@ -507,7 +507,6 @@ impl TestServer {
clickhouse_password: None,
clickhouse_database: None,
zed_client_checksum_seed: None,
- slack_panics_webhook: None,
},
})
}
@@ -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?;
@@ -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 -- $@