From ecfb23595366a275e70884cca62574655687e15b Mon Sep 17 00:00:00 2001 From: tknickman Date: Wed, 10 May 2023 22:53:36 -0400 Subject: [PATCH] feat(turbo): g update args fix debug --- crates/turborepo-lib/src/cli.rs | 79 ++++++++++++++++++- crates/turborepo-lib/src/commands/generate.rs | 60 ++++++++++++++ crates/turborepo-lib/src/commands/mod.rs | 1 + 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 crates/turborepo-lib/src/commands/generate.rs diff --git a/crates/turborepo-lib/src/cli.rs b/crates/turborepo-lib/src/cli.rs index b7d5806bade68..0e0924c25fb34 100644 --- a/crates/turborepo-lib/src/cli.rs +++ b/crates/turborepo-lib/src/cli.rs @@ -14,7 +14,7 @@ use tracing::{debug, error}; use turbopath::AbsoluteSystemPathBuf; use crate::{ - commands::{bin, daemon, link, login, logout, unlink, CommandBase}, + commands::{bin, daemon, generate, link, login, logout, unlink, CommandBase}, get_version, shim::{RepoMode, RepoState}, tracing::TurboSubscriber, @@ -264,6 +264,14 @@ pub enum Command { #[clap(long, value_enum, default_value_t = LinkTarget::RemoteCache)] target: LinkTarget, }, + /// Generate a new app / package + G { + #[clap(long, default_value_t = String::from("latest"), hide = true)] + tag: String, + #[clap(subcommand)] + #[serde(flatten)] + command: GenerateCommand, + }, /// Login to your Vercel account Login { #[clap(long = "sso-team")] @@ -300,6 +308,71 @@ pub enum Command { }, } +#[derive(Parser, Clone, Debug, Default, Serialize, PartialEq)] +pub struct GenerateArgs { + /// The name of the generator to run + pub generator_name: Option, + /// Generator configuration file + #[clap(short = 'c', long)] + pub config: Option, + /// The root of your repository (default: directory with root turbo.json) + #[clap(short = 'r', long)] + pub root: Option, + /// Answers passed directly to generator + #[clap(short = 'a', long, value_delimiter = ' ', num_args = 1..)] + pub args: Vec, +} + +#[derive(Parser, Clone, Debug, Default, Serialize, PartialEq)] +pub struct GenerateAddArgs { + /// Name for the new workspace + #[clap(short = 'n', long)] + pub name: Option, + /// Generate an empty workspace + #[clap( + short = 'b', + long, + conflicts_with = "copy", + default_missing_value = "true" + )] + pub empty: bool, + /// Generate a workspace using an existing workspace as a template + #[clap(short = 'c', long, conflicts_with = "empty")] + pub copy: bool, + /// Where the new workspace should be created + #[clap(short = 'd', long)] + pub destination: Option, + /// The type of workspace to create + #[clap(short = 'w', long)] + pub what: Option, + /// The root of your repository (default: directory with root turbo.json) + #[clap(short = 'r', long)] + pub root: Option, + /// An example package to add. You can use a GitHub URL with any branch + /// and/or subdirectory. + #[clap(short = 'e', long)] + pub example: Option, + /// In a rare case, your GitHub URL might contain a branch name with a slash + /// (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). In this + /// case, you must specify the path to the example separately: + /// --example-path foo/bar + #[clap(short = 'p', long)] + pub example_path: Option, + /// Do not filter available dependencies by the workspace type + #[clap(long)] + pub show_all_dependencies: bool, +} + +#[derive(Subcommand, Clone, Debug, Serialize, PartialEq)] +pub enum GenerateCommand { + /// Add a new package or app to your project + #[clap(name = "add", aliases = &["a"])] + Add(GenerateAddArgs), + /// Run custom generators + #[clap(name = "generate", aliases = &["g", "gen"])] + Gen(GenerateArgs), +} + #[derive(Parser, Clone, Debug, Default, Serialize, PartialEq)] pub struct RunArgs { /// Override the filesystem cache directory. @@ -567,6 +640,10 @@ pub async fn run( Ok(Payload::Rust(Ok(0))) } + Command::G { command, tag } => { + generate::run(command, tag)?; + Ok(Payload::Rust(Ok(0))) + } Command::Daemon { command, idle_time } => { let base = CommandBase::new(cli_args.clone(), repo_root, version, ui)?; diff --git a/crates/turborepo-lib/src/commands/generate.rs b/crates/turborepo-lib/src/commands/generate.rs new file mode 100644 index 0000000000000..4c3dad89dce67 --- /dev/null +++ b/crates/turborepo-lib/src/commands/generate.rs @@ -0,0 +1,60 @@ +use std::process::{Command, Stdio}; + +use anyhow::Result; + +use crate::{child::spawn_child, cli::GenerateCommand}; + +fn verify_requirements() -> Result<()> { + let output = Command::new("npx") + .arg("--version") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status(); + + const ERROR_MSG: &str = "Unable to run generate - missing requirements (npx)"; + match output { + Ok(result) => { + if result.success() { + return Ok(()); + } + return Err(anyhow::anyhow!(ERROR_MSG)); + } + Err(_) => { + return Err(anyhow::anyhow!(ERROR_MSG)); + } + } +} + +fn call_turbo_gen(command: &str, tag: &String, raw_args: &str) -> Result { + let mut npx = Command::new("npx"); + npx.arg("--yes") + .arg(format!("@turbo/gen@{}", tag)) + .arg("raw") + .arg(command) + .args(["--json", raw_args]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()); + + let child = spawn_child(npx)?; + let exit_code = child.wait()?.code().unwrap_or(2); + Ok(exit_code) +} + +pub fn run(command: &GenerateCommand, tag: &String) -> Result<()> { + // ensure npx is available + verify_requirements()?; + + match command { + GenerateCommand::Add(args) => { + // convert args to json + let raw_args = serde_json::to_string(args)?; + call_turbo_gen("add", tag, &raw_args)?; + } + GenerateCommand::Gen(args) => { + let raw_args = serde_json::to_string(args)?; + call_turbo_gen("generate", tag, &raw_args)?; + } + }; + + Ok(()) +} diff --git a/crates/turborepo-lib/src/commands/mod.rs b/crates/turborepo-lib/src/commands/mod.rs index 0a2c9b84870a1..cb807847b8196 100644 --- a/crates/turborepo-lib/src/commands/mod.rs +++ b/crates/turborepo-lib/src/commands/mod.rs @@ -15,6 +15,7 @@ use crate::{ pub(crate) mod bin; pub(crate) mod daemon; +pub(crate) mod generate; pub(crate) mod link; pub(crate) mod login; pub(crate) mod logout;