util.rs
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 }