Add build SHA to panic reports and `zed --version` (on nightly/dev) (#24258)

Michael Sloan created

Release Notes:

- N/A

Change summary

crates/cli/build.rs                             | 16 +++++++++++
crates/cli/src/main.rs                          |  6 +++
crates/remote_server/build.rs                   | 27 +++++++-----------
crates/remote_server/src/main.rs                | 16 +++++++----
crates/remote_server/src/unix.rs                | 18 ++++++++----
crates/telemetry_events/src/telemetry_events.rs |  3 ++
crates/zed/src/main.rs                          |  7 +++-
crates/zed/src/reliability.rs                   | 16 +++++++++-
8 files changed, 76 insertions(+), 33 deletions(-)

Detailed changes

crates/cli/build.rs 🔗

@@ -1,3 +1,5 @@
+use std::process::Command;
+
 fn main() {
     if std::env::var("ZED_UPDATE_EXPLANATION").is_ok() {
         println!(r#"cargo:rustc-cfg=feature="no-bundled-uninstall""#);
@@ -8,4 +10,18 @@ fn main() {
         // Weakly link ScreenCaptureKit to ensure can be used on macOS 10.15+.
         println!("cargo:rustc-link-arg=-Wl,-weak_framework,ScreenCaptureKit");
     }
+
+    // Populate git sha environment variable if git is available
+    println!("cargo:rerun-if-changed=../../.git/logs/HEAD");
+    if let Some(output) = Command::new("git")
+        .args(["rev-parse", "HEAD"])
+        .output()
+        .ok()
+        .filter(|output| output.status.success())
+    {
+        let git_sha = String::from_utf8_lossy(&output.stdout);
+        let git_sha = git_sha.trim();
+
+        println!("cargo:rustc-env=ZED_COMMIT_SHA={git_sha}");
+    }
 }

crates/cli/src/main.rs 🔗

@@ -339,13 +339,17 @@ mod linux {
     impl InstalledApp for App {
         fn zed_version_string(&self) -> String {
             format!(
-                "Zed {}{} – {}",
+                "Zed {}{}{} – {}",
                 if *RELEASE_CHANNEL == "stable" {
                     "".to_string()
                 } else {
                     format!("{} ", *RELEASE_CHANNEL)
                 },
                 option_env!("RELEASE_VERSION").unwrap_or_default(),
+                match option_env!("ZED_COMMIT_SHA") {
+                    Some(commit_sha) => format!(" {commit_sha} "),
+                    None => "".to_string(),
+                },
                 self.0.display(),
             )
         }

crates/remote_server/build.rs 🔗

@@ -14,22 +14,17 @@ fn main() {
         std::env::var("TARGET").unwrap()
     );
 
-    // If we're building this for nightly, we want to set the ZED_COMMIT_SHA
-    if let Some(release_channel) = std::env::var("ZED_RELEASE_CHANNEL").ok() {
-        if release_channel.as_str() == "nightly" {
-            // Populate git sha environment variable if git is available
-            println!("cargo:rerun-if-changed=../../.git/logs/HEAD");
-            if let Some(output) = Command::new("git")
-                .args(["rev-parse", "HEAD"])
-                .output()
-                .ok()
-                .filter(|output| output.status.success())
-            {
-                let git_sha = String::from_utf8_lossy(&output.stdout);
-                let git_sha = git_sha.trim();
+    // Populate git sha environment variable if git is available
+    println!("cargo:rerun-if-changed=../../.git/logs/HEAD");
+    if let Some(output) = Command::new("git")
+        .args(["rev-parse", "HEAD"])
+        .output()
+        .ok()
+        .filter(|output| output.status.success())
+    {
+        let git_sha = String::from_utf8_lossy(&output.stdout);
+        let git_sha = git_sha.trim();
 
-                println!("cargo:rustc-env=ZED_COMMIT_SHA={git_sha}");
-            }
-        }
+        println!("cargo:rustc-env=ZED_COMMIT_SHA={git_sha}");
     }
 }

crates/remote_server/src/main.rs 🔗

@@ -40,6 +40,7 @@ fn main() {
 
 #[cfg(not(windows))]
 fn main() {
+    use release_channel::{ReleaseChannel, RELEASE_CHANNEL};
     use remote::proxy::ProxyLaunchError;
     use remote_server::unix::{execute_proxy, execute_run};
 
@@ -72,12 +73,15 @@ fn main() {
             }
         },
         Some(Commands::Version) => {
-            if let Some(build_sha) = option_env!("ZED_COMMIT_SHA") {
-                println!("{}", build_sha);
-            } else {
-                println!("{}", env!("ZED_PKG_VERSION"));
-            }
-
+            let release_channel = *RELEASE_CHANNEL;
+            match release_channel {
+                ReleaseChannel::Stable | ReleaseChannel::Preview => {
+                    println!("{}", env!("ZED_PKG_VERSION"))
+                }
+                ReleaseChannel::Nightly | ReleaseChannel::Dev => {
+                    println!("{}", env!("ZED_COMMIT_SHA"))
+                }
+            };
             std::process::exit(0);
         }
         None => {

crates/remote_server/src/unix.rs 🔗

@@ -16,7 +16,7 @@ use node_runtime::{NodeBinaryOptions, NodeRuntime};
 use paths::logs_dir;
 use project::project_settings::ProjectSettings;
 
-use release_channel::AppVersion;
+use release_channel::{AppVersion, ReleaseChannel, RELEASE_CHANNEL};
 use remote::proxy::ProxyLaunchError;
 use remote::ssh_session::ChannelClient;
 use remote::{
@@ -149,6 +149,14 @@ fn init_panic_hook() {
             (&backtrace).join("\n")
         );
 
+        let release_channel = *RELEASE_CHANNEL;
+        let version = match release_channel {
+            ReleaseChannel::Stable | ReleaseChannel::Preview => env!("ZED_PKG_VERSION"),
+            ReleaseChannel::Nightly | ReleaseChannel::Dev => {
+                option_env!("ZED_COMMIT_SHA").unwrap_or("missing-zed-commit-sha")
+            }
+        };
+
         let panic_data = telemetry_events::Panic {
             thread: thread_name.into(),
             payload: payload.clone(),
@@ -156,11 +164,9 @@ fn init_panic_hook() {
                 file: location.file().into(),
                 line: location.line(),
             }),
-            app_version: format!(
-                "remote-server-{}",
-                option_env!("ZED_COMMIT_SHA").unwrap_or(&env!("ZED_PKG_VERSION"))
-            ),
-            release_channel: release_channel::RELEASE_CHANNEL.display_name().into(),
+            app_version: format!("remote-server-{version}"),
+            app_commit_sha: option_env!("ZED_COMMIT_SHA").map(|sha| sha.into()),
+            release_channel: release_channel.display_name().into(),
             target: env!("TARGET").to_owned().into(),
             os_name: telemetry::os_name(),
             os_version: Some(telemetry::os_version()),

crates/telemetry_events/src/telemetry_events.rs 🔗

@@ -267,6 +267,9 @@ pub struct Panic {
     pub backtrace: Vec<String>,
     /// Zed version number
     pub app_version: String,
+    /// The Git commit SHA that Zed was built at.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub app_commit_sha: Option<String>,
     /// Zed release channel (stable, preview, dev)
     pub release_channel: String,
     pub target: Option<String>,

crates/zed/src/main.rs 🔗

@@ -188,9 +188,12 @@ fn main() {
     let session_id = Uuid::new_v4().to_string();
     let session = app.background_executor().block(Session::new());
     let app_version = AppVersion::init(env!("CARGO_PKG_VERSION"));
+    let app_commit_sha =
+        option_env!("ZED_COMMIT_SHA").map(|commit_sha| AppCommitSha(commit_sha.to_string()));
 
     reliability::init_panic_hook(
         app_version,
+        app_commit_sha.clone(),
         system_id.as_ref().map(|id| id.to_string()),
         installation_id.as_ref().map(|id| id.to_string()),
         session_id.clone(),
@@ -281,8 +284,8 @@ fn main() {
     app.run(move |cx| {
         release_channel::init(app_version, cx);
         gpui_tokio::init(cx);
-        if let Some(build_sha) = option_env!("ZED_COMMIT_SHA") {
-            AppCommitSha::set_global(AppCommitSha(build_sha.into()), cx);
+        if let Some(app_commit_sha) = app_commit_sha {
+            AppCommitSha::set_global(app_commit_sha, cx);
         }
         settings::init(cx);
         handle_settings_file_changes(user_settings_file_rx, cx, handle_settings_changed);

crates/zed/src/reliability.rs 🔗

@@ -8,7 +8,7 @@ use gpui::{App, SemanticVersion};
 use http_client::{self, HttpClient, HttpClientWithUrl, HttpRequestExt, Method};
 use paths::{crashes_dir, crashes_retired_dir};
 use project::Project;
-use release_channel::{ReleaseChannel, RELEASE_CHANNEL};
+use release_channel::{AppCommitSha, ReleaseChannel, RELEASE_CHANNEL};
 use settings::Settings;
 use smol::stream::StreamExt;
 use std::{
@@ -25,6 +25,7 @@ static PANIC_COUNT: AtomicU32 = AtomicU32::new(0);
 
 pub fn init_panic_hook(
     app_version: SemanticVersion,
+    app_commit_sha: Option<AppCommitSha>,
     system_id: Option<String>,
     installation_id: Option<String>,
     session_id: String,
@@ -54,12 +55,22 @@ pub fn init_panic_hook(
             let location = info.location().unwrap();
             let backtrace = Backtrace::new();
             eprintln!(
-                "Thread {:?} panicked with {:?} at {}:{}:{}\n{:?}",
+                "Thread {:?} panicked with {:?} at {}:{}:{}\n{}{:?}",
                 thread_name,
                 payload,
                 location.file(),
                 location.line(),
                 location.column(),
+                match app_commit_sha.as_ref() {
+                    Some(commit_sha) => format!(
+                        "https://github.com/zed-industries/zed/blob/{}/src/{}#L{} \
+                        (may not be uploaded, line may be incorrect if files modified)\n",
+                        commit_sha.0,
+                        location.file(),
+                        location.line()
+                    ),
+                    None => "".to_string(),
+                },
                 backtrace,
             );
             std::process::exit(-1);
@@ -103,6 +114,7 @@ pub fn init_panic_hook(
                 line: location.line(),
             }),
             app_version: app_version.to_string(),
+            app_commit_sha: app_commit_sha.as_ref().map(|sha| sha.0.clone()),
             release_channel: RELEASE_CHANNEL.dev_name().into(),
             target: env!("TARGET").to_owned().into(),
             os_name: telemetry::os_name(),