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