feat: when writing HTML report pages, write temp file and rename

This avoids creating an empty file if the disk is full.
This commit is contained in:
Lars Wirzenius
2025-09-17 15:35:20 +03:00
parent 4610dcfd3a
commit 62f1ded7e6
2 changed files with 32 additions and 4 deletions
+4 -4
View File
@@ -9,7 +9,6 @@
use std::{
collections::{HashMap, HashSet},
fs::write,
path::{Path, PathBuf},
sync::mpsc::RecvTimeoutError,
};
@@ -33,7 +32,7 @@ use crate::{
msg::{RunId, RunResult},
notif::NotificationReceiver,
run::{Run, RunState, Whence},
util::{parse_timestamp, rfc822_timestamp},
util::{parse_timestamp, rfc822_timestamp, safely_overwrite},
worker::Worker,
};
@@ -53,7 +52,7 @@ pub enum PageError {
Timeformat(#[from] time::error::Format),
#[error("failed to write status page to {0}")]
Write(PathBuf, #[source] std::io::Error),
Write(PathBuf, #[source] crate::util::UtilError),
#[error("no node alias has been set for builder")]
NoAlias,
@@ -972,7 +971,8 @@ impl StatusPage {
}
fn write_file(filename: &Path, text: &str) -> Result<(), PageError> {
write(filename, text).map_err(|e| PageError::Write(filename.into(), e))?;
safely_overwrite(filename, text.as_bytes())
.map_err(|err| PageError::Write(filename.into(), err))?;
Ok(())
}
}
+28
View File
@@ -1,8 +1,10 @@
use std::{
io::Write,
path::{Path, PathBuf},
str::FromStr,
};
use tempfile::NamedTempFile;
use time::{
format_description::{well_known::Rfc2822, FormatItem},
macros::format_description,
@@ -138,6 +140,20 @@ pub fn read_file_as_objectid(filename: &Path) -> Result<ObjectId, UtilError> {
ObjectId::from_str(s.trim()).map_err(|err| UtilError::ReadObjectId(filename.into(), err))
}
pub fn safely_overwrite<P: AsRef<Path>>(filename: P, data: &[u8]) -> Result<(), UtilError> {
let filename = filename.as_ref();
let dirname = filename
.parent()
.ok_or(UtilError::NoParent(filename.to_path_buf()))?;
let mut tmp = NamedTempFile::new_in(dirname)
.map_err(|err| UtilError::CreateTemp(dirname.to_path_buf(), err))?;
tmp.write_all(data)
.map_err(|err| UtilError::WriteTemp(dirname.to_path_buf(), err))?;
tmp.persist(filename)
.map_err(|err| UtilError::RenameTemp(filename.to_path_buf(), err))?;
Ok(())
}
#[derive(Debug, thiserror::Error)]
pub enum UtilError {
#[error("failed to look up node profile")]
@@ -178,4 +194,16 @@ pub enum UtilError {
#[error("failed to read object id from {0}")]
ReadObjectId(PathBuf, #[source] radicle::cob::object::ParseObjectId),
#[error("file name to write to doesn't have a parent directory: {0}")]
NoParent(PathBuf),
#[error("failed to create temporary file in directory {0}")]
CreateTemp(PathBuf, #[source] std::io::Error),
#[error("failed to write to temporary file in {0}")]
WriteTemp(PathBuf, #[source] std::io::Error),
#[error("failed to rename temporary file to {0}")]
RenameTemp(PathBuf, #[source] tempfile::PersistError),
}