Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 0a5f28c

Browse files
xFrednetflip1995
authored andcommitted
Added cargo dev setup git-hook
1 parent 0941d9f commit 0a5f28c

File tree

5 files changed

+126
-14
lines changed

5 files changed

+126
-14
lines changed

clippy_dev/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ use walkdir::WalkDir;
1414

1515
pub mod bless;
1616
pub mod fmt;
17-
pub mod setup;
1817
pub mod new_lint;
1918
pub mod serve;
19+
pub mod setup;
2020
pub mod stderr_length_check;
2121
pub mod update_lints;
2222

clippy_dev/src/main.rs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![warn(rust_2018_idioms, unused_lifetimes)]
44

55
use clap::{App, Arg, ArgMatches, SubCommand};
6-
use clippy_dev::{bless, fmt, setup, new_lint, serve, stderr_length_check, update_lints};
6+
use clippy_dev::{bless, fmt, new_lint, serve, setup, stderr_length_check, update_lints};
77
fn main() {
88
let matches = get_clap_config();
99

@@ -36,7 +36,11 @@ fn main() {
3636
("limit_stderr_length", _) => {
3737
stderr_length_check::check();
3838
},
39-
("ide_setup", Some(matches)) => setup::intellij::run(matches.value_of("rustc-repo-path")),
39+
("setup", Some(sub_command)) => match sub_command.subcommand() {
40+
("intellij", Some(matches)) => setup::intellij::run(matches.value_of("rustc-repo-path")),
41+
("git-hook", Some(matches)) => setup::git_hook::run(matches.is_present("force-override")),
42+
_ => {},
43+
},
4044
("serve", Some(matches)) => {
4145
let port = matches.value_of("port").unwrap().parse().unwrap();
4246
let lint = matches.value_of("lint");
@@ -140,16 +144,31 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
140144
.about("Ensures that stderr files do not grow longer than a certain amount of lines."),
141145
)
142146
.subcommand(
143-
SubCommand::with_name("ide_setup")
144-
.about("Alter dependencies so Intellij Rust can find rustc internals")
145-
.arg(
146-
Arg::with_name("rustc-repo-path")
147-
.long("repo-path")
148-
.short("r")
149-
.help("The path to a rustc repo that will be used for setting the dependencies")
150-
.takes_value(true)
151-
.value_name("path")
152-
.required(true),
147+
SubCommand::with_name("setup")
148+
.about("Support for setting up your personal development environment")
149+
.subcommand(
150+
SubCommand::with_name("intellij")
151+
.about("Alter dependencies so Intellij Rust can find rustc internals")
152+
.arg(
153+
Arg::with_name("rustc-repo-path")
154+
.long("repo-path")
155+
.short("r")
156+
.help("The path to a rustc repo that will be used for setting the dependencies")
157+
.takes_value(true)
158+
.value_name("path")
159+
.required(true),
160+
),
161+
)
162+
.subcommand(
163+
SubCommand::with_name("git-hook")
164+
.about("Add a pre-commit git hook that formats your code to make it look pretty")
165+
.arg(
166+
Arg::with_name("force-override")
167+
.long("force-override")
168+
.short("f")
169+
.help("Forces the override of an existing git pre-commit hook")
170+
.required(false),
171+
),
153172
),
154173
)
155174
.subcommand(

clippy_dev/src/setup/git_hook.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use std::fs;
2+
use std::path::Path;
3+
4+
/// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo.
5+
/// I've decided against this for the sake of simplicity and to make sure that it doesn't install
6+
/// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool
7+
/// for formatting and should therefor only be used in a normal clone of clippy
8+
const REPO_GIT_DIR: &str = ".git";
9+
const HOOK_SOURCE_PATH: &str = "util/etc/pre-commit.sh";
10+
const HOOK_TARGET_PATH: &str = ".git/hooks/pre-commit";
11+
12+
pub fn run(force_override: bool) {
13+
if let Err(_) = check_precondition(force_override) {
14+
return;
15+
}
16+
17+
// So a little bit of a funny story. Git on unix requires the pre-commit file
18+
// to have the `execute` permission to be set. The Rust functions for modifying
19+
// these flags doesn't seem to work when executed with normal user permissions.
20+
//
21+
// However, there is a little hack that is also being used by Rust itself in their
22+
// setup script. Git saves the `execute` flag when syncing files. This means
23+
// that we can check in a file with execution permissions and the sync it to create
24+
// a file with the flag set. We then copy this file here. The copy function will also
25+
// include the `execute` permission.
26+
match fs::copy(HOOK_SOURCE_PATH, HOOK_TARGET_PATH) {
27+
Ok(_) => println!("Git hook successfully installed :)"),
28+
Err(err) => println!(
29+
"error: unable to copy `{}` to `{}` ({})",
30+
HOOK_SOURCE_PATH, HOOK_TARGET_PATH, err
31+
),
32+
}
33+
}
34+
35+
fn check_precondition(force_override: bool) -> Result<(), ()> {
36+
// Make sure that we can find the git repository
37+
let git_path = Path::new(REPO_GIT_DIR);
38+
if !git_path.exists() || !git_path.is_dir() {
39+
println!("error: clippy_dev was unable to find the `.git` directory");
40+
return Err(());
41+
}
42+
43+
// Make sure that we don't override an existing hook by accident
44+
let path = Path::new(HOOK_TARGET_PATH);
45+
if path.exists() {
46+
if !force_override {
47+
println!("warn: The found `.git` directory already has a commit hook");
48+
}
49+
50+
if force_override || super::ask_yes_no_question("Do you want to override it?") {
51+
if fs::remove_file(path).is_err() {
52+
println!("error: unable to delete existing pre-commit git hook");
53+
return Err(());
54+
}
55+
} else {
56+
return Err(());
57+
}
58+
}
59+
60+
Ok(())
61+
}

clippy_dev/src/setup/mod.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,30 @@
1-
pub mod intellij;
1+
use std::io::{self, Write};
2+
pub mod git_hook;
3+
pub mod intellij;
4+
5+
/// This function will asked the user the given question and wait for user input
6+
/// either `true` for yes and `false` for no.
7+
fn ask_yes_no_question(question: &str) -> bool {
8+
// This code was proudly stolen from rusts bootstrapping tool.
9+
10+
fn ask_with_result(question: &str) -> io::Result<bool> {
11+
let mut input = String::new();
12+
Ok(loop {
13+
print!("{}: [y/N] ", question);
14+
io::stdout().flush()?;
15+
input.clear();
16+
io::stdin().read_line(&mut input)?;
17+
break match input.trim().to_lowercase().as_str() {
18+
"y" | "yes" => true,
19+
"n" | "no" | "" => false,
20+
_ => {
21+
println!("error: unrecognized option '{}'", input.trim());
22+
println!("note: press Ctrl+C to exit");
23+
continue;
24+
},
25+
};
26+
})
27+
}
28+
29+
ask_with_result(question).unwrap_or_default()
30+
}

util/etc/pre-commit.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
cargo dev fmt

0 commit comments

Comments
 (0)