Skip to content

Commit

Permalink
Merge pull request #2266 from itowlson/trigger-metadata-protocol
Browse files Browse the repository at this point in the history
Multi-trigger: ask triggers which flags they can accept
  • Loading branch information
itowlson authored Feb 13, 2024
2 parents 84511c0 + febbb99 commit 2c83cc5
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 70 deletions.
6 changes: 5 additions & 1 deletion crates/plugins/src/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::io::IsTerminal;

use anyhow::{anyhow, Context, Result};
use semver::{Version, VersionReq};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -181,7 +183,9 @@ fn inner_warn_unsupported_version(
if !is_version_fully_compatible(supported_on, spin_version)? {
let version = Version::parse(spin_version)?;
if !version.pre.is_empty() {
terminal::warn!("You're using a pre-release version of Spin ({spin_version}). This plugin might not be compatible (supported: {supported_on}). Continuing anyway.");
if std::io::stderr().is_terminal() {
terminal::warn!("You're using a pre-release version of Spin ({spin_version}). This plugin might not be compatible (supported: {supported_on}). Continuing anyway.");
}
} else if override_compatibility_check {
terminal::warn!("Plugin is not compatible with this version of Spin (supported: {supported_on}, actual: {spin_version}). Check overridden ... continuing to install or execute plugin.");
} else {
Expand Down
13 changes: 13 additions & 0 deletions crates/trigger/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ use crate::{
};
use crate::{TriggerExecutor, TriggerExecutorBuilder};

mod launch_metadata;
pub use launch_metadata::LaunchMetadata;

pub const APP_LOG_DIR: &str = "APP_LOG_DIR";
pub const DISABLE_WASMTIME_CACHE: &str = "DISABLE_WASMTIME_CACHE";
pub const FOLLOW_LOG_OPT: &str = "FOLLOW_ID";
Expand Down Expand Up @@ -125,6 +128,9 @@ where

#[clap(long = "help-args-only", hide = true)]
pub help_args_only: bool,

#[clap(long = "launch-metadata-only", hide = true)]
pub launch_metadata_only: bool,
}

/// An empty implementation of clap::Args to be used as TriggerExecutor::RunConfig
Expand All @@ -147,6 +153,13 @@ where
return Ok(());
}

if self.launch_metadata_only {
let lm = LaunchMetadata::infer::<Executor>();
let json = serde_json::to_string_pretty(&lm)?;
eprintln!("{json}");
return Ok(());
}

// Required env vars
let working_dir = std::env::var(SPIN_WORKING_DIR).context(SPIN_WORKING_DIR)?;
let locked_url = std::env::var(SPIN_LOCKED_URL).context(SPIN_LOCKED_URL)?;
Expand Down
86 changes: 86 additions & 0 deletions crates/trigger/src/cli/launch_metadata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use clap::{Args, CommandFactory};
use serde::{Deserialize, Serialize};
use std::ffi::OsString;

use crate::{cli::TriggerExecutorCommand, TriggerExecutor};

/// Contains information about the trigger flags (and potentially
/// in future configuration) that a consumer (such as `spin up`)
/// can query using `--launch-metadata-only`.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct LaunchMetadata {
all_flags: Vec<LaunchFlag>,
}

// This assumes no triggers that want to participate in multi-trigger
// use positional arguments. This is a restriction we'll have to make
// anyway: suppose triggers A and B both take one positional arg, and
// the user writes `spin up 123 456` - which value would go to which trigger?
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct LaunchFlag {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
short: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
long: Option<String>,
}

impl LaunchMetadata {
pub fn infer<Executor: TriggerExecutor>() -> Self
where
Executor::RunConfig: Args,
{
let all_flags: Vec<_> = TriggerExecutorCommand::<Executor>::command()
.get_arguments()
.map(LaunchFlag::infer)
.collect();

LaunchMetadata { all_flags }
}

pub fn matches<'a>(&self, groups: &[Vec<&'a OsString>]) -> Vec<&'a OsString> {
let mut matches = vec![];

for group in groups {
if group.is_empty() {
continue;
}
if self.is_match(group[0]) {
matches.extend(group);
}
}

matches
}

fn is_match(&self, arg: &OsString) -> bool {
self.all_flags.iter().any(|f| f.is_match(arg))
}

pub fn is_group_match(&self, group: &[&OsString]) -> bool {
if group.is_empty() {
false
} else {
self.all_flags.iter().any(|f| f.is_match(group[0]))
}
}
}

impl LaunchFlag {
fn infer(arg: &clap::Arg) -> Self {
Self {
long: arg.get_long().map(|s| format!("--{s}")),
short: arg.get_short().map(|ch| format!("-{ch}")),
}
}

fn is_match(&self, candidate: &OsString) -> bool {
let Some(s) = candidate.to_str() else {
return false;
};
let candidate = Some(s.to_owned());

candidate == self.long || candidate == self.short
}
}
Loading

0 comments on commit 2c83cc5

Please sign in to comment.