Stop writing legacy run status and conclusion files

This commit is contained in:
Bryan Helmkamp 2026-04-01 20:27:25 -07:00
parent 15389780f3
commit c41594d3a1
No known key found for this signature in database
5 changed files with 45 additions and 71 deletions

View file

@ -1,6 +1,6 @@
use fabro_test::{fabro_snapshot, test_context};
use crate::support::{example_fixture, fabro_json_snapshot, read_json};
use crate::support::{example_fixture, fabro_json_snapshot};
use super::support::{output_stdout, resolve_run, wait_for_status, write_gated_workflow};
@ -58,21 +58,21 @@ fn start_by_run_id_starts_created_run() {
.assert()
.success();
let run_dir = context.find_run_dir(run_id);
let status = read_json(run_dir.join("status.json"));
let conclusion = read_json(run_dir.join("conclusion.json"));
let output = context
.command()
.args(["wait", "--json", run_id])
.output()
.expect("wait should execute");
assert!(output.status.success(), "wait should succeed");
let value: serde_json::Value = serde_json::from_slice(&output.stdout).expect("wait JSON");
fabro_json_snapshot!(
context,
serde_json::json!({
"status": status["status"],
"reason": status["reason"],
"conclusion_status": conclusion["status"],
"status": value["status"],
}),
@r#"
{
"status": "succeeded",
"reason": "completed",
"conclusion_status": "success"
"status": "succeeded"
}
"#
);
@ -98,25 +98,24 @@ fn start_by_run_id_starts_created_run_without_run_json_or_status_json() {
let run_dir = context.find_run_dir(run_id);
std::fs::remove_file(run_dir.join("run.json")).unwrap();
std::fs::remove_file(run_dir.join("status.json")).unwrap();
context.command().args(["start", run_id]).assert().success();
context
let output = context
.command()
.args(["wait", run_id])
.args(["wait", "--json", run_id])
.timeout(std::time::Duration::from_secs(10))
.assert()
.success();
let conclusion = read_json(run_dir.join("conclusion.json"));
.output()
.expect("wait should execute");
assert!(output.status.success(), "wait should succeed");
let value: serde_json::Value = serde_json::from_slice(&output.stdout).expect("wait JSON");
fabro_json_snapshot!(
context,
serde_json::json!({
"conclusion_status": conclusion["status"],
"status": value["status"],
}),
@r#"
{
"conclusion_status": "success"
"status": "succeeded"
}
"#
);
@ -190,13 +189,20 @@ digraph Smoke {
.assert()
.success();
let new_run_dir = context.find_run_dir(new_run_id);
let status = read_json(new_run_dir.join("status.json"));
fabro_json_snapshot!(context, &status, @r#"
let output = context
.command()
.args(["wait", "--json", new_run_id])
.output()
.expect("wait should execute");
assert!(output.status.success(), "wait should succeed");
let status: serde_json::Value = serde_json::from_slice(&output.stdout).expect("wait JSON");
fabro_json_snapshot!(context, &serde_json::json!({
"run_id": status["run_id"],
"status": status["status"],
}), @r#"
{
"status": "succeeded",
"reason": "completed",
"updated_at": "[TIMESTAMP]"
"run_id": "[ULID]",
"status": "succeeded"
}
"#);
}

View file

@ -15,7 +15,7 @@ use crate::pipeline::types::PersistOptions;
use crate::pipeline::{self, Persisted, TransformOptions, Validated};
use crate::records::RunRecord;
use crate::run_lookup::default_runs_base;
use crate::run_status::{RunStatus, RunStatusRecord, write_run_status};
use crate::run_status::{RunStatus, RunStatusRecord};
use crate::transforms::{Transform, expand_vars};
use fabro_sandbox::daytona::detect_repo_info;
@ -120,7 +120,6 @@ pub async fn create(store: &dyn Store, request: CreateRunInput) -> Result<Create
)?;
write_run_config_snapshot(&run_dir, resolved.workflow_toml_path.as_deref())?;
write_run_status(&run_dir, RunStatus::Submitted, None);
emit_run_created_event(
&persisted,
&resolved.raw_source,
@ -451,8 +450,6 @@ mod tests {
use std::time::Duration;
use crate::operations::{ValidateInput, validate};
use crate::run_status::RunStatusRecordExt;
fn memory_store() -> InMemoryStore {
InMemoryStore::default()
}
@ -752,10 +749,9 @@ mod tests {
created.persisted.run_record().workflow_slug.as_deref(),
Some("slug")
);
let run_store = store.open_run(&fixtures::RUN_1).await.unwrap().unwrap();
assert_eq!(
crate::run_status::RunStatusRecord::load(&created.run_dir.join("status.json"))
.unwrap()
.status,
run_store.get_status().await.unwrap().unwrap().status,
crate::run_status::RunStatus::Submitted
);
assert!(!created.run_dir.join("id.txt").exists());

View file

@ -46,7 +46,6 @@ pub async fn resume(run_dir: &Path, services: StartServices) -> Result<Started,
.ok_or_else(|| FabroError::Precondition("no checkpoint to resume from".to_string()))?;
cleanup_resume_artifacts(run_dir);
run_status::write_run_status(run_dir, RunStatus::Submitted, None);
services
.run_store
.put_status(&run_status::RunStatusRecord::new(

View file

@ -30,7 +30,7 @@ use crate::pipeline::{
PullRequestOptions, RetroOptions, SandboxEnvSpec, build_conclusion_from_store,
classify_engine_result,
};
use crate::records::{Checkpoint, Conclusion, ConclusionExt, RunRecord, RunRecordExt};
use crate::records::{Checkpoint, Conclusion, RunRecord, RunRecordExt};
use crate::run_options::{GitCheckpointOptions, LifecycleOptions, RunOptions};
use crate::run_status::{self, RunStatus, StatusReason};
use fabro_config::run::PullRequestSettings;
@ -600,7 +600,6 @@ impl RunSession {
}
struct DetachedRunBootstrapGuard {
run_dir: PathBuf,
run_store: Arc<dyn RunStore>,
cancel_token: Option<Arc<AtomicBool>>,
active: bool,
@ -608,17 +607,11 @@ struct DetachedRunBootstrapGuard {
impl DetachedRunBootstrapGuard {
fn arm(
run_dir: &Path,
_run_dir: &Path,
run_store: Arc<dyn RunStore>,
cancel_token: Option<Arc<AtomicBool>>,
) -> Self {
run_status::write_run_status(
run_dir,
RunStatus::Starting,
Some(StatusReason::SandboxInitializing),
);
Self {
run_dir: run_dir.to_path_buf(),
run_store,
cancel_token,
active: true,
@ -642,7 +635,6 @@ impl Drop for DetachedRunBootstrapGuard {
} else {
StatusReason::SandboxInitFailed
};
run_status::write_run_status(&self.run_dir, RunStatus::Failed, Some(reason));
let run_store = Arc::clone(&self.run_store);
if let Ok(handle) = Handle::try_current() {
handle.spawn(async move {
@ -715,10 +707,6 @@ impl Drop for DetachedRunCompletionGuard {
"postrun_aborted"
};
run_status::write_run_status(&self.run_dir, RunStatus::Failed, Some(reason));
if !self.run_dir.join("conclusion.json").exists() {
let _ = write_failure_conclusion(&self.run_dir, message, Some(reason));
}
let serialized_notice = load_run_id(&self.run_dir).and_then(|run_id| {
let envelope = canonicalize_event(
&run_id,
@ -832,8 +820,7 @@ async fn persist_detached_failure(
)
.map_err(|err| FabroError::Io(err.to_string()))?;
let conclusion = write_failure_conclusion(run_dir, &message, Some(reason))?;
run_status::write_run_status(run_dir, RunStatus::Failed, Some(reason));
let conclusion = build_failure_conclusion(&message);
if let Err(err) = run_store.put_conclusion(&conclusion).await {
tracing::warn!(error = %err, "Failed to save detached failure conclusion to store");
}
@ -872,20 +859,6 @@ async fn persist_detached_failure(
Ok(())
}
fn write_failure_conclusion(
run_dir: &Path,
message: &str,
_reason: Option<StatusReason>,
) -> Result<Conclusion, FabroError> {
if run_dir.join("conclusion.json").exists() {
return Conclusion::load(&run_dir.join("conclusion.json"));
}
let conclusion = build_failure_conclusion(message);
conclusion.save(&run_dir.join("conclusion.json"))?;
Ok(conclusion)
}
fn build_failure_conclusion(message: &str) -> Conclusion {
Conclusion {
timestamp: Utc::now(),
@ -922,7 +895,7 @@ mod tests {
use crate::handler::exit::ExitHandler;
use crate::handler::start::StartHandler;
use crate::operations::resume;
use crate::records::CheckpointExt;
use crate::records::{CheckpointExt, ConclusionExt};
const MINIMAL_DOT: &str = r#"digraph Test {
graph [goal="Build feature"]
@ -1044,7 +1017,8 @@ mod tests {
.unwrap();
assert_eq!(started.finalized.conclusion.status, StageStatus::Success);
assert!(run_dir.join("conclusion.json").exists());
let run_store = store.open_run(&fixtures::RUN_1).await.unwrap().unwrap();
assert!(run_store.get_conclusion().await.unwrap().is_some());
}
#[tokio::test]

View file

@ -5,9 +5,9 @@ use crate::error::FabroError;
use crate::event::{EventEmitter, RunNoticeLevel, WorkflowRunEvent};
use crate::git::{MetadataStore, scan_node_files_from_store};
use crate::outcome::{Outcome, OutcomeExt, StageStatus};
use crate::records::{Checkpoint, CheckpointExt, Conclusion, ConclusionExt, StageSummary};
use crate::records::{Checkpoint, CheckpointExt, Conclusion, StageSummary};
use crate::run_options::RunOptions;
use crate::run_status::{RunStatus, StatusReason, write_run_status};
use crate::run_status::{RunStatus, StatusReason};
use crate::sandbox_git::git_push_host;
use fabro_hooks::{HookContext, HookEvent, HookRunner};
use fabro_retro::retro::extract_stage_durations;
@ -249,13 +249,12 @@ fn build_conclusion_from_parts(
}
pub fn persist_terminal_outcome(
run_dir: &Path,
_run_dir: &Path,
conclusion: &Conclusion,
run_status: RunStatus,
status_reason: Option<StatusReason>,
) {
let _ = conclusion.save(&run_dir.join("conclusion.json"));
write_run_status(run_dir, run_status, status_reason);
let _ = (conclusion, run_status, status_reason);
}
/// Write a finalize commit to the shadow branch with retro.json and final node files.
@ -512,7 +511,7 @@ mod tests {
.await
.unwrap();
assert!(run_dir.join("conclusion.json").exists());
assert!(run_store.get_conclusion().await.unwrap().is_some());
assert_eq!(concluded.conclusion.status, StageStatus::Success);
}
}