Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support init-templatedir #101

Merged
merged 3 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ owo-colors = "4.1.0"
rand = "0.8.5"
rayon = "1.10.0"
rusqlite = { version = "0.32.1", features = ["bundled"] }
same-file = "1.0.6"
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.132"
serde_yaml = "0.9.34"
Expand Down
52 changes: 50 additions & 2 deletions src/cli/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ use std::path::{Path, PathBuf};
use anyhow::Result;
use indoc::indoc;
use owo_colors::OwoColorize;
use same_file::is_same_file;

use crate::cli::run;
use crate::cli::{ExitStatus, HookType};
use crate::fs::Simplified;
use crate::git;
use crate::git::git_cmd;
use crate::hook::Project;
use crate::printer::Printer;
use crate::store::Store;
Expand All @@ -21,8 +23,9 @@ pub(crate) async fn install(
overwrite: bool,
allow_missing_config: bool,
printer: Printer,
git_dir: Option<&Path>,
) -> Result<ExitStatus> {
if git::has_hooks_path_set().await? {
if git_dir.is_none() && git::has_hooks_path_set().await? {
writeln!(
printer.stderr(),
indoc::indoc! {"
Expand All @@ -35,7 +38,12 @@ pub(crate) async fn install(

let hook_types = get_hook_types(config.clone(), hook_types);

let hooks_path = git::get_git_common_dir().await?.join("hooks");
let hooks_path = if let Some(dir) = git_dir {
dir.join("hooks")
} else {
git::get_git_common_dir().await?.join("hooks")
};

fs_err::create_dir_all(&hooks_path)?;

let project = Project::from_config_file(config);
Expand Down Expand Up @@ -233,3 +241,43 @@ pub(crate) async fn uninstall(

Ok(ExitStatus::Success)
}

pub(crate) async fn init_template_dir(
directory: PathBuf,
config: Option<PathBuf>,
hook_types: Vec<HookType>,
requires_config: bool,
printer: Printer,
) -> Result<ExitStatus> {
install(
config,
hook_types,
false,
true,
!requires_config,
printer,
Some(&directory),
)
.await?;

let output = git_cmd("git config")?
.arg("config")
.arg("init.templateDir")
.check(false)
.output()
.await?;
let template_dir = String::from_utf8_lossy(&output.stdout).trim().to_string();

if template_dir.is_empty() || !is_same_file(&directory, Path::new(&template_dir))? {
writeln!(
printer.stderr(),
"{}",
indoc::formatdoc! {"
`init.templateDir` not set to the target directory
try `git config --global init.templateDir '{directory}'`?
", directory = directory.user_display().cyan() }
)?;
}

Ok(ExitStatus::Success)
}
18 changes: 16 additions & 2 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod validate;

pub(crate) use clean::clean;
pub(crate) use hook_impl::hook_impl;
pub(crate) use install::{install, uninstall};
pub(crate) use install::{init_template_dir, install, uninstall};
pub(crate) use run::run;
pub(crate) use sample_config::sample_config;
pub(crate) use self_update::self_update;
Expand Down Expand Up @@ -172,7 +172,7 @@ pub(crate) enum Command {
Clean,
/// Install hook script in a directory intended for use with `git config init.templateDir`.
#[command(name = "init-templatedir")]
InitTemplateDir,
InitTemplateDir(InitTemplateDirArgs),
/// Try the pre-commit hooks in the current repo.
TryRepo(Box<RunArgs>),

Expand Down Expand Up @@ -339,3 +339,17 @@ pub(crate) struct GenerateShellCompletionArgs {
#[arg(value_enum)]
pub shell: clap_complete::Shell,
}

#[derive(Debug, Args)]
pub(crate) struct InitTemplateDirArgs {
/// The directory in which to write the hook script.
pub(crate) directory: PathBuf,

/// Assume cloned repos should have a `pre-commit` config.
#[arg(long)]
pub(crate) no_allow_missing_config: bool,

/// Which hook type to install.
#[arg(short = 't', long = "hook-type", value_name = "HOOK_TYPE", value_enum)]
pub(crate) hook_types: Vec<HookType>,
}
13 changes: 13 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.overwrite,
args.allow_missing_config,
printer,
None,
)
.await
}
Expand Down Expand Up @@ -241,6 +242,18 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
clap_complete::generate(args.shell, &mut command, bin_name, &mut std::io::stdout());
Ok(ExitStatus::Success)
}
Command::InitTemplateDir(args) => {
show_settings!(args);

cli::init_template_dir(
args.directory,
cli.globals.config,
args.hook_types,
args.no_allow_missing_config,
printer,
)
.await
}
_ => {
writeln!(printer.stderr(), "Command not implemented yet")?;
Ok(ExitStatus::Failure)
Expand Down
17 changes: 17 additions & 0 deletions tests/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,20 @@ fn uninstall() -> anyhow::Result<()> {

Ok(())
}

#[test]
fn init_template_dir() {
let context = TestContext::new();
context.init_project();

cmd_snapshot!(context.filters(), context.command().arg("init-templatedir").arg(".git"), @r#"
success: true
exit_code: 0
----- stdout -----
pre-commit installed at .git/hooks/pre-commit

----- stderr -----
`init.templateDir` not set to the target directory
try `git config --global init.templateDir '.git'`?
"#);
}