render workspace git context as xml

This commit is contained in:
pash
2026-01-30 11:24:18 -08:00
parent bdd376dfc9
commit 62ea1d6940
3 changed files with 42 additions and 27 deletions
+1 -1
View File
@@ -1874,7 +1874,7 @@ impl Session {
fn build_workspace_configuration(
cwd: &Path,
git_info: Option<&codex_protocol::protocol::GitInfo>,
remote_urls: Option<&Vec<String>>,
remote_urls: Option<&BTreeMap<String, String>>,
) -> Option<WorkspaceConfiguration> {
let latest_git_commit_hash = git_info.and_then(|info| info.commit_hash.clone());
let associated_remote_urls = remote_urls.cloned();
+35 -20
View File
@@ -72,7 +72,7 @@ pub(crate) struct WorkspaceConfiguration {
pub(crate) struct WorkspaceEntry {
pub hint: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub associated_remote_urls: Option<Vec<String>>,
pub associated_remote_urls: Option<BTreeMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub latest_git_commit_hash: Option<String>,
}
@@ -97,12 +97,31 @@ impl EnvironmentContext {
let shell_name = self.shell.name();
lines.push(format!(" <shell>{shell_name}</shell>"));
if let Some(workspace_configuration) = self.workspace_configuration
&& let Ok(json) = serde_json::to_string_pretty(&workspace_configuration)
{
if let Some(workspace_configuration) = self.workspace_configuration {
lines.push(" <workspace_configuration>".to_string());
for line in json.lines() {
lines.push(format!(" {line}"));
for (path, workspace) in workspace_configuration.workspaces {
lines.push(format!(
" <workspace path=\"{path}\" hint=\"{}\">",
workspace.hint
));
if let Some(latest_git_commit_hash) = workspace.latest_git_commit_hash {
lines.push(format!(
" <latest_git_commit_hash>{latest_git_commit_hash}</latest_git_commit_hash>"
));
}
if let Some(associated_remote_urls) = workspace.associated_remote_urls
&& !associated_remote_urls.is_empty()
{
lines.push(" <associated_remote_urls>".to_string());
for (name, url) in associated_remote_urls {
lines.push(format!(" <remote name=\"{name}\">{url}</remote>"));
}
lines.push(" </associated_remote_urls>".to_string());
}
lines.push(" </workspace>".to_string());
}
lines.push(" </workspace_configuration>".to_string());
}
@@ -257,9 +276,10 @@ mod tests {
cwd.to_string_lossy().to_string(),
WorkspaceEntry {
hint: "repo".to_string(),
associated_remote_urls: Some(vec![
"origin: https://example.com/repo.git".to_string(),
]),
associated_remote_urls: Some(BTreeMap::from([(
"origin".to_string(),
"https://example.com/repo.git".to_string(),
)])),
latest_git_commit_hash: Some("abc123".to_string()),
},
);
@@ -274,17 +294,12 @@ mod tests {
<cwd>/repo</cwd>
<shell>bash</shell>
<workspace_configuration>
{
"workspaces": {
"/repo": {
"hint": "repo",
"associated_remote_urls": [
"origin: https://example.com/repo.git"
],
"latest_git_commit_hash": "abc123"
}
}
}
<workspace path="/repo" hint="repo">
<latest_git_commit_hash>abc123</latest_git_commit_hash>
<associated_remote_urls>
<remote name="origin">https://example.com/repo.git</remote>
</associated_remote_urls>
</workspace>
</workspace_configuration>
</environment_context>"#;
+6 -6
View File
@@ -1,4 +1,4 @@
use std::collections::BTreeSet;
use std::collections::BTreeMap;
use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;
@@ -110,8 +110,8 @@ pub async fn collect_git_info(cwd: &Path) -> Option<GitInfo> {
Some(git_info)
}
/// Collect fetch remotes in a multi-root-friendly format: ["origin: https://..."].
pub async fn get_git_remote_urls(cwd: &Path) -> Option<Vec<String>> {
/// Collect fetch remotes in a multi-root-friendly format: {"origin": "https://..."}.
pub async fn get_git_remote_urls(cwd: &Path) -> Option<BTreeMap<String, String>> {
let is_git_repo = run_git_command_with_timeout(&["rev-parse", "--git-dir"], cwd)
.await?
.status
@@ -126,21 +126,21 @@ pub async fn get_git_remote_urls(cwd: &Path) -> Option<Vec<String>> {
}
let stdout = String::from_utf8(output.stdout).ok()?;
let mut remotes = BTreeSet::new();
let mut remotes = BTreeMap::new();
for line in stdout.lines() {
if !line.contains("(fetch)") {
continue;
}
let mut parts = line.split_whitespace();
if let (Some(name), Some(url)) = (parts.next(), parts.next()) {
remotes.insert(format!("{name}: {url}"));
remotes.insert(name.to_string(), url.to_string());
}
}
if remotes.is_empty() {
None
} else {
Some(remotes.into_iter().collect())
Some(remotes)
}
}