diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 000000000..524a65e38 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --manifest-path=xtask/Cargo.toml --" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 269bbbbc9..cb8687839 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -103,6 +103,9 @@ jobs: - name: Rustfmt check working-directory: ./mdbook-spec run: cargo fmt --check + - name: Xtask rustfmt check + working-directory: ./xtask + run: cargo fmt --check preview: if: github.event_name == 'pull_request' diff --git a/docs/authoring.md b/docs/authoring.md index 6654af11e..73ca9dbed 100644 --- a/docs/authoring.md +++ b/docs/authoring.md @@ -28,7 +28,7 @@ This document serves as a guide for editors and reviewers. Some conventions and ``` * See the [Conventions] section for formatting callouts such as notes, edition differences, and warnings. -There are automated checks for some of these rules. Run `cargo run --manifest-path style-check/Cargo.toml -- src` to run them locally. +There are automated checks for some of these rules. Run `cargo xtask style-check` to run them locally. [atx]: https://spec.commonmark.org/0.31.2/#atx-headings [conventions]: ../src/introduction.md#conventions @@ -58,6 +58,16 @@ See the [rustdoc documentation] for more detail. [rustdoc documentation]: https://doc.rust-lang.org/rustdoc/documentation-tests.html +You can verify the samples pass by running `mdbook test`. + +### Linkcheck + +To verify that links are not broken, run `cargo xtask linkcheck`. + +### Running all tests + +As a last step before opening a PR, it is recommended to run `cargo xtask test-all`. This will go through and run most of the tests that are required for CI to pass. See `xtask/src/main.rs` for what all this does. + ## Special markdown constructs The following are extensions provided by [`mdbook-spec`](https://github.com/rust-lang/spec/tree/main/mdbook-spec). diff --git a/xtask/.gitignore b/xtask/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/xtask/.gitignore @@ -0,0 +1 @@ +/target diff --git a/xtask/Cargo.lock b/xtask/Cargo.lock new file mode 100644 index 000000000..249bf303d --- /dev/null +++ b/xtask/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "xtask" +version = "0.0.0" diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 000000000..ccade17e4 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "xtask" +edition = "2024" + +[dependencies] diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 000000000..e4a216cbc --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,106 @@ +use std::error::Error; +use std::process::Command; +use std::process::exit; + +type Result = std::result::Result>; + +fn main() -> Result<()> { + let mut args = std::env::args().skip(1); + let cmd = args.next(); + const OPTIONS: &str = "linkcheck, style-check, test-all"; + match cmd.as_deref() { + Some("test-all") => { + mdbook_test()?; + style_check()?; + fmt()?; + linkcheck(args)?; + cargo_test()?; + eprintln!("all tests passed!"); + } + Some("linkcheck") => linkcheck(args)?, + Some("style-check") => style_check()?, + Some("-h" | "--help") => eprintln!("valid options: {OPTIONS}"), + Some(x) => { + eprintln!("error: unknown command `{x}` (valid options: {OPTIONS})"); + exit(1); + } + None => { + eprintln!("error: specify a command (valid options: {OPTIONS})"); + exit(1); + } + } + Ok(()) +} + +fn mdbook_test() -> Result<()> { + eprintln!("Testing inline code tests..."); + let status = Command::new("mdbook") + .arg("test") + .status() + .expect("mdbook should be installed"); + if !status.success() { + return Err("inline code tests failed".into()); + } + Ok(()) +} + +fn style_check() -> Result<()> { + eprintln!("Running style checks..."); + let status = Command::new("cargo") + .args(["run", "--manifest-path=style-check/Cargo.toml", "--", "src"]) + .status() + .expect("cargo should be installed"); + if !status.success() { + return Err("style check failed".into()); + } + Ok(()) +} + +fn fmt() -> Result<()> { + eprintln!("Checking code formatting..."); + for dir in ["style-check", "mdbook-spec", "xtask"] { + let status = Command::new("cargo") + .args(["fmt", "--check"]) + .current_dir(dir) + .status() + .expect("cargo should be installed"); + if !status.success() { + return Err(format!("fmt check failed for {dir}").into()); + } + } + Ok(()) +} + +fn cargo_test() -> Result<()> { + eprintln!("Running cargo tests..."); + let status = Command::new("cargo") + .arg("test") + .current_dir("mdbook-spec") + .status() + .expect("cargo should be installed"); + if !status.success() { + return Err("mdbook-spec test failed".into()); + } + Ok(()) +} + +fn linkcheck(args: impl Iterator) -> Result<()> { + eprintln!("Running linkcheck..."); + let status = Command::new("curl") + .args(["-sSLo", "linkcheck.sh", "https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh"]) + .status() + .expect("curl should be installed"); + if !status.success() { + return Err("failed to fetch script from GitHub".into()); + } + + let status = Command::new("sh") + .args(["linkcheck.sh", "--all", "reference"]) + .args(args) + .status() + .expect("sh should be installed"); + if !status.success() { + return Err("linkcheck failed".into()); + } + Ok(()) +}