From fadb5f962d5efd107877e0d43380f72dffaca7df Mon Sep 17 00:00:00 2001 From: Tushar Date: Tue, 7 Apr 2026 09:04:55 +0530 Subject: [PATCH 1/2] feat(http): append debug request body to file --- crates/forge_app/src/infra.rs | 3 +++ crates/forge_fs/src/write.rs | 19 +++++++++++++++++++ crates/forge_infra/src/forge_infra.rs | 4 ++++ crates/forge_infra/src/fs_write.rs | 5 +++++ crates/forge_infra/src/http.rs | 10 +++++++++- crates/forge_repo/src/forge_repo.rs | 3 +++ .../forge_repo/src/provider/provider_repo.rs | 16 ++++++++++++++++ crates/forge_services/src/attachment.rs | 15 +++++++++++++++ 8 files changed, 74 insertions(+), 1 deletion(-) diff --git a/crates/forge_app/src/infra.rs b/crates/forge_app/src/infra.rs index bc57aa38dc..f81dc752e7 100644 --- a/crates/forge_app/src/infra.rs +++ b/crates/forge_app/src/infra.rs @@ -102,6 +102,9 @@ pub trait FileWriterInfra: Send + Sync { /// Writes the content of a file at the specified path. async fn write(&self, path: &Path, contents: Bytes) -> anyhow::Result<()>; + /// Appends content to a file at the specified path, creating it if it does not exist. + async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()>; + /// Writes content to a temporary file with the given prefix and extension, /// and returns its path. The file will be kept (not deleted) after /// creation. diff --git a/crates/forge_fs/src/write.rs b/crates/forge_fs/src/write.rs index e5019041bc..7972e6e94c 100644 --- a/crates/forge_fs/src/write.rs +++ b/crates/forge_fs/src/write.rs @@ -1,6 +1,7 @@ use std::path::Path; use anyhow::{Context, Result}; +use tokio::io::AsyncWriteExt as _; impl crate::ForgeFS { pub async fn create_dir_all>(path: T) -> Result<()> { @@ -15,6 +16,24 @@ impl crate::ForgeFS { .with_context(|| format!("Failed to write file {}", path.as_ref().display())) } + /// Appends content to an existing file, or creates it if it does not exist. + pub async fn append, U: AsRef<[u8]>>(path: T, contents: U) -> Result<()> { + let mut file = tokio::fs::OpenOptions::new() + .create(true) + .append(true) + .open(path.as_ref()) + .await + .with_context(|| { + format!( + "Failed to open file for appending {}", + path.as_ref().display() + ) + })?; + file.write_all(contents.as_ref()) + .await + .with_context(|| format!("Failed to append to file {}", path.as_ref().display())) + } + pub async fn remove_file>(path: T) -> Result<()> { tokio::fs::remove_file(path.as_ref()) .await diff --git a/crates/forge_infra/src/forge_infra.rs b/crates/forge_infra/src/forge_infra.rs index e5188c99b1..399584fe04 100644 --- a/crates/forge_infra/src/forge_infra.rs +++ b/crates/forge_infra/src/forge_infra.rs @@ -175,6 +175,10 @@ impl FileWriterInfra for ForgeInfra { self.file_write_service.write(path, contents).await } + async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> { + self.file_write_service.append(path, contents).await + } + async fn write_temp(&self, prefix: &str, ext: &str, content: &str) -> anyhow::Result { self.file_write_service .write_temp(prefix, ext, content) diff --git a/crates/forge_infra/src/fs_write.rs b/crates/forge_infra/src/fs_write.rs index b1836fe3b1..442a9a2529 100644 --- a/crates/forge_infra/src/fs_write.rs +++ b/crates/forge_infra/src/fs_write.rs @@ -38,6 +38,11 @@ impl FileWriterInfra for ForgeFileWriteService { Ok(forge_fs::ForgeFS::write(path, contents.to_vec()).await?) } + async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> { + self.create_parent_dirs(path).await?; + Ok(forge_fs::ForgeFS::append(path, contents.to_vec()).await?) + } + async fn write_temp(&self, prefix: &str, ext: &str, content: &str) -> anyhow::Result { let path = tempfile::Builder::new() .disable_cleanup(true) diff --git a/crates/forge_infra/src/http.rs b/crates/forge_infra/src/http.rs index 7bb0ca8cfb..a900eb1cad 100644 --- a/crates/forge_infra/src/http.rs +++ b/crates/forge_infra/src/http.rs @@ -231,7 +231,7 @@ impl ForgeHttpInfra { let body_clone = body.clone(); let debug_path = debug_path.clone(); tokio::spawn(async move { - let _ = file_writer.write(&debug_path, body_clone).await; + let _ = file_writer.append(&debug_path, body_clone).await; }); } } @@ -332,6 +332,14 @@ mod tests { Ok(()) } + async fn append(&self, path: &std::path::Path, contents: Bytes) -> anyhow::Result<()> { + self.writes + .lock() + .await + .push((path.to_path_buf(), contents)); + Ok(()) + } + async fn write_temp( &self, _prefix: &str, diff --git a/crates/forge_repo/src/forge_repo.rs b/crates/forge_repo/src/forge_repo.rs index 478aa26979..4fa8ebedf0 100644 --- a/crates/forge_repo/src/forge_repo.rs +++ b/crates/forge_repo/src/forge_repo.rs @@ -320,6 +320,9 @@ where async fn write(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> { self.infra.write(path, contents).await } + async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> { + self.infra.append(path, contents).await + } async fn write_temp(&self, prefix: &str, ext: &str, content: &str) -> anyhow::Result { self.infra.write_temp(prefix, ext, content).await } diff --git a/crates/forge_repo/src/provider/provider_repo.rs b/crates/forge_repo/src/provider/provider_repo.rs index 243aac260b..9bde6fa4c5 100644 --- a/crates/forge_repo/src/provider/provider_repo.rs +++ b/crates/forge_repo/src/provider/provider_repo.rs @@ -865,6 +865,14 @@ mod env_tests { Ok(()) } + async fn append( + &self, + _path: &std::path::Path, + _content: Bytes, + ) -> anyhow::Result<()> { + Ok(()) + } + async fn write_temp( &self, _prefix: &str, @@ -1343,6 +1351,14 @@ mod env_tests { Ok(()) } + async fn append( + &self, + _path: &std::path::Path, + _content: Bytes, + ) -> anyhow::Result<()> { + Ok(()) + } + async fn write_temp( &self, _prefix: &str, diff --git a/crates/forge_services/src/attachment.rs b/crates/forge_services/src/attachment.rs index ef6e515430..1ec1db7e7b 100644 --- a/crates/forge_services/src/attachment.rs +++ b/crates/forge_services/src/attachment.rs @@ -380,6 +380,21 @@ pub mod tests { Ok(()) } + async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> { + let mut existing = bytes::Bytes::new(); + let index = self.files.lock().unwrap().iter().position(|v| v.0 == path); + if let Some(index) = index { + existing = self.files.lock().unwrap().remove(index).1; + } + let mut combined = existing.to_vec(); + combined.extend_from_slice(&contents); + self.files + .lock() + .unwrap() + .push((path.to_path_buf(), combined.into())); + Ok(()) + } + async fn write_temp(&self, _: &str, _: &str, content: &str) -> anyhow::Result { let temp_dir = crate::utils::TempDir::new().unwrap(); let path = temp_dir.path(); From a0d92a3bcaa968da0a5eb66bfbe3737fd6f13550 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 03:36:44 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- crates/forge_app/src/infra.rs | 3 ++- crates/forge_repo/src/provider/provider_repo.rs | 12 ++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/crates/forge_app/src/infra.rs b/crates/forge_app/src/infra.rs index f81dc752e7..3d8d438af4 100644 --- a/crates/forge_app/src/infra.rs +++ b/crates/forge_app/src/infra.rs @@ -102,7 +102,8 @@ pub trait FileWriterInfra: Send + Sync { /// Writes the content of a file at the specified path. async fn write(&self, path: &Path, contents: Bytes) -> anyhow::Result<()>; - /// Appends content to a file at the specified path, creating it if it does not exist. + /// Appends content to a file at the specified path, creating it if it does + /// not exist. async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()>; /// Writes content to a temporary file with the given prefix and extension, diff --git a/crates/forge_repo/src/provider/provider_repo.rs b/crates/forge_repo/src/provider/provider_repo.rs index 9bde6fa4c5..2806ede74d 100644 --- a/crates/forge_repo/src/provider/provider_repo.rs +++ b/crates/forge_repo/src/provider/provider_repo.rs @@ -865,11 +865,7 @@ mod env_tests { Ok(()) } - async fn append( - &self, - _path: &std::path::Path, - _content: Bytes, - ) -> anyhow::Result<()> { + async fn append(&self, _path: &std::path::Path, _content: Bytes) -> anyhow::Result<()> { Ok(()) } @@ -1351,11 +1347,7 @@ mod env_tests { Ok(()) } - async fn append( - &self, - _path: &std::path::Path, - _content: Bytes, - ) -> anyhow::Result<()> { + async fn append(&self, _path: &std::path::Path, _content: Bytes) -> anyhow::Result<()> { Ok(()) }