1 // Copyright (c) The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or https://opensource.org/license/mit/.
4 5 use std::env;
6 use std::path::PathBuf;
7 use std::process::Command;
8 9 /// A possible error returned by any of the linters.
10 ///
11 /// The error string should explain the failure type and list all violations.
12 pub type LintError = String;
13 pub type LintResult = Result<(), LintError>;
14 pub type LintFn = fn() -> LintResult;
15 16 /// Return the git command
17 ///
18 /// Lint functions should use this command, so that only files tracked by git are considered and
19 /// temporary and untracked files are ignored. For example, instead of 'grep', 'git grep' should be
20 /// used.
21 pub fn git() -> Command {
22 let mut git = Command::new("git");
23 git.arg("--no-pager");
24 git
25 }
26 27 /// Return stdout on success and a LintError on failure, when invalid UTF8 was detected or the
28 /// command did not succeed.
29 pub fn check_output(cmd: &mut Command) -> Result<String, LintError> {
30 let out = cmd.output().expect("command error");
31 if !out.status.success() {
32 return Err(String::from_utf8_lossy(&out.stderr).to_string());
33 }
34 Ok(String::from_utf8(out.stdout)
35 .map_err(|e| {
36 format!("All path names, source code, messages, and output must be valid UTF8!\n{e}")
37 })?
38 .trim()
39 .to_string())
40 }
41 42 /// Return the git root as utf8, or panic
43 pub fn get_git_root() -> PathBuf {
44 PathBuf::from(check_output(git().args(["rev-parse", "--show-toplevel"])).unwrap())
45 }
46 47 /// Return the commit range, or panic
48 pub fn commit_range() -> String {
49 // Use the env var, if set. E.g. COMMIT_RANGE='HEAD~n..HEAD' for the last 'n' commits.
50 env::var("COMMIT_RANGE").unwrap_or_else(|_| {
51 // Otherwise, assume that a merge commit exists. This merge commit is assumed
52 // to be the base, after which linting will be done. If the merge commit is
53 // HEAD, the range will be empty.
54 format!(
55 "{}..HEAD",
56 check_output(git().args(["rev-list", "--max-count=1", "--merges", "HEAD"]))
57 .expect("check_output failed")
58 )
59 })
60 }
61 62 /// Return all subtree paths
63 pub fn get_subtrees() -> Vec<&'static str> {
64 // Keep in sync with [test/lint/README.md#git-subtree-checksh]
65 vec![
66 "src/crc32c",
67 "src/crypto/ctaes",
68 "src/ipc/libmultiprocess",
69 "src/leveldb",
70 "src/minisketch",
71 "src/secp256k1",
72 ]
73 }
74 75 /// Return the pathspecs to exclude by default
76 pub fn get_pathspecs_default_excludes() -> Vec<String> {
77 get_subtrees()
78 .iter()
79 .chain(&[
80 "doc/release-notes/release-notes-*", // archived notes
81 ])
82 .map(|s| format!(":(exclude){s}"))
83 .collect()
84 }
85