diff --git a/Cargo.lock b/Cargo.lock index f979c8d7b1..c1dc2ab75c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8775,6 +8775,7 @@ dependencies = [ "spin-manifest", "tar", "tempfile", + "terminal", "tokio", "toml", "toml_edit", diff --git a/crates/templates/Cargo.toml b/crates/templates/Cargo.toml index 84c4704330..142054c8c5 100644 --- a/crates/templates/Cargo.toml +++ b/crates/templates/Cargo.toml @@ -27,6 +27,7 @@ spin-common = { path = "../common" } spin-manifest = { path = "../manifest" } tar = { workspace = true } tempfile = { workspace = true } +terminal = { path = "../terminal" } tokio = { workspace = true, features = ["fs", "process", "rt", "macros"] } toml = { workspace = true } toml_edit = { workspace = true } diff --git a/crates/templates/src/git.rs b/crates/templates/src/git.rs index 75d75caa6e..e4618ce8d9 100644 --- a/crates/templates/src/git.rs +++ b/crates/templates/src/git.rs @@ -1,4 +1,6 @@ -use std::io::ErrorKind; +use std::{io::ErrorKind, path::Path, process::Stdio}; + +use anyhow::Context; // TODO: the following and the second half of plugins/git.rs are duplicates @@ -46,3 +48,27 @@ impl UnderstandGitResult for Result { } } } + +pub(crate) async fn is_in_git_repo(dir: &Path) -> anyhow::Result { + let mut cmd = tokio::process::Command::new("git"); + cmd.arg("-C") + .arg(dir) + .arg("rev-parse") + .arg("--git-dir") + .stdout(Stdio::null()) + .stderr(Stdio::null()); + + let status = cmd + .status() + .await + .context("checking if new app is in a git repo")?; + Ok(status.success()) +} + +pub(crate) async fn init_git_repo(dir: &Path) -> Result<(), GitError> { + let mut cmd = tokio::process::Command::new("git"); + cmd.arg("-C").arg(dir).arg("init"); + + let result = cmd.output().await; + result.understand_git_result().map(|_| ()) +} diff --git a/crates/templates/src/run.rs b/crates/templates/src/run.rs index afea19a722..12098c5234 100644 --- a/crates/templates/src/run.rs +++ b/crates/templates/src/run.rs @@ -10,6 +10,7 @@ use walkdir::WalkDir; use crate::{ cancellable::Cancellable, + git, interaction::{InteractionStrategy, Interactive, Silent}, renderer::MergeTarget, template::{ExtraOutputAction, TemplateVariantInfo}, @@ -74,6 +75,8 @@ impl Run { .and_then(|t| t.render()) .and_then_async(|o| async move { o.write().await }) .await + .and_then_async(|_| self.maybe_initialise_git()) + .await .err() } @@ -352,6 +355,32 @@ impl Run { } } + async fn maybe_initialise_git(&self) -> anyhow::Result<()> { + if !matches!(self.options.variant, TemplateVariantInfo::NewApplication) { + return Ok(()); + } + + if self.options.no_vcs { + return Ok(()); + } + + let target_dir = self.generation_target_dir(); + + let skip_initing_repo = git::is_in_git_repo(&target_dir).await.unwrap_or(true); + + if skip_initing_repo { + return Ok(()); + } + + if let Err(e) = git::init_git_repo(&target_dir).await { + if !matches!(e, git::GitError::ProgramNotFound) { + terminal::warn!("Spin was unable to initialise a Git repository. Run `git init` manually if you want one."); + } + } + + Ok(()) + } + fn list_content_files(from: &Path) -> anyhow::Result> { let walker = WalkDir::new(from); let files = walker