/ test / lint / test_runner / src / lint_docs.rs
lint_docs.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::io::ErrorKind;
 6  use std::process::{Command, Stdio};
 7  
 8  use crate::util::{check_output, get_subtrees, git, LintResult};
 9  
10  pub fn lint_doc_release_note_snippets() -> LintResult {
11      let non_release_notes = check_output(git().args([
12          "ls-files",
13          "--",
14          "doc/release-notes/",
15          ":(exclude)doc/release-notes/*.*.md", // Assume that at least one dot implies a proper release note
16      ]))?;
17      if non_release_notes.is_empty() {
18          Ok(())
19      } else {
20          println!("{non_release_notes}");
21          Err(r#"
22  Release note snippets and other docs must be put into the doc/ folder directly.
23  
24  The doc/release-notes/ folder is for archived release notes of previous releases only. Snippets are
25  expected to follow the naming "/doc/release-notes-<PR number>.md".
26              "#
27          .trim()
28          .to_string())
29      }
30  }
31  
32  pub fn lint_doc_args() -> LintResult {
33      if Command::new("test/lint/check-doc.py")
34          .status()
35          .expect("command error")
36          .success()
37      {
38          Ok(())
39      } else {
40          Err("".to_string())
41      }
42  }
43  
44  pub fn lint_markdown() -> LintResult {
45      let bin_name = "mlc";
46      let mut md_ignore_paths = get_subtrees();
47      md_ignore_paths.push("./doc/README_doxygen.md");
48      let md_ignore_path_str = md_ignore_paths.join(",");
49  
50      let mut cmd = Command::new(bin_name);
51      cmd.args([
52          "--offline",
53          "--ignore-path",
54          md_ignore_path_str.as_str(),
55          "--gitignore",
56          "--gituntracked",
57          "--root-dir",
58          ".",
59      ])
60      .stdout(Stdio::null()); // Suppress overly-verbose output
61  
62      match cmd.output() {
63          Ok(output) if output.status.success() => Ok(()),
64          Ok(output) => {
65              let stderr = String::from_utf8_lossy(&output.stderr);
66              Err(format!(
67                  r#"
68  One or more markdown links are broken.
69  
70  Note: relative links are preferred as jump-to-file works natively within Emacs, but they are not required.
71  
72  Markdown link errors found:
73  {stderr}
74                  "#
75              )
76              .trim()
77              .to_string())
78          }
79          Err(e) if e.kind() == ErrorKind::NotFound => {
80              println!("`mlc` was not found in $PATH, skipping markdown lint check.");
81              Ok(())
82          }
83          Err(e) => Err(format!("Error running mlc: {e}")), // Misc errors
84      }
85  }