Skip to content

Commit

Permalink
feat: support pre-commit init-templatedir (#101)
Browse files Browse the repository at this point in the history
* feat: support init-templatedir

* chore: update

* Fix and add test

---------

Co-authored-by: j178 <[email protected]>
  • Loading branch information
XmchxUp and j178 authored Nov 22, 2024
1 parent cd70f74 commit 81455fb
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 4 deletions.
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 @@ -238,3 +238,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'`?
"#);
}

0 comments on commit 81455fb

Please sign in to comment.