diff --git a/Cargo.lock b/Cargo.lock index 3707a22bc..b73e3fa27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2355,6 +2355,7 @@ name = "fabro-vault" version = "0.217.0-nightly.0" dependencies = [ "chrono", + "fabro-types", "serde", "serde_json", "tempfile", diff --git a/docs/public/api-reference/fabro-api.yaml b/docs/public/api-reference/fabro-api.yaml index 93b95e6e0..0a2f57a73 100644 --- a/docs/public/api-reference/fabro-api.yaml +++ b/docs/public/api-reference/fabro-api.yaml @@ -3583,7 +3583,7 @@ components: description: CLI working directory at invocation time. example: "/tmp/project" git: - $ref: "#/components/schemas/ManifestGit" + $ref: "#/components/schemas/GitContext" goal: $ref: "#/components/schemas/ManifestGoal" args: @@ -3599,14 +3599,13 @@ components: additionalProperties: $ref: "#/components/schemas/ManifestWorkflow" - ManifestGit: - description: Observable git state from the CLI working directory. + GitContext: + description: Observable git state captured before the run starts. type: object required: - origin_url - branch - - sha - - clean + - dirty - push_outcome properties: origin_url: @@ -3618,17 +3617,32 @@ components: description: Current branch name. example: feature/foo sha: - type: string - description: Current commit SHA. + type: ["string", "null"] + description: Current commit SHA, when known. example: abc123def - clean: - type: boolean - description: Whether the working tree has uncommitted changes. + dirty: + $ref: "#/components/schemas/DirtyStatus" push_outcome: - $ref: "#/components/schemas/ManifestPreRunPushOutcome" + $ref: "#/components/schemas/PreRunPushOutcome" - ManifestPreRunPushOutcome: + 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 @@ -3637,18 +3651,67 @@ components: 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", "null"] - branch: - type: ["string", "null"] - message: - type: ["string", "null"] + type: string repo_origin_url: - type: ["string", "null"] + type: string ManifestGoal: description: Resolved goal with provenance. @@ -4466,43 +4529,6 @@ components: - dirty - unknown - PreRunPushOutcome: - description: Outcome of the CLI submitter's best-effort pre-run push. - type: object - required: - - type - properties: - type: - type: string - enum: - - not_attempted - - succeeded - - failed - - skipped_no_remote - - skipped_remote_mismatch - remote: - type: ["string", "null"] - branch: - type: ["string", "null"] - message: - type: ["string", "null"] - repo_origin_url: - type: ["string", "null"] - - PreRunGitContext: - description: Submitter-side git context captured before run creation. - type: object - required: - - local_dirty - - push_outcome - properties: - display_base_sha: - type: ["string", "null"] - local_dirty: - $ref: "#/components/schemas/DirtyStatus" - push_outcome: - $ref: "#/components/schemas/PreRunPushOutcome" - ForkSourceRef: description: Source checkpoint used to initialize a forked or rewound run. type: object @@ -4535,10 +4561,6 @@ components: type: ["string", "null"] source_directory: type: ["string", "null"] - repo_origin_url: - type: ["string", "null"] - base_branch: - type: ["string", "null"] labels: type: object additionalProperties: @@ -4550,9 +4572,9 @@ components: type: ["string", "null"] definition_blob: type: ["string", "null"] - pre_run_git: + git: oneOf: - - $ref: "#/components/schemas/PreRunGitContext" + - $ref: "#/components/schemas/GitContext" - type: "null" fork_source_ref: oneOf: diff --git a/lib/crates/fabro-api/build.rs b/lib/crates/fabro-api/build.rs index cd7a6abee..b4ce4c378 100644 --- a/lib/crates/fabro-api/build.rs +++ b/lib/crates/fabro-api/build.rs @@ -318,6 +318,11 @@ fn main() { "fabro_types::settings::run::MergeStrategy", &[], ), + ("SecretType", "fabro_types::SecretType", &[]), + ("DiffStats", "fabro_types::DiffStats", &[]), + ("PreRunPushOutcome", "fabro_types::PreRunPushOutcome", &[]), + ("DirtyStatus", "fabro_types::DirtyStatus", &[]), + ("GitContext", "fabro_types::GitContext", &[]), ]; for (name, path, impls) in replacements { settings.with_replacement(*name, *path, impls.iter().copied()); diff --git a/lib/crates/fabro-api/src/lib.rs b/lib/crates/fabro-api/src/lib.rs index f21a37c46..f939bbe84 100644 --- a/lib/crates/fabro-api/src/lib.rs +++ b/lib/crates/fabro-api/src/lib.rs @@ -27,7 +27,10 @@ pub mod types { pub use fabro_types::status::{ BlockedReason, FailureReason, RunControlAction, RunStatus, SuccessReason, TerminalStatus, }; - pub use fabro_types::{RepositoryReference, RunSummary, ServerSettings, WorkflowSettings}; + pub use fabro_types::{ + DiffStats, DirtyStatus, GitContext, PreRunPushOutcome, RepositoryReference, RunSummary, + SecretType, ServerSettings, WorkflowSettings, + }; pub use crate::generated::types::*; } diff --git a/lib/crates/fabro-api/tests/diff_stats_round_trip.rs b/lib/crates/fabro-api/tests/diff_stats_round_trip.rs new file mode 100644 index 000000000..f0609a179 --- /dev/null +++ b/lib/crates/fabro-api/tests/diff_stats_round_trip.rs @@ -0,0 +1,46 @@ +use std::any::{TypeId, type_name}; + +use fabro_api::types::DiffStats as ApiDiffStats; +use fabro_types::DiffStats; +use serde_json::json; + +#[test] +fn diff_stats_reuses_canonical_type() { + assert_same_type::(); +} + +#[test] +fn diff_stats_serializes_with_required_integer_fields() { + let stats = DiffStats { + additions: 567, + deletions: 234, + }; + assert_eq!( + serde_json::to_value(stats).unwrap(), + json!({ + "additions": 567, + "deletions": 234, + }) + ); +} + +#[test] +fn diff_stats_deserializes_from_required_payload() { + let stats: DiffStats = serde_json::from_value(json!({ + "additions": 1, + "deletions": 0, + })) + .unwrap(); + assert_eq!(stats.additions, 1); + assert_eq!(stats.deletions, 0); +} + +fn assert_same_type() { + assert_eq!( + TypeId::of::(), + TypeId::of::(), + "{} should be the same type as {}", + type_name::(), + type_name::() + ); +} diff --git a/lib/crates/fabro-api/tests/git_context_round_trip.rs b/lib/crates/fabro-api/tests/git_context_round_trip.rs new file mode 100644 index 000000000..e055ba91d --- /dev/null +++ b/lib/crates/fabro-api/tests/git_context_round_trip.rs @@ -0,0 +1,101 @@ +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 serde_json::json; + +#[test] +fn git_context_reuses_canonical_types() { + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); +} + +#[test] +fn dirty_status_serializes_with_snake_case_strings() { + assert_eq!( + serde_json::to_value(DirtyStatus::Clean).unwrap(), + json!("clean") + ); + assert_eq!( + serde_json::to_value(DirtyStatus::Dirty).unwrap(), + json!("dirty") + ); + assert_eq!( + serde_json::to_value(DirtyStatus::Unknown).unwrap(), + json!("unknown") + ); +} + +#[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(), + }, + }; + let json = serde_json::to_value(&ctx).unwrap(); + assert_eq!( + json, + json!({ + "origin_url": "https://github.com/acme/widgets", + "branch": "main", + "sha": "abc123", + "dirty": "clean", + "push_outcome": { + "type": "succeeded", + "remote": "origin", + "branch": "main", + }, + }) + ); + let round_trip: GitContext = serde_json::from_value(json).unwrap(); + assert_eq!(round_trip, ctx); +} + +#[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, + }; + 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] +fn git_context_deserializes_when_sha_is_absent() { + let ctx: GitContext = serde_json::from_value(json!({ + "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); +} + +fn assert_same_type() { + assert_eq!( + TypeId::of::(), + TypeId::of::(), + "{} should be the same type as {}", + type_name::(), + type_name::() + ); +} diff --git a/lib/crates/fabro-api/tests/pre_run_push_outcome_round_trip.rs b/lib/crates/fabro-api/tests/pre_run_push_outcome_round_trip.rs new file mode 100644 index 000000000..92e2d45f6 --- /dev/null +++ b/lib/crates/fabro-api/tests/pre_run_push_outcome_round_trip.rs @@ -0,0 +1,128 @@ +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::(); +} + +#[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() { + assert_eq!( + TypeId::of::(), + TypeId::of::(), + "{} should be the same type as {}", + type_name::(), + type_name::() + ); +} diff --git a/lib/crates/fabro-api/tests/secret_type_round_trip.rs b/lib/crates/fabro-api/tests/secret_type_round_trip.rs new file mode 100644 index 000000000..d3ff0398b --- /dev/null +++ b/lib/crates/fabro-api/tests/secret_type_round_trip.rs @@ -0,0 +1,46 @@ +use std::any::{TypeId, type_name}; + +use fabro_api::types::SecretType as ApiSecretType; +use fabro_types::SecretType; +use serde_json::json; + +#[test] +fn secret_type_reuses_canonical_type() { + assert_same_type::(); +} + +#[test] +fn secret_type_serializes_as_snake_case_strings() { + assert_eq!( + serde_json::to_value(SecretType::Environment).unwrap(), + json!("environment") + ); + assert_eq!( + serde_json::to_value(SecretType::File).unwrap(), + json!("file") + ); + assert_eq!( + serde_json::to_value(SecretType::Credential).unwrap(), + json!("credential") + ); +} + +#[test] +fn secret_type_deserializes_each_variant() { + let env: SecretType = serde_json::from_value(json!("environment")).unwrap(); + assert_eq!(env, SecretType::Environment); + let file: SecretType = serde_json::from_value(json!("file")).unwrap(); + assert_eq!(file, SecretType::File); + let cred: SecretType = serde_json::from_value(json!("credential")).unwrap(); + assert_eq!(cred, SecretType::Credential); +} + +fn assert_same_type() { + assert_eq!( + TypeId::of::(), + TypeId::of::(), + "{} should be the same type as {}", + type_name::(), + type_name::() + ); +} diff --git a/lib/crates/fabro-cli/src/commands/run/checkpoints.rs b/lib/crates/fabro-cli/src/commands/run/checkpoints.rs index 2ca5cf190..7c6e50dd2 100644 --- a/lib/crates/fabro-cli/src/commands/run/checkpoints.rs +++ b/lib/crates/fabro-cli/src/commands/run/checkpoints.rs @@ -31,7 +31,7 @@ pub(crate) async fn ensure_origin_if_local( let state = client.get_run_state(run_id).await?; if let Some(run_spec) = state.spec { - ensure_matching_repo_origin(run_spec.repo_origin_url.as_deref(), verb)?; + ensure_matching_repo_origin(run_spec.repo_origin_url(), verb)?; } Ok(()) } diff --git a/lib/crates/fabro-cli/src/manifest_builder.rs b/lib/crates/fabro-cli/src/manifest_builder.rs index 9f3f6b757..66bdf3a3a 100644 --- a/lib/crates/fabro-cli/src/manifest_builder.rs +++ b/lib/crates/fabro-cli/src/manifest_builder.rs @@ -7,14 +7,14 @@ use std::collections::{HashMap, HashSet}; use std::path::{Component, Path, PathBuf}; use anyhow::{Context, Result, anyhow}; -use fabro_api::types::{self, ManifestPreRunPushOutcome, ManifestPreRunPushOutcomeType}; +use fabro_api::types; use fabro_config::project::{self, discover_project_config, resolve_workflow_path}; use fabro_config::run::{resolve_run_goal_from_layer, resolve_run_goal_from_namespace}; use fabro_config::{CliLayer, DaytonaDockerfileLayer, RunLayer, WorkflowSettingsBuilder}; use fabro_graphviz::graph::AttrValue; use fabro_graphviz::parser; use fabro_types::settings::run::{ResolvedGoalSource, ResolvedRunGoal}; -use fabro_types::{RunId, WorkflowSettings}; +use fabro_types::{DirtyStatus, GitContext, PreRunPushOutcome, RunId, WorkflowSettings}; use fabro_workflow::git::{GitSyncStatus, branch_needs_push, head_sha, push_branch, sync_status}; use crate::args::{PreflightArgs, RunArgs}; @@ -138,7 +138,7 @@ pub(crate) fn build_run_manifest(input: ManifestBuildInput) -> Result types::ManifestGoal { } } -fn build_manifest_git( +fn build_git_context( repo_path: &Path, configured_repo_origin_url: Option<&str>, -) -> Option { +) -> Option { let (origin_url, branch) = detect_manifest_repo_info(repo_path)?; - let sha = head_sha(repo_path).ok()?; - let status = sync_status(repo_path, "origin", Some(&branch)); - let clean = status != GitSyncStatus::Dirty; + let sha = head_sha(repo_path).ok(); + let dirty = match sync_status(repo_path, "origin", Some(&branch)) { + GitSyncStatus::Dirty => DirtyStatus::Dirty, + GitSyncStatus::Synced | GitSyncStatus::Unsynced => DirtyStatus::Clean, + }; let repo_origin_url = configured_repo_origin_url .map(fabro_github::normalize_repo_origin_url) .filter(|url| !url.is_empty()) @@ -522,12 +524,12 @@ fn build_manifest_git( origin_url.as_deref(), configured_repo_origin_url, ); - Some(types::ManifestGit { - branch, - clean, + Some(GitContext { origin_url: repo_origin_url, - push_outcome, + branch, sha, + dirty, + push_outcome, }) } @@ -565,58 +567,37 @@ fn build_manifest_push_outcome( branch: &str, origin_url: Option<&str>, configured_repo_origin_url: Option<&str>, -) -> ManifestPreRunPushOutcome { - if origin_url.is_none() { - return ManifestPreRunPushOutcome { - type_: ManifestPreRunPushOutcomeType::SkippedNoRemote, - remote: None, - branch: Some(branch.to_string()), - message: None, - repo_origin_url: None, - }; - } +) -> PreRunPushOutcome { + let Some(origin_url) = origin_url else { + return PreRunPushOutcome::SkippedNoRemote; + }; + if let Some(repo_origin_url) = configured_repo_origin_url .map(fabro_github::normalize_repo_origin_url) .filter(|url| !url.is_empty()) { - let remote = origin_url - .map(fabro_github::normalize_repo_origin_url) - .unwrap_or_default(); + let remote = fabro_github::normalize_repo_origin_url(origin_url); if remote != repo_origin_url { - return ManifestPreRunPushOutcome { - type_: ManifestPreRunPushOutcomeType::SkippedRemoteMismatch, - remote: Some(remote), - branch: Some(branch.to_string()), - message: None, - repo_origin_url: Some(repo_origin_url), + return PreRunPushOutcome::SkippedRemoteMismatch { + remote, + repo_origin_url, }; } } if !branch_needs_push(repo_path, "origin", branch) { - return ManifestPreRunPushOutcome { - type_: ManifestPreRunPushOutcomeType::NotAttempted, - remote: None, - branch: None, - message: None, - repo_origin_url: None, - }; + return PreRunPushOutcome::NotAttempted; } match push_branch(repo_path, "origin", branch) { - Ok(()) => ManifestPreRunPushOutcome { - type_: ManifestPreRunPushOutcomeType::Succeeded, - remote: Some("origin".to_string()), - branch: Some(branch.to_string()), - message: None, - repo_origin_url: None, + Ok(()) => PreRunPushOutcome::Succeeded { + remote: "origin".to_string(), + branch: branch.to_string(), }, - Err(err) => ManifestPreRunPushOutcome { - type_: ManifestPreRunPushOutcomeType::Failed, - remote: Some("origin".to_string()), - branch: Some(branch.to_string()), - message: Some(err.to_string()), - repo_origin_url: None, + Err(err) => PreRunPushOutcome::Failed { + remote: "origin".to_string(), + branch: branch.to_string(), + message: err.to_string(), }, } } @@ -1018,18 +999,10 @@ 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.type_, - ManifestPreRunPushOutcomeType::SkippedRemoteMismatch - ); - assert_eq!( - git.push_outcome.remote.as_deref(), - Some("https://github.com/user/forked-target") - ); - assert_eq!( - git.push_outcome.repo_origin_url.as_deref(), - Some("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(), + }); } fn init_git_repo(path: &Path, branch: &str, origin_url: &str) { diff --git a/lib/crates/fabro-server/src/run_files.rs b/lib/crates/fabro-server/src/run_files.rs index 162807361..f891c163f 100644 --- a/lib/crates/fabro-server/src/run_files.rs +++ b/lib/crates/fabro-server/src/run_files.rs @@ -34,8 +34,8 @@ use fabro_sandbox::reconnect::reconnect; use fabro_static::EnvVars; use fabro_types::RunId; use fabro_workflow::sandbox_git::{ - DiffError, DiffLineStats, DiffNumstat, RawDiffEntry, SubmoduleChange, SymlinkChange, - list_changed_files_raw, list_diff_numstat, stream_blob_metadata, stream_blobs, + DiffError, DiffNumstat, RawDiffEntry, SubmoduleChange, SymlinkChange, list_changed_files_raw, + list_diff_numstat, stream_blob_metadata, stream_blobs, }; use futures_util::FutureExt; use serde::Deserialize; @@ -373,13 +373,6 @@ async fn materialize_sandbox_path(state: &Arc, run_id: &RunId) -> List }) } -fn line_stats_to_api(stats: DiffLineStats) -> DiffStats { - DiffStats { - additions: i64::try_from(stats.additions).unwrap_or(i64::MAX), - deletions: i64::try_from(stats.deletions).unwrap_or(i64::MAX), - } -} - /// Choose a degraded reason given the current projection. Docker-provider /// runs aren't supported by the deployed server; completed runs are "gone"; /// everything else is a transient "unreachable" (sandbox may come back). @@ -451,7 +444,7 @@ fn build_fallback_response( /// summation we use on the live-sandbox path so the toolbar shows the same /// numbers regardless of which response branch we took. fn patch_to_stats(patch: &str) -> DiffStats { - let mut stats = DiffLineStats::default(); + let mut stats = DiffStats::default(); for section in patch.split("diff --git ").skip(1) { if patch_section_omits_line_stats(section) { continue; @@ -467,7 +460,7 @@ fn patch_to_stats(patch: &str) -> DiffStats { } } } - line_stats_to_api(stats) + stats } fn patch_section_omits_line_stats(section: &str) -> bool { @@ -729,7 +722,7 @@ fn classify_entries( is_sensitive_fn: fn(&str) -> bool, ) -> ClassifiedEntries { let mut out = Vec::with_capacity(raw.len()); - let mut stats = DiffLineStats::default(); + let mut stats = DiffStats::default(); for entry in raw { let (new_path, old_path) = match entry { @@ -786,7 +779,7 @@ fn classify_entries( ClassifiedEntries { entries: out, - stats: line_stats_to_api(stats), + stats, } } @@ -1638,7 +1631,7 @@ diff --git a/src/main.rs b/src/main.rs ] { numstat .line_stats_by_path - .insert(path.to_string(), DiffLineStats { + .insert(path.to_string(), DiffStats { additions, deletions, }); diff --git a/lib/crates/fabro-server/src/run_manifest.rs b/lib/crates/fabro-server/src/run_manifest.rs index 200cc0f47..d6ab35473 100644 --- a/lib/crates/fabro-server/src/run_manifest.rs +++ b/lib/crates/fabro-server/src/run_manifest.rs @@ -27,7 +27,7 @@ use fabro_types::settings::run::{ ApprovalMode, DaytonaNetworkLayer, DaytonaSettings, DockerSettings, DockerfileSource, RunGoal, RunMode, RunNamespace, WorktreeMode, }; -use fabro_types::{DirtyStatus, PreRunGitContext, PreRunPushOutcome, RunId, WorkflowSettings}; +use fabro_types::{RunId, WorkflowSettings}; use fabro_util::check_report::{CheckDetail, CheckReport, CheckResult, CheckSection, CheckStatus}; use fabro_validate::Severity; use fabro_workflow::Error as WorkflowError; @@ -41,7 +41,7 @@ use crate::server::AppState; #[derive(Clone)] pub(crate) struct PreparedManifest { pub cwd: PathBuf, - pub git: Option, + pub git: Option, pub root_source: String, pub run_id: Option, pub settings: WorkflowSettings, @@ -165,12 +165,7 @@ pub(crate) fn create_run_input( workflow_bundle: Some(prepared.workflow_bundle), submitted_manifest_bytes: None, run_id: prepared.run_id, - repo_origin_url: prepared.git.as_ref().and_then(|git| { - let origin_url = fabro_github::normalize_repo_origin_url(&git.origin_url); - (!origin_url.is_empty()).then_some(origin_url) - }), - base_branch: prepared.git.as_ref().map(|git| git.branch.clone()), - pre_run_git: prepared.git.as_ref().map(pre_run_git_from_manifest), + git: prepared.git, fork_source_ref: None, in_place: prepared.in_place, provenance: None, @@ -178,48 +173,6 @@ pub(crate) fn create_run_input( } } -fn pre_run_git_from_manifest(git: &types::ManifestGit) -> PreRunGitContext { - PreRunGitContext { - display_base_sha: Some(git.sha.clone()), - local_dirty: if git.clean { - DirtyStatus::Clean - } else { - DirtyStatus::Dirty - }, - push_outcome: pre_run_push_outcome_from_manifest(&git.push_outcome), - } -} - -fn pre_run_push_outcome_from_manifest( - outcome: &types::ManifestPreRunPushOutcome, -) -> PreRunPushOutcome { - match outcome.type_ { - types::ManifestPreRunPushOutcomeType::NotAttempted => PreRunPushOutcome::NotAttempted, - types::ManifestPreRunPushOutcomeType::Succeeded => PreRunPushOutcome::Succeeded { - remote: outcome - .remote - .clone() - .unwrap_or_else(|| "origin".to_string()), - branch: outcome.branch.clone().unwrap_or_default(), - }, - types::ManifestPreRunPushOutcomeType::Failed => PreRunPushOutcome::Failed { - remote: outcome - .remote - .clone() - .unwrap_or_else(|| "origin".to_string()), - branch: outcome.branch.clone().unwrap_or_default(), - message: outcome.message.clone().unwrap_or_default(), - }, - types::ManifestPreRunPushOutcomeType::SkippedNoRemote => PreRunPushOutcome::SkippedNoRemote, - types::ManifestPreRunPushOutcomeType::SkippedRemoteMismatch => { - PreRunPushOutcome::SkippedRemoteMismatch { - remote: outcome.remote.clone().unwrap_or_default(), - repo_origin_url: outcome.repo_origin_url.clone().unwrap_or_default(), - } - } - } -} - pub(crate) async fn run_preflight( state: &AppState, prepared: &PreparedManifest, @@ -565,13 +518,19 @@ fn base_preflight_checks(prepared: &PreparedManifest, graph: &Graph) -> Vec "clean", + fabro_types::DirtyStatus::Dirty => "dirty", + fabro_types::DirtyStatus::Unknown => "unknown", + }) ), - warn: prepared.git.as_ref().is_some_and(|git| !git.clean), + warn: prepared + .git + .as_ref() + .is_some_and(|git| git.dirty != fabro_types::DirtyStatus::Clean), }, ], remediation: None, diff --git a/lib/crates/fabro-server/src/server.rs b/lib/crates/fabro-server/src/server.rs index 3e0b5c2a1..e611db324 100644 --- a/lib/crates/fabro-server/src/server.rs +++ b/lib/crates/fabro-server/src/server.rs @@ -35,9 +35,9 @@ pub use fabro_api::types::{ QuestionType as ApiQuestionType, RenderWorkflowGraphDirection, RenderWorkflowGraphRequest, RewindRequest, RewindResponse, RunArtifactEntry, RunArtifactListResponse, RunBilling, RunBillingStage, RunBillingTotals, RunError, RunManifest, RunStage, RunStatusResponse, - SandboxFileEntry, SandboxFileListResponse, SecretType as ApiSecretType, SshAccessRequest, - SshAccessResponse, StageStatus as ApiStageStatus, StartRunRequest, SubmitAnswerRequest, - SystemFeatures, SystemInfoResponse, SystemRunCounts, TimelineEntryResponse, WriteBlobResponse, + SandboxFileEntry, SandboxFileListResponse, SshAccessRequest, SshAccessResponse, + StageStatus as ApiStageStatus, StartRunRequest, SubmitAnswerRequest, SystemFeatures, + SystemInfoResponse, SystemRunCounts, TimelineEntryResponse, WriteBlobResponse, }; use fabro_auth::{ CredentialSource, VaultCredentialSource, auth_issue_message, parse_credential_secret, @@ -1826,20 +1826,12 @@ async fn list_secrets(_auth: AuthenticatedService, State(state): State SecretType { - match secret_type { - ApiSecretType::Environment => SecretType::Environment, - ApiSecretType::File => SecretType::File, - ApiSecretType::Credential => SecretType::Credential, - } -} - async fn create_secret( _auth: AuthenticatedService, State(state): State>, Json(body): Json, ) -> Response { - let secret_type = secret_type_from_api(body.type_); + let secret_type = body.type_; let name = body.name; let value = body.value; let description = body.description; @@ -5407,14 +5399,14 @@ impl<'a> RunPrInputs<'a> { "Run spec missing from store.", ) })?; - let origin_url = run_spec.repo_origin_url.as_deref().ok_or_else(|| { + let origin_url = run_spec.repo_origin_url().ok_or_else(|| { ApiError::with_code( StatusCode::BAD_REQUEST, "Run has no repo origin URL — pull request creation requires git metadata.", "missing_repo_origin", ) })?; - let base_branch = run_spec.base_branch.as_deref().ok_or_else(|| { + let base_branch = run_spec.base_branch().ok_or_else(|| { ApiError::with_code( StatusCode::BAD_REQUEST, "Run has no base branch — pull request creation requires git metadata.", @@ -9724,19 +9716,27 @@ strategy = "token" "goal".to_string(), AttrValue::String("Ship the server-side PR".to_string()), ); + 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, + }), + _ => None, + }; let run_spec = RunSpec { run_id, settings: fabro_types::WorkflowSettings::default(), graph, workflow_slug: Some("test".to_string()), source_directory: Some("/tmp/project".to_string()), - repo_origin_url: repo_origin_url.map(str::to_string), - base_branch: base_branch.map(str::to_string), + git: git.clone(), labels: HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, }; @@ -9751,13 +9751,11 @@ strategy = "token" labels: run_spec.labels.clone().into_iter().collect(), run_dir: run_spec.source_directory.clone().unwrap_or_default(), source_directory: run_spec.source_directory.clone(), - repo_origin_url: run_spec.repo_origin_url.clone(), - base_branch: run_spec.base_branch.clone(), workflow_slug: run_spec.workflow_slug.clone(), db_prefix: None, provenance: run_spec.provenance.clone(), manifest_blob: None, - pre_run_git: None, + git, fork_source_ref: None, in_place: false, }, diff --git a/lib/crates/fabro-store/src/run_state.rs b/lib/crates/fabro-store/src/run_state.rs index 5ce226fd4..f32f72685 100644 --- a/lib/crates/fabro-store/src/run_state.rs +++ b/lib/crates/fabro-store/src/run_state.rs @@ -54,13 +54,11 @@ impl RunProjectionReducer for RunProjection { graph: props.graph.clone(), workflow_slug: props.workflow_slug.clone(), source_directory: props.source_directory.clone(), - repo_origin_url: props.repo_origin_url.clone(), - base_branch: props.base_branch.clone(), labels, provenance: props.provenance.clone(), manifest_blob: props.manifest_blob, definition_blob: None, - pre_run_git: props.pre_run_git.clone(), + git: props.git.clone(), fork_source_ref: props.fork_source_ref.clone(), in_place: props.in_place, }); @@ -397,7 +395,8 @@ pub(crate) fn build_summary(state: &RunProjection, run_id: &RunId) -> RunSummary state .spec .as_ref() - .and_then(|spec| spec.repo_origin_url.clone()), + .and_then(|spec| spec.git.as_ref()) + .map(|git| git.origin_url.clone()), state.start.as_ref().map(|start| start.start_time), state.status.unwrap_or(RunStatus::Submitted), state.pending_control, @@ -990,13 +989,11 @@ mod tests { graph: fabro_types::Graph::new("test"), workflow_slug: Some("test".to_string()), source_directory: Some("/tmp/repo".to_string()), - repo_origin_url: None, - base_branch: None, + git: None, labels: HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, }); diff --git a/lib/crates/fabro-store/src/slate/mod.rs b/lib/crates/fabro-store/src/slate/mod.rs index fefbd1082..e54ed3cc5 100644 --- a/lib/crates/fabro-store/src/slate/mod.rs +++ b/lib/crates/fabro-store/src/slate/mod.rs @@ -357,13 +357,17 @@ mod tests { graph, workflow_slug: Some("night-sky".to_string()), source_directory: Some(format!("/tmp/{label}")), - repo_origin_url: Some("https://github.com/fabro-sh/fabro".to_string()), - base_branch: Some("main".to_string()), labels: std::collections::HashMap::from([("team".to_string(), "infra".to_string())]), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: 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, + }), fork_source_ref: None, in_place: false, } @@ -400,8 +404,7 @@ mod tests { "workflow_slug": run_spec.workflow_slug, "source_directory": run_spec.source_directory, "run_dir": format!("/tmp/{label}"), - "repo_origin_url": run_spec.repo_origin_url, - "base_branch": run_spec.base_branch, + "git": run_spec.git, "labels": run_spec.labels, }), )) diff --git a/lib/crates/fabro-store/tests/serializable_projection.rs b/lib/crates/fabro-store/tests/serializable_projection.rs index c0321e07d..16063bdfd 100644 --- a/lib/crates/fabro-store/tests/serializable_projection.rs +++ b/lib/crates/fabro-store/tests/serializable_projection.rs @@ -17,13 +17,17 @@ fn sample_run_spec() -> RunSpec { graph: Graph::new("ship"), workflow_slug: Some("demo".to_string()), source_directory: Some("/tmp/project".to_string()), - repo_origin_url: Some("https://github.com/fabro-sh/fabro.git".to_string()), - base_branch: Some("main".to_string()), labels: HashMap::from([("team".to_string(), "platform".to_string())]), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: 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, + }), fork_source_ref: None, in_place: false, } diff --git a/lib/crates/fabro-types/src/diff.rs b/lib/crates/fabro-types/src/diff.rs new file mode 100644 index 000000000..201c26515 --- /dev/null +++ b/lib/crates/fabro-types/src/diff.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub struct DiffStats { + pub additions: i64, + pub deletions: i64, +} diff --git a/lib/crates/fabro-types/src/lib.rs b/lib/crates/fabro-types/src/lib.rs index f0a69b29b..6075e8e96 100644 --- a/lib/crates/fabro-types/src/lib.rs +++ b/lib/crates/fabro-types/src/lib.rs @@ -7,6 +7,7 @@ pub mod blob_ref; pub mod checkpoint; pub mod conclusion; pub mod dense; +pub mod diff; pub mod event_envelope; pub mod failure_signature; pub mod graph; @@ -23,6 +24,7 @@ pub mod run_id; pub mod run_projection; pub mod run_summary; pub mod sandbox_record; +pub mod secret; pub mod settings; pub mod stage_id; pub mod start; @@ -42,6 +44,7 @@ pub use blob_ref::{ pub use checkpoint::Checkpoint; pub use conclusion::{Conclusion, StageSummary}; pub use dense::{ServerSettings, UserSettings, WorkflowSettings}; +pub use diff::DiffStats; pub use event_envelope::EventEnvelope; pub use failure_signature::FailureSignature; pub use graph::{AttrValue, Edge, Graph, Node, is_llm_handler_type, shape_to_handler_type}; @@ -57,8 +60,8 @@ pub use retro::{ OpenItemKind, Retro, RetroNarrative, SmoothnessRating, StageRetro, }; pub use run::{ - DirtyStatus, ForkSourceRef, PreRunGitContext, PreRunPushOutcome, RunAuthMethod, - RunClientProvenance, RunProvenance, RunServerProvenance, RunSpec, RunSubjectProvenance, + DirtyStatus, ForkSourceRef, GitContext, PreRunPushOutcome, RunAuthMethod, RunClientProvenance, + RunProvenance, RunServerProvenance, RunSpec, RunSubjectProvenance, }; pub use run_blob_id::RunBlobId; pub use run_event::{ActorKind, ActorRef, EventBody, RunEvent, RunNoticeLevel}; @@ -66,6 +69,7 @@ pub use run_id::{RunId, fixtures}; pub use run_projection::{NodeState, PendingInterviewRecord, RunProjection}; pub use run_summary::RunSummary; pub use sandbox_record::SandboxRecord; +pub use secret::SecretType; pub use stage_id::{ParallelBranchId, StageId}; pub use start::StartRecord; pub use status::{ diff --git a/lib/crates/fabro-types/src/run.rs b/lib/crates/fabro-types/src/run.rs index 93b6bdd09..e53cfbb2e 100644 --- a/lib/crates/fabro-types/src/run.rs +++ b/lib/crates/fabro-types/src/run.rs @@ -76,11 +76,13 @@ pub enum PreRunPushOutcome { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct PreRunGitContext { +pub struct GitContext { + pub origin_url: String, + pub branch: String, #[serde(default, skip_serializing_if = "Option::is_none")] - pub display_base_sha: Option, - pub local_dirty: DirtyStatus, - pub push_outcome: PreRunPushOutcome, + pub sha: Option, + pub dirty: DirtyStatus, + pub push_outcome: PreRunPushOutcome, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -98,10 +100,6 @@ pub struct RunSpec { pub workflow_slug: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub source_directory: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub repo_origin_url: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub base_branch: Option, #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub labels: HashMap, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -111,7 +109,7 @@ pub struct RunSpec { #[serde(default, skip_serializing_if = "Option::is_none")] pub definition_blob: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub pre_run_git: Option, + pub git: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub fork_source_ref: Option, #[serde(default)] @@ -151,17 +149,17 @@ impl RunSpec { #[must_use] pub fn repo_origin_url(&self) -> Option<&str> { - self.repo_origin_url.as_deref() + self.git.as_ref().map(|git| git.origin_url.as_str()) } #[must_use] pub fn base_branch(&self) -> Option<&str> { - self.base_branch.as_deref() + self.git.as_ref().map(|git| git.branch.as_str()) } #[must_use] - pub fn pre_run_git(&self) -> Option<&PreRunGitContext> { - self.pre_run_git.as_ref() + pub fn git(&self) -> Option<&GitContext> { + self.git.as_ref() } #[must_use] diff --git a/lib/crates/fabro-types/src/run_event/run.rs b/lib/crates/fabro-types/src/run_event/run.rs index b0d845890..92f12842d 100644 --- a/lib/crates/fabro-types/src/run_event/run.rs +++ b/lib/crates/fabro-types/src/run_event/run.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use super::{ActorRef, BilledTokenCounts, RunNoticeLevel}; use crate::status::{BlockedReason, FailureReason, SuccessReason}; use crate::{ - ForkSourceRef, Graph, PreRunGitContext, RunBlobId, RunControlAction, RunId, RunProvenance, + ForkSourceRef, GitContext, Graph, RunBlobId, RunControlAction, RunId, RunProvenance, WorkflowSettings, }; @@ -23,10 +23,6 @@ pub struct RunCreatedProps { #[serde(default, skip_serializing_if = "Option::is_none")] pub source_directory: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub repo_origin_url: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub base_branch: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] pub workflow_slug: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub db_prefix: Option, @@ -35,7 +31,7 @@ pub struct RunCreatedProps { #[serde(default, skip_serializing_if = "Option::is_none")] pub manifest_blob: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub pre_run_git: Option, + pub git: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub fork_source_ref: Option, #[serde(default)] diff --git a/lib/crates/fabro-types/src/secret.rs b/lib/crates/fabro-types/src/secret.rs new file mode 100644 index 000000000..87d1dc94a --- /dev/null +++ b/lib/crates/fabro-types/src/secret.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; +use strum::Display; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Display, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum SecretType { + #[default] + Environment, + File, + Credential, +} diff --git a/lib/crates/fabro-types/tests/run_event_serde.rs b/lib/crates/fabro-types/tests/run_event_serde.rs index 47a244e0b..bed58e213 100644 --- a/lib/crates/fabro-types/tests/run_event_serde.rs +++ b/lib/crates/fabro-types/tests/run_event_serde.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use fabro_types::graph::Graph; -use fabro_types::run::{DirtyStatus, ForkSourceRef, PreRunGitContext, PreRunPushOutcome}; +use fabro_types::run::{DirtyStatus, ForkSourceRef, GitContext, PreRunPushOutcome}; use fabro_types::run_event::run::RunCreatedProps; use fabro_types::settings::InterpString; use fabro_types::settings::run::RunGoal; @@ -23,16 +23,16 @@ fn run_created_props_round_trip_templated_settings() { labels: BTreeMap::from([("team".to_string(), "platform".to_string())]), run_dir: "/tmp/run".to_string(), source_directory: Some("/Users/client/project".to_string()), - repo_origin_url: Some("https://github.com/fabro-sh/fabro.git".to_string()), - base_branch: Some("main".to_string()), workflow_slug: Some("demo".to_string()), db_prefix: Some("run_".to_string()), provenance: None, manifest_blob: None, - pre_run_git: Some(PreRunGitContext { - display_base_sha: Some("abc123".to_string()), - local_dirty: DirtyStatus::Unknown, - push_outcome: PreRunPushOutcome::SkippedNoRemote, + 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, }), fork_source_ref: Some(ForkSourceRef { source_run_id: fixtures::RUN_2, @@ -46,9 +46,12 @@ fn run_created_props_round_trip_templated_settings() { assert!(json.get("host_repo_path").is_none()); assert_eq!(json["source_directory"], "/Users/client/project"); assert_eq!( - json["pre_run_git"]["push_outcome"]["type"], - "skipped_no_remote" + json["git"]["origin_url"], + "https://github.com/fabro-sh/fabro.git" ); + assert_eq!(json["git"]["branch"], "main"); + assert_eq!(json["git"]["dirty"], "unknown"); + assert_eq!(json["git"]["push_outcome"]["type"], "skipped_no_remote"); assert_eq!(json["in_place"], true); let round_trip: RunCreatedProps = diff --git a/lib/crates/fabro-types/tests/run_spec_methods.rs b/lib/crates/fabro-types/tests/run_spec_methods.rs index 000a6b225..96b37b909 100644 --- a/lib/crates/fabro-types/tests/run_spec_methods.rs +++ b/lib/crates/fabro-types/tests/run_spec_methods.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use fabro_types::graph::Graph; -use fabro_types::run::{DirtyStatus, PreRunGitContext, PreRunPushOutcome, RunSpec}; +use fabro_types::run::{DirtyStatus, GitContext, PreRunPushOutcome, RunSpec}; use fabro_types::{WorkflowSettings, fixtures}; fn sample_run_spec() -> RunSpec { @@ -11,16 +11,16 @@ fn sample_run_spec() -> RunSpec { graph: Graph::new("ship"), workflow_slug: Some("demo".to_string()), source_directory: Some("/Users/client/project".to_string()), - repo_origin_url: Some("https://github.com/fabro-sh/fabro.git".to_string()), - base_branch: Some("main".to_string()), labels: HashMap::from([("team".to_string(), "platform".to_string())]), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: Some(PreRunGitContext { - display_base_sha: Some("abc123".to_string()), - local_dirty: DirtyStatus::Dirty, - push_outcome: PreRunPushOutcome::SkippedRemoteMismatch { + 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(), }, @@ -44,9 +44,7 @@ fn run_spec_getters_return_declared_fields() { Some("platform") ); assert_eq!( - run_spec - .pre_run_git() - .and_then(|ctx| ctx.display_base_sha.as_deref()), + run_spec.git().and_then(|ctx| ctx.sha.as_deref()), Some("abc123") ); assert_eq!( diff --git a/lib/crates/fabro-types/tests/run_spec_serde.rs b/lib/crates/fabro-types/tests/run_spec_serde.rs index 9875d58f1..e00e04210 100644 --- a/lib/crates/fabro-types/tests/run_spec_serde.rs +++ b/lib/crates/fabro-types/tests/run_spec_serde.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use fabro_types::graph::Graph; -use fabro_types::run::{DirtyStatus, ForkSourceRef, PreRunGitContext, PreRunPushOutcome, RunSpec}; +use fabro_types::run::{DirtyStatus, ForkSourceRef, GitContext, PreRunPushOutcome, RunSpec}; use fabro_types::settings::InterpString; use fabro_types::settings::run::RunGoal; use fabro_types::{WorkflowSettings, fixtures}; @@ -20,16 +20,16 @@ fn run_spec_round_trips_templated_settings() { graph: Graph::new("ship"), workflow_slug: Some("demo".to_string()), source_directory: Some("/Users/client/project".to_string()), - repo_origin_url: Some("https://github.com/fabro-sh/fabro.git".to_string()), - base_branch: Some("main".to_string()), labels: HashMap::from([("team".to_string(), "platform".to_string())]), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: Some(PreRunGitContext { - display_base_sha: Some("abc123".to_string()), - local_dirty: DirtyStatus::Clean, - push_outcome: PreRunPushOutcome::Succeeded { + 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(), }, @@ -45,8 +45,14 @@ fn run_spec_round_trips_templated_settings() { assert!(json.get("working_directory").is_none()); assert!(json.get("host_repo_path").is_none()); assert_eq!(json["source_directory"], "/Users/client/project"); - assert_eq!(json["pre_run_git"]["local_dirty"], "clean"); - assert_eq!(json["pre_run_git"]["push_outcome"]["type"], "succeeded"); + assert_eq!( + json["git"]["origin_url"], + "https://github.com/fabro-sh/fabro.git" + ); + 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_eq!(json["fork_source_ref"]["checkpoint_sha"], "def456"); assert_eq!(json["in_place"], false); diff --git a/lib/crates/fabro-vault/Cargo.toml b/lib/crates/fabro-vault/Cargo.toml index 29602442a..f03479fb0 100644 --- a/lib/crates/fabro-vault/Cargo.toml +++ b/lib/crates/fabro-vault/Cargo.toml @@ -14,6 +14,7 @@ workspace = true [dependencies] chrono.workspace = true +fabro-types = { path = "../fabro-types" } serde.workspace = true serde_json.workspace = true ulid.workspace = true diff --git a/lib/crates/fabro-vault/src/lib.rs b/lib/crates/fabro-vault/src/lib.rs index 121fa520d..f79cad879 100644 --- a/lib/crates/fabro-vault/src/lib.rs +++ b/lib/crates/fabro-vault/src/lib.rs @@ -7,14 +7,7 @@ use std::collections::HashMap; use std::path::{Component, Path, PathBuf}; use std::{fmt, io}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum SecretType { - #[default] - Environment, - File, - Credential, -} +pub use fabro_types::SecretType; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct SecretEntry { diff --git a/lib/crates/fabro-workflow/src/event.rs b/lib/crates/fabro-workflow/src/event.rs index f19c5c635..8f8bc5297 100644 --- a/lib/crates/fabro-workflow/src/event.rs +++ b/lib/crates/fabro-workflow/src/event.rs @@ -5,8 +5,8 @@ use std::sync::Arc; use std::sync::atomic::{AtomicI64, Ordering}; use ::fabro_types::{ - ActorRef, BilledTokenCounts, BlockedReason, FailureReason, ForkSourceRef, ParallelBranchId, - PreRunGitContext, PullRequestRecord, RunBlobId, RunControlAction, RunEvent, RunId, + ActorRef, BilledTokenCounts, BlockedReason, FailureReason, ForkSourceRef, GitContext, + ParallelBranchId, PullRequestRecord, RunBlobId, RunControlAction, RunEvent, RunId, RunProvenance, StageId, StageStatus, SuccessReason, run_event as fabro_types, }; use anyhow::{Context, Result}; @@ -49,10 +49,6 @@ pub enum Event { #[serde(default, skip_serializing_if = "Option::is_none")] source_directory: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - repo_origin_url: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - base_branch: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] workflow_slug: Option, #[serde(default, skip_serializing_if = "Option::is_none")] db_prefix: Option, @@ -61,7 +57,7 @@ pub enum Event { #[serde(default, skip_serializing_if = "Option::is_none")] manifest_blob: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pre_run_git: Option, + git: Option, #[serde(default, skip_serializing_if = "Option::is_none")] fork_source_ref: Option, #[serde(default)] @@ -1522,13 +1518,11 @@ fn event_body_from_event(event: &Event) -> EventBody { labels, run_dir, source_directory, - repo_origin_url, - base_branch, workflow_slug, db_prefix, provenance, manifest_blob, - pre_run_git, + git, fork_source_ref, in_place, .. @@ -1541,13 +1535,11 @@ fn event_body_from_event(event: &Event) -> EventBody { labels: labels.clone(), run_dir: run_dir.clone(), source_directory: source_directory.clone(), - repo_origin_url: repo_origin_url.clone(), - base_branch: base_branch.clone(), workflow_slug: workflow_slug.clone(), db_prefix: db_prefix.clone(), provenance: provenance.clone(), manifest_blob: *manifest_blob, - pre_run_git: pre_run_git.clone(), + git: git.clone(), fork_source_ref: fork_source_ref.clone(), in_place: *in_place, }), @@ -3679,13 +3671,11 @@ mod tests { labels: BTreeMap::default(), run_dir: "/tmp/run".to_string(), source_directory: Some("/tmp/run".to_string()), - repo_origin_url: None, - base_branch: None, workflow_slug: None, db_prefix: None, provenance: Some(provenance), manifest_blob: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, }); diff --git a/lib/crates/fabro-workflow/src/operations/create.rs b/lib/crates/fabro-workflow/src/operations/create.rs index 376eb9ae7..57e6866d7 100644 --- a/lib/crates/fabro-workflow/src/operations/create.rs +++ b/lib/crates/fabro-workflow/src/operations/create.rs @@ -15,7 +15,7 @@ use fabro_sandbox::SandboxProvider; use fabro_store::Database; use fabro_template::{TemplateContext, render as render_template}; use fabro_types::settings::run::{RunMode, RunNamespace}; -use fabro_types::{ForkSourceRef, PreRunGitContext, RunId, RunProvenance, WorkflowSettings}; +use fabro_types::{ForkSourceRef, GitContext, RunId, RunProvenance, WorkflowSettings}; use fabro_util::json::normalize_json_value; use tokio::task::spawn_blocking; @@ -41,9 +41,7 @@ pub struct CreateRunInput { pub workflow_bundle: Option, pub submitted_manifest_bytes: Option>, pub run_id: Option, - pub repo_origin_url: Option, - pub base_branch: Option, - pub pre_run_git: Option, + pub git: Option, pub fork_source_ref: Option, pub in_place: bool, pub provenance: Option, @@ -64,10 +62,8 @@ struct PersistCreateOptions { run_dir: Option, workflow_slug: Option, labels: HashMap, - base_branch: Option, source_directory: Option, - repo_origin_url: Option, - pre_run_git: Option, + git: Option, fork_source_ref: Option, in_place: bool, provenance: Option, @@ -101,9 +97,7 @@ pub async fn create( workflow_bundle, submitted_manifest_bytes, run_id, - repo_origin_url, - base_branch, - pre_run_git, + git, fork_source_ref, in_place, provenance, @@ -138,10 +132,8 @@ pub async fn create( run_dir: Some(persisted_run_dir), workflow_slug: workflow_slug.or(resolved_workflow_slug), labels, - base_branch, source_directory, - repo_origin_url, - pre_run_git, + git, fork_source_ref, in_place, provenance, @@ -228,13 +220,11 @@ async fn persist_created_run( .collect::>(), run_dir: persisted.run_dir().display().to_string(), source_directory: record.source_directory.clone(), - repo_origin_url: record.repo_origin_url.clone(), - base_branch: record.base_branch.clone(), workflow_slug: record.workflow_slug.clone(), db_prefix: None, provenance: record.provenance.clone(), manifest_blob, - pre_run_git: record.pre_run_git.clone(), + git: record.git.clone(), fork_source_ref: record.fork_source_ref.clone(), in_place: record.in_place, }, @@ -350,10 +340,8 @@ fn persist_validated( run_dir, workflow_slug, labels, - base_branch, source_directory, - repo_origin_url, - pre_run_git, + git, fork_source_ref, in_place, provenance, @@ -376,13 +364,11 @@ fn persist_validated( graph: validated.graph().clone(), workflow_slug, source_directory, - repo_origin_url, - base_branch, labels, provenance, manifest_blob: None, definition_blob: None, - pre_run_git, + git, fork_source_ref, in_place, }; @@ -722,9 +708,7 @@ mod tests { workflow_bundle: None, submitted_manifest_bytes: None, run_id: None, - repo_origin_url: None, - base_branch: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, provenance: None, @@ -767,9 +751,7 @@ mod tests { workflow_bundle: None, submitted_manifest_bytes: None, run_id: None, - repo_origin_url: None, - base_branch: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, provenance: None, @@ -828,9 +810,13 @@ mod tests { workflow_bundle: None, submitted_manifest_bytes: None, run_id: Some(fixtures::RUN_1), - repo_origin_url: None, - base_branch: Some("main".to_string()), - pre_run_git: 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, + }), fork_source_ref: None, in_place: false, provenance: None, @@ -938,9 +924,7 @@ mod tests { workflow_bundle: None, submitted_manifest_bytes: None, run_id: Some(fixtures::RUN_2), - repo_origin_url: None, - base_branch: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, provenance: None, @@ -976,9 +960,13 @@ mod tests { workflow_bundle: None, submitted_manifest_bytes: None, run_id: Some(fixtures::RUN_2), - repo_origin_url: Some("https://github.com/acme/widgets".to_string()), - base_branch: None, - pre_run_git: 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, + }), fork_source_ref: None, in_place: false, provenance: None, @@ -990,7 +978,7 @@ mod tests { .unwrap(); assert_eq!( - created.persisted.run_spec().repo_origin_url.as_deref(), + created.persisted.run_spec().repo_origin_url(), Some("https://github.com/acme/widgets") ); } @@ -1042,9 +1030,7 @@ mod tests { workflow_bundle: None, submitted_manifest_bytes: None, run_id: Some(fixtures::RUN_3), - repo_origin_url: None, - base_branch: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, provenance: None, @@ -1087,9 +1073,7 @@ mod tests { workflow_bundle: None, submitted_manifest_bytes: None, run_id: Some(fixtures::RUN_64), - repo_origin_url: None, - base_branch: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, provenance: Some(fabro_types::RunProvenance { diff --git a/lib/crates/fabro-workflow/src/operations/fork.rs b/lib/crates/fabro-workflow/src/operations/fork.rs index d9fa71d24..a99b3f48b 100644 --- a/lib/crates/fabro-workflow/src/operations/fork.rs +++ b/lib/crates/fabro-workflow/src/operations/fork.rs @@ -123,7 +123,7 @@ fn validate_source_spec( "target checkpoint has an empty git_commit_sha; cannot fork".to_string(), )); } - let Some(origin) = spec.repo_origin_url.as_ref() else { + let Some(origin) = spec.repo_origin_url() else { return Err(Error::Validation( "source run has no repo_origin_url; cannot validate fork origin".to_string(), )); @@ -180,13 +180,11 @@ async fn persist_forked_run( labels: spec.labels.clone().into_iter().collect(), run_dir: String::new(), source_directory: spec.source_directory.clone(), - repo_origin_url: spec.repo_origin_url.clone(), - base_branch: spec.base_branch.clone(), workflow_slug: spec.workflow_slug.clone(), db_prefix: None, provenance: spec.provenance.clone(), manifest_blob: spec.manifest_blob, - pre_run_git: spec.pre_run_git.clone(), + git: spec.git.clone(), fork_source_ref: spec.fork_source_ref.clone(), in_place: spec.in_place, }) @@ -330,13 +328,17 @@ mod tests { labels: BTreeMap::new(), run_dir: "/tmp/source".to_string(), source_directory: Some("/client/source".to_string()), - repo_origin_url: Some("https://github.com/example/repo.git".to_string()), - base_branch: Some("main".to_string()), workflow_slug: Some("fork-source".to_string()), db_prefix: None, provenance: None, manifest_blob: None, - pre_run_git: 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, + }), fork_source_ref: None, in_place: false, }) diff --git a/lib/crates/fabro-workflow/src/operations/start.rs b/lib/crates/fabro-workflow/src/operations/start.rs index 1561a77ac..cba495574 100644 --- a/lib/crates/fabro-workflow/src/operations/start.rs +++ b/lib/crates/fabro-workflow/src/operations/start.rs @@ -359,8 +359,8 @@ impl RunSession { config: resolve_docker_config(resolved).unwrap_or_default(), github_app: services.github_app.clone(), run_id: Some(record.run_id), - clone_origin_url: record.repo_origin_url.clone(), - clone_branch: record.base_branch.clone(), + clone_origin_url: record.repo_origin_url().map(str::to_string), + clone_branch: record.base_branch().map(str::to_string), }, SandboxProvider::Daytona => { let api_key = match &services.vault { @@ -375,8 +375,8 @@ impl RunSession { config: resolve_daytona_config(resolved).unwrap_or_default(), github_app: services.github_app.clone(), run_id: Some(record.run_id), - clone_origin_url: record.repo_origin_url.clone(), - clone_branch: record.base_branch.clone(), + clone_origin_url: record.repo_origin_url().map(str::to_string), + clone_branch: record.base_branch().map(str::to_string), api_key, } } @@ -394,7 +394,7 @@ impl RunSession { devcontainer_env: HashMap::new(), toml_env, github_permissions, - origin_url: record.repo_origin_url.clone(), + origin_url: record.repo_origin_url().map(str::to_string), }; let devcontainer = resolved.sandbox.devcontainer.then(|| DevcontainerSpec { @@ -447,7 +447,7 @@ impl RunSession { preserve_sandbox: resolved.sandbox.preserve, pr_config, pr_github_app: services.github_app, - pr_origin_url: record.repo_origin_url.clone(), + pr_origin_url: record.repo_origin_url().map(str::to_string), pr_model: model, workflow_path, workflow_bundle, @@ -698,9 +698,9 @@ impl RunSession { labels: record.labels.clone(), workflow_slug: record.workflow_slug.clone(), github_app: self.github_app.clone(), - pre_run_git: record.pre_run_git.clone(), + pre_run_git: record.git.clone(), fork_source_ref: record.fork_source_ref.clone(), - base_branch: record.base_branch.clone(), + base_branch: record.base_branch().map(str::to_string), display_base_sha: None, git: self.git.clone(), }; @@ -1074,9 +1074,7 @@ mod tests { workflow_bundle: None, submitted_manifest_bytes: None, run_id: Some(fixtures::RUN_1), - repo_origin_url: None, - base_branch: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, provenance: None, @@ -1254,9 +1252,7 @@ mod tests { workflow_bundle: Some(workflow_bundle), submitted_manifest_bytes: None, run_id: Some(fixtures::RUN_1), - repo_origin_url: None, - base_branch: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, provenance: None, diff --git a/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs b/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs index 945fd7c97..6953fc7ac 100644 --- a/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs +++ b/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs @@ -145,13 +145,17 @@ fn persisted_workflow(graph: Graph, source: String, run_dir: &Path, run_id: RunI .display() .to_string(), ), - repo_origin_url: None, - base_branch: Some("main".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, + }), labels: HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, }, diff --git a/lib/crates/fabro-workflow/src/pipeline/initialize.rs b/lib/crates/fabro-workflow/src/pipeline/initialize.rs index 8e3743dea..b1f3b3f27 100644 --- a/lib/crates/fabro-workflow/src/pipeline/initialize.rs +++ b/lib/crates/fabro-workflow/src/pipeline/initialize.rs @@ -138,7 +138,7 @@ fn resolve_worktree_plan(options: &mut InitOptions) -> Option { .run_options .pre_run_git .as_ref() - .map(|git| git.local_dirty); + .map(|git| git.dirty); if matches!(local_dirty, Some(fabro_types::DirtyStatus::Dirty)) { let env_name = if !is_local { @@ -162,7 +162,7 @@ fn resolve_worktree_plan(options: &mut InitOptions) -> Option { .run_options .pre_run_git .as_ref() - .and_then(|git| git.display_base_sha.clone()); + .and_then(|git| git.sha.clone()); return None; } @@ -184,7 +184,7 @@ fn resolve_worktree_plan(options: &mut InitOptions) -> Option { .run_options .pre_run_git .as_ref() - .and_then(|git| git.display_base_sha.clone()), + .and_then(|git| git.sha.clone()), ) }; options.run_options.display_base_sha.clone_from(&base_sha); @@ -865,13 +865,17 @@ mod tests { graph, workflow_slug: Some("test".to_string()), source_directory: Some(std::env::current_dir().unwrap().display().to_string()), - repo_origin_url: None, - base_branch: Some("main".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, + }), labels: HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, }, diff --git a/lib/crates/fabro-workflow/src/pipeline/persist.rs b/lib/crates/fabro-workflow/src/pipeline/persist.rs index 277ca87aa..eb8c1bb7a 100644 --- a/lib/crates/fabro-workflow/src/pipeline/persist.rs +++ b/lib/crates/fabro-workflow/src/pipeline/persist.rs @@ -137,8 +137,13 @@ mod tests { graph, workflow_slug: Some("ship".to_string()), source_directory: Some("/tmp/project".to_string()), - repo_origin_url: None, - base_branch: Some("main".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, + }), labels: HashMap::from([ ("env".to_string(), "test".to_string()), ("team".to_string(), "workflow".to_string()), @@ -146,7 +151,6 @@ mod tests { provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, } @@ -164,13 +168,11 @@ mod tests { labels: record.labels.clone().into_iter().collect(), run_dir: run_dir.to_string_lossy().to_string(), source_directory: record.source_directory.clone(), - repo_origin_url: record.repo_origin_url.clone(), - base_branch: record.base_branch.clone(), workflow_slug: record.workflow_slug.clone(), db_prefix: None, provenance: record.provenance.clone(), manifest_blob: None, - pre_run_git: record.pre_run_git.clone(), + git: record.git.clone(), fork_source_ref: record.fork_source_ref.clone(), in_place: record.in_place, }) @@ -265,7 +267,7 @@ mod tests { ); assert_eq!(loaded_record.workflow_slug, expected.workflow_slug); assert_eq!(loaded_record.source_directory, expected.source_directory); - assert_eq!(loaded_record.base_branch, expected.base_branch); + assert_eq!(loaded_record.base_branch(), expected.base_branch()); assert_eq!(loaded_record.labels, expected.labels); assert_eq!(loaded.source(), source); assert!(loaded.diagnostics().is_empty()); diff --git a/lib/crates/fabro-workflow/src/pipeline/pull_request.rs b/lib/crates/fabro-workflow/src/pipeline/pull_request.rs index 6cd14b3fc..48572a331 100644 --- a/lib/crates/fabro-workflow/src/pipeline/pull_request.rs +++ b/lib/crates/fabro-workflow/src/pipeline/pull_request.rs @@ -1172,13 +1172,17 @@ mod tests { graph: Graph::new("test"), workflow_slug: Some("test".to_string()), source_directory: Some("/tmp/project".to_string()), - repo_origin_url: None, - base_branch: Some("main".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, + }), labels: HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, }; @@ -1191,13 +1195,11 @@ mod tests { labels: run_spec.labels.clone().into_iter().collect(), run_dir: "/tmp/project".to_string(), source_directory: run_spec.source_directory.clone(), - repo_origin_url: run_spec.repo_origin_url.clone(), - base_branch: run_spec.base_branch.clone(), workflow_slug: run_spec.workflow_slug.clone(), db_prefix: None, provenance: run_spec.provenance.clone(), manifest_blob: None, - pre_run_git: None, + git: run_spec.git.clone(), fork_source_ref: None, in_place: false, }) @@ -1239,13 +1241,17 @@ mod tests { graph: Graph::new("test"), workflow_slug: Some("test".to_string()), source_directory: Some("/tmp/project".to_string()), - repo_origin_url: None, - base_branch: Some("main".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, + }), labels: HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, }; @@ -1258,13 +1264,11 @@ mod tests { labels: run_spec.labels.clone().into_iter().collect(), run_dir: "/tmp/project".to_string(), source_directory: run_spec.source_directory.clone(), - repo_origin_url: run_spec.repo_origin_url.clone(), - base_branch: run_spec.base_branch.clone(), workflow_slug: run_spec.workflow_slug.clone(), db_prefix: None, provenance: run_spec.provenance.clone(), manifest_blob: None, - pre_run_git: None, + git: run_spec.git.clone(), fork_source_ref: None, in_place: false, }) @@ -1539,13 +1543,11 @@ mod tests { graph: Graph::new("test"), workflow_slug: None, source_directory: Some(tmp.path().display().to_string()), - repo_origin_url: None, - base_branch: None, + git: None, labels: std::collections::HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, }; @@ -1558,13 +1560,11 @@ mod tests { labels: run_spec.labels.clone().into_iter().collect(), run_dir: tmp.path().display().to_string(), source_directory: run_spec.source_directory.clone(), - repo_origin_url: run_spec.repo_origin_url.clone(), - base_branch: None, workflow_slug: None, db_prefix: None, provenance: run_spec.provenance.clone(), manifest_blob: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, }) diff --git a/lib/crates/fabro-workflow/src/pipeline/retro.rs b/lib/crates/fabro-workflow/src/pipeline/retro.rs index 5a449cd67..be661be87 100644 --- a/lib/crates/fabro-workflow/src/pipeline/retro.rs +++ b/lib/crates/fabro-workflow/src/pipeline/retro.rs @@ -216,13 +216,11 @@ mod tests { graph: Graph::new("test"), workflow_slug: None, source_directory: Some(run_dir.to_string_lossy().to_string()), - repo_origin_url: None, - base_branch: None, + git: None, labels: std::collections::HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, }; @@ -235,13 +233,11 @@ mod tests { labels: run_spec.labels.clone().into_iter().collect(), run_dir: run_dir.to_string_lossy().to_string(), source_directory: run_spec.source_directory.clone(), - repo_origin_url: run_spec.repo_origin_url.clone(), - base_branch: None, workflow_slug: None, db_prefix: None, provenance: run_spec.provenance.clone(), manifest_blob: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, }) diff --git a/lib/crates/fabro-workflow/src/run_dump.rs b/lib/crates/fabro-workflow/src/run_dump.rs index 898768240..9c8cbf78c 100644 --- a/lib/crates/fabro-workflow/src/run_dump.rs +++ b/lib/crates/fabro-workflow/src/run_dump.rs @@ -428,13 +428,17 @@ mod tests { graph: Graph::new("ship"), workflow_slug: Some("demo".to_string()), source_directory: Some("/tmp/project".to_string()), - repo_origin_url: Some("https://github.com/fabro-sh/fabro.git".to_string()), - base_branch: Some("main".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, + }), labels: HashMap::from([("team".to_string(), "platform".to_string())]), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, } diff --git a/lib/crates/fabro-workflow/src/run_lookup.rs b/lib/crates/fabro-workflow/src/run_lookup.rs index 716561b8a..fd8a09f99 100644 --- a/lib/crates/fabro-workflow/src/run_lookup.rs +++ b/lib/crates/fabro-workflow/src/run_lookup.rs @@ -439,13 +439,17 @@ mod tests { graph: Graph::new("test"), workflow_slug: Some("test".to_string()), source_directory: Some("/tmp/project".to_string()), - repo_origin_url: None, - base_branch: Some("main".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, + }), labels: HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, } @@ -469,13 +473,11 @@ mod tests { labels: run_spec.labels.clone().into_iter().collect(), run_dir: run_dir.display().to_string(), source_directory: run_spec.source_directory.clone(), - repo_origin_url: run_spec.repo_origin_url.clone(), - base_branch: run_spec.base_branch.clone(), workflow_slug: run_spec.workflow_slug.clone(), db_prefix: None, provenance: run_spec.provenance.clone(), manifest_blob: None, - pre_run_git: run_spec.pre_run_git.clone(), + git: run_spec.git.clone(), fork_source_ref: run_spec.fork_source_ref.clone(), in_place: run_spec.in_place, }) diff --git a/lib/crates/fabro-workflow/src/run_options.rs b/lib/crates/fabro-workflow/src/run_options.rs index 02c44d80f..705dc6a65 100644 --- a/lib/crates/fabro-workflow/src/run_options.rs +++ b/lib/crates/fabro-workflow/src/run_options.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use std::sync::atomic::AtomicBool; use fabro_types::settings::run::RunMode; -use fabro_types::{ForkSourceRef, PreRunGitContext, RunId, WorkflowSettings}; +use fabro_types::{ForkSourceRef, GitContext, RunId, WorkflowSettings}; use crate::git::{GitAuthor, git_author_from_settings}; @@ -31,7 +31,7 @@ pub struct RunOptions { /// GitHub credentials for pushing metadata branches to origin. pub github_app: Option, /// Submitter-side git context captured before the run was created. - pub pre_run_git: Option, + pub pre_run_git: Option, /// Source checkpoint ref used by fork/rewind-created runs. pub fork_source_ref: Option, /// Name of the branch the run was started from (for PR base). diff --git a/lib/crates/fabro-workflow/src/runtime_store.rs b/lib/crates/fabro-workflow/src/runtime_store.rs index b7323c806..25c4933a6 100644 --- a/lib/crates/fabro-workflow/src/runtime_store.rs +++ b/lib/crates/fabro-workflow/src/runtime_store.rs @@ -135,13 +135,11 @@ mod tests { graph: Graph::new("test"), workflow_slug: Some("test".to_string()), source_directory: Some("/tmp/test".to_string()), - repo_origin_url: None, - base_branch: None, + git: None, labels: HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, } @@ -160,13 +158,11 @@ mod tests { labels: std::collections::BTreeMap::new(), run_dir: "/tmp/test".to_string(), source_directory: Some("/tmp/test".to_string()), - repo_origin_url: None, - base_branch: None, workflow_slug: Some("test".to_string()), db_prefix: None, provenance: None, manifest_blob: None, - pre_run_git: None, + git: None, fork_source_ref: None, in_place: false, }) diff --git a/lib/crates/fabro-workflow/src/sandbox_git.rs b/lib/crates/fabro-workflow/src/sandbox_git.rs index 31497028f..9b2c7104f 100644 --- a/lib/crates/fabro-workflow/src/sandbox_git.rs +++ b/lib/crates/fabro-workflow/src/sandbox_git.rs @@ -527,11 +527,7 @@ fn classify_entry( }) } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub struct DiffLineStats { - pub additions: u64, - pub deletions: u64, -} +pub use fabro_types::DiffStats; /// Output of `git diff --numstat`: which paths are binary, plus per-path /// `+/-` line totals for text files in the range. Both pieces come from a @@ -541,7 +537,7 @@ pub struct DiffNumstat { /// Repo-relative paths (post-rename) that git classifies as binary. pub binary_paths: HashSet, /// Repo-relative paths (post-rename) to line stats for text files. - pub line_stats_by_path: HashMap, + pub line_stats_by_path: HashMap, } /// Run `git diff --numstat` once and return both the set of binary paths and @@ -593,14 +589,14 @@ pub async fn list_diff_numstat( let Some(path_s) = parts.next() else { continue; }; - let Ok(adds) = adds_s.parse::() else { + let Ok(adds) = adds_s.parse::() else { continue; }; - let Ok(dels) = dels_s.parse::() else { + let Ok(dels) = dels_s.parse::() else { continue; }; let path = extract_new_path_from_numstat(path_s); - out.line_stats_by_path.insert(path, DiffLineStats { + out.line_stats_by_path.insert(path, DiffStats { additions: adds, deletions: dels, }); @@ -1186,13 +1182,17 @@ mod tests { graph: fabro_types::Graph::new("metadata"), workflow_slug: Some("metadata".to_string()), source_directory: Some("/Users/client/project".to_string()), - repo_origin_url: Some("https://github.com/fabro-sh/fabro.git".to_string()), - base_branch: Some("main".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, + }), labels: HashMap::new(), provenance: None, manifest_blob: None, definition_blob: None, - pre_run_git: None, fork_source_ref: None, in_place: false, }); diff --git a/lib/crates/fabro-workflow/src/test_support.rs b/lib/crates/fabro-workflow/src/test_support.rs index 3480925cc..074845e58 100644 --- a/lib/crates/fabro-workflow/src/test_support.rs +++ b/lib/crates/fabro-workflow/src/test_support.rs @@ -120,13 +120,11 @@ async fn initialized( .collect::>(), run_dir: run_options.run_dir.display().to_string(), source_directory: Some(sandbox.working_directory().to_string()), - repo_origin_url: None, - base_branch: run_options.base_branch.clone(), workflow_slug: run_options.workflow_slug.clone(), db_prefix: None, provenance: None, manifest_blob: None, - pre_run_git: run_options.pre_run_git.clone(), + git: run_options.pre_run_git.clone(), fork_source_ref: run_options.fork_source_ref.clone(), in_place: false, }) diff --git a/lib/packages/fabro-api-client/src/.openapi-generator/FILES b/lib/packages/fabro-api-client/src/.openapi-generator/FILES index 4df60b6d9..8c34eb72c 100644 --- a/lib/packages/fabro-api-client/src/.openapi-generator/FILES +++ b/lib/packages/fabro-api-client/src/.openapi-generator/FILES @@ -75,6 +75,7 @@ models/file-diff.ts models/fork-request.ts models/fork-response.ts models/fork-source-ref.ts +models/git-context.ts models/git-hub-meta-hooks-entry.ts models/github-integration-settings.ts models/github-integration-strategy.ts @@ -113,9 +114,7 @@ models/manifest-args.ts models/manifest-config.ts models/manifest-file-entry.ts models/manifest-file-ref.ts -models/manifest-git.ts models/manifest-goal.ts -models/manifest-pre-run-push-outcome.ts models/manifest-target.ts models/manifest-workflow-config.ts models/manifest-workflow.ts @@ -146,7 +145,11 @@ models/paginated-saved-query-list.ts models/paginated-stage-turn-list.ts models/pagination-meta.ts models/pending-interview-record.ts -models/pre-run-git-context.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 diff --git a/lib/packages/fabro-api-client/src/models/git-context.ts b/lib/packages/fabro-api-client/src/models/git-context.ts new file mode 100644 index 000000000..eb6165dba --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/git-context.ts @@ -0,0 +1,44 @@ +/* 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 { 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. + */ +export interface GitContext { + /** + * Remote origin URL with any embedded credentials removed. + */ + 'origin_url': string; + /** + * Current branch name. + */ + 'branch': string; + /** + * Current commit SHA, when known. + */ + 'sha'?: string | null; + 'dirty': DirtyStatus; + 'push_outcome': PreRunPushOutcome; +} + + + diff --git a/lib/packages/fabro-api-client/src/models/index.ts b/lib/packages/fabro-api-client/src/models/index.ts index 5ca5c4f7a..023aac3cf 100644 --- a/lib/packages/fabro-api-client/src/models/index.ts +++ b/lib/packages/fabro-api-client/src/models/index.ts @@ -55,6 +55,7 @@ export * from './file-diff'; export * from './fork-request'; export * from './fork-response'; export * from './fork-source-ref'; +export * from './git-context'; export * from './git-hub-meta-hooks-entry'; export * from './github-integration-settings'; export * from './github-integration-strategy'; @@ -92,9 +93,7 @@ export * from './manifest-args'; export * from './manifest-config'; export * from './manifest-file-entry'; export * from './manifest-file-ref'; -export * from './manifest-git'; export * from './manifest-goal'; -export * from './manifest-pre-run-push-outcome'; export * from './manifest-target'; export * from './manifest-workflow'; export * from './manifest-workflow-config'; @@ -125,8 +124,12 @@ export * from './paginated-saved-query-list'; export * from './paginated-stage-turn-list'; export * from './pagination-meta'; export * from './pending-interview-record'; -export * from './pre-run-git-context'; 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'; diff --git a/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-failed.ts b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-failed.ts new file mode 100644 index 000000000..d8a49ad2a --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-failed.ts @@ -0,0 +1,30 @@ +/* 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]; + + diff --git a/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-not-attempted.ts b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-not-attempted.ts new file mode 100644 index 000000000..6139a00c5 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-not-attempted.ts @@ -0,0 +1,27 @@ +/* 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]; + + diff --git a/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-skipped-no-remote.ts b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-skipped-no-remote.ts new file mode 100644 index 000000000..bb72132f7 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-skipped-no-remote.ts @@ -0,0 +1,27 @@ +/* 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]; + + diff --git a/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-skipped-remote-mismatch.ts b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-skipped-remote-mismatch.ts new file mode 100644 index 000000000..cafb60318 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-skipped-remote-mismatch.ts @@ -0,0 +1,29 @@ +/* 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]; + + diff --git a/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-succeeded.ts b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-succeeded.ts new file mode 100644 index 000000000..7bf16077e --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome-succeeded.ts @@ -0,0 +1,29 @@ +/* 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]; + + diff --git a/lib/packages/fabro-api-client/src/models/pre-run-push-outcome.ts b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome.ts index e9258cd21..3e9d02d87 100644 --- a/lib/packages/fabro-api-client/src/models/pre-run-push-outcome.ts +++ b/lib/packages/fabro-api-client/src/models/pre-run-push-outcome.ts @@ -13,26 +13,26 @@ */ +// 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'; /** - * Outcome of the CLI submitter\'s best-effort pre-run push. + * @type PreRunPushOutcome + * Outcome of the CLI\'s best-effort pre-run push. */ -export interface PreRunPushOutcome { - 'type': PreRunPushOutcomeTypeEnum; - 'remote'?: string | null; - 'branch'?: string | null; - 'message'?: string | null; - 'repo_origin_url'?: string | null; -} - -export const PreRunPushOutcomeTypeEnum = { - NOT_ATTEMPTED: 'not_attempted', - SUCCEEDED: 'succeeded', - FAILED: 'failed', - SKIPPED_NO_REMOTE: 'skipped_no_remote', - SKIPPED_REMOTE_MISMATCH: 'skipped_remote_mismatch' -} as const; - -export type PreRunPushOutcomeTypeEnum = typeof PreRunPushOutcomeTypeEnum[keyof typeof PreRunPushOutcomeTypeEnum]; +export type PreRunPushOutcome = { type: 'failed' } & PreRunPushOutcomeFailed | { type: 'not_attempted' } & PreRunPushOutcomeNotAttempted | { type: 'skipped_no_remote' } & PreRunPushOutcomeSkippedNoRemote | { type: 'skipped_remote_mismatch' } & PreRunPushOutcomeSkippedRemoteMismatch | { type: 'succeeded' } & PreRunPushOutcomeSucceeded; diff --git a/lib/packages/fabro-api-client/src/models/run-manifest.ts b/lib/packages/fabro-api-client/src/models/run-manifest.ts index 06d015900..182b97ffc 100644 --- a/lib/packages/fabro-api-client/src/models/run-manifest.ts +++ b/lib/packages/fabro-api-client/src/models/run-manifest.ts @@ -13,6 +13,9 @@ */ +// May contain unused imports in some cases +// @ts-ignore +import type { GitContext } from './git-context'; // May contain unused imports in some cases // @ts-ignore import type { ManifestArgs } from './manifest-args'; @@ -21,9 +24,6 @@ import type { ManifestArgs } from './manifest-args'; import type { ManifestConfig } from './manifest-config'; // May contain unused imports in some cases // @ts-ignore -import type { ManifestGit } from './manifest-git'; -// May contain unused imports in some cases -// @ts-ignore import type { ManifestGoal } from './manifest-goal'; // May contain unused imports in some cases // @ts-ignore @@ -48,7 +48,7 @@ export interface RunManifest { * CLI working directory at invocation time. */ 'cwd': string; - 'git'?: ManifestGit; + 'git'?: GitContext; 'goal'?: ManifestGoal; 'args'?: ManifestArgs; 'target': ManifestTarget; diff --git a/lib/packages/fabro-api-client/src/models/run-spec.ts b/lib/packages/fabro-api-client/src/models/run-spec.ts index fa548a6f0..a40699412 100644 --- a/lib/packages/fabro-api-client/src/models/run-spec.ts +++ b/lib/packages/fabro-api-client/src/models/run-spec.ts @@ -18,7 +18,7 @@ import type { ForkSourceRef } from './fork-source-ref'; // May contain unused imports in some cases // @ts-ignore -import type { PreRunGitContext } from './pre-run-git-context'; +import type { GitContext } from './git-context'; /** * Durable workflow run specification reconstructed from run.created events. @@ -32,13 +32,11 @@ export interface RunSpec { 'graph': { [key: string]: any; }; 'workflow_slug'?: string | null; 'source_directory'?: string | null; - 'repo_origin_url'?: string | null; - 'base_branch'?: string | null; 'labels'?: { [key: string]: string; }; 'provenance'?: { [key: string]: any; } | null; 'manifest_blob'?: string | null; 'definition_blob'?: string | null; - 'pre_run_git'?: PreRunGitContext | null; + 'git'?: GitContext | null; 'fork_source_ref'?: ForkSourceRef | null; 'in_place': boolean; }