vdev/commands/meta/
install_git_hooks.rs1use anyhow::{Ok, Result};
2use std::fs::File;
3use std::io::Write;
4
5#[cfg(unix)]
6use std::os::unix::fs::PermissionsExt;
7
8use crate::app;
9use std::path::Path;
10
11const SIGNOFF_HOOK: &str = r#"#!/usr/bin/env bash
12set -euo pipefail
13
14# Automatically sign off your commits.
15#
16# Installation:
17#
18# cp scripts/signoff-git-hook.sh .git/hooks/commit-msg
19#
20# It's also possible to symlink the script, however that's a security hazard and
21# is not recommended.
22
23NAME="$(git config user.name)"
24EMAIL="$(git config user.email)"
25
26if [ -z "$NAME" ]; then
27 echo "empty git config user.name"
28 exit 1
29fi
30
31if [ -z "$EMAIL" ]; then
32 echo "empty git config user.email"
33 exit 1
34fi
35
36git interpret-trailers --if-exists doNothing --trailer \
37 "Signed-off-by: $NAME <$EMAIL>" \
38 --in-place "$1"
39
40"#;
41
42#[derive(clap::Args, Debug)]
45#[command()]
46pub struct Cli {}
47
48impl Cli {
49 pub fn exec(self) -> Result<()> {
50 let hook_dir = Path::new(app::path()).join(".git").join("hooks");
51
52 std::fs::create_dir_all(&hook_dir)?;
56
57 let file_path = hook_dir.join("commit-msg");
58 let mut file = File::create(&file_path)?;
59 file.write_all(SIGNOFF_HOOK.as_bytes())?;
60 #[cfg(unix)]
61 {
62 file.metadata()?.permissions().set_mode(0o755);
63 }
64 println!("Created signoff script in {}", file_path.display());
65
66 Ok(())
67 }
68}