mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-06 02:48:25 +00:00
Merge pull request #723 from fabro-sh/remove-pre-run-push-outcome
Remove the recorded pre-run push outcome while preserving the push
This commit is contained in:
commit
6ce418a415
37 changed files with 256 additions and 657 deletions
|
|
@ -45,16 +45,11 @@ Emitted when the run record is created.
|
|||
"properties": {
|
||||
"workflow_slug": "my-workflow",
|
||||
"source_directory": "/home/user/src/my-project",
|
||||
"repo_origin_url": "https://github.com/acme/my-project.git",
|
||||
"base_branch": "main",
|
||||
"pre_run_git": {
|
||||
"display_base_sha": "abc123",
|
||||
"local_dirty": "clean",
|
||||
"push_outcome": {
|
||||
"type": "succeeded",
|
||||
"remote": "origin",
|
||||
"branch": "main"
|
||||
}
|
||||
"git": {
|
||||
"origin_url": "https://github.com/acme/my-project",
|
||||
"branch": "main",
|
||||
"sha": "abc123",
|
||||
"dirty": "clean"
|
||||
},
|
||||
"fork_source_ref": null,
|
||||
"in_place": false,
|
||||
|
|
@ -80,18 +75,17 @@ Emitted when the run record is created.
|
|||
| `workflow_source` | string? | Workflow source text |
|
||||
| `labels` | object | Run labels |
|
||||
| `source_directory` | string? | Submitter-side source directory |
|
||||
| `repo_origin_url` | string? | Normalized repository origin URL used by clone-based sandboxes |
|
||||
| `base_branch` | string? | Submitter-side base branch |
|
||||
| `workflow_slug` | string? | Workflow slug |
|
||||
| `provenance` | object | Actor and request provenance |
|
||||
| `manifest_blob` | string? | Blob id for the submitted manifest |
|
||||
| `pre_run_git` | object? | Submitter-side pre-run git context and push outcome |
|
||||
| `git` | object? | Git provenance observed before the run: normalized `origin_url`, `branch`, optional `sha`, and `dirty` status |
|
||||
| `fork_source_ref` | object? | Source run/checkpoint reference when this run was forked |
|
||||
| `in_place` | boolean | Whether the run was created with `--in-place` (no git checkpoints) |
|
||||
|
||||
Readers remain tolerant of the legacy `workflow_config`, `run_dir`, and
|
||||
`db_prefix` properties when replaying historical events, but newly emitted
|
||||
`run.created` events omit them.
|
||||
`db_prefix` properties, and of a legacy `push_outcome` object nested inside
|
||||
`git`, when replaying historical events; newly emitted `run.created` events
|
||||
omit them.
|
||||
|
||||
### `run.started`
|
||||
|
||||
|
|
|
|||
|
|
@ -9089,7 +9089,6 @@ components:
|
|||
- origin_url
|
||||
- branch
|
||||
- dirty
|
||||
- push_outcome
|
||||
properties:
|
||||
origin_url:
|
||||
type: string
|
||||
|
|
@ -9105,96 +9104,6 @@ components:
|
|||
example: abc123def
|
||||
dirty:
|
||||
$ref: "#/components/schemas/DirtyStatus"
|
||||
push_outcome:
|
||||
$ref: "#/components/schemas/PreRunPushOutcome"
|
||||
|
||||
PreRunPushOutcome:
|
||||
description: Outcome of the CLI's best-effort pre-run push.
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/PreRunPushOutcomeNotAttempted"
|
||||
- $ref: "#/components/schemas/PreRunPushOutcomeSucceeded"
|
||||
- $ref: "#/components/schemas/PreRunPushOutcomeFailed"
|
||||
- $ref: "#/components/schemas/PreRunPushOutcomeSkippedNoRemote"
|
||||
- $ref: "#/components/schemas/PreRunPushOutcomeSkippedRemoteMismatch"
|
||||
discriminator:
|
||||
propertyName: type
|
||||
mapping:
|
||||
not_attempted: "#/components/schemas/PreRunPushOutcomeNotAttempted"
|
||||
succeeded: "#/components/schemas/PreRunPushOutcomeSucceeded"
|
||||
failed: "#/components/schemas/PreRunPushOutcomeFailed"
|
||||
skipped_no_remote: "#/components/schemas/PreRunPushOutcomeSkippedNoRemote"
|
||||
skipped_remote_mismatch: "#/components/schemas/PreRunPushOutcomeSkippedRemoteMismatch"
|
||||
|
||||
PreRunPushOutcomeNotAttempted:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- not_attempted
|
||||
|
||||
PreRunPushOutcomeSucceeded:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- remote
|
||||
- branch
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- succeeded
|
||||
remote:
|
||||
type: string
|
||||
branch:
|
||||
type: string
|
||||
|
||||
PreRunPushOutcomeFailed:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- remote
|
||||
- branch
|
||||
- message
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- failed
|
||||
remote:
|
||||
type: string
|
||||
branch:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
|
||||
PreRunPushOutcomeSkippedNoRemote:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- skipped_no_remote
|
||||
|
||||
PreRunPushOutcomeSkippedRemoteMismatch:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- remote
|
||||
- repo_origin_url
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- skipped_remote_mismatch
|
||||
remote:
|
||||
type: string
|
||||
repo_origin_url:
|
||||
type: string
|
||||
|
||||
ManifestGoal:
|
||||
description: Resolved goal kind and content.
|
||||
|
|
|
|||
|
|
@ -845,11 +845,6 @@ async fn seed_git_backed_changed_run(context: &TestContext) -> SeededGitRunSetup
|
|||
"branch": "main",
|
||||
"sha": base_sha,
|
||||
"dirty": "clean",
|
||||
"push_outcome": {
|
||||
"type": "succeeded",
|
||||
"remote": "origin",
|
||||
"branch": "main",
|
||||
},
|
||||
})),
|
||||
)
|
||||
.await;
|
||||
|
|
@ -889,11 +884,6 @@ async fn seed_git_backed_noop_run(context: &TestContext) -> RunSetup {
|
|||
"branch": "main",
|
||||
"sha": base_sha,
|
||||
"dirty": "clean",
|
||||
"push_outcome": {
|
||||
"type": "succeeded",
|
||||
"remote": "origin",
|
||||
"branch": "main",
|
||||
},
|
||||
})),
|
||||
)
|
||||
.await;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use fabro_api::types::RunManifest;
|
|||
use fabro_automation::{AutomationId, AutomationTarget};
|
||||
use fabro_config::{EnvironmentLayer, MergeMap};
|
||||
use fabro_manifest::ManifestBuildInput;
|
||||
use fabro_types::{DirtyStatus, GitContext, GitHubRepositorySlug, PreRunPushOutcome, RunId};
|
||||
use fabro_types::{DirtyStatus, GitContext, GitHubRepositorySlug, RunId};
|
||||
use fabro_util::error::collect_chain;
|
||||
use tokio::{fs, task};
|
||||
|
||||
|
|
@ -199,11 +199,10 @@ fn build_manifest_from_checkout(
|
|||
|
||||
let mut manifest = built.manifest;
|
||||
manifest.git = Some(GitContext {
|
||||
origin_url: github_metadata_url(&git_context.repo),
|
||||
branch: git_context.ref_selector,
|
||||
sha: Some(git_context.checked_out_sha),
|
||||
dirty: DirtyStatus::Clean,
|
||||
push_outcome: PreRunPushOutcome::NotAttempted,
|
||||
origin_url: github_metadata_url(&git_context.repo),
|
||||
branch: git_context.ref_selector,
|
||||
sha: Some(git_context.checked_out_sha),
|
||||
dirty: DirtyStatus::Clean,
|
||||
});
|
||||
let submitted_manifest_bytes = serde_json::to_vec(&manifest)
|
||||
.context("failed to serialize materialized run manifest")
|
||||
|
|
@ -299,7 +298,7 @@ mod tests {
|
|||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
|
||||
use fabro_types::{DirtyStatus, PreRunPushOutcome};
|
||||
use fabro_types::DirtyStatus;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::*;
|
||||
|
|
@ -367,7 +366,6 @@ mod tests {
|
|||
assert_eq!(git.branch, "release");
|
||||
assert_eq!(git.sha.as_deref(), Some(sha.as_str()));
|
||||
assert_eq!(git.dirty, DirtyStatus::Clean);
|
||||
assert_eq!(git.push_outcome, PreRunPushOutcome::NotAttempted);
|
||||
let submitted_manifest: serde_json::Value =
|
||||
serde_json::from_slice(&materialized.submitted_manifest_bytes)
|
||||
.expect("submitted bytes should be a manifest");
|
||||
|
|
|
|||
|
|
@ -1695,11 +1695,10 @@ digraph Demo {{
|
|||
|
||||
fn git_context(origin_url: &str, branch: &str) -> types::GitContext {
|
||||
types::GitContext {
|
||||
origin_url: origin_url.to_string(),
|
||||
branch: branch.to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: origin_url.to_string(),
|
||||
branch: branch.to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3784,8 +3784,7 @@ layer = "project"
|
|||
"origin_url": "https://github.com/acme/payments.git",
|
||||
"branch": "feature/compiler",
|
||||
"sha": "0123456789abcdef",
|
||||
"dirty": "clean",
|
||||
"push_outcome": { "type": "not_attempted" }
|
||||
"dirty": "clean"
|
||||
});
|
||||
let manifest: RunManifest = serde_json::from_value(manifest_json).unwrap();
|
||||
let submitted_manifest_bytes = serde_json::to_vec(&manifest).unwrap();
|
||||
|
|
@ -7054,11 +7053,10 @@ async fn create_completed_run_ready_for_pull_request(
|
|||
);
|
||||
let git = match (repo_origin_url, base_branch) {
|
||||
(Some(origin), Some(branch)) => Some(fabro_types::GitContext {
|
||||
origin_url: origin.to_string(),
|
||||
branch: branch.to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: origin.to_string(),
|
||||
branch: branch.to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -492,11 +492,10 @@ mod tests {
|
|||
automation: None,
|
||||
source_directory: Some("/tmp/project".to_string()),
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
labels: HashMap::from([("team".to_string(), "platform".to_string())]),
|
||||
provenance: test_support::test_run_provenance(),
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use fabro_template::{
|
|||
};
|
||||
use fabro_types::settings::interp::InterpString;
|
||||
use fabro_types::settings::run::{ApprovalMode, ResolvedGoalSource, ResolvedRunGoal, RunMode};
|
||||
use fabro_types::{DirtyStatus, GitContext, ManifestPath, PreRunPushOutcome, WorkflowSettings};
|
||||
use fabro_types::{DirtyStatus, GitContext, ManifestPath, WorkflowSettings};
|
||||
use fabro_workflow::git::{
|
||||
GitSyncStatus, branch_needs_push, head_sha, push_branch_noninteractive, sync_status,
|
||||
};
|
||||
|
|
@ -754,7 +754,7 @@ fn build_git_context(
|
|||
.filter(|url| !url.is_empty())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let push_outcome = build_manifest_push_outcome(
|
||||
push_manifest_branch_best_effort(
|
||||
repo_path,
|
||||
&branch,
|
||||
origin_url.as_deref(),
|
||||
|
|
@ -765,7 +765,6 @@ fn build_git_context(
|
|||
branch,
|
||||
sha,
|
||||
dirty,
|
||||
push_outcome,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -798,14 +797,18 @@ fn detect_manifest_repo_info(repo_path: &Path) -> Option<(Option<String>, String
|
|||
Some((origin_url, branch))
|
||||
}
|
||||
|
||||
fn build_manifest_push_outcome(
|
||||
/// Best-effort push of the local branch so clone-based execution can see
|
||||
/// local commits. A failed push must not fail manifest creation, and the
|
||||
/// discarded push error may contain raw Git stderr, so it is deliberately
|
||||
/// neither returned nor logged here.
|
||||
fn push_manifest_branch_best_effort(
|
||||
repo_path: &Path,
|
||||
branch: &str,
|
||||
origin_url: Option<&str>,
|
||||
configured_repo_origin_url: Option<&str>,
|
||||
) -> PreRunPushOutcome {
|
||||
) {
|
||||
let Some(origin_url) = origin_url else {
|
||||
return PreRunPushOutcome::SkippedNoRemote;
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(repo_origin_url) = configured_repo_origin_url
|
||||
|
|
@ -814,28 +817,15 @@ fn build_manifest_push_outcome(
|
|||
{
|
||||
let remote = fabro_github::normalize_repo_origin_url(origin_url);
|
||||
if remote != repo_origin_url {
|
||||
return PreRunPushOutcome::SkippedRemoteMismatch {
|
||||
remote,
|
||||
repo_origin_url,
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if !branch_needs_push(repo_path, "origin", branch) {
|
||||
return PreRunPushOutcome::NotAttempted;
|
||||
return;
|
||||
}
|
||||
|
||||
match push_branch_noninteractive(repo_path, "origin", branch) {
|
||||
Ok(()) => PreRunPushOutcome::Succeeded {
|
||||
remote: "origin".to_string(),
|
||||
branch: branch.to_string(),
|
||||
},
|
||||
Err(err) => PreRunPushOutcome::Failed {
|
||||
remote: "origin".to_string(),
|
||||
branch: branch.to_string(),
|
||||
message: err.to_string(),
|
||||
},
|
||||
}
|
||||
let _ = push_branch_noninteractive(repo_path, "origin", branch);
|
||||
}
|
||||
|
||||
fn normalize_absolute_path(base_dir: &Path, reference: &str) -> Option<PathBuf> {
|
||||
|
|
@ -1709,19 +1699,58 @@ working_dir = "repos/target"
|
|||
.expect("manifest git info should be detected");
|
||||
assert_eq!(git.branch, "target-branch");
|
||||
assert_eq!(git.origin_url, "https://github.com/example/target");
|
||||
assert_eq!(git.push_outcome, PreRunPushOutcome::NotAttempted);
|
||||
}
|
||||
|
||||
/// A local branch ahead of its origin is pushed as a side effect of
|
||||
/// building the manifest, so clone-based execution sees local commits.
|
||||
#[test]
|
||||
fn build_manifest_pushes_local_commits_to_bare_origin() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let workspace = temp.path().join("workspace");
|
||||
std::fs::create_dir_all(&workspace).unwrap();
|
||||
let bare_origin = init_bare_origin(temp.path());
|
||||
|
||||
init_git_repo(&workspace, "feature", bare_origin.to_str().unwrap());
|
||||
|
||||
let workflow_dir = workspace.join(".fabro/workflows/demo");
|
||||
std::fs::create_dir_all(&workflow_dir).unwrap();
|
||||
std::fs::write(workspace.join(".fabro/project.toml"), "_version = 1\n").unwrap();
|
||||
std::fs::write(
|
||||
workflow_dir.join("workflow.toml"),
|
||||
"_version = 1\n\n[workflow]\ngraph = \"workflow.fabro\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
workflow_dir.join("workflow.fabro"),
|
||||
r"digraph Demo { start [shape=Mdiamond] exit [shape=Msquare] start -> exit }",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let built = build_run_manifest(ManifestBuildInput {
|
||||
workflow: PathBuf::from(".fabro/workflows/demo/workflow.toml"),
|
||||
cwd: workspace.clone(),
|
||||
environment_defaults: test_environment_defaults(),
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert!(built.manifest.git.is_some());
|
||||
let local_head = head_sha(&workspace).expect("workspace HEAD should resolve");
|
||||
assert_eq!(
|
||||
bare_remote_branch_sha(&bare_origin, "feature").as_deref(),
|
||||
Some(local_head.trim()),
|
||||
"the local branch should be pushed to the bare origin during manifest build",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_manifest_git_skips_push_when_configured_repository_differs_from_origin() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let workspace = temp.path();
|
||||
let workspace = temp.path().join("workspace");
|
||||
std::fs::create_dir_all(&workspace).unwrap();
|
||||
let bare_origin = init_bare_origin(temp.path());
|
||||
|
||||
init_git_repo(
|
||||
workspace,
|
||||
"feature",
|
||||
"https://github.com/user/forked-target.git",
|
||||
);
|
||||
init_git_repo(&workspace, "feature", bare_origin.to_str().unwrap());
|
||||
|
||||
let workflow_dir = workspace.join(".fabro/workflows/demo");
|
||||
std::fs::create_dir_all(&workflow_dir).unwrap();
|
||||
|
|
@ -1749,7 +1778,7 @@ repository = "target"
|
|||
|
||||
let built = build_run_manifest(ManifestBuildInput {
|
||||
workflow: PathBuf::from(".fabro/workflows/demo/workflow.toml"),
|
||||
cwd: workspace.to_path_buf(),
|
||||
cwd: workspace.clone(),
|
||||
environment_defaults: test_environment_defaults(),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -1760,10 +1789,11 @@ repository = "target"
|
|||
.git
|
||||
.expect("manifest git info should be detected");
|
||||
assert_eq!(git.origin_url, "https://github.com/example/target");
|
||||
assert_eq!(git.push_outcome, PreRunPushOutcome::SkippedRemoteMismatch {
|
||||
remote: "https://github.com/user/forked-target".to_string(),
|
||||
repo_origin_url: "https://github.com/example/target".to_string(),
|
||||
});
|
||||
assert_eq!(
|
||||
bare_remote_branch_sha(&bare_origin, "feature"),
|
||||
None,
|
||||
"a mismatched configured repository must not be pushed to",
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
|
|
@ -1820,13 +1850,9 @@ exit 1
|
|||
environment_defaults: test_environment_defaults(),
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap();
|
||||
.expect("a failed push must not fail manifest creation");
|
||||
|
||||
let git = built
|
||||
.manifest
|
||||
.git
|
||||
.expect("manifest git info should be detected");
|
||||
assert!(matches!(git.push_outcome, PreRunPushOutcome::Failed { .. }));
|
||||
assert!(built.manifest.git.is_some());
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1859,6 +1885,21 @@ exit 1
|
|||
run_git(path, &["update-ref", &remote_ref, "HEAD"]);
|
||||
}
|
||||
|
||||
fn init_bare_origin(parent: &Path) -> PathBuf {
|
||||
let bare = parent.join("origin.git");
|
||||
std::fs::create_dir_all(&bare).unwrap();
|
||||
run_git(&bare, &["init", "--bare", "--quiet"]);
|
||||
bare
|
||||
}
|
||||
|
||||
fn bare_remote_branch_sha(bare_path: &Path, branch: &str) -> Option<String> {
|
||||
let repo = git2::Repository::open_bare(bare_path).expect("bare origin should open");
|
||||
repo.find_reference(&format!("refs/heads/{branch}"))
|
||||
.ok()
|
||||
.and_then(|reference| reference.target())
|
||||
.map(|oid| oid.to_string())
|
||||
}
|
||||
|
||||
fn run_git(path: &Path, args: &[&str]) {
|
||||
use std::process::Command;
|
||||
let output = Command::new("git")
|
||||
|
|
|
|||
|
|
@ -596,11 +596,10 @@ mod tests {
|
|||
manifest_blob: None,
|
||||
definition_blob: None,
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: "https://github.com/fabro-sh/fabro".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: "https://github.com/fabro-sh/fabro".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
fork_source_ref: None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,11 +26,10 @@ fn sample_run_spec() -> RunSpec {
|
|||
manifest_blob: None,
|
||||
definition_blob: None,
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
fork_source_ref: None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1883,11 +1883,10 @@ reasoning = false
|
|||
title: None,
|
||||
automation: None,
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
fork_source_ref: None,
|
||||
parent_id: None,
|
||||
|
|
@ -2244,11 +2243,10 @@ reasoning = false
|
|||
title: None,
|
||||
automation: None,
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: "https://github.com/acme/widgets".to_string(),
|
||||
branch: String::new(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: "https://github.com/acme/widgets".to_string(),
|
||||
branch: String::new(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
fork_source_ref: None,
|
||||
parent_id: None,
|
||||
|
|
|
|||
|
|
@ -382,11 +382,10 @@ mod tests {
|
|||
provenance: test_support::test_run_provenance(),
|
||||
manifest_blob: None,
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: "https://github.com/example/repo.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: "https://github.com/example/repo.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
fork_source_ref: None,
|
||||
retried_from: None,
|
||||
|
|
|
|||
|
|
@ -118,8 +118,8 @@ mod tests {
|
|||
use fabro_store::{Database, RunProjectionReducer};
|
||||
use fabro_types::{
|
||||
AuthMethod, DirtyStatus, FailureReason, ForkSourceRef, GitContext, Graph, IdpIdentity,
|
||||
PreRunPushOutcome, Principal, PullRequestLink, RunBlobId, RunRunnableSource,
|
||||
RunServerProvenance, RunTiming, WorkflowSettings, fixtures,
|
||||
Principal, PullRequestLink, RunBlobId, RunRunnableSource, RunServerProvenance, RunTiming,
|
||||
WorkflowSettings, fixtures,
|
||||
};
|
||||
use object_store::memory::InMemory;
|
||||
|
||||
|
|
@ -154,11 +154,10 @@ mod tests {
|
|||
|
||||
fn git_context() -> GitContext {
|
||||
GitContext {
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: Some("abc123".to_string()),
|
||||
dirty: DirtyStatus::Clean,
|
||||
push_outcome: PreRunPushOutcome::NotAttempted,
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: Some("abc123".to_string()),
|
||||
dirty: DirtyStatus::Clean,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -164,11 +164,10 @@ fn persisted_workflow(graph: Graph, source: String, run_dir: &Path, run_id: RunI
|
|||
.to_string(),
|
||||
),
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
labels: HashMap::new(),
|
||||
provenance: test_support::test_run_provenance(),
|
||||
|
|
|
|||
|
|
@ -861,11 +861,10 @@ mod tests {
|
|||
automation: None,
|
||||
source_directory: Some(std::env::current_dir().unwrap().display().to_string()),
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
labels: HashMap::new(),
|
||||
provenance: test_support::test_run_provenance(),
|
||||
|
|
|
|||
|
|
@ -138,11 +138,10 @@ mod tests {
|
|||
automation: None,
|
||||
source_directory: Some("/tmp/project".to_string()),
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
labels: HashMap::from([
|
||||
("env".to_string(), "test".to_string()),
|
||||
|
|
|
|||
|
|
@ -1030,11 +1030,10 @@ mod tests {
|
|||
automation: None,
|
||||
source_directory: Some("/tmp/project".to_string()),
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
labels: HashMap::new(),
|
||||
provenance: test_support::test_run_provenance(),
|
||||
|
|
@ -1098,11 +1097,10 @@ mod tests {
|
|||
automation: None,
|
||||
source_directory: Some("/tmp/project".to_string()),
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
labels: HashMap::new(),
|
||||
provenance: test_support::test_run_provenance(),
|
||||
|
|
|
|||
|
|
@ -478,11 +478,10 @@ mod tests {
|
|||
automation: None,
|
||||
source_directory: Some("/tmp/project".to_string()),
|
||||
git: Some(fabro_types::GitContext {
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
push_outcome: fabro_types::PreRunPushOutcome::NotAttempted,
|
||||
origin_url: String::new(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: fabro_types::DirtyStatus::Clean,
|
||||
}),
|
||||
labels: HashMap::new(),
|
||||
provenance: test_support::test_run_provenance(),
|
||||
|
|
|
|||
|
|
@ -537,9 +537,7 @@ mod tests {
|
|||
use std::sync::Arc;
|
||||
|
||||
use fabro_store::RunProjection;
|
||||
use fabro_types::{
|
||||
DirtyStatus, GitContext, PreRunPushOutcome, RunSpec, WorkflowSettings, test_support,
|
||||
};
|
||||
use fabro_types::{DirtyStatus, GitContext, RunSpec, WorkflowSettings, test_support};
|
||||
use git2::{ErrorClass, ErrorCode};
|
||||
|
||||
use super::*;
|
||||
|
|
@ -634,11 +632,10 @@ mod tests {
|
|||
automation: None,
|
||||
source_directory: Some("/Users/client/project".to_string()),
|
||||
git: Some(GitContext {
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: DirtyStatus::Clean,
|
||||
push_outcome: PreRunPushOutcome::NotAttempted,
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: DirtyStatus::Clean,
|
||||
}),
|
||||
labels: HashMap::new(),
|
||||
provenance: test_support::test_run_provenance(),
|
||||
|
|
@ -670,11 +667,10 @@ mod tests {
|
|||
},
|
||||
)),
|
||||
pre_run_git: Some(GitContext {
|
||||
origin_url: origin_url.to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: DirtyStatus::Clean,
|
||||
push_outcome: PreRunPushOutcome::NotAttempted,
|
||||
origin_url: origin_url.to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: DirtyStatus::Clean,
|
||||
}),
|
||||
fork_source_ref: None,
|
||||
base_branch: None,
|
||||
|
|
|
|||
|
|
@ -576,7 +576,6 @@ fn main() {
|
|||
),
|
||||
("SecretType", "fabro_types::SecretType", &[]),
|
||||
("DiffStats", "fabro_types::DiffStats", &[]),
|
||||
("PreRunPushOutcome", "fabro_types::PreRunPushOutcome", &[]),
|
||||
("DirtyStatus", "fabro_types::DirtyStatus", &[]),
|
||||
("GitContext", "fabro_types::GitContext", &[]),
|
||||
(
|
||||
|
|
|
|||
|
|
@ -53,15 +53,14 @@ pub mod types {
|
|||
McpTransportView, Message, PairId, PairMessageId, PairMessageRecord, PairMessageRequest,
|
||||
PairRecord, PairStartRequest, PairStatus, PairTarget, PairTranscriptEntry,
|
||||
PairTranscriptResponse, ParallelBranchId, ParallelBranchResult, PendingInterviewRecord,
|
||||
PermissionLevel, PreRunPushOutcome, Principal, PullRequest, PullRequestDetails,
|
||||
PullRequestDetailsStatus, PullRequestDetailsUnavailableReason, PullRequestLink,
|
||||
PullRequestMeta, PullRequestResponse, QuestionType, ReasoningOutput, RepositoryRef,
|
||||
ReviewTarget, ReviewTargetKind, Role, Run, RunApproval, RunApprovalState,
|
||||
RunClientProvenance, RunEvent, RunEventDetailContentKind, RunEventDetailResponse,
|
||||
RunFailure, RunPairStatusResponse, RunProjection, RunProvenance, RunRunnableSource,
|
||||
RunSandbox, RunSandboxFailure, RunSandboxInstance, RunSandboxKind, RunSandboxPlan,
|
||||
RunSandboxRuntime, RunServerProvenance, RunSize, SandboxDetails, SandboxInfo,
|
||||
SandboxListMeta, SandboxListResponse, SandboxNetwork, SandboxNetworkPolicy,
|
||||
PermissionLevel, Principal, PullRequest, PullRequestDetails, PullRequestDetailsStatus,
|
||||
PullRequestDetailsUnavailableReason, PullRequestLink, PullRequestMeta, PullRequestResponse,
|
||||
QuestionType, ReasoningOutput, RepositoryRef, ReviewTarget, ReviewTargetKind, Role, Run,
|
||||
RunApproval, RunApprovalState, RunClientProvenance, RunEvent, RunEventDetailContentKind,
|
||||
RunEventDetailResponse, RunFailure, RunPairStatusResponse, RunProjection, RunProvenance,
|
||||
RunRunnableSource, RunSandbox, RunSandboxFailure, RunSandboxInstance, RunSandboxKind,
|
||||
RunSandboxPlan, RunSandboxRuntime, RunServerProvenance, RunSize, SandboxDetails,
|
||||
SandboxInfo, SandboxListMeta, SandboxListResponse, SandboxNetwork, SandboxNetworkPolicy,
|
||||
SandboxNetworkPolicyMode, SandboxProviderKind, SandboxProviderLookupError,
|
||||
SandboxResources, SandboxService, SandboxServiceListResponse, SandboxState,
|
||||
SandboxTimestamps, SecretMetadata, SecretType, ServerSettings, SessionDetail, SessionId,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
use std::any::{TypeId, type_name};
|
||||
|
||||
use fabro_api::types::{
|
||||
DirtyStatus as ApiDirtyStatus, GitContext as ApiGitContext,
|
||||
PreRunPushOutcome as ApiPreRunPushOutcome,
|
||||
};
|
||||
use fabro_types::{DirtyStatus, GitContext, PreRunPushOutcome};
|
||||
use fabro_api::types::{DirtyStatus as ApiDirtyStatus, GitContext as ApiGitContext};
|
||||
use fabro_types::{DirtyStatus, GitContext};
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn git_context_reuses_canonical_types() {
|
||||
assert_same_type::<ApiGitContext, GitContext>();
|
||||
assert_same_type::<ApiDirtyStatus, DirtyStatus>();
|
||||
assert_same_type::<ApiPreRunPushOutcome, PreRunPushOutcome>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -33,14 +29,10 @@ fn dirty_status_serializes_with_snake_case_strings() {
|
|||
#[test]
|
||||
fn git_context_with_known_sha_round_trips() {
|
||||
let ctx = GitContext {
|
||||
origin_url: "https://github.com/acme/widgets".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: Some("abc123".to_string()),
|
||||
dirty: DirtyStatus::Clean,
|
||||
push_outcome: PreRunPushOutcome::Succeeded {
|
||||
remote: "origin".to_string(),
|
||||
branch: "main".to_string(),
|
||||
},
|
||||
origin_url: "https://github.com/acme/widgets".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: Some("abc123".to_string()),
|
||||
dirty: DirtyStatus::Clean,
|
||||
};
|
||||
let json = serde_json::to_value(&ctx).unwrap();
|
||||
assert_eq!(
|
||||
|
|
@ -50,11 +42,6 @@ fn git_context_with_known_sha_round_trips() {
|
|||
"branch": "main",
|
||||
"sha": "abc123",
|
||||
"dirty": "clean",
|
||||
"push_outcome": {
|
||||
"type": "succeeded",
|
||||
"remote": "origin",
|
||||
"branch": "main",
|
||||
},
|
||||
})
|
||||
);
|
||||
let round_trip: GitContext = serde_json::from_value(json).unwrap();
|
||||
|
|
@ -64,16 +51,14 @@ fn git_context_with_known_sha_round_trips() {
|
|||
#[test]
|
||||
fn git_context_omits_absent_sha_on_serialize() {
|
||||
let ctx = GitContext {
|
||||
origin_url: "https://github.com/acme/widgets".to_string(),
|
||||
branch: "feature/foo".to_string(),
|
||||
sha: None,
|
||||
dirty: DirtyStatus::Unknown,
|
||||
push_outcome: PreRunPushOutcome::SkippedNoRemote,
|
||||
origin_url: "https://github.com/acme/widgets".to_string(),
|
||||
branch: "feature/foo".to_string(),
|
||||
sha: None,
|
||||
dirty: DirtyStatus::Unknown,
|
||||
};
|
||||
let json = serde_json::to_value(&ctx).unwrap();
|
||||
assert!(json.get("sha").is_none());
|
||||
assert_eq!(json["dirty"], "unknown");
|
||||
assert_eq!(json["push_outcome"]["type"], "skipped_no_remote");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -82,12 +67,23 @@ fn git_context_deserializes_when_sha_is_absent() {
|
|||
"origin_url": "https://github.com/acme/widgets",
|
||||
"branch": "main",
|
||||
"dirty": "dirty",
|
||||
"push_outcome": { "type": "not_attempted" },
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(ctx.sha, None);
|
||||
assert_eq!(ctx.dirty, DirtyStatus::Dirty);
|
||||
assert_eq!(ctx.push_outcome, PreRunPushOutcome::NotAttempted);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_context_tolerates_legacy_push_outcome_field() {
|
||||
let ctx: GitContext = serde_json::from_value(json!({
|
||||
"origin_url": "https://github.com/acme/widgets",
|
||||
"branch": "main",
|
||||
"dirty": "clean",
|
||||
"push_outcome": { "type": "not_attempted" },
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(ctx.origin_url, "https://github.com/acme/widgets");
|
||||
assert_eq!(ctx.dirty, DirtyStatus::Clean);
|
||||
}
|
||||
|
||||
fn assert_same_type<T: 'static, U: 'static>() {
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
use std::any::{TypeId, type_name};
|
||||
|
||||
use fabro_api::types::PreRunPushOutcome as ApiPreRunPushOutcome;
|
||||
use fabro_types::PreRunPushOutcome;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn pre_run_push_outcome_reuses_canonical_type() {
|
||||
assert_same_type::<ApiPreRunPushOutcome, PreRunPushOutcome>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn singletons_serialize_with_only_a_type_field() {
|
||||
assert_eq!(
|
||||
serde_json::to_value(PreRunPushOutcome::NotAttempted).unwrap(),
|
||||
json!({ "type": "not_attempted" })
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_value(PreRunPushOutcome::SkippedNoRemote).unwrap(),
|
||||
json!({ "type": "skipped_no_remote" })
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn succeeded_carries_remote_and_branch() {
|
||||
let outcome = PreRunPushOutcome::Succeeded {
|
||||
remote: "origin".to_string(),
|
||||
branch: "feature/foo".to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
serde_json::to_value(&outcome).unwrap(),
|
||||
json!({
|
||||
"type": "succeeded",
|
||||
"remote": "origin",
|
||||
"branch": "feature/foo",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_carries_message_alongside_remote_and_branch() {
|
||||
let outcome = PreRunPushOutcome::Failed {
|
||||
remote: "origin".to_string(),
|
||||
branch: "feature/foo".to_string(),
|
||||
message: "permission denied".to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
serde_json::to_value(&outcome).unwrap(),
|
||||
json!({
|
||||
"type": "failed",
|
||||
"remote": "origin",
|
||||
"branch": "feature/foo",
|
||||
"message": "permission denied",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skipped_remote_mismatch_carries_remote_and_repo_origin_url() {
|
||||
let outcome = PreRunPushOutcome::SkippedRemoteMismatch {
|
||||
remote: "git@github.com:user/fork.git".to_string(),
|
||||
repo_origin_url: "https://github.com/acme/canonical.git".to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
serde_json::to_value(&outcome).unwrap(),
|
||||
json!({
|
||||
"type": "skipped_remote_mismatch",
|
||||
"remote": "git@github.com:user/fork.git",
|
||||
"repo_origin_url": "https://github.com/acme/canonical.git",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserializes_each_variant_from_discriminator_payloads() {
|
||||
let not_attempted: PreRunPushOutcome =
|
||||
serde_json::from_value(json!({ "type": "not_attempted" })).unwrap();
|
||||
assert_eq!(not_attempted, PreRunPushOutcome::NotAttempted);
|
||||
|
||||
let succeeded: PreRunPushOutcome = serde_json::from_value(json!({
|
||||
"type": "succeeded",
|
||||
"remote": "origin",
|
||||
"branch": "main",
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(succeeded, PreRunPushOutcome::Succeeded {
|
||||
remote: "origin".to_string(),
|
||||
branch: "main".to_string(),
|
||||
});
|
||||
|
||||
let failed: PreRunPushOutcome = serde_json::from_value(json!({
|
||||
"type": "failed",
|
||||
"remote": "origin",
|
||||
"branch": "main",
|
||||
"message": "denied",
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(failed, PreRunPushOutcome::Failed {
|
||||
remote: "origin".to_string(),
|
||||
branch: "main".to_string(),
|
||||
message: "denied".to_string(),
|
||||
});
|
||||
|
||||
let skipped_no_remote: PreRunPushOutcome =
|
||||
serde_json::from_value(json!({ "type": "skipped_no_remote" })).unwrap();
|
||||
assert_eq!(skipped_no_remote, PreRunPushOutcome::SkippedNoRemote);
|
||||
|
||||
let skipped_mismatch: PreRunPushOutcome = serde_json::from_value(json!({
|
||||
"type": "skipped_remote_mismatch",
|
||||
"remote": "git@github.com:user/fork.git",
|
||||
"repo_origin_url": "https://github.com/acme/canonical.git",
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(skipped_mismatch, PreRunPushOutcome::SkippedRemoteMismatch {
|
||||
remote: "git@github.com:user/fork.git".to_string(),
|
||||
repo_origin_url: "https://github.com/acme/canonical.git".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
fn assert_same_type<T: 'static, U: 'static>() {
|
||||
assert_eq!(
|
||||
TypeId::of::<T>(),
|
||||
TypeId::of::<U>(),
|
||||
"{} should be the same type as {}",
|
||||
type_name::<T>(),
|
||||
type_name::<U>()
|
||||
);
|
||||
}
|
||||
|
|
@ -109,7 +109,7 @@ pub use pull_request::{
|
|||
pub use reasoning::ReasoningOutput;
|
||||
pub use repository::{GitHubRepositorySlug, RepositoryProvider, RepositoryRef};
|
||||
pub use run::{
|
||||
DirtyStatus, ForkSourceRef, GitContext, PreRunPushOutcome, RunClientProvenance, RunProvenance,
|
||||
DirtyStatus, ForkSourceRef, GitContext, RunClientProvenance, RunProvenance,
|
||||
RunServerProvenance, RunSpec,
|
||||
};
|
||||
pub use run_blob_id::RunBlobId;
|
||||
|
|
|
|||
|
|
@ -41,34 +41,13 @@ pub enum DirtyStatus {
|
|||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum PreRunPushOutcome {
|
||||
NotAttempted,
|
||||
Succeeded {
|
||||
remote: String,
|
||||
branch: String,
|
||||
},
|
||||
Failed {
|
||||
remote: String,
|
||||
branch: String,
|
||||
message: String,
|
||||
},
|
||||
SkippedNoRemote,
|
||||
SkippedRemoteMismatch {
|
||||
remote: String,
|
||||
repo_origin_url: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct GitContext {
|
||||
pub origin_url: String,
|
||||
pub branch: String,
|
||||
pub origin_url: String,
|
||||
pub branch: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub sha: Option<String>,
|
||||
pub dirty: DirtyStatus,
|
||||
pub push_outcome: PreRunPushOutcome,
|
||||
pub sha: Option<String>,
|
||||
pub dirty: DirtyStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use fabro_types::graph::Graph;
|
||||
use fabro_types::run::{DirtyStatus, ForkSourceRef, GitContext, PreRunPushOutcome};
|
||||
use fabro_types::run::{DirtyStatus, ForkSourceRef, GitContext};
|
||||
use fabro_types::run_event::run::{RunCreatedProps, RunParentLinkedProps, RunParentUnlinkedProps};
|
||||
use fabro_types::run_event::{RunSessionTurnFailedCode, RunSessionTurnFailedProps};
|
||||
use fabro_types::settings::InterpString;
|
||||
|
|
@ -33,11 +33,10 @@ fn run_created_props_round_trip_templated_settings() {
|
|||
provenance: test_run_provenance(),
|
||||
manifest_blob: None,
|
||||
git: Some(GitContext {
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: DirtyStatus::Unknown,
|
||||
push_outcome: PreRunPushOutcome::SkippedNoRemote,
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: None,
|
||||
dirty: DirtyStatus::Unknown,
|
||||
}),
|
||||
fork_source_ref: Some(ForkSourceRef {
|
||||
source_run_id: fixtures::RUN_2,
|
||||
|
|
@ -58,7 +57,7 @@ fn run_created_props_round_trip_templated_settings() {
|
|||
);
|
||||
assert_eq!(json["git"]["branch"], "main");
|
||||
assert_eq!(json["git"]["dirty"], "unknown");
|
||||
assert_eq!(json["git"]["push_outcome"]["type"], "skipped_no_remote");
|
||||
assert!(json["git"].get("push_outcome").is_none());
|
||||
assert_eq!(
|
||||
json["web_url"],
|
||||
"http://localhost:3000/runs/01JNQVR7M0EJ5GKAT2SC4ERS1Z"
|
||||
|
|
@ -138,6 +137,41 @@ fn run_created_props_defaults_additive_fields_for_legacy_events() {
|
|||
assert_eq!(props.automation, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_created_props_tolerates_legacy_git_push_outcome() {
|
||||
let json = serde_json::json!({
|
||||
"title": null,
|
||||
"settings": WorkflowSettings::default(),
|
||||
"graph": Graph::new("ship"),
|
||||
"labels": {},
|
||||
"provenance": test_run_provenance(),
|
||||
"git": {
|
||||
"origin_url": "https://github.com/fabro-sh/fabro.git",
|
||||
"branch": "main",
|
||||
"sha": "abc123",
|
||||
"dirty": "clean",
|
||||
"push_outcome": {
|
||||
"type": "failed",
|
||||
"remote": "origin",
|
||||
"branch": "main",
|
||||
"message": "remote rejected"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let props: RunCreatedProps =
|
||||
serde_json::from_value(json).expect("legacy event with push_outcome should deserialize");
|
||||
let git = props.git.as_ref().expect("git context should be present");
|
||||
assert_eq!(git.origin_url, "https://github.com/fabro-sh/fabro.git");
|
||||
assert_eq!(git.branch, "main");
|
||||
assert_eq!(git.sha.as_deref(), Some("abc123"));
|
||||
assert_eq!(git.dirty, DirtyStatus::Clean);
|
||||
|
||||
let reserialized = serde_json::to_value(&props).expect("props should reserialize");
|
||||
assert!(reserialized["git"].get("push_outcome").is_none());
|
||||
assert_eq!(reserialized["git"]["origin_url"], git.origin_url);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_parent_events_round_trip_parent_ids() {
|
||||
let linked = EventBody::RunParentLinked(RunParentLinkedProps {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use fabro_types::graph::Graph;
|
||||
use fabro_types::run::{DirtyStatus, GitContext, PreRunPushOutcome, RunSpec};
|
||||
use fabro_types::run::{DirtyStatus, GitContext, RunSpec};
|
||||
use fabro_types::settings::{ProjectNamespace, WorkflowNamespace};
|
||||
use fabro_types::test_support::test_run_provenance;
|
||||
use fabro_types::{WorkflowSettings, fixtures};
|
||||
|
|
@ -32,14 +32,10 @@ fn sample_run_spec() -> RunSpec {
|
|||
manifest_blob: None,
|
||||
definition_blob: None,
|
||||
git: Some(GitContext {
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: Some("abc123".to_string()),
|
||||
dirty: DirtyStatus::Dirty,
|
||||
push_outcome: PreRunPushOutcome::SkippedRemoteMismatch {
|
||||
remote: "https://github.com/user/fork.git".to_string(),
|
||||
repo_origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
},
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: Some("abc123".to_string()),
|
||||
dirty: DirtyStatus::Dirty,
|
||||
}),
|
||||
fork_source_ref: None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use fabro_types::graph::Graph;
|
||||
use fabro_types::run::{DirtyStatus, ForkSourceRef, GitContext, PreRunPushOutcome, RunSpec};
|
||||
use fabro_types::run::{DirtyStatus, ForkSourceRef, GitContext, RunSpec};
|
||||
use fabro_types::settings::InterpString;
|
||||
use fabro_types::settings::run::RunGoal;
|
||||
use fabro_types::test_support::test_run_provenance;
|
||||
|
|
@ -32,14 +32,10 @@ fn run_spec_round_trips_templated_settings() {
|
|||
manifest_blob: None,
|
||||
definition_blob: None,
|
||||
git: Some(GitContext {
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: Some("abc123".to_string()),
|
||||
dirty: DirtyStatus::Clean,
|
||||
push_outcome: PreRunPushOutcome::Succeeded {
|
||||
remote: "origin".to_string(),
|
||||
branch: "main".to_string(),
|
||||
},
|
||||
origin_url: "https://github.com/fabro-sh/fabro.git".to_string(),
|
||||
branch: "main".to_string(),
|
||||
sha: Some("abc123".to_string()),
|
||||
dirty: DirtyStatus::Clean,
|
||||
}),
|
||||
fork_source_ref: Some(ForkSourceRef {
|
||||
source_run_id: fixtures::RUN_2,
|
||||
|
|
@ -58,7 +54,7 @@ fn run_spec_round_trips_templated_settings() {
|
|||
assert_eq!(json["git"]["branch"], "main");
|
||||
assert_eq!(json["git"]["sha"], "abc123");
|
||||
assert_eq!(json["git"]["dirty"], "clean");
|
||||
assert_eq!(json["git"]["push_outcome"]["type"], "succeeded");
|
||||
assert!(json["git"].get("push_outcome").is_none());
|
||||
assert_eq!(json["fork_source_ref"]["checkpoint_sha"], "def456");
|
||||
assert_eq!(json["automation"]["id"], "nightly");
|
||||
assert_eq!(json["automation"]["trigger_id"], "schedule_1");
|
||||
|
|
|
|||
|
|
@ -271,12 +271,6 @@ models/parallel-branch-result.ts
|
|||
models/pending-interview-record.ts
|
||||
models/pending-reason.ts
|
||||
models/permission-level.ts
|
||||
models/pre-run-push-outcome-failed.ts
|
||||
models/pre-run-push-outcome-not-attempted.ts
|
||||
models/pre-run-push-outcome-skipped-no-remote.ts
|
||||
models/pre-run-push-outcome-skipped-remote-mismatch.ts
|
||||
models/pre-run-push-outcome-succeeded.ts
|
||||
models/pre-run-push-outcome.ts
|
||||
models/preflight-check-detail.ts
|
||||
models/preflight-check-report.ts
|
||||
models/preflight-check-result.ts
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@
|
|||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { DirtyStatus } from './dirty-status';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PreRunPushOutcome } from './pre-run-push-outcome';
|
||||
|
||||
/**
|
||||
* Observable git state captured before the run starts.
|
||||
|
|
@ -37,5 +34,4 @@ export interface GitContext {
|
|||
*/
|
||||
'sha'?: string | null;
|
||||
'dirty': DirtyStatus;
|
||||
'push_outcome': PreRunPushOutcome;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -241,12 +241,6 @@ export * from './parallel-branch-result';
|
|||
export * from './pending-interview-record';
|
||||
export * from './pending-reason';
|
||||
export * from './permission-level';
|
||||
export * from './pre-run-push-outcome';
|
||||
export * from './pre-run-push-outcome-failed';
|
||||
export * from './pre-run-push-outcome-not-attempted';
|
||||
export * from './pre-run-push-outcome-skipped-no-remote';
|
||||
export * from './pre-run-push-outcome-skipped-remote-mismatch';
|
||||
export * from './pre-run-push-outcome-succeeded';
|
||||
export * from './preflight-check-detail';
|
||||
export * from './preflight-check-report';
|
||||
export * from './preflight-check-result';
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface PreRunPushOutcomeFailed {
|
||||
'type': PreRunPushOutcomeFailedTypeEnum;
|
||||
'remote': string;
|
||||
'branch': string;
|
||||
'message': string;
|
||||
}
|
||||
|
||||
export const PreRunPushOutcomeFailedTypeEnum = {
|
||||
FAILED: 'failed'
|
||||
} as const;
|
||||
|
||||
export type PreRunPushOutcomeFailedTypeEnum = typeof PreRunPushOutcomeFailedTypeEnum[keyof typeof PreRunPushOutcomeFailedTypeEnum];
|
||||
|
|
@ -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.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface PreRunPushOutcomeNotAttempted {
|
||||
'type': PreRunPushOutcomeNotAttemptedTypeEnum;
|
||||
}
|
||||
|
||||
export const PreRunPushOutcomeNotAttemptedTypeEnum = {
|
||||
NOT_ATTEMPTED: 'not_attempted'
|
||||
} as const;
|
||||
|
||||
export type PreRunPushOutcomeNotAttemptedTypeEnum = typeof PreRunPushOutcomeNotAttemptedTypeEnum[keyof typeof PreRunPushOutcomeNotAttemptedTypeEnum];
|
||||
|
|
@ -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.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface PreRunPushOutcomeSkippedNoRemote {
|
||||
'type': PreRunPushOutcomeSkippedNoRemoteTypeEnum;
|
||||
}
|
||||
|
||||
export const PreRunPushOutcomeSkippedNoRemoteTypeEnum = {
|
||||
SKIPPED_NO_REMOTE: 'skipped_no_remote'
|
||||
} as const;
|
||||
|
||||
export type PreRunPushOutcomeSkippedNoRemoteTypeEnum = typeof PreRunPushOutcomeSkippedNoRemoteTypeEnum[keyof typeof PreRunPushOutcomeSkippedNoRemoteTypeEnum];
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface PreRunPushOutcomeSkippedRemoteMismatch {
|
||||
'type': PreRunPushOutcomeSkippedRemoteMismatchTypeEnum;
|
||||
'remote': string;
|
||||
'repo_origin_url': string;
|
||||
}
|
||||
|
||||
export const PreRunPushOutcomeSkippedRemoteMismatchTypeEnum = {
|
||||
SKIPPED_REMOTE_MISMATCH: 'skipped_remote_mismatch'
|
||||
} as const;
|
||||
|
||||
export type PreRunPushOutcomeSkippedRemoteMismatchTypeEnum = typeof PreRunPushOutcomeSkippedRemoteMismatchTypeEnum[keyof typeof PreRunPushOutcomeSkippedRemoteMismatchTypeEnum];
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface PreRunPushOutcomeSucceeded {
|
||||
'type': PreRunPushOutcomeSucceededTypeEnum;
|
||||
'remote': string;
|
||||
'branch': string;
|
||||
}
|
||||
|
||||
export const PreRunPushOutcomeSucceededTypeEnum = {
|
||||
SUCCEEDED: 'succeeded'
|
||||
} as const;
|
||||
|
||||
export type PreRunPushOutcomeSucceededTypeEnum = typeof PreRunPushOutcomeSucceededTypeEnum[keyof typeof PreRunPushOutcomeSucceededTypeEnum];
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PreRunPushOutcomeFailed } from './pre-run-push-outcome-failed';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PreRunPushOutcomeNotAttempted } from './pre-run-push-outcome-not-attempted';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PreRunPushOutcomeSkippedNoRemote } from './pre-run-push-outcome-skipped-no-remote';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PreRunPushOutcomeSkippedRemoteMismatch } from './pre-run-push-outcome-skipped-remote-mismatch';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { PreRunPushOutcomeSucceeded } from './pre-run-push-outcome-succeeded';
|
||||
|
||||
/**
|
||||
* @type PreRunPushOutcome
|
||||
* Outcome of the CLI\'s best-effort pre-run push.
|
||||
*/
|
||||
export type PreRunPushOutcome = { type: 'failed' } & PreRunPushOutcomeFailed | { type: 'not_attempted' } & PreRunPushOutcomeNotAttempted | { type: 'skipped_no_remote' } & PreRunPushOutcomeSkippedNoRemote | { type: 'skipped_remote_mismatch' } & PreRunPushOutcomeSkippedRemoteMismatch | { type: 'succeeded' } & PreRunPushOutcomeSucceeded;
|
||||
Loading…
Add table
Reference in a new issue