diff --git a/README.md b/README.md index 60aca55..ab20d28 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,18 @@ cargo build --release ## Preparation -For your plugin, create a `spin-pluginify.toml` file with the following content: +You can scaffold a new `spin-pluginify.toml` manifest using `--init`: + +```bash +spin pluginify --init --name --package <./PATH/TO/EXECUTABLE> +``` + +This creates a `spin-pluginify.toml` file with reasonable defaults. Alternatively, you can manually create a `spin-pluginify.toml` file with the following content: ```toml name = "" version = "0.1" -spin_compatibility = ">=0.7" +spin_compatibility = ">=4.0.0" license = "Apache-2.0" package = "<./PATH/TO/EXECUTABLE>" # optional - if present these files will be added to the plugin tar file diff --git a/src/main.rs b/src/main.rs index a838f32..996f4b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,17 +60,52 @@ struct PluginifyCommand { /// Install the plugin when done. #[clap(short, long)] install: bool, + + /// Scaffold a new spin-pluginify manifest. + #[clap( + name = "INIT", + long = "init", + conflicts_with = "FILE", + conflicts_with = "BINARY", + conflicts_with = "MERGE", + conflicts_with = "install", + requires = "NAME", + requires = "PACKAGE" + )] + init: bool, + + /// The name of the plugin (requires --init). + #[clap( + name = "NAME", + short = 'n', + long = "name", + help_heading = "Init Options", + requires = "INIT" + )] + name: Option, + + /// Path to the plugin executable (requires --init). + #[clap( + name = "PACKAGE", + short = 'p', + long = "package", + help_heading = "Init Options", + requires = "INIT" + )] + package: Option, } fn main() -> Result<(), Error> { let cmd = PluginifyCommand::parse(); - if cmd.merge { - cmd.run_merge() - } else { - cmd.run_local() + match (cmd.init, cmd.merge) { + (true, _) => cmd.run_init(), + (_, true) => cmd.run_merge(), + _ => cmd.run_local(), } + } + impl PluginifyCommand { fn packaging_settings(&self) -> Result { if let Some(binary) = self.immediate.as_ref() { @@ -98,6 +133,45 @@ impl PluginifyCommand { Ok(ps) } + fn run_init(&self) -> Result<(), Error> { + let output_path = PathBuf::from("spin-pluginify.toml"); + + if output_path.exists() { + anyhow::bail!( + "Manifest file `{}` already exists", + output_path.display() + ); + } + + let name = self.name.clone().context("plugin name is required")?; + let package = self.package.clone().context("package path is required")?; + + let manifest = PackagingSettings { + name, + package, + spin_compatibility: ">=4.0.0".to_string(), + license: "Apache-2.0".to_string(), + version: PluginVersion::Exact("0.1.0".to_string()), + description: None, + homepage: None, + assets: None, + }; + + let toml_string = toml::to_string_pretty(&manifest) + .context("failed to serialize manifest to TOML")?; + + if self.verbose { + eprintln!("Scaffolded manifest content:\n{toml_string}"); + } + + std::fs::write(&output_path, &toml_string) + .with_context(|| format!("failed to write manifest to `{}`", output_path.display()))?; + + println!("Created `{}`", output_path.display()); + + Ok(()) + } + fn run_local(&self) -> Result<(), Error> { let ps = self.packaging_settings()?; @@ -327,7 +401,7 @@ fn file_digest_string(path: &PathBuf) -> Result { Ok(digest_string) } -#[derive(Debug, serde::Deserialize)] +#[derive(Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] struct PackagingSettings { name: String, @@ -347,7 +421,7 @@ impl PackagingSettings { } } -#[derive(Clone, Debug, serde::Deserialize)] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(untagged)] enum PluginVersion { Exact(String), @@ -382,4 +456,4 @@ fn try_infer_version(file: impl AsRef) -> anyhow::Result { } Err(anyhow!("`spin pluginify` doesn't know how to infer version from `{filed}`")) -} +} \ No newline at end of file