mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-04 02:33:56 +00:00
feat(types): expose bridge helpers and expand v2 accessors
Stage 6.1 prep follow-ups that consumers need when walking v2 directly: - `bridge::bridge_sandbox`, `bridge_mcp_entry`, `bridge_mcps`, `bridge_hook`, `bridge_exec`, `bridge_worktree_mode`, `bridge_merge_strategy` are now `pub`, so callers can lift the runtime shape they need out of the v2 tree without round-tripping through the full `bridge_to_old` legacy Settings builder. - New `bridge::bridge_pull_request` and `bridge::bridge_run_artifacts` helpers extract their respective runtime shapes from v2 layers. - `SettingsFile::run_prepare_commands()` / `run_prepare_timeout_ms()` flatten `run.prepare.steps` into the legacy script-string vector shape consumers pass to `LifecycleOptions::setup_commands`. - `SettingsFile::run_inputs_as_strings()` stringifies `run.inputs` TOML values for var-expansion call sites. - `fabro_checkpoint::GitAuthor` now has `From<&v2::run::GitAuthorLayer>` so consumers can construct a runtime author directly from the v2 subtree without going through the legacy flat `GitAuthorSettings`. All changes are additive. `bridge_to_old` still exists and nothing has migrated off the flat `Settings` shape yet -- those moves land in follow-up commits once each consumer crate is converted independently. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3f32bdb874
commit
842ab71eb8
3 changed files with 90 additions and 7 deletions
|
|
@ -1,6 +1,8 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use fabro_types::settings::server::GitAuthorSettings;
|
||||
use fabro_types::settings::v2::InterpString;
|
||||
use fabro_types::settings::v2::run::GitAuthorLayer;
|
||||
|
||||
/// Resolved git author identity for checkpoint commits.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
@ -54,3 +56,12 @@ impl From<&GitAuthorSettings> for GitAuthor {
|
|||
Self::from_options(value.name.clone(), value.email.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&GitAuthorLayer> for GitAuthor {
|
||||
fn from(value: &GitAuthorLayer) -> Self {
|
||||
Self::from_options(
|
||||
value.name.as_ref().map(InterpString::as_source),
|
||||
value.email.as_ref().map(InterpString::as_source),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,40 @@ impl SettingsFile {
|
|||
self.run.as_ref().and_then(|r| r.prepare.as_ref())
|
||||
}
|
||||
|
||||
/// Flattened prepare-step commands: each `script` is kept as-is, and
|
||||
/// `command` argv is joined with spaces. Env-interpolation tokens are
|
||||
/// emitted verbatim via [`InterpString::as_source`].
|
||||
#[must_use]
|
||||
pub fn run_prepare_commands(&self) -> Vec<String> {
|
||||
let Some(prepare) = self.run_prepare() else {
|
||||
return Vec::new();
|
||||
};
|
||||
prepare
|
||||
.steps
|
||||
.iter()
|
||||
.filter_map(|step| {
|
||||
if let Some(script) = &step.script {
|
||||
Some(script.as_source())
|
||||
} else {
|
||||
step.command.as_ref().map(|argv| {
|
||||
argv.iter()
|
||||
.map(InterpString::as_source)
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Prepare-step timeout in milliseconds.
|
||||
#[must_use]
|
||||
pub fn run_prepare_timeout_ms(&self) -> Option<u64> {
|
||||
self.run_prepare()
|
||||
.and_then(|p| p.timeout)
|
||||
.map(|d| u64::try_from(d.as_std().as_millis()).unwrap_or(u64::MAX))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn run_checkpoint(&self) -> Option<&RunCheckpointLayer> {
|
||||
self.run.as_ref().and_then(|r| r.checkpoint.as_ref())
|
||||
|
|
@ -132,6 +166,26 @@ impl SettingsFile {
|
|||
self.run.as_ref().and_then(|r| r.inputs.as_ref())
|
||||
}
|
||||
|
||||
/// Stringified view of `run.inputs`: non-string TOML values are rendered
|
||||
/// via their canonical TOML representation (integers, booleans, and
|
||||
/// arrays are flattened through `Display`). Returns `None` when no
|
||||
/// inputs are set.
|
||||
#[must_use]
|
||||
pub fn run_inputs_as_strings(&self) -> Option<HashMap<String, String>> {
|
||||
self.run_inputs().map(|inputs| {
|
||||
inputs
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let stringified = match v {
|
||||
toml::Value::String(s) => s.clone(),
|
||||
other => other.to_string(),
|
||||
};
|
||||
(k.clone(), stringified)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn run_metadata(&self) -> Option<&HashMap<String, String>> {
|
||||
self.run.as_ref().map(|r| &r.metadata)
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ fn bridge_run(run: &RunLayer, out: &mut Settings) {
|
|||
}
|
||||
}
|
||||
|
||||
fn bridge_sandbox(sb: &super::run::RunSandboxLayer) -> SandboxSettings {
|
||||
pub fn bridge_sandbox(sb: &super::run::RunSandboxLayer) -> SandboxSettings {
|
||||
SandboxSettings {
|
||||
provider: sb.provider.clone(),
|
||||
preserve: sb.preserve,
|
||||
|
|
@ -312,7 +312,7 @@ fn bridge_sandbox(sb: &super::run::RunSandboxLayer) -> SandboxSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn bridge_worktree_mode(m: V2WorktreeMode) -> OldWorktreeMode {
|
||||
pub fn bridge_worktree_mode(m: V2WorktreeMode) -> OldWorktreeMode {
|
||||
match m {
|
||||
V2WorktreeMode::Always => OldWorktreeMode::Always,
|
||||
V2WorktreeMode::Clean => OldWorktreeMode::Clean,
|
||||
|
|
@ -321,7 +321,7 @@ fn bridge_worktree_mode(m: V2WorktreeMode) -> OldWorktreeMode {
|
|||
}
|
||||
}
|
||||
|
||||
fn bridge_merge_strategy(m: V2MergeStrategy) -> OldMergeStrategy {
|
||||
pub fn bridge_merge_strategy(m: V2MergeStrategy) -> OldMergeStrategy {
|
||||
match m {
|
||||
V2MergeStrategy::Squash => OldMergeStrategy::Squash,
|
||||
V2MergeStrategy::Merge => OldMergeStrategy::Merge,
|
||||
|
|
@ -329,13 +329,31 @@ fn bridge_merge_strategy(m: V2MergeStrategy) -> OldMergeStrategy {
|
|||
}
|
||||
}
|
||||
|
||||
fn bridge_mcps(mcps: &HashMap<String, McpEntryLayer>) -> HashMap<String, McpServerEntry> {
|
||||
pub fn bridge_pull_request(pr: &super::run::RunPullRequestLayer) -> PullRequestSettings {
|
||||
PullRequestSettings {
|
||||
enabled: pr.enabled.unwrap_or(false),
|
||||
draft: pr.draft.unwrap_or(true),
|
||||
auto_merge: pr.auto_merge.unwrap_or(false),
|
||||
merge_strategy: pr
|
||||
.merge_strategy
|
||||
.map(bridge_merge_strategy)
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bridge_run_artifacts(artifacts: &super::run::RunArtifactsLayer) -> ArtifactsSettings {
|
||||
ArtifactsSettings {
|
||||
include: artifacts.include.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bridge_mcps(mcps: &HashMap<String, McpEntryLayer>) -> HashMap<String, McpServerEntry> {
|
||||
mcps.iter()
|
||||
.map(|(name, entry)| (name.clone(), bridge_mcp_entry(entry)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn bridge_mcp_entry(entry: &McpEntryLayer) -> McpServerEntry {
|
||||
pub fn bridge_mcp_entry(entry: &McpEntryLayer) -> McpServerEntry {
|
||||
let transport = match entry {
|
||||
McpEntryLayer::Stdio {
|
||||
script,
|
||||
|
|
@ -418,7 +436,7 @@ fn bridge_mcp_entry(entry: &McpEntryLayer) -> McpServerEntry {
|
|||
}
|
||||
}
|
||||
|
||||
fn bridge_hook(hook: &V2HookEntry) -> HookDefinition {
|
||||
pub fn bridge_hook(hook: &V2HookEntry) -> HookDefinition {
|
||||
let hook_type = resolve_hook_type(hook);
|
||||
// If the hook is a script/command form, emit via the shorthand so the
|
||||
// old HookDefinition.command field holds the full command and
|
||||
|
|
@ -550,7 +568,7 @@ fn bridge_cli(cli: &CliLayer, out: &mut Settings) {
|
|||
}
|
||||
}
|
||||
|
||||
fn bridge_exec(exec: &CliExecLayer) -> ExecSettings {
|
||||
pub fn bridge_exec(exec: &CliExecLayer) -> ExecSettings {
|
||||
ExecSettings {
|
||||
provider: exec
|
||||
.model
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue