Skip to content

Commit

Permalink
init command generates a node project (#302)
Browse files Browse the repository at this point in the history
  • Loading branch information
phiSgr authored Jan 31, 2024
1 parent 66379ba commit 5a0d86a
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 21 deletions.
8 changes: 7 additions & 1 deletion apps/create-moose-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env node

import { spawnSync } from "child_process";
import { mkdirSync } from "fs";
import { mkdirSync, existsSync } from "fs";

/**
* Returns the executable path which is located inside `node_modules`
Expand Down Expand Up @@ -39,6 +39,12 @@ function run() {
const args = process.argv.slice(2);
const name = args[0];
if (name !== undefined) {
if (existsSync(name)) {
console.log(
`${name} already exists. Either try using a new name, or remove the directory.`
);
process.exit(1);
}
mkdirSync(name);
}
const processResult = spawnSync(getExePath(), ["init"].concat(args), {
Expand Down
54 changes: 54 additions & 0 deletions apps/igloo-kit-cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/igloo-kit-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ http-body-util = "0.1"
lazy_static = "1.4.0"
anyhow = "1.0"
spinners = "4.1.1"
git2 = "0.18.1"

[dev-dependencies]
clickhouse = { version = "0.11.5", features = ["uuid", "test-util"] }
Expand Down
26 changes: 26 additions & 0 deletions apps/igloo-kit-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ use self::{
};
use crate::cli::settings::init_config_file;
use crate::project::Project;
use crate::utilities::git::is_git_repo;
use clap::Parser;
use commands::Commands;
use home::home_dir;
use logger::setup_logging;
use settings::{read_settings, Settings};
use std::path::Path;
use std::process::exit;

#[derive(Parser)]
#[command(author, version, about, long_about = None, arg_required_else_help(true))]
Expand Down Expand Up @@ -60,6 +63,19 @@ async fn top_command_handler(settings: Settings, commands: &Commands) {
);

let dir_path = Path::new(location);

if dir_path.canonicalize().unwrap() == home_dir().unwrap().canonicalize().unwrap() {
show_message!(
MessageType::Error,
Message {
action: "Init".to_string(),
details: "You cannot create a project in your home directory"
.to_string(),
}
);
exit(1);
}

let project = Project::from_dir(dir_path, name.clone(), *language);

debug!("Project: {:?}", project);
Expand All @@ -74,6 +90,16 @@ async fn top_command_handler(settings: Settings, commands: &Commands) {
project
.write_to_file()
.expect("Failed to write project to file");

let is_git_repo =
is_git_repo(dir_path).expect("Failed to check if directory is a git repo");
if !is_git_repo {
crate::utilities::git::create_init_commit(&project, dir_path);
show_message!(
MessageType::Success,
Message::new("Created".to_string(), "Git Repository".to_string())
);
}
}
Commands::Dev {} => {
info!("Running dev command");
Expand Down
2 changes: 1 addition & 1 deletion apps/igloo-kit-cli/src/cli/routines/initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ pub struct CreateDockerNetwork {
}

impl CreateDockerNetwork {
fn new(network_name: &'static str) -> Self {
pub fn new(network_name: &'static str) -> Self {
Self { network_name }
}
}
Expand Down
5 changes: 3 additions & 2 deletions apps/igloo-kit-cli/src/cli/routines/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use super::{
Routine, RoutineFailure, RoutineSuccess, RunMode,
};
use crate::cli::display::with_spinner;
use crate::cli::routines::initialize::CreateIglooTempDirectoryTree;
use crate::cli::routines::initialize::{CreateDockerNetwork, CreateIglooTempDirectoryTree};
use crate::cli::routines::util::ensure_docker_running;
use crate::utilities::constants::CLI_PROJECT_INTERNAL_DIR;
use crate::utilities::constants::{CLI_PROJECT_INTERNAL_DIR, PANDA_NETWORK};
use crate::{
cli::display::Message,
project::Project,
Expand Down Expand Up @@ -48,6 +48,7 @@ impl Routine for RunLocalInfrastructure {
CreateIglooTempDirectoryTree::new(RunMode::Explicit {}, self.project.clone())
.run_explicit()?;
ValidateMountVolumes::new(igloo_dir).run_explicit()?;
CreateDockerNetwork::new(PANDA_NETWORK).run_explicit()?;
ValidatePandaHouseNetwork::new().run_explicit()?;
RunRedPandaContainer::new(self.project.clone()).run_explicit()?;
ValidateRedPandaRun::new().run_explicit()?;
Expand Down
2 changes: 1 addition & 1 deletion apps/igloo-kit-cli/src/infrastructure/olap/clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ pub async fn check_ready(
crate::utilities::retry::retry(
|| run_query(&dummy_query, configured_client),
|i, e| {
i < 5
i < 10
&& match e {
clickhouse::error::Error::Network(v) => {
let err_string = v.to_string();
Expand Down
39 changes: 26 additions & 13 deletions apps/igloo-kit-cli/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//! - `project_file_location` - The location of the project file on disk
//! ```

use std::collections::HashMap;
use std::path::PathBuf;

use crate::cli::local_webserver::LocalWebserverConfig;
Expand All @@ -20,17 +21,21 @@ use crate::infrastructure::olap::clickhouse::config::ClickhouseConfig;
use crate::infrastructure::stream::redpanda::RedpandaConfig;

use crate::utilities::constants::{
APP_DIR, APP_DIR_LAYOUT, CLI_PROJECT_INTERNAL_DIR, PROJECT_CONFIG_FILE, SCHEMAS_DIR,
APP_DIR, APP_DIR_LAYOUT, CLI_PROJECT_INTERNAL_DIR, PROJECT_CONFIG_FILE_TS, SCHEMAS_DIR,
};
use config::{Config, ConfigError, File};
use log::debug;
use serde::{Deserialize, Serialize};
use std::path::Path;

#[derive(Serialize, Deserialize, Debug, Clone)]
struct ProjectConfigFile {
#[serde(rename_all = "camelCase")]
struct PackageJsonFile {
pub name: String,
pub language: SupportedLanguages,
pub version: String,
pub scripts: HashMap<String, String>,
pub dependencies: HashMap<String, String>,
pub dev_dependencies: HashMap<String, String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
Expand Down Expand Up @@ -69,7 +74,7 @@ impl Project {
location = location
.canonicalize()
.expect("The directory provided does not exist");
location.push(PROJECT_CONFIG_FILE);
location.push(PROJECT_CONFIG_FILE_TS);

debug!("Project file location: {:?}", location);

Expand All @@ -91,7 +96,7 @@ impl Project {

pub fn load(directory: PathBuf) -> Result<Self, ConfigError> {
let mut project_file = directory.clone();
project_file.push(PROJECT_CONFIG_FILE);
project_file.push(PROJECT_CONFIG_FILE_TS);

let project_file_location = project_file
.clone()
Expand All @@ -101,6 +106,8 @@ impl Project {

let s = Config::builder()
.add_source(File::from(project_file).required(true))
// TODO: infer language from the project file
.set_default("language", "Typescript")?
.set_override("project_file_location", project_file_location)?
.build()?;

Expand Down Expand Up @@ -137,7 +144,7 @@ impl Project {
}

// This is a Result of io::Error because the caller
// can be retruning a Result of io::Error or a Routine Failure
// can be returning a Result of io::Error or a Routine Failure
pub fn internal_dir(&self) -> std::io::Result<PathBuf> {
let mut internal_dir = self.project_file_location.clone();
internal_dir.pop();
Expand All @@ -146,13 +153,13 @@ impl Project {
if !internal_dir.is_dir() {
if internal_dir.exists() {
debug!("Internal dir exists as a file: {:?}", internal_dir);
std::io::Error::new(
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"The {} file exists but is not a directory",
CLI_PROJECT_INTERNAL_DIR
),
);
));
} else {
debug!("Creating internal dir: {:?}", internal_dir);
std::fs::create_dir_all(&internal_dir)?;
Expand All @@ -165,14 +172,20 @@ impl Project {
}

pub fn write_to_file(&self) -> Result<(), std::io::Error> {
let config_file = ProjectConfigFile {
let config_file = PackageJsonFile {
name: self.name.clone(),
language: self.language,
version: "0.0".to_string(),
// For local development of the CLI
// change `igloo-cli` to `<REPO_PATH>/apps/igloo-kit-cli/target/debug/igloo-cli`
scripts: HashMap::from([("dev".to_string(), "igloo-cli dev".to_string())]),
dependencies: HashMap::new(),
dev_dependencies: HashMap::from([(
"@514labs/moose-cli".to_string(),
"latest".to_string(),
)]),
};

let toml_project = toml::to_string(&config_file);

match toml_project {
match serde_json::to_string_pretty(&config_file) {
Ok(project) => {
std::fs::write(&self.project_file_location, project)?;
Ok(())
Expand Down
1 change: 1 addition & 0 deletions apps/igloo-kit-cli/src/utilities.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod constants;
pub mod docker;
pub mod git;
pub mod package_managers;
pub mod retry;
pub mod system;
2 changes: 1 addition & 1 deletion apps/igloo-kit-cli/src/utilities/constants.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub const CLI_VERSION: &str = env!("CARGO_PKG_VERSION");

pub const PROJECT_CONFIG_FILE: &str = "project.toml";
pub const PROJECT_CONFIG_FILE_TS: &str = "package.json";

pub const CLI_CONFIG_FILE: &str = "config.toml";
pub const CLI_USER_DIRECTORY: &str = ".igloo";
Expand Down
45 changes: 45 additions & 0 deletions apps/igloo-kit-cli/src/utilities/git.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::path::Path;

use crate::framework::languages::SupportedLanguages;
use git2::{Error, Repository, Signature};

use crate::project::Project;

pub fn is_git_repo(dir_path: &Path) -> Result<bool, Error> {
match Repository::open(dir_path) {
Ok(_) => Ok(true),
Err(e) if e.code() == git2::ErrorCode::NotFound => Ok(false),
Err(e) => Err(e),
}
}

pub fn create_init_commit(project: &Project, dir_path: &Path) {
let mut git_ignore_file = project.project_file_location.clone();
git_ignore_file.pop();
git_ignore_file.push(".gitignore");
let mut git_ignore_entries = vec![".igloo"];
git_ignore_entries.append(&mut match project.language {
SupportedLanguages::Typescript => vec!["node_modules", "dist", "coverage"],
});
let mut git_ignore = git_ignore_entries.join("\n");
git_ignore.push_str("\n\n");
std::fs::write(git_ignore_file, git_ignore).unwrap();

let repo = Repository::init(dir_path).expect("Failed to initialize git repo");
let author =
Signature::now("Moose CLI", "[email protected]").expect("Failed to create signature");

// Now let's create an empty tree for this commit
let mut index = repo.index().expect("Failed to get repo index");
index
.add_all(["."], git2::IndexAddOption::DEFAULT, None)
.expect("Failed to add path to index");
index.write().expect("Failed to write index");
let tree_id = index.write_tree().expect("Failed to write tree");

let tree = repo.find_tree(tree_id).expect("Failed to find tree");

// empty parent because it's the first commit
repo.commit(Some("HEAD"), &author, &author, "Initial commit", &tree, &[])
.expect("Failed to create initial commit");
}
4 changes: 2 additions & 2 deletions apps/igloo-kit-cli/tests/cli_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn can_run_cli_init() -> Result<(), Box<dyn std::error::Error>> {
// TODO add more specific tests when the layout of the
// app is more stable
temp.child(".igloo").assert(predicate::path::exists());
temp.child("project.toml").assert(predicate::path::exists());
temp.child("package.json").assert(predicate::path::exists());
temp.child("app").assert(predicate::path::exists());

Ok(())
Expand All @@ -61,7 +61,7 @@ fn should_not_run_if_coming_soon_wall_is_blocking() -> Result<(), Box<dyn std::e
cmd.assert().success();

temp.child(".igloo").assert(predicate::path::missing());
temp.child("project.toml")
temp.child("package.json")
.assert(predicate::path::missing());
temp.child("app").assert(predicate::path::missing());

Expand Down

0 comments on commit 5a0d86a

Please sign in to comment.