feat: add commit-msg-rs
This commit is contained in:
241
commit-msg-rs/Cargo.lock
generated
Normal file
241
commit-msg-rs/Cargo.lock
generated
Normal file
@@ -0,0 +1,241 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "commit-msg-rs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"rust_util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "rust_util"
|
||||
version = "0.6.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df24005feacce81f4ae340464b39c380f7e01e7225bfdef62d40cb44cb1c11d7"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"term",
|
||||
"term_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"rustversion",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term_size"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
10
commit-msg-rs/Cargo.toml
Normal file
10
commit-msg-rs/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "commit-msg-rs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
regex = "1.7.1"
|
||||
rust_util = "0.6"
|
||||
190
commit-msg-rs/src/main.rs
Normal file
190
commit-msg-rs/src/main.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env runrs
|
||||
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! regex = "1.7.1"
|
||||
//! rust_util = "0.6"
|
||||
//! ```
|
||||
|
||||
use std::{env, fs};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rust_util::{debugging, failure_and_exit, information, success, warning};
|
||||
use rust_util::util_file::get_home_path;
|
||||
|
||||
const BOLD: &str = "\x1B[1m";
|
||||
const UNDER: &str = "\x1B[4m";
|
||||
const END: &str = "\x1B[0m";
|
||||
|
||||
const DEFAULT_COMMIT_MSG_REGEXP: &str = "^(feat|fix|docs|style|refactor|test|chore)(\\([\\w\\-_.]+\\))?:\\s*.*";
|
||||
|
||||
/// Git commit message format check util
|
||||
///
|
||||
/// Commit format MUST be <type>(<scope>): subject
|
||||
///
|
||||
/// Usage:
|
||||
/// `commit-msg.rs usage`
|
||||
///
|
||||
/// Install:
|
||||
/// `commit-msg.rs install`
|
||||
fn main() {
|
||||
debugging!("Arguments:\n- {}", env::args().collect::<Vec<_>>().join("\n- "));
|
||||
let arg1 = env::args().nth(1).unwrap_or_else(|| {
|
||||
failure_and_exit!("Commit message is EMPTY!");
|
||||
});
|
||||
|
||||
if arg1 == "usage" || arg1 == "USAGE" || arg1 == "--help" || arg1 == "-h" {
|
||||
print_usage();
|
||||
return;
|
||||
}
|
||||
|
||||
let lower_arg1 = arg1.to_ascii_lowercase();
|
||||
let is_install = lower_arg1 == "install";
|
||||
let is_force_install = lower_arg1 == "forceinstall" || lower_arg1 == "force-install"
|
||||
|| lower_arg1 == "installforce" || lower_arg1 == "install-force";
|
||||
if is_install || is_force_install {
|
||||
install_commit_msg(is_force_install);
|
||||
return;
|
||||
}
|
||||
|
||||
let commit_message = fs::read_to_string(arg1).unwrap_or_else(|e| {
|
||||
failure_and_exit!("Read commit message failed: {}", e);
|
||||
});
|
||||
|
||||
let re_str = get_commit_msg_regexp();
|
||||
let re = match regex::Regex::new(re_str.trim()) {
|
||||
Ok(re) => re,
|
||||
Err(e) => failure_and_exit!("Parse regexp: {}, failed: {}", re_str, e),
|
||||
};
|
||||
let is_commit_message_matches = re.is_match(&commit_message);
|
||||
|
||||
information!("Commit message: {}{}{}", UNDER, commit_message.trim(), END);
|
||||
if is_commit_message_matches {
|
||||
success!("Commit message rule matches");
|
||||
} else {
|
||||
print_usage();
|
||||
failure_and_exit!("Commit message rule is NOT matched");
|
||||
}
|
||||
}
|
||||
|
||||
fn print_usage() {
|
||||
if let Some(usage) = get_commit_msg_usage() {
|
||||
information!("{}",&usage);
|
||||
return;
|
||||
}
|
||||
information!(r#"Please follow the commit spec:
|
||||
Format: {b}<type>{e}(<scope>){b}: <subject>{e}
|
||||
<scope> is optional
|
||||
|
||||
{b}feat{e}: add hat wobble
|
||||
^--^ ^------------^
|
||||
| |
|
||||
| +-> Summary in present tense.
|
||||
|
|
||||
+-------> Type: chore, docs, feat, fix, refactor, style, or test.
|
||||
|
||||
{b}feat{e} : new feature for the user, not a new feature for build script
|
||||
{b}fix{e} : bug fix for the user, not a fix to a build script
|
||||
{b}docs{e} : changes to the documentation
|
||||
{b}style{e} : formatting, missing semi colons, etc; no production code change
|
||||
{b}refactor{e} : refactoring production code, eg. renaming a variable
|
||||
{b}test{e} : adding missing tests, refactoring tests; no production code change
|
||||
{b}chore{e} : updating grunt tasks etc; no production code change
|
||||
|
||||
Reference: {u}https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716{e}
|
||||
or mirror: {u}https://hatter.ink/wiki/view_raw.action?__access_token=PUBLIC&id=42{e}
|
||||
"#, b = BOLD, e = END, u = UNDER);
|
||||
}
|
||||
|
||||
fn search_git_root_path() -> Option<PathBuf> {
|
||||
let mut p = PathBuf::from(".").canonicalize().unwrap_or_else(|e|
|
||||
failure_and_exit!("Get current dir failed: {}", e)
|
||||
);
|
||||
loop {
|
||||
debugging!("Check git path: {:?}", p);
|
||||
if p.join(".git").is_dir() {
|
||||
debugging!("Found git path: {:?}", p);
|
||||
return Some(p);
|
||||
}
|
||||
if !p.pop() { return None; }
|
||||
}
|
||||
}
|
||||
|
||||
fn search_git_root_path_e() -> PathBuf {
|
||||
search_git_root_path().unwrap_or_else(|| failure_and_exit!("Git root path not found!"))
|
||||
}
|
||||
|
||||
fn get_home_e() -> PathBuf {
|
||||
get_home_path().unwrap_or_else(|| failure_and_exit!("Get user home failed!"))
|
||||
}
|
||||
|
||||
fn get_commit_msg_file(file: &str) -> Option<String> {
|
||||
let settings_regexp = search_git_root_path_e().join("settings").join(file);
|
||||
debugging!("Check file: {:?}, exists: {}", settings_regexp, settings_regexp.exists());
|
||||
if settings_regexp.exists() {
|
||||
information!("Found file: {:?}", settings_regexp);
|
||||
return Some(fs::read_to_string(settings_regexp).unwrap());
|
||||
}
|
||||
|
||||
let path_regexp = search_git_root_path_e().join(".git").join("hooks").join(file);
|
||||
debugging!("Check file: {:?}, exists: {}", path_regexp, path_regexp.exists());
|
||||
if path_regexp.exists() {
|
||||
information!("Found file: {:?}", path_regexp);
|
||||
return Some(fs::read_to_string(path_regexp).unwrap());
|
||||
}
|
||||
|
||||
let home_path_regexp = get_home_e().join(&(".".to_owned() + file));
|
||||
debugging!("Check file: {:?}, exists: {}", home_path_regexp, home_path_regexp.exists());
|
||||
if home_path_regexp.exists() {
|
||||
information!("Found file: {:?}", home_path_regexp);
|
||||
return Some(fs::read_to_string(home_path_regexp).unwrap());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn get_commit_msg_regexp() -> String {
|
||||
get_commit_msg_file("commit-msg-regexp").unwrap_or_else(|| DEFAULT_COMMIT_MSG_REGEXP.to_owned())
|
||||
}
|
||||
|
||||
fn get_commit_msg_usage() -> Option<String> {
|
||||
get_commit_msg_file("commit-msg-regexp-usage")
|
||||
}
|
||||
|
||||
fn copy_file_and_apply_executable_permission(from: &PathBuf, to: &PathBuf) {
|
||||
information!("Copy file: {:?} to: {:?}", from, to);
|
||||
let from_content = fs::read(&from).unwrap_or_else(|e|
|
||||
failure_and_exit!("Read file: {:?}, failed: {}", from, e)
|
||||
);
|
||||
fs::write(to, from_content).unwrap_or_else(|e|
|
||||
failure_and_exit!("Write file: {:?}, failed: {}", to, e)
|
||||
);
|
||||
fs::set_permissions(to, PermissionsExt::from_mode(0o755)).unwrap_or_else(|e|
|
||||
failure_and_exit!("Apply executable permission on file: {:?}, failed: {}", to, e)
|
||||
);
|
||||
}
|
||||
|
||||
fn install_commit_msg(force: bool) {
|
||||
let commit_msg_crs = get_home_e().join("bin").join("commit-msg.rs");
|
||||
let commig_msg_exec_file = if commit_msg_crs.exists() {
|
||||
commit_msg_crs
|
||||
} else {
|
||||
warning!("File {:?} NOT exists!", commit_msg_crs);
|
||||
PathBuf::from(env::args().next().unwrap())
|
||||
};
|
||||
|
||||
let git_hooks = search_git_root_path_e().join(".git").join("hooks");
|
||||
if !git_hooks.exists() {
|
||||
failure_and_exit!("Path {:?} NOT exists!", git_hooks);
|
||||
}
|
||||
let git_hooks_commit_msg = git_hooks.join("commit-msg");
|
||||
if git_hooks_commit_msg.exists() && !force {
|
||||
failure_and_exit!("File {:?} exists! or try {}forceinstall{}.", git_hooks_commit_msg, BOLD, END);
|
||||
}
|
||||
|
||||
copy_file_and_apply_executable_permission(&commig_msg_exec_file, &git_hooks_commit_msg);
|
||||
|
||||
success!("Install commit-msg to repo succeed!");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user