Skip to content

Commit cc81c12

Browse files
committed
Separate common trigger options from Factors specific ones.
Signed-off-by: Ryan Levick <[email protected]>
1 parent 30962b7 commit cc81c12

File tree

4 files changed

+88
-99
lines changed

4 files changed

+88
-99
lines changed

crates/trigger/src/cli.rs

Lines changed: 50 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ mod sqlite_statements;
33
mod summary;
44

55
use std::future::Future;
6-
use std::path::{Path, PathBuf};
6+
use std::path::PathBuf;
77

88
use anyhow::{Context, Result};
99
use clap::{Args, IntoApp, Parser};
1010
use spin_app::App;
11+
use spin_common::sloth;
1112
use spin_common::ui::quoted_path;
1213
use spin_common::url::parse_file_url;
13-
use spin_common::{arg_parser::parse_kv, sloth};
1414
use spin_core::async_trait;
1515
use spin_factors::RuntimeFactors;
1616
use spin_factors_executor::{ComponentLoader, FactorsExecutor};
@@ -20,7 +20,6 @@ use summary::{
2020
summarize_runtime_config, KeyValueDefaultStoreSummaryHook, SqliteDefaultStoreSummaryHook,
2121
};
2222

23-
use crate::factors::{TriggerFactors, TriggerFactorsRuntimeConfig};
2423
use crate::stdio::{FollowComponents, StdioLoggingExecutorHooks};
2524
use crate::{Trigger, TriggerApp};
2625
pub use launch_metadata::LaunchMetadata;
@@ -40,9 +39,9 @@ pub const SPIN_WORKING_DIR: &str = "SPIN_WORKING_DIR";
4039
#[derive(Parser, Debug)]
4140
#[clap(
4241
usage = "spin [COMMAND] [OPTIONS]",
43-
next_help_heading = help_heading::<T, F>()
42+
next_help_heading = help_heading::<T, B::Factors>()
4443
)]
45-
pub struct FactorsTriggerCommand<T: Trigger<F>, F: RuntimeFactors> {
44+
pub struct FactorsTriggerCommand<T: Trigger<B::Factors>, B: RuntimeFactorsBuilder> {
4645
/// Log directory for the stdout and stderr of components. Setting to
4746
/// the empty string disables logging to disk.
4847
#[clap(
@@ -117,16 +116,8 @@ pub struct FactorsTriggerCommand<T: Trigger<F>, F: RuntimeFactors> {
117116
#[clap(flatten)]
118117
pub trigger_args: T::CliArgs,
119118

120-
/// Set a key/value pair (key=value) in the application's
121-
/// default store. Any existing value will be overwritten.
122-
/// Can be used multiple times.
123-
#[clap(long = "key-value", parse(try_from_str = parse_kv))]
124-
key_values: Vec<(String, String)>,
125-
126-
/// Run a SQLite statement such as a migration against the default database.
127-
/// To run from a file, prefix the filename with @ e.g. spin up --sqlite @migration.sql
128-
#[clap(long = "sqlite")]
129-
sqlite_statements: Vec<String>,
119+
#[clap(flatten)]
120+
pub builder_args: B::Options,
130121

131122
#[clap(long = "help-args-only", hide = true)]
132123
pub help_args_only: bool,
@@ -135,16 +126,33 @@ pub struct FactorsTriggerCommand<T: Trigger<F>, F: RuntimeFactors> {
135126
pub launch_metadata_only: bool,
136127
}
137128

129+
/// Configuration options that are common to all triggers.
130+
#[derive(Debug)]
131+
pub struct CommonTriggerOptions {
132+
/// The Spin working directory.
133+
pub working_dir: PathBuf,
134+
/// Path to the runtime config file.
135+
pub runtime_config_file: Option<PathBuf>,
136+
/// Path to the state directory.
137+
pub state_dir: Option<String>,
138+
/// Path to the local app directory.
139+
pub local_app_dir: Option<String>,
140+
/// Whether to allow transient writes to mounted files
141+
pub allow_transient_write: bool,
142+
/// Which components should have their logs followed.
143+
pub follow_components: FollowComponents,
144+
/// Log directory for component stdout/stderr.
145+
pub log_dir: Option<PathBuf>,
146+
}
147+
138148
/// An empty implementation of clap::Args to be used as TriggerExecutor::RunConfig
139149
/// for executors that do not need additional CLI args.
140150
#[derive(Args)]
141151
pub struct NoCliArgs;
142152

143-
impl<T: Trigger<F>, F: RuntimeFactors> FactorsTriggerCommand<T, F> {
153+
impl<T: Trigger<B::Factors>, B: RuntimeFactorsBuilder> FactorsTriggerCommand<T, B> {
144154
/// Create a new TriggerExecutorBuilder from this TriggerExecutorCommand.
145-
pub async fn run<B: RuntimeFactorsBuilder<Factors = F, Options = TriggerAppOptions>>(
146-
self,
147-
) -> Result<()> {
155+
pub async fn run(self) -> Result<()> {
148156
// Handle --help-args-only
149157
if self.help_args_only {
150158
Self::command()
@@ -156,7 +164,7 @@ impl<T: Trigger<F>, F: RuntimeFactors> FactorsTriggerCommand<T, F> {
156164

157165
// Handle --launch-metadata-only
158166
if self.launch_metadata_only {
159-
let lm = LaunchMetadata::infer::<T, F>();
167+
let lm = LaunchMetadata::infer::<T, B>();
160168
let json = serde_json::to_string_pretty(&lm)?;
161169
eprintln!("{json}");
162170
return Ok(());
@@ -185,8 +193,7 @@ impl<T: Trigger<F>, F: RuntimeFactors> FactorsTriggerCommand<T, F> {
185193
}
186194

187195
let trigger = T::new(self.trigger_args, &app)?;
188-
let mut builder: TriggerAppBuilder<T, B> =
189-
TriggerAppBuilder::new(trigger, PathBuf::from(working_dir));
196+
let mut builder: TriggerAppBuilder<T, B> = TriggerAppBuilder::new(trigger);
190197
let config = builder.engine_config();
191198

192199
// Apply --cache / --disable-cache
@@ -198,21 +205,16 @@ impl<T: Trigger<F>, F: RuntimeFactors> FactorsTriggerCommand<T, F> {
198205
config.disable_pooling();
199206
}
200207

201-
let run_fut = builder
202-
.run(
203-
app,
204-
TriggerAppOptions {
205-
runtime_config_file: self.runtime_config_file.clone(),
206-
state_dir: self.state_dir.clone(),
207-
local_app_dir: local_app_dir.clone(),
208-
initial_key_values: self.key_values,
209-
sqlite_statements: self.sqlite_statements,
210-
allow_transient_write: self.allow_transient_write,
211-
follow_components,
212-
log_dir: self.log,
213-
},
214-
)
215-
.await?;
208+
let common_options = CommonTriggerOptions {
209+
working_dir: PathBuf::from(working_dir),
210+
runtime_config_file: self.runtime_config_file.clone(),
211+
state_dir: self.state_dir.clone(),
212+
local_app_dir: local_app_dir.clone(),
213+
allow_transient_write: self.allow_transient_write,
214+
follow_components,
215+
log_dir: self.log,
216+
};
217+
let run_fut = builder.run(app, common_options, self.builder_args).await?;
216218

217219
let (abortable, abort_handle) = futures::future::abortable(run_fut);
218220
ctrlc::set_handler(move || abort_handle.abort())?;
@@ -271,37 +273,14 @@ fn help_heading<T: Trigger<F>, F: RuntimeFactors>() -> Option<&'static str> {
271273
/// A builder for a [`TriggerApp`].
272274
pub struct TriggerAppBuilder<T, F> {
273275
engine_config: spin_core::Config,
274-
working_dir: PathBuf,
275276
pub trigger: T,
276277
_factors: std::marker::PhantomData<F>,
277278
}
278279

279-
/// Options for building a [`TriggerApp`].
280-
#[derive(Default)]
281-
pub struct TriggerAppOptions {
282-
/// Path to the runtime config file.
283-
pub runtime_config_file: Option<PathBuf>,
284-
/// Path to the state directory.
285-
pub state_dir: Option<String>,
286-
/// Path to the local app directory.
287-
pub local_app_dir: Option<String>,
288-
/// Initial key/value pairs to set in the app's default store.
289-
pub initial_key_values: Vec<(String, String)>,
290-
/// SQLite statements to run.
291-
pub sqlite_statements: Vec<String>,
292-
/// Whether to allow transient writes to mounted files
293-
pub allow_transient_write: bool,
294-
/// Which components should have their logs followed.
295-
pub follow_components: FollowComponents,
296-
/// Log directory for component stdout/stderr.
297-
pub log_dir: Option<PathBuf>,
298-
}
299-
300-
impl<T: Trigger<F::Factors>, F: RuntimeFactorsBuilder> TriggerAppBuilder<T, F> {
301-
pub fn new(trigger: T, working_dir: PathBuf) -> Self {
280+
impl<T: Trigger<B::Factors>, B: RuntimeFactorsBuilder> TriggerAppBuilder<T, B> {
281+
pub fn new(trigger: T) -> Self {
302282
Self {
303283
engine_config: spin_core::Config::default(),
304-
working_dir,
305284
trigger,
306285
_factors: Default::default(),
307286
}
@@ -315,16 +294,17 @@ impl<T: Trigger<F::Factors>, F: RuntimeFactorsBuilder> TriggerAppBuilder<T, F> {
315294
pub async fn build(
316295
&mut self,
317296
app: App,
318-
options: F::Options,
319-
) -> anyhow::Result<TriggerApp<T, F::Factors>> {
297+
common_options: CommonTriggerOptions,
298+
options: B::Options,
299+
) -> anyhow::Result<TriggerApp<T, B::Factors>> {
320300
let mut core_engine_builder = {
321301
self.trigger.update_core_config(&mut self.engine_config)?;
322302

323303
spin_core::Engine::builder(&self.engine_config)?
324304
};
325305
self.trigger.add_to_linker(core_engine_builder.linker())?;
326306

327-
let (factors, runtime_config) = F::new().build(self.working_dir.clone(), options)?;
307+
let (factors, runtime_config) = B::build(common_options, options)?;
328308

329309
// TODO: port the rest of the component loader logic
330310
struct SimpleComponentLoader;
@@ -409,24 +389,20 @@ impl<T: Trigger<F::Factors>, F: RuntimeFactorsBuilder> TriggerAppBuilder<T, F> {
409389
pub async fn run(
410390
mut self,
411391
app: App,
412-
options: F::Options,
392+
common_options: CommonTriggerOptions,
393+
options: B::Options,
413394
) -> anyhow::Result<impl Future<Output = anyhow::Result<()>>> {
414-
let configured_app = self.build(app, options).await?;
395+
let configured_app = self.build(app, common_options, options).await?;
415396
Ok(self.trigger.run(configured_app))
416397
}
417398
}
418399

419400
pub trait RuntimeFactorsBuilder {
420-
type Options;
401+
type Options: clap::Args;
421402
type Factors: RuntimeFactors;
422403

423-
fn new() -> Self
424-
where
425-
Self: Sized;
426-
427404
fn build(
428-
self,
429-
working_dir: PathBuf,
405+
common_options: CommonTriggerOptions,
430406
options: Self::Options,
431407
) -> anyhow::Result<(
432408
Self::Factors,

crates/trigger/src/cli/launch_metadata.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use clap::CommandFactory;
22
use serde::{Deserialize, Serialize};
3-
use spin_factors::RuntimeFactors;
43
use std::ffi::OsString;
54

65
use crate::{cli::FactorsTriggerCommand, Trigger};
76

7+
use super::RuntimeFactorsBuilder;
8+
89
/// Contains information about the trigger flags (and potentially
910
/// in future configuration) that a consumer (such as `spin up`)
1011
/// can query using `--launch-metadata-only`.
@@ -28,8 +29,8 @@ struct LaunchFlag {
2829
}
2930

3031
impl LaunchMetadata {
31-
pub fn infer<T: Trigger<F>, F: RuntimeFactors>() -> Self {
32-
let all_flags: Vec<_> = FactorsTriggerCommand::<T, F>::command()
32+
pub fn infer<T: Trigger<B::Factors>, B: RuntimeFactorsBuilder>() -> Self {
33+
let all_flags: Vec<_> = FactorsTriggerCommand::<T, B>::command()
3334
.get_arguments()
3435
.map(LaunchFlag::infer)
3536
.collect();

crates/trigger/src/factors.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::path::PathBuf;
22

33
use anyhow::Context as _;
4+
use spin_common::arg_parser::parse_kv;
45
use spin_factor_key_value::KeyValueFactor;
56
use spin_factor_llm::LlmFactor;
67
use spin_factor_outbound_http::OutboundHttpFactor;
@@ -88,3 +89,22 @@ impl TryFrom<TomlRuntimeConfigSource<'_, '_>> for TriggerFactorsRuntimeConfig {
8889
Self::from_source(value)
8990
}
9091
}
92+
93+
/// Options for building a [`TriggerFactors`].
94+
#[derive(Default, clap::Args)]
95+
pub struct TriggerAppOptions {
96+
/// Set the static assets of the components in the temporary directory as writable.
97+
#[clap(long = "allow-transient-write")]
98+
pub allow_transient_write: bool,
99+
100+
/// Set a key/value pair (key=value) in the application's
101+
/// default store. Any existing value will be overwritten.
102+
/// Can be used multiple times.
103+
#[clap(long = "key-value", parse(try_from_str = parse_kv))]
104+
key_values: Vec<(String, String)>,
105+
106+
/// Run a SQLite statement such as a migration against the default database.
107+
/// To run from a file, prefix the filename with @ e.g. spin up --sqlite @migration.sql
108+
#[clap(long = "sqlite")]
109+
sqlite_statements: Vec<String>,
110+
}

src/bin/spin.rs

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use spin_cli::commands::{
1919
use spin_cli::{build_info::*, subprocess::ExitStatusError};
2020
use spin_runtime_config::{ResolvedRuntimeConfig, UserProvidedPath};
2121
use spin_trigger::cli::help::HelpArgsOnlyTrigger;
22-
use spin_trigger::cli::{FactorsTriggerCommand, RuntimeFactorsBuilder, TriggerAppOptions};
23-
use spin_trigger::{TriggerFactors, TriggerFactorsRuntimeConfig};
22+
use spin_trigger::cli::{CommonTriggerOptions, FactorsTriggerCommand, RuntimeFactorsBuilder};
23+
use spin_trigger::{TriggerAppOptions, TriggerFactors, TriggerFactorsRuntimeConfig};
2424
use spin_trigger_http::HttpTrigger;
2525
use spin_trigger_redis::RedisTrigger;
2626

@@ -143,10 +143,10 @@ enum SpinApp {
143143

144144
#[derive(Subcommand)]
145145
enum TriggerCommands {
146-
Http(FactorsTriggerCommand<HttpTrigger, TriggerFactors>),
147-
Redis(FactorsTriggerCommand<RedisTrigger, TriggerFactors>),
146+
Http(FactorsTriggerCommand<HttpTrigger, Builder>),
147+
Redis(FactorsTriggerCommand<RedisTrigger, Builder>),
148148
#[clap(name = spin_cli::HELP_ARGS_ONLY_TRIGGER_TYPE, hide = true)]
149-
HelpArgsOnly(FactorsTriggerCommand<HelpArgsOnlyTrigger, TriggerFactors>),
149+
HelpArgsOnly(FactorsTriggerCommand<HelpArgsOnlyTrigger, Builder>),
150150
}
151151

152152
impl SpinApp {
@@ -161,9 +161,9 @@ impl SpinApp {
161161
Self::Login(cmd) => cmd.run(SpinApp::command()).await,
162162
Self::Registry(cmd) => cmd.run().await,
163163
Self::Build(cmd) => cmd.run().await,
164-
Self::Trigger(TriggerCommands::Http(cmd)) => cmd.run::<Builder>().await,
165-
Self::Trigger(TriggerCommands::Redis(cmd)) => cmd.run::<Builder>().await,
166-
Self::Trigger(TriggerCommands::HelpArgsOnly(cmd)) => cmd.run::<Builder>().await,
164+
Self::Trigger(TriggerCommands::Http(cmd)) => cmd.run().await,
165+
Self::Trigger(TriggerCommands::Redis(cmd)) => cmd.run().await,
166+
Self::Trigger(TriggerCommands::HelpArgsOnly(cmd)) => cmd.run().await,
167167
Self::Plugins(cmd) => cmd.run().await,
168168
Self::External(cmd) => execute_external_subcommand(cmd, app).await,
169169
Self::Watch(cmd) => cmd.run().await,
@@ -178,30 +178,22 @@ impl RuntimeFactorsBuilder for Builder {
178178
type Options = TriggerAppOptions;
179179
type Factors = TriggerFactors;
180180

181-
fn new() -> Self
182-
where
183-
Self: Sized,
184-
{
185-
Builder
186-
}
187-
188181
fn build(
189-
self,
190-
working_dir: PathBuf,
182+
common_options: CommonTriggerOptions,
191183
options: Self::Options,
192184
) -> anyhow::Result<(
193185
Self::Factors,
194186
<Self::Factors as spin_factors::RuntimeFactors>::RuntimeConfig,
195187
)> {
196-
let runtime_config_path = options.runtime_config_file;
197-
let local_app_dir = options.local_app_dir.map(PathBuf::from);
198-
let state_dir = match options.state_dir {
188+
let runtime_config_path = common_options.runtime_config_file;
189+
let local_app_dir = common_options.local_app_dir.map(PathBuf::from);
190+
let state_dir = match common_options.state_dir {
199191
// Make sure `--state-dir=""` unsets the state dir
200192
Some(s) if s.is_empty() => UserProvidedPath::Unset,
201193
Some(s) => UserProvidedPath::Provided(PathBuf::from(s)),
202194
None => UserProvidedPath::Default,
203195
};
204-
let log_dir = match &options.log_dir {
196+
let log_dir = match &common_options.log_dir {
205197
// Make sure `--log-dir=""` unsets the log dir
206198
Some(p) if p.as_os_str().is_empty() => UserProvidedPath::Unset,
207199
Some(p) => UserProvidedPath::Provided(p.clone()),
@@ -225,7 +217,7 @@ impl RuntimeFactorsBuilder for Builder {
225217
let log_dir = runtime_config.log_dir();
226218
let factors = TriggerFactors::new(
227219
runtime_config.state_dir(),
228-
working_dir,
220+
common_options.working_dir,
229221
options.allow_transient_write,
230222
runtime_config.key_value_resolver,
231223
runtime_config.sqlite_resolver,

0 commit comments

Comments
 (0)