diff --git a/assets/default-model-project/README.md b/assets/default-model-project/README.md new file mode 100644 index 00000000..c5d511fe --- /dev/null +++ b/assets/default-model-project/README.md @@ -0,0 +1,9 @@ +# {project_name} +Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}. + +## Getting Started +To build this library or plugin, use: + +```bash +rojo build -o "{project_name}.rbxmx" +``` \ No newline at end of file diff --git a/assets/model.project.json b/assets/default-model-project/default.project.json similarity index 100% rename from assets/model.project.json rename to assets/default-model-project/default.project.json diff --git a/assets/default-model-project/gitignore.txt b/assets/default-model-project/gitignore.txt new file mode 100644 index 00000000..0dfcd838 --- /dev/null +++ b/assets/default-model-project/gitignore.txt @@ -0,0 +1,3 @@ +# Roblox Studio lock files +/*.rbxlx.lock +/*.rbxl.lock \ No newline at end of file diff --git a/assets/default-model.lua b/assets/default-model-project/src-init.lua similarity index 100% rename from assets/default-model.lua rename to assets/default-model-project/src-init.lua diff --git a/assets/default-place-project/README.md b/assets/default-place-project/README.md new file mode 100644 index 00000000..d2738511 --- /dev/null +++ b/assets/default-place-project/README.md @@ -0,0 +1,17 @@ +# {project_name} +Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}. + +## Getting Started +To build the place from scratch, use: + +```bash +rojo build -o Place.rbxlx +``` + +Next, open `Place.rbxlx` in Roblox Studio and start the Rojo server: + +```bash +rojo serve +``` + +For more help, check out [the Rojo documentation](https://rojo.space/docs). \ No newline at end of file diff --git a/assets/place.project.json b/assets/default-place-project/default.project.json similarity index 100% rename from assets/place.project.json rename to assets/default-place-project/default.project.json diff --git a/assets/default-place-project/gitignore.txt b/assets/default-place-project/gitignore.txt new file mode 100644 index 00000000..41d6a8ab --- /dev/null +++ b/assets/default-place-project/gitignore.txt @@ -0,0 +1,6 @@ +# Project place file +/{project_name}.rbxlx + +# Roblox Studio lock files +/*.rbxlx.lock +/*.rbxl.lock \ No newline at end of file diff --git a/src/cli/init.rs b/src/cli/init.rs index 5fbbdafa..064bf392 100644 --- a/src/cli/init.rs +++ b/src/cli/init.rs @@ -2,16 +2,23 @@ use std::{ fs::{self, OpenOptions}, io::{self, Write}, path::Path, + process::{Command, Stdio}, }; use snafu::Snafu; use crate::cli::{InitCommand, InitKind}; -static DEFAULT_PLACE_PROJECT: &str = include_str!("../../assets/place.project.json"); +static MODEL_PROJECT: &str = + include_str!("../../assets/default-model-project/default.project.json"); +static MODEL_README: &str = include_str!("../../assets/default-model-project/README.md"); +static MODEL_INIT: &str = include_str!("../../assets/default-model-project/src-init.lua"); +static MODEL_GIT_IGNORE: &str = include_str!("../../assets/default-model-project/gitignore.txt"); -static DEFAULT_MODEL_PROJECT: &str = include_str!("../../assets/model.project.json"); -static DEFAULT_MODEL_INIT: &str = include_str!("../../assets/default-model.lua"); +static PLACE_PROJECT: &str = + include_str!("../../assets/default-place-project/default.project.json"); +static PLACE_README: &str = include_str!("../../assets/default-place-project/README.md"); +static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt"); #[derive(Debug, Snafu)] pub struct InitError(Error); @@ -21,6 +28,9 @@ enum Error { #[snafu(display("A project file named default.project.json already exists in this folder"))] AlreadyExists, + #[snafu(display("git init failed"))] + GitInit, + #[snafu(display("I/O error"))] Io { source: io::Error }, } @@ -37,6 +47,8 @@ pub fn init(options: InitCommand) -> Result<(), InitError> { fn init_inner(options: InitCommand) -> Result<(), Error> { let base_path = options.absolute_path(); + fs::create_dir_all(&base_path)?; + let canonical = fs::canonicalize(&base_path)?; let project_name = canonical .file_name() @@ -54,9 +66,14 @@ fn init_inner(options: InitCommand) -> Result<(), Error> { } fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), Error> { - let project_file = project_params.render_template(DEFAULT_PLACE_PROJECT); + eprintln!("Creating new place project '{}'", project_params.name); + + let project_file = project_params.render_template(PLACE_PROJECT); try_create_project(base_path, &project_file)?; + let readme = project_params.render_template(PLACE_README); + write_if_not_exists(&base_path.join("README.md"), &readme)?; + let src = base_path.join("src"); fs::create_dir_all(&src)?; @@ -84,19 +101,34 @@ fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), Err "print(\"Hello world, from client!\")", )?; + let git_ignore = project_params.render_template(PLACE_GIT_IGNORE); + try_git_init(base_path, &git_ignore)?; + + eprintln!("Created project successfully."); + Ok(()) } fn init_model(base_path: &Path, project_params: ProjectParams) -> Result<(), Error> { - let project_file = project_params.render_template(DEFAULT_MODEL_PROJECT); + eprintln!("Creating new model project '{}'", project_params.name); + + let project_file = project_params.render_template(MODEL_PROJECT); try_create_project(base_path, &project_file)?; + let readme = project_params.render_template(MODEL_README); + write_if_not_exists(&base_path.join("README.md"), &readme)?; + let src = base_path.join("src"); fs::create_dir_all(&src)?; - let init = project_params.render_template(DEFAULT_MODEL_INIT); + let init = project_params.render_template(MODEL_INIT); write_if_not_exists(&src.join("init.lua"), &init)?; + let git_ignore = project_params.render_template(MODEL_GIT_IGNORE); + try_git_init(base_path, &git_ignore)?; + + eprintln!("Created project successfully."); + Ok(()) } @@ -108,7 +140,47 @@ struct ProjectParams { impl ProjectParams { /// Render a template by replacing variables with project parameters. fn render_template(&self, template: &str) -> String { - template.replace("{project_name}", &self.name) + template + .replace("{project_name}", &self.name) + .replace("{rojo_version}", env!("CARGO_PKG_VERSION")) + } +} + +/// Attempt to initialize a Git repository if necessary, and create .gitignore. +fn try_git_init(path: &Path, git_ignore: &str) -> Result<(), Error> { + if should_git_init(path) { + log::debug!("Initializing Git repository..."); + + let status = Command::new("git").arg("init").current_dir(path).status()?; + + if !status.success() { + return Err(Error::GitInit); + } + } + + write_if_not_exists(&path.join(".gitignore"), git_ignore)?; + + Ok(()) +} + +/// Tells whether we should initialize a Git repository inside the given path. +/// +/// Will return false if the user doesn't have Git installed or if the path is +/// already inside a Git repository. +fn should_git_init(path: &Path) -> bool { + let result = Command::new("git") + .args(&["rev-parse", "--is-inside-work-tree"]) + .stdout(Stdio::null()) + .current_dir(path) + .status(); + + match result { + // If the command ran, but returned a non-zero exit code, we are not in + // a Git repo and we should initialize one. + Ok(status) => !status.success(), + + // If the command failed to run, we probably don't have Git installed. + Err(_) => false, } }