mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-01 02:04:24 +00:00
Format the Petri test fixtures after the engine key removal
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
1f0dbd86ae
commit
3e157d3356
13 changed files with 2 additions and 748 deletions
|
|
@ -1,118 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use cli_table::format::{Border, Separator};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table};
|
||||
use fabro_api::types::TimelineEntryResponse;
|
||||
use fabro_types::RunId;
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
use git2::Repository;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::server_client::Client;
|
||||
use crate::shared::color_if;
|
||||
use crate::shared::repo::ensure_matching_repo_origin;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct TimelineEntryJson {
|
||||
ordinal: usize,
|
||||
node_name: String,
|
||||
visit: usize,
|
||||
run_commit_sha: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn ensure_origin_if_local(
|
||||
client: &Client,
|
||||
run_id: &RunId,
|
||||
verb: &str,
|
||||
) -> Result<()> {
|
||||
if Repository::discover(".").is_err() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let state = client.get_run_state(run_id).await?;
|
||||
ensure_matching_repo_origin(state.spec.repo_origin_url(), verb)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn timeline_entries_json(entries: &[TimelineEntryResponse]) -> Vec<TimelineEntryJson> {
|
||||
entries
|
||||
.iter()
|
||||
.map(|entry| TimelineEntryJson {
|
||||
ordinal: usize::try_from(entry.ordinal.get())
|
||||
.expect("timeline ordinal should fit in usize"),
|
||||
node_name: entry.node_name.clone(),
|
||||
visit: usize::try_from(entry.visit.get())
|
||||
.expect("timeline visit should fit in usize"),
|
||||
run_commit_sha: entry.run_commit_sha.clone(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn short_id(run_id: &str) -> &str {
|
||||
&run_id[..8.min(run_id.len())]
|
||||
}
|
||||
|
||||
pub(crate) fn print_timeline(entries: &[TimelineEntryJson], styles: &Styles, printer: Printer) {
|
||||
if entries.is_empty() {
|
||||
fabro_util::printerr!(printer, "No checkpoints found.");
|
||||
return;
|
||||
}
|
||||
|
||||
let use_color = styles.use_color;
|
||||
let title = vec![
|
||||
"@".cell().bold(use_color),
|
||||
"Node".cell().bold(use_color),
|
||||
"Details".cell().bold(use_color),
|
||||
];
|
||||
|
||||
let rows: Vec<Vec<CellStruct>> = entries
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
let ordinal_str = format!("@{}", entry.ordinal);
|
||||
let mut details = Vec::new();
|
||||
if entry.visit > 1 {
|
||||
details.push(format!("visit {}, loop", entry.visit));
|
||||
}
|
||||
if entry.run_commit_sha.is_none() {
|
||||
details.push("no run commit".to_string());
|
||||
}
|
||||
|
||||
let detail_str = if details.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("({})", details.join(", "))
|
||||
};
|
||||
|
||||
vec![
|
||||
ordinal_str
|
||||
.cell()
|
||||
.foreground_color(color_if(use_color, Color::Cyan)),
|
||||
entry.node_name.clone().cell(),
|
||||
detail_str
|
||||
.cell()
|
||||
.foreground_color(color_if(use_color, Color::Ansi256(8))),
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
let color_choice = if use_color {
|
||||
cli_table::ColorChoice::Auto
|
||||
} else {
|
||||
cli_table::ColorChoice::Never
|
||||
};
|
||||
let table = rows
|
||||
.table()
|
||||
.title(title)
|
||||
.color_choice(color_choice)
|
||||
.border(Border::builder().build())
|
||||
.separator(Separator::builder().build());
|
||||
#[allow(
|
||||
clippy::print_stderr,
|
||||
reason = "The checkpoint timeline table is operator feedback, not command output."
|
||||
)]
|
||||
if let Ok(display) = table.display() {
|
||||
for line in display.to_string().lines() {
|
||||
eprintln!("{}", line.trim_end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use fabro_api::types::ForkRequest;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::ForkArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) async fn run(args: &ForkArgs, styles: &Styles, base_ctx: &CommandContext) -> Result<()> {
|
||||
let printer = base_ctx.printer();
|
||||
let ctx = base_ctx.with_target(&args.server)?;
|
||||
let client = ctx.server().await?;
|
||||
let run_id = client.resolve_run(&args.run_id).await?.id;
|
||||
super::checkpoints::ensure_origin_if_local(client.as_ref(), &run_id, "fork").await?;
|
||||
|
||||
if args.list {
|
||||
let timeline = client.run_timeline(&run_id).await?;
|
||||
if ctx.json_output() {
|
||||
print_json_pretty(&super::checkpoints::timeline_entries_json(&timeline))?;
|
||||
return Ok(());
|
||||
}
|
||||
let entries = super::checkpoints::timeline_entries_json(&timeline);
|
||||
super::checkpoints::print_timeline(&entries, styles, printer);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let response = client
|
||||
.fork_run(&run_id, ForkRequest {
|
||||
target: args.target.clone(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
if ctx.json_output() {
|
||||
print_json_pretty(&response)?;
|
||||
} else {
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
"\nForked run {} -> {}",
|
||||
super::checkpoints::short_id(&response.source_run_id),
|
||||
super::checkpoints::short_id(&response.new_run_id)
|
||||
);
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
"To resume: fabro resume {}",
|
||||
super::checkpoints::short_id(&response.new_run_id)
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use fabro_api::types::RewindRequest;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use super::checkpoints::{ensure_origin_if_local, print_timeline, short_id, timeline_entries_json};
|
||||
use crate::args::RewindArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) async fn run(
|
||||
args: &RewindArgs,
|
||||
styles: &Styles,
|
||||
base_ctx: &CommandContext,
|
||||
) -> Result<()> {
|
||||
let printer = base_ctx.printer();
|
||||
let ctx = base_ctx.with_target(&args.server)?;
|
||||
let client = ctx.server().await?;
|
||||
let run_id = client.resolve_run(&args.run_id).await?.id;
|
||||
ensure_origin_if_local(client.as_ref(), &run_id, "rewind").await?;
|
||||
|
||||
if args.list || args.target.is_none() {
|
||||
let timeline = client.run_timeline(&run_id).await?;
|
||||
if ctx.json_output() {
|
||||
print_json_pretty(&timeline_entries_json(&timeline))?;
|
||||
return Ok(());
|
||||
}
|
||||
print_timeline(&timeline_entries_json(&timeline), styles, printer);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let target = args
|
||||
.target
|
||||
.clone()
|
||||
.expect("rewind target should be present unless listing");
|
||||
let result = client
|
||||
.rewind_run(&run_id, RewindRequest {
|
||||
target: Some(target),
|
||||
})
|
||||
.await?;
|
||||
let response = result.response;
|
||||
|
||||
if ctx.json_output() {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"source_run_id": response.source_run_id,
|
||||
"new_run_id": response.new_run_id,
|
||||
"target": response.target,
|
||||
"archived": response.archived,
|
||||
"archive_error": response.archive_error,
|
||||
"status": result.status,
|
||||
}))?;
|
||||
} else {
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
"\nRewound {}; new run {}",
|
||||
short_id(&response.source_run_id),
|
||||
short_id(&response.new_run_id)
|
||||
);
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
"To resume: fabro resume {}",
|
||||
short_id(&response.new_run_id)
|
||||
);
|
||||
if !response.archived {
|
||||
let archive_error = response.archive_error.as_deref().unwrap_or("unknown error");
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
"Warning: source not archived: {archive_error}. Run `fabro archive {}` to finish.",
|
||||
short_id(&response.source_run_id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
|
||||
/// Detect the git remote URL and current branch from a local repository.
|
||||
///
|
||||
/// Uses `git2` to discover the repo at `path`, reads the `origin` remote URL
|
||||
/// and the HEAD branch name.
|
||||
pub(crate) fn detect_repo_info(path: &Path) -> Result<(String, Option<String>)> {
|
||||
let repo = git2::Repository::discover(path)
|
||||
.with_context(|| format!("Failed to discover git repo at {}", path.display()))?;
|
||||
|
||||
let url = repo
|
||||
.find_remote("origin")
|
||||
.context("Failed to find 'origin' remote")?
|
||||
.url()
|
||||
.context("origin remote URL is not valid UTF-8")?
|
||||
.to_string();
|
||||
|
||||
let branch = repo
|
||||
.head()
|
||||
.ok()
|
||||
.and_then(|head| head.shorthand().map(String::from));
|
||||
|
||||
Ok((url, branch))
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_matching_repo_origin(
|
||||
expected_origin_url: Option<&str>,
|
||||
action: &str,
|
||||
) -> Result<()> {
|
||||
let Some(expected_origin_url) = expected_origin_url else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let cwd = std::env::current_dir()?;
|
||||
let (origin_url, _) = detect_repo_info(&cwd).map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"Current directory is not a git repository with an origin remote; refusing to {action} run from repository '{expected_origin_url}'"
|
||||
)
|
||||
})?;
|
||||
let current_origin_url = fabro_github::normalize_repo_origin_url(&origin_url);
|
||||
|
||||
if current_origin_url != expected_origin_url {
|
||||
bail!(
|
||||
"Current repository origin '{current_origin_url}' does not match run repository '{expected_origin_url}'; refusing to {action} this run from the wrong checkout"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{detect_repo_info, ensure_matching_repo_origin};
|
||||
|
||||
#[test]
|
||||
fn missing_expected_origin_skips_guard() {
|
||||
ensure_matching_repo_origin(None, "fork").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_git_remote_from_repo() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let repo = git2::Repository::init(dir.path()).unwrap();
|
||||
repo.remote("origin", "https://github.com/org/repo.git")
|
||||
.unwrap();
|
||||
|
||||
let (url, _branch) = detect_repo_info(dir.path()).unwrap();
|
||||
assert_eq!(url, "https://github.com/org/repo.git");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_repo_info_returns_worktree_branch() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let repo = git2::Repository::init(dir.path()).unwrap();
|
||||
let sig = git2::Signature::now("Test", "test@test.com").unwrap();
|
||||
let tree_id = repo.index().unwrap().write_tree().unwrap();
|
||||
let tree = repo.find_tree(tree_id).unwrap();
|
||||
let commit = repo
|
||||
.commit(Some("HEAD"), &sig, &sig, "init", &tree, &[])
|
||||
.unwrap();
|
||||
repo.remote("origin", "https://github.com/org/repo.git")
|
||||
.unwrap();
|
||||
let commit_obj = repo.find_commit(commit).unwrap();
|
||||
repo.branch("fabro/run/ABC", &commit_obj, false).unwrap();
|
||||
repo.set_head("refs/heads/fabro/run/ABC").unwrap();
|
||||
|
||||
let (_, branch) = detect_repo_info(dir.path()).unwrap();
|
||||
assert_eq!(branch, Some("fabro/run/ABC".into()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
use fabro_test::{fabro_snapshot, run_and_format, test_context};
|
||||
use insta::assert_snapshot;
|
||||
|
||||
use super::support::{
|
||||
git_filters, output_stdout, run_state_by_id, setup_seeded_git_backed_changed_run,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn help() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["fork", "--help"]);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Fork a workflow run from an earlier checkpoint into a new run
|
||||
|
||||
Usage: fabro fork [OPTIONS] <RUN_ID> [TARGET]
|
||||
|
||||
Arguments:
|
||||
<RUN_ID> Run ID (or unambiguous prefix)
|
||||
[TARGET] Target checkpoint: node name, node@visit, or @ordinal (omit to fork from latest)
|
||||
|
||||
Options:
|
||||
--json Output as JSON [env: FABRO_JSON=]
|
||||
--server <SERVER> Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=]
|
||||
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||||
--list Show the checkpoint timeline instead of forking
|
||||
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
||||
--quiet Suppress non-essential output [env: FABRO_QUIET=]
|
||||
--verbose Enable verbose output [env: FABRO_VERBOSE=]
|
||||
-h, --help Print help
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fork_outside_git_repo_errors() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["fork", "01ARZ3NDEKTSV4RRFFQ69G5FAW"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
× No run found matching '[ULID]' (tried run ID prefix and workflow name)
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fork_latest_prints_new_run_and_resume_hint() {
|
||||
let context = test_context!();
|
||||
let setup = setup_seeded_git_backed_changed_run(&context);
|
||||
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["fork", &setup.run.run_id]);
|
||||
|
||||
let (snapshot, output) = run_and_format(&mut cmd, &git_filters(&context));
|
||||
assert_snapshot!(snapshot, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
|
||||
Forked run [RUN_PREFIX] -> [RUN_PREFIX]
|
||||
To resume: fabro resume [RUN_PREFIX]
|
||||
");
|
||||
assert!(output.status.success(), "fork should succeed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fork_from_earlier_checkpoint_uses_expected_sha() {
|
||||
let context = test_context!();
|
||||
let setup = setup_seeded_git_backed_changed_run(&context);
|
||||
|
||||
let output = context
|
||||
.command()
|
||||
.args(["fork", &setup.run.run_id, "@2", "--json"])
|
||||
.output()
|
||||
.expect("fork should execute");
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"fork should succeed\nstdout:\n{}\nstderr:\n{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
|
||||
let fork_response: serde_json::Value =
|
||||
serde_json::from_str(&output_stdout(&output)).expect("fork json should parse");
|
||||
let new_run_id = fork_response["new_run_id"]
|
||||
.as_str()
|
||||
.expect("fork json should include new_run_id");
|
||||
let run_snapshot = run_state_by_id(&context, new_run_id);
|
||||
assert_eq!(
|
||||
run_snapshot
|
||||
.current_checkpoint()
|
||||
.map(|checkpoint| checkpoint.current_node.as_str()),
|
||||
Some("step_one")
|
||||
);
|
||||
assert_eq!(
|
||||
run_snapshot
|
||||
.current_checkpoint()
|
||||
.and_then(|checkpoint| checkpoint.git_commit_sha.as_deref()),
|
||||
Some(setup.step_one_sha.as_str())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
run_snapshot
|
||||
.spec
|
||||
.fork_source_ref
|
||||
.as_ref()
|
||||
.map(|source| source.checkpoint_sha.as_str()),
|
||||
Some(setup.step_one_sha.as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
run_snapshot
|
||||
.spec
|
||||
.fork_source_ref
|
||||
.as_ref()
|
||||
.map(|source| source.source_run_id.to_string()),
|
||||
Some(setup.run.run_id.clone())
|
||||
);
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
use fabro_test::{fabro_snapshot, run_and_format, test_context};
|
||||
use insta::assert_snapshot;
|
||||
|
||||
use super::support::{
|
||||
git_filters, output_stderr as support_stderr, run_events, run_state, run_state_by_id,
|
||||
setup_seeded_git_backed_changed_run,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn help() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["rewind", "--help"]);
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
Rewind a workflow run to an earlier checkpoint
|
||||
|
||||
Usage: fabro rewind [OPTIONS] <RUN_ID> [TARGET]
|
||||
|
||||
Arguments:
|
||||
<RUN_ID> Run ID (or unambiguous prefix)
|
||||
[TARGET] Target checkpoint: node name, node@visit, or @ordinal (omit with --list)
|
||||
|
||||
Options:
|
||||
--json Output as JSON [env: FABRO_JSON=]
|
||||
--server <SERVER> Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=]
|
||||
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||||
--list Show the checkpoint timeline instead of rewinding
|
||||
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
||||
--quiet Suppress non-essential output [env: FABRO_QUIET=]
|
||||
--verbose Enable verbose output [env: FABRO_VERBOSE=]
|
||||
-h, --help Print help
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rewind_outside_git_repo_errors() {
|
||||
let context = test_context!();
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["rewind", "01ARZ3NDEKTSV4RRFFQ69G5FAW", "--list"]);
|
||||
|
||||
fabro_snapshot!(context.filters(), cmd, @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
× No run found matching '[ULID]' (tried run ID prefix and workflow name)
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rewind_list_prints_timeline_for_completed_git_run() {
|
||||
let context = test_context!();
|
||||
let setup = setup_seeded_git_backed_changed_run(&context);
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["rewind", &setup.run.run_id, "--list"]);
|
||||
|
||||
fabro_snapshot!(git_filters(&context), cmd, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
@ Node Details
|
||||
@1 start (no run commit)
|
||||
@2 step_one
|
||||
@3 step_two
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rewind_target_updates_metadata_and_resume_hint() {
|
||||
let context = test_context!();
|
||||
let setup = setup_seeded_git_backed_changed_run(&context);
|
||||
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["rewind", &setup.run.run_id, "@2"]);
|
||||
|
||||
let (snapshot, output) = run_and_format(&mut cmd, &git_filters(&context));
|
||||
assert_snapshot!(snapshot, @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
----- stderr -----
|
||||
|
||||
Rewound [RUN_PREFIX]; new run [RUN_PREFIX]
|
||||
To resume: fabro resume [RUN_PREFIX]
|
||||
");
|
||||
assert!(output.status.success(), "rewind should succeed");
|
||||
|
||||
let state = run_state(&setup.run.run_dir);
|
||||
assert!(state.archived_at.is_some());
|
||||
let new_run_id = state
|
||||
.superseded_by
|
||||
.expect("rewind should record replacement run");
|
||||
let replacement = run_state_by_id(&context, &new_run_id.to_string());
|
||||
assert_eq!(
|
||||
replacement
|
||||
.current_checkpoint()
|
||||
.and_then(|checkpoint| checkpoint.git_commit_sha.clone()),
|
||||
Some(setup.step_one_sha)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rewind_archives_source_and_records_superseded_by() {
|
||||
let context = test_context!();
|
||||
let setup = setup_seeded_git_backed_changed_run(&context);
|
||||
let before_events = run_events(&setup.run.run_dir);
|
||||
assert!(
|
||||
before_events
|
||||
.iter()
|
||||
.any(|event| event.event.event_name() == "run.completed"),
|
||||
"setup run should be completed before rewind"
|
||||
);
|
||||
|
||||
let mut cmd = context.command();
|
||||
cmd.args(["rewind", &setup.run.run_id, "@2"]);
|
||||
let output = cmd.output().expect("rewind should execute");
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"rewind should succeed\nstdout:\n{}\nstderr:\n{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
support_stderr(&output),
|
||||
);
|
||||
|
||||
let after_events = run_events(&setup.run.run_dir);
|
||||
assert_eq!(
|
||||
after_events.len(),
|
||||
before_events.len() + 2,
|
||||
"rewind should append run.archived and run.superseded_by"
|
||||
);
|
||||
assert_eq!(
|
||||
after_events[..before_events.len()]
|
||||
.iter()
|
||||
.map(|event| event.event.event_name())
|
||||
.collect::<Vec<_>>(),
|
||||
before_events
|
||||
.iter()
|
||||
.map(|event| event.event.event_name())
|
||||
.collect::<Vec<_>>(),
|
||||
"rewind should preserve the prior event prefix"
|
||||
);
|
||||
assert_eq!(
|
||||
after_events[before_events.len()].event.event_name(),
|
||||
"run.archived"
|
||||
);
|
||||
assert_eq!(
|
||||
after_events[before_events.len() + 1].event.event_name(),
|
||||
"run.superseded_by"
|
||||
);
|
||||
|
||||
let state = run_state(&setup.run.run_dir);
|
||||
assert!(state.archived_at.is_some());
|
||||
assert!(state.superseded_by.is_some());
|
||||
}
|
||||
|
|
@ -58,8 +58,7 @@ const CHILD_DOT: &str = r#"digraph Child {
|
|||
say [shape=parallelogram, script="echo hello from the child", max_retries=0]
|
||||
start -> say -> exit
|
||||
}"#;
|
||||
const CHILD_SETTINGS: &str =
|
||||
"_version = 1\n\n[workflow]\ngraph = \"workflow.fabro\"\n";
|
||||
const CHILD_SETTINGS: &str = "_version = 1\n\n[workflow]\ngraph = \"workflow.fabro\"\n";
|
||||
|
||||
/// A `[[run.hooks]]` entry that blocks every `fabro_run_search` call.
|
||||
const BLOCKING_HOOK: &str = r#"
|
||||
|
|
|
|||
|
|
@ -189,8 +189,7 @@ mod tests {
|
|||
start -> exit
|
||||
}"#;
|
||||
|
||||
const PETRI_SETTINGS: &str =
|
||||
"_version = 1\n\n[workflow]\ngraph = \"workflow.fabro\"\n";
|
||||
const PETRI_SETTINGS: &str = "_version = 1\n\n[workflow]\ngraph = \"workflow.fabro\"\n";
|
||||
|
||||
/// A worker runtime whose one worker runs until the test ends it, so
|
||||
/// the test can act while the server waits on the worker. It keeps the
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.2.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Request body for creating a new run from a source run checkpoint.
|
||||
*/
|
||||
export interface ForkRequest {
|
||||
/**
|
||||
* Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
|
||||
*/
|
||||
'target'?: string | null;
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.2.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Response returned after creating a forked run.
|
||||
*/
|
||||
export interface ForkResponse {
|
||||
'source_run_id': string;
|
||||
'new_run_id': string;
|
||||
'target': string;
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.2.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Request body for creating a replacement run from a source run checkpoint.
|
||||
*/
|
||||
export interface RewindRequest {
|
||||
/**
|
||||
* Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
|
||||
*/
|
||||
'target'?: string | null;
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.2.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Response returned after rewind creates a new run.
|
||||
*/
|
||||
export interface RewindResponse {
|
||||
'source_run_id': string;
|
||||
'new_run_id': string;
|
||||
'target': string;
|
||||
'archived': boolean;
|
||||
'archive_error'?: string | null;
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.2.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checkpoint timeline entry for a run.
|
||||
*/
|
||||
export interface TimelineEntryResponse {
|
||||
'ordinal': number;
|
||||
'node_name': string;
|
||||
'visit': number;
|
||||
'checkpoint_seq': number;
|
||||
'run_commit_sha'?: string | null;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue