mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Use AbsolutePathBuf for cwd state (#15710)
Migrate `cwd` and related session/config state to `AbsolutePathBuf` so downstream consumers consistently see absolute working directories. Add test-only `.abs()` helpers for `Path`, `PathBuf`, and `TempDir`, and update branch-local tests to use them instead of `AbsolutePathBuf::try_from(...)`. For the remaining TUI/app-server snapshot coverage that renders absolute cwd values, keep the snapshots unchanged and skip the Windows-only cases where the platform-specific absolute path layout differs.
This commit is contained in:
committed by
GitHub
Unverified
parent
178c3b15b4
commit
504aeb0e09
@@ -159,11 +159,11 @@ impl ChatWidget {
|
||||
cwd: PathBuf,
|
||||
result: Result<PluginListResponse, String>,
|
||||
) {
|
||||
if self.plugins_fetch_state.in_flight_cwd.as_ref() == Some(&cwd) {
|
||||
if self.plugins_fetch_state.in_flight_cwd.as_deref() == Some(cwd.as_path()) {
|
||||
self.plugins_fetch_state.in_flight_cwd = None;
|
||||
}
|
||||
|
||||
if self.config.cwd != cwd {
|
||||
if self.config.cwd.as_path() != cwd.as_path() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,13 +191,13 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn prefetch_plugins(&mut self) {
|
||||
let cwd = self.config.cwd.clone();
|
||||
if self.plugins_fetch_state.in_flight_cwd.as_ref() == Some(&cwd) {
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
if self.plugins_fetch_state.in_flight_cwd.as_deref() == Some(cwd.as_path()) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.plugins_fetch_state.in_flight_cwd = Some(cwd.clone());
|
||||
if self.plugins_fetch_state.cache_cwd.as_ref() != Some(&cwd) {
|
||||
if self.plugins_fetch_state.cache_cwd.as_deref() != Some(cwd.as_path()) {
|
||||
self.plugins_cache = PluginsCacheState::Loading;
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn plugins_cache_for_current_cwd(&self) -> PluginsCacheState {
|
||||
if self.plugins_fetch_state.cache_cwd.as_ref() == Some(&self.config.cwd) {
|
||||
if self.plugins_fetch_state.cache_cwd.as_deref() == Some(self.config.cwd.as_path()) {
|
||||
self.plugins_cache.clone()
|
||||
} else {
|
||||
PluginsCacheState::Uninitialized
|
||||
@@ -253,7 +253,7 @@ impl ChatWidget {
|
||||
cwd: PathBuf,
|
||||
result: Result<PluginReadResponse, String>,
|
||||
) {
|
||||
if self.config.cwd != cwd {
|
||||
if self.config.cwd.as_path() != cwd.as_path() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ impl ChatWidget {
|
||||
plugin_display_name: String,
|
||||
result: Result<PluginInstallResponse, String>,
|
||||
) -> bool {
|
||||
if self.config.cwd != cwd {
|
||||
if self.config.cwd.as_path() != cwd.as_path() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ impl ChatWidget {
|
||||
plugin_display_name: String,
|
||||
result: Result<PluginUninstallResponse, String>,
|
||||
) {
|
||||
if self.config.cwd != cwd {
|
||||
if self.config.cwd.as_path() != cwd.as_path() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -676,7 +676,7 @@ impl ChatWidget {
|
||||
..Default::default()
|
||||
}];
|
||||
if let Some(plugins_response) = plugins_response.cloned() {
|
||||
let cwd = self.config.cwd.clone();
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
items.push(SelectionItem {
|
||||
name: "Back to plugins".to_string(),
|
||||
description: Some("Return to the plugin list.".to_string()),
|
||||
@@ -770,7 +770,7 @@ impl ChatWidget {
|
||||
"{display_name} {} {} {}",
|
||||
plugin.id, plugin.name, marketplace_label
|
||||
);
|
||||
let cwd = self.config.cwd.clone();
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
let plugin_display_name = display_name.clone();
|
||||
let marketplace_path = marketplace.path.clone();
|
||||
let plugin_name = plugin.name.clone();
|
||||
@@ -861,7 +861,7 @@ impl ChatWidget {
|
||||
header.push(Line::from(description.dim()));
|
||||
}
|
||||
|
||||
let cwd = self.config.cwd.clone();
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
let plugins_response = plugins_response.clone();
|
||||
let mut items = vec![SelectionItem {
|
||||
name: "Back to plugins".to_string(),
|
||||
@@ -877,7 +877,7 @@ impl ChatWidget {
|
||||
}];
|
||||
|
||||
if plugin.summary.installed {
|
||||
let uninstall_cwd = self.config.cwd.clone();
|
||||
let uninstall_cwd = self.config.cwd.to_path_buf();
|
||||
let plugin_id = plugin.summary.id.clone();
|
||||
let plugin_display_name = display_name;
|
||||
items.push(SelectionItem {
|
||||
@@ -906,7 +906,7 @@ impl ChatWidget {
|
||||
..Default::default()
|
||||
});
|
||||
} else {
|
||||
let install_cwd = self.config.cwd.clone();
|
||||
let install_cwd = self.config.cwd.to_path_buf();
|
||||
let marketplace_path = plugin.marketplace_path.clone();
|
||||
let plugin_name = plugin.summary.name.clone();
|
||||
let plugin_display_name = display_name;
|
||||
|
||||
@@ -326,7 +326,9 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn status_line_cwd(&self) -> &Path {
|
||||
self.current_cwd.as_ref().unwrap_or(&self.config.cwd)
|
||||
self.current_cwd
|
||||
.as_deref()
|
||||
.unwrap_or(self.config.cwd.as_path())
|
||||
}
|
||||
|
||||
/// Resolves the project root associated with `cwd`.
|
||||
|
||||
@@ -16,6 +16,8 @@ use crate::bottom_pane::MentionBinding;
|
||||
use crate::chatwidget::realtime::RealtimeConversationPhase;
|
||||
use crate::history_cell::UserHistoryCell;
|
||||
use crate::test_backend::VT100Backend;
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::test_support::test_path_display;
|
||||
use crate::tui::FrameRequester;
|
||||
use assert_matches::assert_matches;
|
||||
use codex_app_server_protocol::AppSummary;
|
||||
@@ -434,10 +436,10 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::new_workspace_write_policy())
|
||||
.expect("set sandbox policy");
|
||||
chat.config.cwd = PathBuf::from("/home/user/main");
|
||||
chat.config.cwd = PathBuf::from("/home/user/main").abs();
|
||||
|
||||
let expected_sandbox = SandboxPolicy::new_read_only_policy();
|
||||
let expected_cwd = PathBuf::from("/home/user/sub-agent");
|
||||
let expected_cwd = PathBuf::from("/home/user/sub-agent").abs();
|
||||
let configured = codex_protocol::protocol::SessionConfiguredEvent {
|
||||
session_id: ThreadId::new(),
|
||||
forked_from_id: None,
|
||||
@@ -448,7 +450,7 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
sandbox_policy: expected_sandbox.clone(),
|
||||
cwd: expected_cwd.clone(),
|
||||
cwd: expected_cwd.to_path_buf(),
|
||||
reasoning_effort: Some(ReasoningEffortConfig::default()),
|
||||
history_log_id: 0,
|
||||
history_entry_count: 0,
|
||||
@@ -5801,7 +5803,7 @@ async fn slash_init_skips_when_project_doc_exists() {
|
||||
let tempdir = tempdir().unwrap();
|
||||
let existing_path = tempdir.path().join(DEFAULT_PROJECT_DOC_FILENAME);
|
||||
std::fs::write(&existing_path, "existing instructions").unwrap();
|
||||
chat.config.cwd = tempdir.path().to_path_buf();
|
||||
chat.config.cwd = tempdir.path().to_path_buf().abs();
|
||||
|
||||
chat.dispatch_command(SlashCommand::Init);
|
||||
|
||||
@@ -6822,13 +6824,17 @@ async fn custom_prompt_enter_empty_does_not_send() {
|
||||
#[tokio::test]
|
||||
async fn view_image_tool_call_adds_history_cell() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
let image_path = chat.config.cwd.join("example.png");
|
||||
let image_path = chat
|
||||
.config
|
||||
.cwd
|
||||
.join("example.png")
|
||||
.expect("absolute image path");
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "sub-image".into(),
|
||||
msg: EventMsg::ViewImageToolCall(ViewImageToolCallEvent {
|
||||
call_id: "call-image".into(),
|
||||
path: image_path,
|
||||
path: image_path.to_path_buf(),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -7118,12 +7124,10 @@ fn strip_osc8_for_snapshot(text: &str) -> String {
|
||||
}
|
||||
|
||||
fn plugins_test_absolute_path(path: &str) -> AbsolutePathBuf {
|
||||
AbsolutePathBuf::try_from(
|
||||
std::env::temp_dir()
|
||||
.join("codex-plugin-menu-tests")
|
||||
.join(path),
|
||||
)
|
||||
.expect("expected absolute test path")
|
||||
std::env::temp_dir()
|
||||
.join("codex-plugin-menu-tests")
|
||||
.join(path)
|
||||
.abs()
|
||||
}
|
||||
|
||||
fn plugins_test_interface(
|
||||
@@ -7205,7 +7209,7 @@ fn plugins_test_response(marketplaces: Vec<PluginMarketplaceEntry>) -> PluginLis
|
||||
|
||||
fn render_loaded_plugins_popup(chat: &mut ChatWidget, response: PluginListResponse) -> String {
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd, Ok(response));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(response));
|
||||
chat.add_plugins_output();
|
||||
render_bottom_popup(chat, 100)
|
||||
}
|
||||
@@ -7356,10 +7360,10 @@ async fn plugin_detail_popup_snapshot_shows_install_actions_and_capability_summa
|
||||
summary.clone(),
|
||||
])]);
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd.clone(), Ok(response));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(response));
|
||||
chat.add_plugins_output();
|
||||
chat.on_plugin_detail_loaded(
|
||||
cwd,
|
||||
cwd.to_path_buf(),
|
||||
Ok(PluginReadResponse {
|
||||
plugin: plugins_test_detail(
|
||||
summary,
|
||||
@@ -7396,10 +7400,10 @@ async fn plugin_detail_popup_hides_disclosure_for_installed_plugins() {
|
||||
summary.clone(),
|
||||
])]);
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd.clone(), Ok(response));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(response));
|
||||
chat.add_plugins_output();
|
||||
chat.on_plugin_detail_loaded(
|
||||
cwd,
|
||||
cwd.to_path_buf(),
|
||||
Ok(PluginReadResponse {
|
||||
plugin: plugins_test_detail(
|
||||
summary,
|
||||
@@ -7486,7 +7490,7 @@ async fn plugins_popup_refresh_replaces_selection_with_first_row() {
|
||||
),
|
||||
])]);
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd, Ok(refreshed));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(refreshed));
|
||||
|
||||
let after = render_bottom_popup(&chat, 100);
|
||||
assert!(
|
||||
@@ -7555,7 +7559,7 @@ async fn plugins_popup_refreshes_installed_counts_after_install() {
|
||||
),
|
||||
])]);
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd, Ok(refreshed));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(refreshed));
|
||||
|
||||
let after = render_bottom_popup(&chat, 100);
|
||||
assert!(
|
||||
@@ -8271,8 +8275,7 @@ async fn apps_initial_load_applies_enabled_state_from_config() {
|
||||
chat.bottom_pane.set_connectors_enabled(true);
|
||||
|
||||
let temp = tempdir().expect("tempdir");
|
||||
let config_toml_path =
|
||||
AbsolutePathBuf::try_from(temp.path().join("config.toml")).expect("absolute config path");
|
||||
let config_toml_path = temp.path().join("config.toml").abs();
|
||||
let user_config = toml::from_str::<TomlValue>(
|
||||
"[apps.connector_1]\nenabled = false\ndisabled_reason = \"user\"\n",
|
||||
)
|
||||
@@ -8336,8 +8339,7 @@ async fn apps_initial_load_applies_enabled_state_from_requirements_with_user_ove
|
||||
..Default::default()
|
||||
};
|
||||
let temp = tempdir().expect("tempdir");
|
||||
let config_toml_path =
|
||||
AbsolutePathBuf::try_from(temp.path().join("config.toml")).expect("absolute config path");
|
||||
let config_toml_path = temp.path().join("config.toml").abs();
|
||||
chat.config.config_layer_stack =
|
||||
ConfigLayerStack::new(Vec::new(), ConfigRequirements::default(), requirements)
|
||||
.expect("requirements stack")
|
||||
@@ -8912,7 +8914,7 @@ async fn preset_matching_accepts_workspace_write_with_extra_roots() {
|
||||
.find(|p| p.id == "auto")
|
||||
.expect("auto preset exists");
|
||||
let current_sandbox = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![AbsolutePathBuf::try_from("C:\\extra").unwrap()],
|
||||
writable_roots: vec![PathBuf::from("C:\\extra").abs()],
|
||||
read_only_access: Default::default(),
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: false,
|
||||
@@ -9716,8 +9718,7 @@ async fn permissions_selection_marks_guardian_approvals_current_with_custom_work
|
||||
.features
|
||||
.set_enabled(Feature::GuardianApproval, true);
|
||||
|
||||
let extra_root = AbsolutePathBuf::try_from("/tmp/guardian-approvals-extra")
|
||||
.expect("absolute extra writable root");
|
||||
let extra_root = PathBuf::from("/tmp/guardian-approvals-extra").abs();
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "session-configured-custom-workspace".to_string(),
|
||||
@@ -11627,7 +11628,7 @@ async fn default_terminal_title_refreshes_when_spinner_state_changes() {
|
||||
let cwd = chat
|
||||
.current_cwd
|
||||
.clone()
|
||||
.unwrap_or_else(|| chat.config.cwd.clone());
|
||||
.unwrap_or_else(|| chat.config.cwd.to_path_buf());
|
||||
let project = get_git_repo_root(&cwd)
|
||||
.map(|root| {
|
||||
root.file_name()
|
||||
@@ -11866,7 +11867,7 @@ async fn status_line_fast_mode_footer_snapshot() {
|
||||
#[tokio::test]
|
||||
async fn status_line_model_with_reasoning_includes_fast_for_gpt54_only() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.config.cwd = PathBuf::from("/tmp/project");
|
||||
chat.config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
chat.config.tui_status_line = Some(vec![
|
||||
"model-with-reasoning".to_string(),
|
||||
"context-remaining".to_string(),
|
||||
@@ -11876,10 +11877,11 @@ async fn status_line_model_with_reasoning_includes_fast_for_gpt54_only() {
|
||||
chat.set_service_tier(Some(ServiceTier::Fast));
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.refresh_status_surfaces();
|
||||
let test_cwd = test_path_display("/tmp/project");
|
||||
|
||||
assert_eq!(
|
||||
status_line_text(&chat),
|
||||
Some("gpt-5.4 xhigh fast · 100% left · /tmp/project".to_string())
|
||||
Some(format!("gpt-5.4 xhigh fast · 100% left · {test_cwd}"))
|
||||
);
|
||||
|
||||
chat.set_model("gpt-5.3-codex");
|
||||
@@ -11887,18 +11889,22 @@ async fn status_line_model_with_reasoning_includes_fast_for_gpt54_only() {
|
||||
|
||||
assert_eq!(
|
||||
status_line_text(&chat),
|
||||
Some("gpt-5.3-codex xhigh · 100% left · /tmp/project".to_string())
|
||||
Some(format!("gpt-5.3-codex xhigh · 100% left · {test_cwd}"))
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(
|
||||
target_os = "windows",
|
||||
ignore = "snapshot path rendering differs on Windows"
|
||||
)]
|
||||
async fn status_line_model_with_reasoning_fast_footer_snapshot() {
|
||||
use ratatui::Terminal;
|
||||
use ratatui::backend::TestBackend;
|
||||
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.show_welcome_banner = false;
|
||||
chat.config.cwd = PathBuf::from("/tmp/project");
|
||||
chat.config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
chat.config.tui_status_line = Some(vec![
|
||||
"model-with-reasoning".to_string(),
|
||||
"context-remaining".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user