mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-10 22:43:37 +00:00
refactor(cli): centralize command settings and server access
Add CommandContext to load machine settings once per invocation, cache server access, and route migrated commands through the shared ServerStoreClient path instead of reloading settings and reconnecting ad hoc.
This commit is contained in:
parent
911831fc23
commit
367fd9302b
39 changed files with 345 additions and 232 deletions
111
lib/crates/fabro-cli/src/command_context.rs
Normal file
111
lib/crates/fabro-cli/src/command_context.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use fabro_types::Settings;
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
use crate::args::{ServerConnectionArgs, ServerTargetArgs};
|
||||
use crate::server_client::ServerStoreClient;
|
||||
use crate::{server_client, user_config};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum ServerMode {
|
||||
None,
|
||||
ByTarget {
|
||||
target_override: Option<String>,
|
||||
},
|
||||
ByStorageDir {
|
||||
target_override: Option<String>,
|
||||
storage_dir_override: Option<PathBuf>,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) struct CommandContext {
|
||||
cwd: PathBuf,
|
||||
base_config_path: PathBuf,
|
||||
machine_settings: Settings,
|
||||
server_mode: ServerMode,
|
||||
server: OnceCell<Arc<ServerStoreClient>>,
|
||||
}
|
||||
|
||||
impl CommandContext {
|
||||
pub(crate) fn base() -> Result<Self> {
|
||||
Self::new(ServerMode::None)
|
||||
}
|
||||
|
||||
pub(crate) fn for_target(args: &ServerTargetArgs) -> Result<Self> {
|
||||
Self::new(ServerMode::ByTarget {
|
||||
target_override: args.server.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn for_connection(args: &ServerConnectionArgs) -> Result<Self> {
|
||||
Self::new(ServerMode::ByStorageDir {
|
||||
target_override: args.target.server.clone(),
|
||||
storage_dir_override: args.storage_dir.clone_path(),
|
||||
})
|
||||
}
|
||||
|
||||
fn new(server_mode: ServerMode) -> Result<Self> {
|
||||
let cwd = std::env::current_dir().context("Failed to get current directory")?;
|
||||
let base_config_path = user_config::active_settings_path(None);
|
||||
let machine_settings = match &server_mode {
|
||||
ServerMode::None | ServerMode::ByTarget { .. } => user_config::load_settings()?,
|
||||
ServerMode::ByStorageDir {
|
||||
storage_dir_override,
|
||||
..
|
||||
} => user_config::load_settings_with_storage_dir(storage_dir_override.as_deref())?,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
cwd,
|
||||
base_config_path,
|
||||
machine_settings,
|
||||
server_mode,
|
||||
server: OnceCell::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn cwd(&self) -> &Path {
|
||||
&self.cwd
|
||||
}
|
||||
|
||||
pub(crate) fn base_config_path(&self) -> &Path {
|
||||
&self.base_config_path
|
||||
}
|
||||
|
||||
pub(crate) fn machine_settings(&self) -> &Settings {
|
||||
&self.machine_settings
|
||||
}
|
||||
|
||||
pub(crate) async fn server(&self) -> Result<Arc<ServerStoreClient>> {
|
||||
let server_mode = self.server_mode.clone();
|
||||
let base_config_path = self.base_config_path.clone();
|
||||
let machine_settings = self.machine_settings.clone();
|
||||
|
||||
let client = self
|
||||
.server
|
||||
.get_or_try_init(|| async move {
|
||||
let target = match server_mode {
|
||||
ServerMode::None => bail!("This command context does not have server access"),
|
||||
ServerMode::ByTarget { target_override }
|
||||
| ServerMode::ByStorageDir {
|
||||
target_override, ..
|
||||
} => ServerTargetArgs {
|
||||
server: target_override,
|
||||
},
|
||||
};
|
||||
server_client::connect_server_with_settings(
|
||||
&target,
|
||||
&machine_settings,
|
||||
&base_config_path,
|
||||
)
|
||||
.await
|
||||
.map(Arc::new)
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(Arc::clone(client))
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ use anyhow::{Context, Result};
|
|||
use fabro_types::{RunId, StageId};
|
||||
|
||||
use crate::args::{ArtifactCommand, ArtifactNamespace, GlobalArgs, ServerTargetArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client::ServerStoreClient;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
|
||||
|
|
@ -24,7 +25,8 @@ pub(super) async fn resolve_artifacts(
|
|||
node: Option<&str>,
|
||||
retry: Option<u32>,
|
||||
) -> Result<(RunId, ServerStoreClient, Vec<ArtifactEntry>)> {
|
||||
let lookup = ServerSummaryLookup::connect(server).await?;
|
||||
let ctx = CommandContext::for_target(server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(run_selector)?;
|
||||
let run_id = run.run_id();
|
||||
let mut entries = Vec::new();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::io::Write;
|
|||
use std::path::Path;
|
||||
|
||||
use crate::args::{GlobalArgs, SettingsArgs};
|
||||
use crate::server_client;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::print_json_pretty;
|
||||
use crate::user_config;
|
||||
use fabro_config::ConfigLayer;
|
||||
|
|
@ -11,13 +11,19 @@ use fabro_config::effective_settings::{EffectiveSettingsLayers, EffectiveSetting
|
|||
use fabro_config::project;
|
||||
use fabro_types::Settings;
|
||||
|
||||
fn config_layers(workflow: Option<&Path>) -> anyhow::Result<EffectiveSettingsLayers> {
|
||||
let cwd = std::env::current_dir()?;
|
||||
fn config_layers(
|
||||
ctx: &CommandContext,
|
||||
workflow: Option<&Path>,
|
||||
) -> anyhow::Result<EffectiveSettingsLayers> {
|
||||
let cwd = ctx.cwd();
|
||||
let (workflow_layer, project_layer) = match workflow {
|
||||
Some(path) => workflow_and_project_layers(path, &cwd)?,
|
||||
None => (ConfigLayer::default(), ConfigLayer::project(&cwd)?),
|
||||
Some(path) => workflow_and_project_layers(path, cwd)?,
|
||||
None => (ConfigLayer::default(), ConfigLayer::project(cwd)?),
|
||||
};
|
||||
let user_layer = user_config::settings_layer_with_storage_dir(None)?;
|
||||
let user_layer = user_config::settings_layer_with_config_and_storage_dir(
|
||||
Some(ctx.base_config_path()),
|
||||
None,
|
||||
)?;
|
||||
Ok(EffectiveSettingsLayers::new(
|
||||
ConfigLayer::default(),
|
||||
workflow_layer,
|
||||
|
|
@ -52,7 +58,8 @@ fn workflow_and_project_layers(
|
|||
}
|
||||
|
||||
async fn merged_config(args: &SettingsArgs) -> anyhow::Result<Settings> {
|
||||
let layers = config_layers(args.workflow.as_deref())?;
|
||||
let base_ctx = CommandContext::base()?;
|
||||
let layers = config_layers(&base_ctx, args.workflow.as_deref())?;
|
||||
if args.local {
|
||||
return effective_settings::resolve_settings(
|
||||
layers,
|
||||
|
|
@ -61,10 +68,9 @@ async fn merged_config(args: &SettingsArgs) -> anyhow::Result<Settings> {
|
|||
);
|
||||
}
|
||||
|
||||
let machine_settings = user_config::load_settings()?;
|
||||
let target = user_config::resolve_server_target(&args.target, &machine_settings)?;
|
||||
let client = server_client::connect_server_only(&args.target).await?;
|
||||
let server_settings = client.retrieve_server_settings().await?;
|
||||
let ctx = CommandContext::for_target(&args.target)?;
|
||||
let target = user_config::resolve_server_target(&args.target, ctx.machine_settings())?;
|
||||
let server_settings = ctx.server().await?.retrieve_server_settings().await?;
|
||||
let mode = match target {
|
||||
user_config::ServerTarget::HttpUrl { .. } => EffectiveSettingsMode::RemoteServer,
|
||||
user_config::ServerTarget::UnixSocket(_) => EffectiveSettingsMode::LocalDaemon,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use regex::Regex;
|
|||
use semver::Version;
|
||||
|
||||
use crate::args::{DoctorArgs, GlobalArgs};
|
||||
use crate::server_client;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) struct DepSpec {
|
||||
|
|
@ -357,8 +357,38 @@ pub(crate) async fn run_doctor(
|
|||
}],
|
||||
};
|
||||
|
||||
let client = match server_client::connect_server_backed_api_client(&args.target).await {
|
||||
Ok(client) => client,
|
||||
let ctx = match CommandContext::for_target(&args.target) {
|
||||
Ok(ctx) => ctx,
|
||||
Err(err) => {
|
||||
report.sections.push(CheckSection {
|
||||
title: "Server".to_string(),
|
||||
checks: vec![CheckResult {
|
||||
name: "Fabro server".to_string(),
|
||||
status: CheckStatus::Error,
|
||||
summary: "settings resolution failed".to_string(),
|
||||
details: vec![CheckDetail::new(err.to_string())],
|
||||
remediation: Some(
|
||||
"Fix the local CLI settings or provide `--server`, then run doctor again."
|
||||
.to_string(),
|
||||
),
|
||||
}],
|
||||
});
|
||||
|
||||
if let Some(spinner) = spinner {
|
||||
spinner.finish_and_clear();
|
||||
}
|
||||
|
||||
if globals.json {
|
||||
print_json_pretty(&report)?;
|
||||
} else {
|
||||
render_report(&report, &styles, verbose);
|
||||
}
|
||||
return Ok(1);
|
||||
}
|
||||
};
|
||||
|
||||
let server = match ctx.server().await {
|
||||
Ok(server) => server,
|
||||
Err(err) => {
|
||||
report.sections.push(CheckSection {
|
||||
title: "Server".to_string(),
|
||||
|
|
@ -387,7 +417,7 @@ pub(crate) async fn run_doctor(
|
|||
}
|
||||
};
|
||||
|
||||
let health = match client.get_health().send().await {
|
||||
let health = match server.api().get_health().send().await {
|
||||
Ok(response) => response.into_inner(),
|
||||
Err(err) => {
|
||||
report.sections.push(CheckSection {
|
||||
|
|
@ -420,7 +450,7 @@ pub(crate) async fn run_doctor(
|
|||
.checks
|
||||
.push(check_version_parity(&health.version));
|
||||
|
||||
match client.run_diagnostics().send().await {
|
||||
match server.api().run_diagnostics().send().await {
|
||||
Ok(response) => {
|
||||
let diagnostics = response.into_inner();
|
||||
report
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ use fabro_util::terminal::Styles;
|
|||
use tracing::debug;
|
||||
|
||||
use crate::args::{GlobalArgs, GraphArgs, GraphDirection, GraphOutputFormat};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::run::output::api_diagnostics_to_local;
|
||||
use crate::manifest_builder::{ManifestBuildInput, build_run_manifest};
|
||||
use crate::server_client;
|
||||
use crate::shared::{absolute_or_current, print_diagnostics, print_json_pretty, relative_path};
|
||||
|
||||
pub(crate) async fn run(
|
||||
|
|
@ -21,15 +21,15 @@ pub(crate) async fn run(
|
|||
globals.require_no_json()?;
|
||||
}
|
||||
|
||||
let cwd = std::env::current_dir()?;
|
||||
let ctx = CommandContext::for_target(&args.target)?;
|
||||
let built = build_run_manifest(ManifestBuildInput {
|
||||
workflow: args.workflow.clone(),
|
||||
cwd,
|
||||
cwd: ctx.cwd().to_path_buf(),
|
||||
args_layer: ConfigLayer::default(),
|
||||
args: None,
|
||||
run_id: None,
|
||||
})?;
|
||||
let client = server_client::connect_server_only(&args.target).await?;
|
||||
let client = ctx.server().await?;
|
||||
let preflight = client.run_preflight(built.manifest.clone()).await?;
|
||||
let diagnostics = api_diagnostics_to_local(&preflight.workflow.diagnostics);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use cli_table::format::{Border, Justify, Separator};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table};
|
||||
use fabro_api::{self, types as api_types};
|
||||
|
|
@ -8,6 +8,7 @@ use serde::Serialize;
|
|||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::args::{GlobalArgs, ModelListArgs, ModelTestArgs, ModelsCommand};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
@ -42,9 +43,10 @@ pub(crate) async fn execute(command: Option<ModelsCommand>, globals: &GlobalArgs
|
|||
ModelsCommand::List(args) => &args.target,
|
||||
ModelsCommand::Test(args) => &args.target,
|
||||
};
|
||||
let client = server_client::connect_server_backed_api_client(target_args).await?;
|
||||
let ctx = CommandContext::for_target(target_args)?;
|
||||
let server = ctx.server().await?;
|
||||
|
||||
run_models(command, client, globals.json).await
|
||||
run_models(command, server.api(), globals.json).await
|
||||
}
|
||||
|
||||
fn format_context_window(tokens: i64) -> String {
|
||||
|
|
@ -171,33 +173,6 @@ fn model_test_row_from_status(model: &Model, status: &str, result_color: Color)
|
|||
}
|
||||
}
|
||||
|
||||
fn map_api_error<E>(err: progenitor_client::Error<E>) -> anyhow::Error
|
||||
where
|
||||
E: serde::Serialize + std::fmt::Debug,
|
||||
{
|
||||
match err {
|
||||
progenitor_client::Error::ErrorResponse(response) => {
|
||||
let status = response.status();
|
||||
if let Ok(value) = serde_json::to_value(response.into_inner()) {
|
||||
if let Some(detail) = value
|
||||
.get("errors")
|
||||
.and_then(serde_json::Value::as_array)
|
||||
.and_then(|errors| errors.first())
|
||||
.and_then(|entry| entry.get("detail"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
{
|
||||
return anyhow!("{detail}");
|
||||
}
|
||||
}
|
||||
anyhow!("request failed with status {status}")
|
||||
}
|
||||
progenitor_client::Error::UnexpectedResponse(response) => {
|
||||
anyhow!("request failed with status {}", response.status())
|
||||
}
|
||||
other => anyhow!("{other}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_type<TInput, TOutput>(value: TInput) -> Result<TOutput>
|
||||
where
|
||||
TInput: serde::Serialize,
|
||||
|
|
@ -223,7 +198,7 @@ async fn fetch_models_from_server(
|
|||
request = request.query(query.to_string());
|
||||
}
|
||||
|
||||
let response = request.send().await.map_err(map_api_error)?;
|
||||
let response = request.send().await.map_err(server_client::map_api_error)?;
|
||||
let parsed = response.into_inner();
|
||||
let count = parsed.data.len() as u64;
|
||||
models.extend(convert_type::<_, Vec<Model>>(parsed.data)?);
|
||||
|
|
@ -245,7 +220,7 @@ async fn test_model_via_server(
|
|||
if let Some(mode) = mode {
|
||||
request = request.mode(mode);
|
||||
}
|
||||
let response = request.send().await.map_err(map_api_error)?;
|
||||
let response = request.send().await.map_err(server_client::map_api_error)?;
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
|
||||
|
|
@ -393,7 +368,7 @@ async fn test_models_via_server(
|
|||
#[allow(clippy::print_stdout)]
|
||||
async fn run_models(
|
||||
command: ModelsCommand,
|
||||
client: fabro_api::Client,
|
||||
client: &fabro_api::Client,
|
||||
json_output: bool,
|
||||
) -> Result<()> {
|
||||
let styles = Styles::detect_stdout();
|
||||
|
|
@ -403,7 +378,7 @@ async fn run_models(
|
|||
provider, query, ..
|
||||
}) => {
|
||||
let models =
|
||||
fetch_models_from_server(&client, provider.as_deref(), query.as_deref()).await?;
|
||||
fetch_models_from_server(client, provider.as_deref(), query.as_deref()).await?;
|
||||
|
||||
if json_output {
|
||||
println!("{}", serde_json::to_string_pretty(&models)?);
|
||||
|
|
@ -418,7 +393,7 @@ async fn run_models(
|
|||
..
|
||||
}) => {
|
||||
test_models_via_server(
|
||||
&client,
|
||||
client,
|
||||
provider.as_deref(),
|
||||
model.as_deref(),
|
||||
deep,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use fabro_workflow::pull_request::maybe_open_pull_request;
|
|||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, PrCreateArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::store::rebuild::rebuild_run_store;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
|
@ -16,7 +17,8 @@ pub(super) async fn create_command(
|
|||
github_app: Option<fabro_github::GitHubAppCredentials>,
|
||||
globals: &GlobalArgs,
|
||||
) -> Result<()> {
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run_id)?;
|
||||
let run_id = run.run_id();
|
||||
let events = lookup.client().list_run_events(&run_id, None, None).await?;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use serde::Serialize;
|
|||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, PrListArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
|
|
@ -24,7 +25,8 @@ pub(super) async fn list_command(
|
|||
let creds = github_app.context(
|
||||
"GitHub App credentials required — set GITHUB_APP_PRIVATE_KEY and configure app_id",
|
||||
)?;
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
|
||||
let mut entries = Vec::new();
|
||||
for run in lookup.runs() {
|
||||
|
|
|
|||
|
|
@ -9,37 +9,21 @@ use anyhow::{Context, Result};
|
|||
use fabro_types::PullRequestRecord;
|
||||
|
||||
use crate::args::{GlobalArgs, PrCommand, PrNamespace, ServerTargetArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::github::build_github_app_credentials;
|
||||
use crate::user_config::load_settings;
|
||||
|
||||
pub(crate) async fn dispatch(ns: PrNamespace, globals: &GlobalArgs) -> Result<()> {
|
||||
let ctx = CommandContext::base()?;
|
||||
let github_app = build_github_app_credentials(ctx.machine_settings().app_id())?;
|
||||
match ns.command {
|
||||
PrCommand::Create(args) => {
|
||||
let cli_settings = load_settings()?;
|
||||
let github_app = build_github_app_credentials(cli_settings.app_id())?;
|
||||
Box::pin(create::create_command(args, github_app, globals)).await
|
||||
}
|
||||
PrCommand::List(args) => {
|
||||
let cli_settings = load_settings()?;
|
||||
let github_app = build_github_app_credentials(cli_settings.app_id())?;
|
||||
list::list_command(args, github_app, globals).await
|
||||
}
|
||||
PrCommand::View(args) => {
|
||||
let cli_settings = load_settings()?;
|
||||
let github_app = build_github_app_credentials(cli_settings.app_id())?;
|
||||
view::view_command(args, github_app, globals).await
|
||||
}
|
||||
PrCommand::Merge(args) => {
|
||||
let cli_settings = load_settings()?;
|
||||
let github_app = build_github_app_credentials(cli_settings.app_id())?;
|
||||
merge::merge_command(args, github_app, globals).await
|
||||
}
|
||||
PrCommand::Close(args) => {
|
||||
let cli_settings = load_settings()?;
|
||||
let github_app = build_github_app_credentials(cli_settings.app_id())?;
|
||||
close::close_command(args, github_app, globals).await
|
||||
}
|
||||
PrCommand::List(args) => list::list_command(args, github_app, globals).await,
|
||||
PrCommand::View(args) => view::view_command(args, github_app, globals).await,
|
||||
PrCommand::Merge(args) => merge::merge_command(args, github_app, globals).await,
|
||||
PrCommand::Close(args) => close::close_command(args, github_app, globals).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +31,8 @@ pub(crate) async fn load_pr_record(
|
|||
server: &ServerTargetArgs,
|
||||
run_id: &str,
|
||||
) -> Result<(PullRequestRecord, fabro_types::RunId)> {
|
||||
let lookup = ServerSummaryLookup::connect(server).await?;
|
||||
let ctx = CommandContext::for_target(server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(run_id)?;
|
||||
let run_id = run.run_id();
|
||||
let state = lookup.client().get_run_state(&run_id).await?;
|
||||
|
|
|
|||
|
|
@ -3,28 +3,26 @@ use fabro_config::ConfigLayer;
|
|||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, PreflightArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::run::output::{
|
||||
api_check_report_to_local, api_diagnostics_to_local, print_preflight_workflow_summary,
|
||||
};
|
||||
use crate::manifest_builder::{ManifestBuildInput, build_run_manifest, preflight_manifest_args};
|
||||
use crate::server_client;
|
||||
use crate::shared::print_json_pretty;
|
||||
use crate::user_config;
|
||||
|
||||
pub(crate) async fn execute(mut args: PreflightArgs, globals: &GlobalArgs) -> anyhow::Result<()> {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let cli_settings = user_config::load_settings()?;
|
||||
args.verbose = args.verbose || cli_settings.verbose_enabled();
|
||||
let ctx = CommandContext::for_target(&args.target)?;
|
||||
args.verbose = args.verbose || ctx.machine_settings().verbose_enabled();
|
||||
|
||||
let cwd = std::env::current_dir()?;
|
||||
let manifest = build_run_manifest(ManifestBuildInput {
|
||||
workflow: args.workflow.clone(),
|
||||
cwd,
|
||||
cwd: ctx.cwd().to_path_buf(),
|
||||
args_layer: ConfigLayer::try_from(&args)?,
|
||||
args: preflight_manifest_args(&args),
|
||||
run_id: None,
|
||||
})?;
|
||||
let client = server_client::connect_server_only(&args.target).await?;
|
||||
let client = ctx.server().await?;
|
||||
let response = client.run_preflight(manifest.manifest).await?;
|
||||
let diagnostics = api_diagnostics_to_local(&response.workflow.diagnostics);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@ use fabro_util::terminal::Styles;
|
|||
use tokio::task::spawn_blocking;
|
||||
|
||||
use crate::args::{GlobalArgs, ProviderLoginArgs};
|
||||
use crate::server_client;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::provider_auth;
|
||||
|
||||
pub(super) async fn login_command(args: ProviderLoginArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
globals.require_no_json()?;
|
||||
let s = Styles::detect_stderr();
|
||||
let client = server_client::connect_server_backed_api_client(&args.target).await?;
|
||||
let ctx = CommandContext::for_target(&args.target)?;
|
||||
let server = ctx.server().await?;
|
||||
|
||||
let use_oauth = args.provider == Provider::OpenAi
|
||||
&& spawn_blocking(|| provider_auth::prompt_confirm("Log in via browser (OAuth)?", true))
|
||||
|
|
@ -36,7 +37,8 @@ pub(super) async fn login_command(args: ProviderLoginArgs, globals: &GlobalArgs)
|
|||
}
|
||||
|
||||
for (name, value) in env_pairs {
|
||||
client
|
||||
server
|
||||
.api()
|
||||
.set_secret()
|
||||
.name(name.clone())
|
||||
.body(types::SetSecretRequest { value })
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use anyhow::{Context, Result, bail};
|
|||
use tokio::task::spawn_blocking;
|
||||
|
||||
use crate::args::{GlobalArgs, RepoInitArgs, ServerTargetArgs};
|
||||
use crate::server_client;
|
||||
use crate::command_context::CommandContext;
|
||||
|
||||
pub(super) fn git_repo_root() -> Result<PathBuf> {
|
||||
let output = std::process::Command::new("git")
|
||||
|
|
@ -166,15 +166,24 @@ async fn check_github_app_installation(target: &ServerTargetArgs) {
|
|||
return; // Not a GitHub repo — skip silently
|
||||
};
|
||||
|
||||
let client = match server_client::connect_server_backed_api_client(target).await {
|
||||
Ok(client) => client,
|
||||
let ctx = match CommandContext::for_target(target) {
|
||||
Ok(ctx) => ctx,
|
||||
Err(err) => {
|
||||
eprintln!("\n Warning: could not resolve fabro server settings: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let server = match ctx.server().await {
|
||||
Ok(server) => server,
|
||||
Err(err) => {
|
||||
eprintln!("\n Warning: could not connect to fabro server: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let check = match client
|
||||
let check = match server
|
||||
.api()
|
||||
.get_github_repo()
|
||||
.owner(owner.clone())
|
||||
.name(repo.clone())
|
||||
|
|
@ -214,7 +223,8 @@ async fn check_github_app_installation(target: &ServerTargetArgs) {
|
|||
})
|
||||
.await;
|
||||
|
||||
match client
|
||||
match server
|
||||
.api()
|
||||
.get_github_repo()
|
||||
.owner(owner.clone())
|
||||
.name(repo.clone())
|
||||
|
|
|
|||
|
|
@ -2,19 +2,19 @@ use anyhow::Result;
|
|||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, RunArgs};
|
||||
use crate::server_client;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::print_json_pretty;
|
||||
use crate::user_config::{self, settings_layer_with_storage_dir};
|
||||
use crate::user_config::settings_layer_with_storage_dir;
|
||||
|
||||
pub(crate) async fn execute(mut args: RunArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let cli_settings = user_config::load_settings()?;
|
||||
let ctx = CommandContext::for_target(&args.target)?;
|
||||
let cli = settings_layer_with_storage_dir(None)?;
|
||||
args.verbose = args.verbose || cli_settings.verbose_enabled();
|
||||
args.verbose = args.verbose || ctx.machine_settings().verbose_enabled();
|
||||
|
||||
let quiet = args.detach;
|
||||
let prevent_idle_sleep = cli_settings.prevent_idle_sleep_enabled();
|
||||
let created_run = Box::pin(super::create::create_run(&args, cli, styles, quiet)).await?;
|
||||
let prevent_idle_sleep = ctx.machine_settings().prevent_idle_sleep_enabled();
|
||||
let created_run = Box::pin(super::create::create_run(&ctx, &args, cli, styles, quiet)).await?;
|
||||
|
||||
#[cfg(feature = "sleep_inhibitor")]
|
||||
let _sleep_guard = crate::sleep_inhibitor::guard(prevent_idle_sleep);
|
||||
|
|
@ -22,7 +22,7 @@ pub(crate) async fn execute(mut args: RunArgs, globals: &GlobalArgs) -> Result<(
|
|||
#[cfg(not(feature = "sleep_inhibitor"))]
|
||||
let _ = prevent_idle_sleep;
|
||||
|
||||
let client = server_client::connect_server_only(&args.target).await?;
|
||||
let client = ctx.server().await?;
|
||||
super::start::start_run_with_client(&client, &created_run.run_id, false).await?;
|
||||
|
||||
if args.detach {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use tokio::fs;
|
|||
use tracing::{debug, info};
|
||||
|
||||
use crate::args::{CpArgs, GlobalArgs, ServerTargetArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client::ServerStoreClient;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::{print_json_pretty, split_run_path};
|
||||
|
|
@ -117,7 +118,8 @@ async fn resolve_client_and_run_id(
|
|||
server: &ServerTargetArgs,
|
||||
run_prefix: &str,
|
||||
) -> Result<(ServerStoreClient, fabro_types::RunId)> {
|
||||
let lookup = ServerSummaryLookup::connect(server).await?;
|
||||
let ctx = CommandContext::for_target(server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(run_prefix)?;
|
||||
Ok((lookup.client().clone_for_reuse(), run.run_id()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use crate::args::RunArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use fabro_config::ConfigLayer;
|
||||
use fabro_config::Storage;
|
||||
use fabro_types::{RunId, Settings};
|
||||
use fabro_types::RunId;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use super::output::{api_diagnostics_to_local, print_preflight_workflow_summary};
|
||||
use crate::manifest_builder::{ManifestBuildInput, build_run_manifest, run_manifest_args};
|
||||
use crate::server_client;
|
||||
use crate::user_config::{self, ServerTarget};
|
||||
|
||||
pub(crate) struct CreatedRun {
|
||||
|
|
@ -20,6 +20,7 @@ pub(crate) struct CreatedRun {
|
|||
///
|
||||
/// This does NOT execute the workflow — it only prepares the run directory.
|
||||
pub(crate) async fn create_run(
|
||||
ctx: &CommandContext,
|
||||
args: &RunArgs,
|
||||
cli_defaults: ConfigLayer,
|
||||
styles: &Styles,
|
||||
|
|
@ -30,13 +31,12 @@ pub(crate) async fn create_run(
|
|||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("--workflow is required"))?;
|
||||
let cli_args_config = ConfigLayer::try_from(args)?;
|
||||
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let _settings: Settings = cli_args_config
|
||||
let cwd = ctx.cwd().to_path_buf();
|
||||
let _settings = cli_args_config
|
||||
.clone()
|
||||
.combine(ConfigLayer::for_workflow(workflow_path, &cwd)?)
|
||||
.combine(cli_defaults)
|
||||
.resolve()?;
|
||||
let machine_settings = user_config::load_settings()?;
|
||||
let run_id = args
|
||||
.run_id
|
||||
.as_deref()
|
||||
|
|
@ -51,8 +51,8 @@ pub(crate) async fn create_run(
|
|||
args: run_manifest_args(args),
|
||||
run_id,
|
||||
})?;
|
||||
let target = user_config::resolve_server_target(&args.target, &machine_settings)?;
|
||||
let client = server_client::connect_server_only(&args.target).await?;
|
||||
let target = user_config::resolve_server_target(&args.target, ctx.machine_settings())?;
|
||||
let client = ctx.server().await?;
|
||||
if !quiet {
|
||||
let preflight = client.run_preflight(built.manifest.clone()).await?;
|
||||
let diagnostics = api_diagnostics_to_local(&preflight.workflow.diagnostics);
|
||||
|
|
@ -67,7 +67,7 @@ pub(crate) async fn create_run(
|
|||
let created_run_id = client.create_run_from_manifest(built.manifest).await?;
|
||||
let local_run_dir = match &target {
|
||||
ServerTarget::UnixSocket(_) => Some(
|
||||
Storage::new(machine_settings.storage_dir())
|
||||
Storage::new(ctx.machine_settings().storage_dir())
|
||||
.run_scratch(&created_run_id)
|
||||
.root()
|
||||
.to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -4,13 +4,15 @@ use anyhow::{Context, Result, bail};
|
|||
use tracing::{debug, info};
|
||||
|
||||
use crate::args::{DiffArgs, GlobalArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client::RunProjection;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) async fn run(args: DiffArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
info!(run_id = %args.run, "Showing diff");
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
let state = lookup.client().get_run_state(&run_id).await?;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use fabro_workflow::operations::{ForkRunInput, RewindTarget, build_timeline_or_r
|
|||
use git2::Repository;
|
||||
|
||||
use crate::args::{ForkArgs, GlobalArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::store::rebuild::rebuild_run_store;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
|
@ -13,7 +14,8 @@ use crate::shared::repo::ensure_matching_repo_origin;
|
|||
|
||||
pub(crate) async fn run(args: &ForkArgs, styles: &Styles, globals: &GlobalArgs) -> Result<()> {
|
||||
let repo = Repository::discover(".").context("not in a git repository")?;
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run_id)?;
|
||||
let run_id = run.run_id();
|
||||
let state = lookup.client().get_run_state(&run_id).await?;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use tokio::time;
|
|||
use tracing::{debug, info};
|
||||
|
||||
use crate::args::{GlobalArgs, LogsArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::format_usd_micros;
|
||||
|
|
@ -18,7 +19,8 @@ use crate::shared::format_usd_micros;
|
|||
const FOLLOW_TERMINAL_GRACE: Duration = Duration::from_millis(500);
|
||||
|
||||
pub(crate) async fn run(args: &LogsArgs, styles: &Styles, globals: &GlobalArgs) -> Result<()> {
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let client = lookup.client();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use anyhow::Result;
|
|||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{AttachArgs, GlobalArgs, RunArgs, RunCommands, RunWorkerArgs, StartArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
use crate::user_config::settings_layer_with_storage_dir;
|
||||
|
|
@ -40,7 +41,8 @@ pub(crate) async fn dispatch(cmd: RunCommands, globals: &GlobalArgs) -> Result<(
|
|||
apply_json_defaults(&mut args, globals);
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let cli = settings_layer_with_storage_dir(None)?;
|
||||
let created_run = Box::pin(create::create_run(&args, cli, styles, true)).await?;
|
||||
let ctx = CommandContext::for_target(&args.target)?;
|
||||
let created_run = Box::pin(create::create_run(&ctx, &args, cli, styles, true)).await?;
|
||||
if globals.json {
|
||||
print_json_pretty(&serde_json::json!({ "run_id": created_run.run_id }))?;
|
||||
} else {
|
||||
|
|
@ -49,7 +51,8 @@ pub(crate) async fn dispatch(cmd: RunCommands, globals: &GlobalArgs) -> Result<(
|
|||
Ok(())
|
||||
}
|
||||
RunCommands::Start(StartArgs { server, run }) => {
|
||||
let lookup = ServerSummaryLookup::connect(&server).await?;
|
||||
let ctx = CommandContext::for_target(&server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run_info = lookup.resolve(&run)?;
|
||||
let run_id = run_info.run_id();
|
||||
start::start_run_with_client(lookup.client(), &run_id, false).await?;
|
||||
|
|
@ -60,7 +63,8 @@ pub(crate) async fn dispatch(cmd: RunCommands, globals: &GlobalArgs) -> Result<(
|
|||
}
|
||||
RunCommands::Attach(AttachArgs { server, run }) => {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let lookup = ServerSummaryLookup::connect(&server).await?;
|
||||
let ctx = CommandContext::for_target(&server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run_info = lookup.resolve(&run)?;
|
||||
let run_id = run_info.run_id();
|
||||
let exit_code = attach::attach_run_with_client(
|
||||
|
|
@ -92,8 +96,8 @@ pub(crate) async fn dispatch(cmd: RunCommands, globals: &GlobalArgs) -> Result<(
|
|||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
#[cfg(feature = "sleep_inhibitor")]
|
||||
let _sleep_guard = {
|
||||
let cli_settings = crate::user_config::load_settings()?;
|
||||
crate::sleep_inhibitor::guard(cli_settings.prevent_idle_sleep_enabled())
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
crate::sleep_inhibitor::guard(ctx.machine_settings().prevent_idle_sleep_enabled())
|
||||
};
|
||||
resume::resume_command(args, styles, globals).await
|
||||
}
|
||||
|
|
@ -103,7 +107,7 @@ pub(crate) async fn dispatch(cmd: RunCommands, globals: &GlobalArgs) -> Result<(
|
|||
}
|
||||
RunCommands::Fork(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
fork::run(&args, &styles, globals).await
|
||||
Box::pin(fork::run(&args, &styles, globals)).await
|
||||
}
|
||||
RunCommands::Wait(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ use anyhow::{Context, Result};
|
|||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, PreviewArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) async fn run(args: PreviewArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
let expires_in_secs =
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, ResumeArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
|
|
@ -14,7 +15,8 @@ pub(crate) async fn resume_command(
|
|||
styles: &'static Styles,
|
||||
globals: &GlobalArgs,
|
||||
) -> anyhow::Result<()> {
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use git2::Repository;
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::args::{GlobalArgs, RewindArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::store::rebuild::rebuild_run_store;
|
||||
use crate::server_client::ServerStoreClient;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
|
|
@ -30,7 +31,8 @@ pub(crate) struct TimelineEntryJson {
|
|||
|
||||
pub(crate) async fn run(args: &RewindArgs, styles: &Styles, globals: &GlobalArgs) -> Result<()> {
|
||||
let repo = Repository::discover(".").context("not in a git repository")?;
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run_id)?;
|
||||
let run_id = run.run_id();
|
||||
let state = lookup.client().get_run_state(&run_id).await?;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use anyhow::{Result, bail};
|
|||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, SshArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
|
|
@ -10,7 +11,8 @@ pub(crate) async fn run(args: SshArgs, globals: &GlobalArgs) -> Result<()> {
|
|||
globals.require_no_json()?;
|
||||
}
|
||||
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
let ssh = lookup
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use fabro_workflow::run_status::RunStatus;
|
|||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, WaitArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::{format_duration_ms, format_usd_micros};
|
||||
|
||||
|
|
@ -17,7 +18,8 @@ const WAIT_STARTUP_GRACE: std::time::Duration = std::time::Duration::from_millis
|
|||
const WAIT_STARTUP_GRACE: std::time::Duration = std::time::Duration::from_secs(3);
|
||||
|
||||
pub(crate) async fn run(args: &WaitArgs, styles: &Styles, globals: &GlobalArgs) -> Result<()> {
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run_info = lookup.resolve(&args.run)?;
|
||||
let client = lookup.client();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use serde::Serialize;
|
|||
use fabro_workflow::run_status::RunStatus;
|
||||
|
||||
use crate::args::{GlobalArgs, InspectArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client::RunProjection;
|
||||
use crate::server_runs::{ServerRunSummaryInfo, ServerSummaryLookup};
|
||||
|
||||
|
|
@ -19,7 +20,8 @@ pub(crate) struct InspectOutput {
|
|||
}
|
||||
|
||||
pub(crate) async fn run(args: &InspectArgs, _globals: &GlobalArgs) -> Result<()> {
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
let state = lookup.client().get_run_state(&run_id).await?;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use fabro_util::text::strip_goal_decoration;
|
|||
use fabro_workflow::run_status::RunStatus;
|
||||
|
||||
use crate::args::{GlobalArgs, RunsListArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::{ServerSummaryLookup, filter_server_runs};
|
||||
use crate::shared::{color_if, format_duration_ms, tilde_path};
|
||||
|
||||
|
|
@ -21,7 +22,8 @@ pub(crate) async fn list_command(
|
|||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
) -> Result<()> {
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let label_filters = parse_label_filters(&args.filter.label);
|
||||
let filtered = filter_server_runs(
|
||||
lookup.runs(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::{Context, Result, bail};
|
||||
|
||||
use crate::args::{GlobalArgs, RunsRemoveArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::server_runs::{
|
||||
ServerRunSummaryInfo, ServerSummaryLookup, resolve_server_run_from_summaries,
|
||||
|
|
@ -10,7 +11,8 @@ use crate::shared::print_json_pretty;
|
|||
use super::short_run_id;
|
||||
|
||||
pub(crate) async fn remove_command(args: &RunsRemoveArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let lookup = ServerSummaryLookup::connect(&args.server).await?;
|
||||
let ctx = CommandContext::for_target(&args.server)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
remove_from(args, lookup.client(), lookup.runs(), globals).await
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use anyhow::Result;
|
|||
use fabro_api::Client;
|
||||
|
||||
use crate::args::{GlobalArgs, SecretListArgs};
|
||||
use crate::server_client;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(super) async fn list_command(
|
||||
|
|
@ -13,7 +14,7 @@ pub(super) async fn list_command(
|
|||
.list_secrets()
|
||||
.send()
|
||||
.await
|
||||
.map_err(super::map_api_error)?;
|
||||
.map_err(server_client::map_api_error)?;
|
||||
let secrets = response.into_inner().data;
|
||||
if globals.json {
|
||||
print_json_pretty(&secrets)?;
|
||||
|
|
|
|||
|
|
@ -2,43 +2,17 @@ mod list;
|
|||
mod rm;
|
||||
mod set;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::args::{GlobalArgs, SecretCommand, SecretNamespace};
|
||||
use crate::server_client;
|
||||
|
||||
fn map_api_error<E>(err: progenitor_client::Error<E>) -> anyhow::Error
|
||||
where
|
||||
E: serde::Serialize + std::fmt::Debug,
|
||||
{
|
||||
match err {
|
||||
progenitor_client::Error::ErrorResponse(response) => {
|
||||
let status = response.status();
|
||||
if let Ok(value) = serde_json::to_value(response.into_inner()) {
|
||||
if let Some(detail) = value
|
||||
.get("errors")
|
||||
.and_then(serde_json::Value::as_array)
|
||||
.and_then(|errors| errors.first())
|
||||
.and_then(|entry| entry.get("detail"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
{
|
||||
return anyhow!("{detail}");
|
||||
}
|
||||
}
|
||||
anyhow!("request failed with status {status}")
|
||||
}
|
||||
progenitor_client::Error::UnexpectedResponse(response) => {
|
||||
anyhow!("request failed with status {}", response.status())
|
||||
}
|
||||
other => anyhow!("{other}"),
|
||||
}
|
||||
}
|
||||
use crate::command_context::CommandContext;
|
||||
|
||||
pub(crate) async fn dispatch(ns: SecretNamespace, globals: &GlobalArgs) -> Result<()> {
|
||||
let client = server_client::connect_server_backed_api_client(&ns.target).await?;
|
||||
let ctx = CommandContext::for_target(&ns.target)?;
|
||||
let server = ctx.server().await?;
|
||||
match ns.command {
|
||||
SecretCommand::List(args) => list::list_command(&client, &args, globals).await,
|
||||
SecretCommand::Rm(args) => rm::rm_command(&client, &args, globals).await,
|
||||
SecretCommand::Set(args) => set::set_command(&client, &args, globals).await,
|
||||
SecretCommand::List(args) => list::list_command(server.api(), &args, globals).await,
|
||||
SecretCommand::Rm(args) => rm::rm_command(server.api(), &args, globals).await,
|
||||
SecretCommand::Set(args) => set::set_command(server.api(), &args, globals).await,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use anyhow::Result;
|
|||
use fabro_api::Client;
|
||||
|
||||
use crate::args::{GlobalArgs, SecretRmArgs};
|
||||
use crate::server_client;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(super) async fn rm_command(
|
||||
|
|
@ -14,7 +15,7 @@ pub(super) async fn rm_command(
|
|||
.name(args.key.clone())
|
||||
.send()
|
||||
.await
|
||||
.map_err(super::map_api_error)?;
|
||||
.map_err(server_client::map_api_error)?;
|
||||
if globals.json {
|
||||
print_json_pretty(&serde_json::json!({ "key": args.key }))?;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use anyhow::Result;
|
|||
use fabro_api::{Client, types};
|
||||
|
||||
use crate::args::{GlobalArgs, SecretSetArgs};
|
||||
use crate::server_client;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(super) async fn set_command(
|
||||
|
|
@ -17,7 +18,7 @@ pub(super) async fn set_command(
|
|||
})
|
||||
.send()
|
||||
.await
|
||||
.map_err(super::map_api_error)?
|
||||
.map_err(server_client::map_api_error)?
|
||||
.into_inner();
|
||||
if globals.json {
|
||||
print_json_pretty(&meta)?;
|
||||
|
|
|
|||
|
|
@ -5,16 +5,15 @@ use cli_table::{Cell, CellStruct, Style, Table};
|
|||
use fabro_api::types;
|
||||
|
||||
use crate::args::{DfArgs, GlobalArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::shared::{format_size, print_json_pretty};
|
||||
|
||||
pub(super) async fn df_command(args: &DfArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let client = server_client::connect_server_backed_api_client_with_storage_dir(
|
||||
&args.connection.target,
|
||||
args.connection.storage_dir.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
let output = client
|
||||
let ctx = CommandContext::for_connection(&args.connection)?;
|
||||
let server = ctx.server().await?;
|
||||
let output = server
|
||||
.api()
|
||||
.get_system_disk_usage()
|
||||
.verbose(args.verbose)
|
||||
.send()
|
||||
|
|
@ -25,7 +24,8 @@ pub(super) async fn df_command(args: &DfArgs, globals: &GlobalArgs) -> Result<()
|
|||
let storage_dir = if globals.json {
|
||||
None
|
||||
} else {
|
||||
client
|
||||
server
|
||||
.api()
|
||||
.get_system_info()
|
||||
.send()
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -2,17 +2,15 @@ use anyhow::Result;
|
|||
use futures::StreamExt;
|
||||
|
||||
use crate::args::{GlobalArgs, SystemEventsArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::sse;
|
||||
|
||||
pub(super) async fn events_command(args: &SystemEventsArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let client = server_client::connect_server_backed_api_client_with_storage_dir(
|
||||
&args.connection.target,
|
||||
args.connection.storage_dir.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
let ctx = CommandContext::for_connection(&args.connection)?;
|
||||
let server = ctx.server().await?;
|
||||
|
||||
let mut request = client.attach_events();
|
||||
let mut request = server.api().attach_events();
|
||||
if !args.run_ids.is_empty() {
|
||||
request = request.run_id(args.run_ids.join(","));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use crate::args::{GlobalArgs, SystemInfoArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(super) async fn info_command(args: &SystemInfoArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let client = server_client::connect_server_backed_api_client_with_storage_dir(
|
||||
&args.connection.target,
|
||||
args.connection.storage_dir.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
let response = client
|
||||
let ctx = CommandContext::for_connection(&args.connection)?;
|
||||
let server = ctx.server().await?;
|
||||
let response = server
|
||||
.api()
|
||||
.get_system_info()
|
||||
.send()
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -6,16 +6,15 @@ use tracing::{debug, info};
|
|||
use fabro_api::types;
|
||||
|
||||
use crate::args::{GlobalArgs, RunsPruneArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::shared::{format_size, print_json_pretty};
|
||||
|
||||
pub(super) async fn prune_command(args: &RunsPruneArgs, globals: &GlobalArgs) -> Result<()> {
|
||||
let client = server_client::connect_server_backed_api_client_with_storage_dir(
|
||||
&args.connection.target,
|
||||
args.connection.storage_dir.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
let response = client
|
||||
let ctx = CommandContext::for_connection(&args.connection)?;
|
||||
let server = ctx.server().await?;
|
||||
let response = server
|
||||
.api()
|
||||
.prune_runs()
|
||||
.body(types::PruneRunsRequest {
|
||||
before: args.filter.before.clone(),
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ use fabro_config::ConfigLayer;
|
|||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, ValidateArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::run::output::api_diagnostics_to_local;
|
||||
use crate::manifest_builder::{ManifestBuildInput, build_run_manifest};
|
||||
use crate::server_client;
|
||||
use crate::shared::{print_diagnostics, print_json_pretty, relative_path};
|
||||
|
||||
pub(crate) async fn run(
|
||||
|
|
@ -13,15 +13,15 @@ pub(crate) async fn run(
|
|||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
) -> anyhow::Result<()> {
|
||||
let cwd = std::env::current_dir()?;
|
||||
let ctx = CommandContext::for_target(&args.target)?;
|
||||
let built = build_run_manifest(ManifestBuildInput {
|
||||
workflow: args.workflow.clone(),
|
||||
cwd,
|
||||
cwd: ctx.cwd().to_path_buf(),
|
||||
args_layer: ConfigLayer::default(),
|
||||
args: None,
|
||||
run_id: None,
|
||||
})?;
|
||||
let client = server_client::connect_server_only(&args.target).await?;
|
||||
let client = ctx.server().await?;
|
||||
let response = client.run_preflight(built.manifest).await?;
|
||||
let diagnostics = api_diagnostics_to_local(&response.workflow.diagnostics);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#![allow(clippy::print_stdout, clippy::print_stderr, clippy::exit)]
|
||||
|
||||
mod args;
|
||||
mod command_context;
|
||||
mod commands;
|
||||
mod logging;
|
||||
mod manifest_builder;
|
||||
|
|
|
|||
|
|
@ -97,11 +97,14 @@ pub(crate) async fn connect_server_target_direct(target: &str) -> Result<ServerS
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn connect_server_only(args: &ServerTargetArgs) -> Result<ServerStoreClient> {
|
||||
let settings = user_config::load_settings()?;
|
||||
let target = user_config::resolve_server_target(args, &settings)?;
|
||||
pub(crate) async fn connect_server_with_settings(
|
||||
args: &ServerTargetArgs,
|
||||
settings: &Settings,
|
||||
base_config_path: &Path,
|
||||
) -> Result<ServerStoreClient> {
|
||||
let target = user_config::resolve_server_target(args, settings)?;
|
||||
let runtime = LocalServerRuntime {
|
||||
active_config_path: user_config::active_settings_path(None),
|
||||
active_config_path: base_config_path.to_path_buf(),
|
||||
storage_dir: settings.storage_dir(),
|
||||
};
|
||||
connect_target_api_client_bundle(&target, &runtime).await
|
||||
|
|
@ -125,15 +128,6 @@ pub(crate) async fn connect_api_client(storage_dir: &Path) -> Result<fabro_api::
|
|||
.map(|client| client.client)
|
||||
}
|
||||
|
||||
async fn connect_target_api_client(
|
||||
target: &user_config::ServerTarget,
|
||||
runtime: &LocalServerRuntime,
|
||||
) -> Result<fabro_api::Client> {
|
||||
connect_target_api_client_bundle(target, runtime)
|
||||
.await
|
||||
.map(|client| client.client)
|
||||
}
|
||||
|
||||
async fn connect_target_api_client_bundle(
|
||||
target: &user_config::ServerTarget,
|
||||
runtime: &LocalServerRuntime,
|
||||
|
|
@ -158,25 +152,6 @@ async fn connect_target_api_client_bundle(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn connect_server_backed_api_client(
|
||||
args: &ServerTargetArgs,
|
||||
) -> Result<fabro_api::Client> {
|
||||
connect_server_backed_api_client_with_storage_dir(args, None).await
|
||||
}
|
||||
|
||||
pub(crate) async fn connect_server_backed_api_client_with_storage_dir(
|
||||
args: &ServerTargetArgs,
|
||||
storage_dir: Option<&Path>,
|
||||
) -> Result<fabro_api::Client> {
|
||||
let settings = user_config::load_settings_with_storage_dir(storage_dir)?;
|
||||
let target = user_config::resolve_server_target(args, &settings)?;
|
||||
let runtime = LocalServerRuntime {
|
||||
active_config_path: user_config::active_settings_path(None),
|
||||
storage_dir: settings.storage_dir(),
|
||||
};
|
||||
connect_target_api_client(&target, &runtime).await
|
||||
}
|
||||
|
||||
fn connect_remote_api_client_bundle(
|
||||
api_url: &str,
|
||||
tls: Option<&user_config::ClientTlsSettings>,
|
||||
|
|
@ -288,6 +263,20 @@ impl ServerStoreClient {
|
|||
self.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn api(&self) -> &fabro_api::Client {
|
||||
&self.client
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn http_client(&self) -> &reqwest::Client {
|
||||
&self.http_client
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn base_url(&self) -> &str {
|
||||
&self.base_url
|
||||
}
|
||||
|
||||
pub(crate) async fn retrieve_server_settings(&self) -> Result<Settings> {
|
||||
let response = self
|
||||
.client
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
|
@ -8,7 +9,6 @@ use fabro_store::RunSummary;
|
|||
use fabro_types::{RunId, RunStatus, StatusReason};
|
||||
use fabro_workflow::run_lookup::{RunInfo, resolve_run_from_summaries, scratch_base};
|
||||
|
||||
use crate::args::ServerTargetArgs;
|
||||
use crate::server_client::{self, ServerStoreClient};
|
||||
|
||||
pub(crate) struct ServerRunLookup {
|
||||
|
|
@ -105,13 +105,12 @@ impl ServerRunSummaryInfo {
|
|||
}
|
||||
|
||||
pub(crate) struct ServerSummaryLookup {
|
||||
client: ServerStoreClient,
|
||||
client: Arc<ServerStoreClient>,
|
||||
runs: Vec<ServerRunSummaryInfo>,
|
||||
}
|
||||
|
||||
impl ServerSummaryLookup {
|
||||
pub(crate) async fn connect(args: &ServerTargetArgs) -> Result<Self> {
|
||||
let client = server_client::connect_server_only(args).await?;
|
||||
pub(crate) async fn from_client(client: Arc<ServerStoreClient>) -> Result<Self> {
|
||||
let summaries = client.list_store_runs().await?;
|
||||
let mut runs = summaries
|
||||
.into_iter()
|
||||
|
|
@ -126,7 +125,7 @@ impl ServerSummaryLookup {
|
|||
}
|
||||
|
||||
pub(crate) fn client(&self) -> &ServerStoreClient {
|
||||
&self.client
|
||||
self.client.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) fn runs(&self) -> &[ServerRunSummaryInfo] {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue