From 2f1358e9f857fec27b3310ee6357ba369af583d2 Mon Sep 17 00:00:00 2001 From: NathanFlurry Date: Thu, 1 Aug 2024 23:50:38 +0000 Subject: [PATCH] feat(sidekick): add backend gen command (#273) --- cli/src/commands/backend/mod.rs | 108 ++++++++++++------- cli/src/commands/sidekick/backend_gen_sdk.rs | 64 +++++++++++ cli/src/commands/sidekick/mod.rs | 4 + 3 files changed, 138 insertions(+), 38 deletions(-) create mode 100644 cli/src/commands/sidekick/backend_gen_sdk.rs diff --git a/cli/src/commands/backend/mod.rs b/cli/src/commands/backend/mod.rs index ee1f3fde..32f4c928 100644 --- a/cli/src/commands/backend/mod.rs +++ b/cli/src/commands/backend/mod.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, io::Write, path::PathBuf}; +use std::{collections::HashMap, io::Write, path::PathBuf, str::FromStr}; use clap::Parser; use cli_core::rivet_api::{apis, models}; @@ -131,13 +131,38 @@ pub struct OpenGbCommandOpts { pub cwd: PathBuf, } -pub async fn run_opengb_command(opts: OpenGbCommandOpts) -> GlobalResult { - let run_native = std::env::var("_RIVET_NATIVE_OPENGB") - .ok() - .map_or(false, |x| &x == "1"); +#[derive(PartialEq)] +pub enum OpenGbTarget { + Native, + Docker, +} + +impl Default for OpenGbTarget { + fn default() -> Self { + Self::Docker + } +} +impl FromStr for OpenGbTarget { + type Err = GlobalError; + + fn from_str(s: &str) -> GlobalResult { + match s { + "native" => Ok(Self::Native), + "docker" => Ok(Self::Docker), + _ => bail!("unknown opengb target: {s}"), + } + } +} + +pub fn build_opengb_command(opts: OpenGbCommandOpts) -> GlobalResult { + let opengb_target = if let Ok(x) = std::env::var("RIVET_OPENGB_TARGET") { + OpenGbTarget::from_str(&x)? + } else { + OpenGbTarget::default() + }; // Check OpenGB installed - if run_native { + if opengb_target == OpenGbTarget::Native { ensure!( which::which("opengb").is_ok(), "OpenGB is not installed. Install it from {}.", @@ -146,39 +171,46 @@ pub async fn run_opengb_command(opts: OpenGbCommandOpts) -> GlobalResult { + let mut cmd = Command::new("opengb"); + cmd.envs(opts.env); + cmd.current_dir(opts.cwd); + cmd.args(&opts.args); + Ok(cmd) } - for (k, v) in opts.env { - writeln!(env_file, "{k}={v}")?; + OpenGbTarget::Docker => { + let image_tag = std::env::var("RIVET_OPENGB_DOCKER_IMAGE") + .ok() + .unwrap_or_else(|| DEFAULT_OPENGB_DOCKER_TAG.to_string()); + + // Build env file + let mut env_file = NamedTempFile::new().expect("Failed to create temp file"); + for (k, v) in std::env::vars() { + writeln!(env_file, "{k}={v}")?; + } + if std::env::var("DATABASE_URL").is_err() { + writeln!(env_file, "DATABASE_URL=postgres://postgres:postgres@host.docker.internal:5432/postgres?sslmode=disable")?; + } + for (k, v) in opts.env { + writeln!(env_file, "{k}={v}")?; + } + + let mut cmd = Command::new("docker"); + cmd.arg("run").arg("-it"); + cmd.arg("--init"); + cmd.arg("--env-file").arg(env_file.path()); + cmd.arg("--add-host=host.docker.internal:host-gateway"); + cmd.arg("--publish=6420:6420"); + cmd.arg(format!("--volume={}:/backend", opts.cwd.display())); + cmd.arg("--workdir=/backend"); + cmd.arg(image_tag); + cmd.args(&opts.args); + Ok(cmd) } - - let mut cmd = Command::new("docker"); - cmd.arg("run").arg("-it"); - cmd.arg("--init"); - cmd.arg("--env-file").arg(env_file.path()); - cmd.arg("--add-host=host.docker.internal:host-gateway"); - cmd.arg("--publish=6420:6420"); - cmd.arg(format!("--volume={}:/backend", opts.cwd.display())); - cmd.arg("--workdir=/backend"); - cmd.arg(image_tag); - cmd.args(&opts.args); - Ok(cmd.status().await?) } } + +pub async fn run_opengb_command(opts: OpenGbCommandOpts) -> GlobalResult { + Ok(build_opengb_command(opts)?.status().await?) +} diff --git a/cli/src/commands/sidekick/backend_gen_sdk.rs b/cli/src/commands/sidekick/backend_gen_sdk.rs new file mode 100644 index 00000000..d8857050 --- /dev/null +++ b/cli/src/commands/sidekick/backend_gen_sdk.rs @@ -0,0 +1,64 @@ +use clap::Parser; +use global_error::prelude::*; +use serde::Serialize; +use std::collections::HashMap; + +use crate::commands::backend; + +use super::SideKickHandler; + +#[derive(Parser)] +pub struct Opts { + #[clap(long)] + output_path: String, + #[clap(long)] + unity: bool, + #[clap(long)] + godot: bool, +} + +#[derive(Serialize)] +pub struct Output { + pub exit_code: i32, + pub stdout: String, + pub stderr: String, +} + +impl SideKickHandler for Output {} + +impl Opts { + pub async fn execute(&self) -> GlobalResult { + // Run command + let target = if self.unity { + "unity" + } else if self.godot { + "godot" + } else { + bail!("no target selected") + }; + let args = vec![ + "sdk".into(), + "generate".into(), + "--output".into(), + self.output_path.clone(), + target.into(), + ]; + let mut env = HashMap::new(); + env.insert("OPENGB_TERM_COLOR".into(), "never".into()); + let opengb_output = backend::build_opengb_command(backend::OpenGbCommandOpts { + args, + env, + cwd: std::env::current_dir()?, + })? + .output() + .await?; + + let output = Output { + exit_code: opengb_output.status.code().unwrap_or(1), + stdout: String::from_utf8(opengb_output.stdout)?, + stderr: String::from_utf8(opengb_output.stderr)?, + }; + + Ok(output) + } +} diff --git a/cli/src/commands/sidekick/mod.rs b/cli/src/commands/sidekick/mod.rs index 1d3f2275..0cbe1223 100644 --- a/cli/src/commands/sidekick/mod.rs +++ b/cli/src/commands/sidekick/mod.rs @@ -9,6 +9,7 @@ use crate::util::{ struct_fmt::{self, Format}, }; +pub mod backend_gen_sdk; pub mod deploy; pub mod generate_config; pub mod get_bootstrap_data; @@ -61,6 +62,7 @@ pub enum SubCommand { GetNamespaceDevelopmentToken(get_namespace_dev_token::Opts), /// Generate config GenerateConfig(generate_config::Opts), + BackendGenerateSdk(backend_gen_sdk::Opts), /// Unlink current game Unlink(unlink::Opts), } @@ -97,6 +99,7 @@ impl SubCommand { SubCommand::CheckLoginState => serialize_output(self.validate_token(&token)), SubCommand::GetCliVersion(opts) => serialize_output(opts.execute().await), SubCommand::GenerateConfig(opts) => serialize_output(opts.execute().await), + SubCommand::BackendGenerateSdk(opts) => serialize_output(opts.execute().await), _ => { // If the command is anything else, we need to check if a token // has already been provided. If not, we need to print an error @@ -148,6 +151,7 @@ impl SubCommand { | SubCommand::CheckLoginState | SubCommand::WaitForLogin(_) | SubCommand::GenerateConfig(_) + | SubCommand::BackendGenerateSdk(_) | SubCommand::GetCliVersion(_) => { unreachable!("This command should be handled before this") }