Skip to content

Commit

Permalink
copilot: Fix onboarding into Copilot requires Zed restart (#26330)
Browse files Browse the repository at this point in the history
Closes #25594

This PR fixes an issue where signing into Copilot required restarting
Zed.

Copilot depends on an OAuth token that comes from either `hosts.json` or
`apps.json`. Initially, both files don't exist. If neither file is
found, we fallback to watching `hosts.json` for updates. However, if the
auth process creates `apps.json`, we won't receive updates from it,
causing the UI to remain outdated.

This PR fixes that by watching the parent `github-copilot` directory
instead, which will always contain one of those files along with an
additional version file.

I have tested this on macOS and Linux Wayland.

Release Notes:

- Fixed an issue where signing into Copilot required restarting Zed.
  • Loading branch information
smitbarmase authored Mar 8, 2025
1 parent 22d9b5d commit f14d667
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 20 deletions.
30 changes: 11 additions & 19 deletions crates/copilot/src/copilot_chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ use std::sync::OnceLock;

use anyhow::{anyhow, Result};
use chrono::DateTime;
use collections::HashSet;
use fs::Fs;
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, StreamExt};
use gpui::{prelude::*, App, AsyncApp, Global};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use paths::home_dir;
use serde::{Deserialize, Serialize};
use settings::watch_config_file;
use settings::watch_config_dir;
use strum::EnumIter;

pub const COPILOT_CHAT_COMPLETION_URL: &str = "https://api.githubcopilot.com/chat/completions";
Expand Down Expand Up @@ -237,27 +238,18 @@ impl CopilotChat {
}

pub fn new(fs: Arc<dyn Fs>, client: Arc<dyn HttpClient>, cx: &App) -> Self {
let config_paths = copilot_chat_config_paths();

let resolve_config_path = {
let fs = fs.clone();
async move {
for config_path in config_paths.iter() {
if fs.metadata(config_path).await.is_ok_and(|v| v.is_some()) {
return config_path.clone();
}
}
config_paths[0].clone()
}
};
let config_paths: HashSet<PathBuf> = copilot_chat_config_paths().into_iter().collect();
let dir_path = copilot_chat_config_dir();

cx.spawn(|cx| async move {
let config_file = resolve_config_path.await;
let mut config_file_rx = watch_config_file(cx.background_executor(), fs, config_file);

while let Some(contents) = config_file_rx.next().await {
let mut parent_watch_rx = watch_config_dir(
cx.background_executor(),
fs.clone(),
dir_path.clone(),
config_paths,
);
while let Some(contents) = parent_watch_rx.next().await {
let oauth_token = extract_oauth_token(contents);

cx.update(|cx| {
if let Some(this) = Self::global(cx).as_ref() {
this.update(cx, |this, cx| {
Expand Down
52 changes: 51 additions & 1 deletion crates/settings/src/settings_file.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{settings_store::SettingsStore, Settings};
use fs::Fs;
use collections::HashSet;
use fs::{Fs, PathEventKind};
use futures::{channel::mpsc, StreamExt};
use gpui::{App, BackgroundExecutor, ReadGlobal};
use std::{path::PathBuf, sync::Arc, time::Duration};
Expand Down Expand Up @@ -78,6 +79,55 @@ pub fn watch_config_file(
rx
}

pub fn watch_config_dir(
executor: &BackgroundExecutor,
fs: Arc<dyn Fs>,
dir_path: PathBuf,
config_paths: HashSet<PathBuf>,
) -> mpsc::UnboundedReceiver<String> {
let (tx, rx) = mpsc::unbounded();
executor
.spawn(async move {
for file_path in &config_paths {
if fs.metadata(file_path).await.is_ok_and(|v| v.is_some()) {
if let Ok(contents) = fs.load(file_path).await {
if tx.unbounded_send(contents).is_err() {
return;
}
}
}
}

let (events, _) = fs.watch(&dir_path, Duration::from_millis(100)).await;
futures::pin_mut!(events);

while let Some(event_batch) = events.next().await {
for event in event_batch {
if config_paths.contains(&event.path) {
match event.kind {
Some(PathEventKind::Removed) => {
if tx.unbounded_send(String::new()).is_err() {
return;
}
}
Some(PathEventKind::Created) | Some(PathEventKind::Changed) => {
if let Ok(contents) = fs.load(&event.path).await {
if tx.unbounded_send(contents).is_err() {
return;
}
}
}
_ => {}
}
}
}
}
})
.detach();

rx
}

pub fn update_settings_file<T: Settings>(
fs: Arc<dyn Fs>,
cx: &App,
Expand Down

0 comments on commit f14d667

Please sign in to comment.