Skip to content

Commit dfbf6b9

Browse files
authored
added xtask bin for tests (#715, #712) (#717)
* xtask without worksapce fix #715, fix #712 (maybe?) - reworked book operations in xtask - updated readme for new build command - updated contribution.md for new test commands - fixed missing dictionary - added contributor tests to xtask (`cargo test`, `mdbook serve -o`, `./ci/spellcheck.sh`, `link-checker ./book` (moved to Lychee)) - updated ci .sh files for the new cookbook dir - fixed broken link * reverted README.md & updated CONTRIBUTION.md - added version check for mdbook * fix #572 - fixed broken link (clients.md/#572 (comment))
1 parent 526cfcb commit dfbf6b9

File tree

11 files changed

+296
-8
lines changed

11 files changed

+296
-8
lines changed

.cargo/config.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[alias]
2+
xtask = "run --manifest-path ./xtask/Cargo.toml --"

.github/PULL_REQUEST_TEMPLATE.md

+2-5
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@ No worries if anything in these lists is unclear. Just submit the PR and ask awa
77
--------------------------
88
### Things to check before submitting a PR
99

10-
- [ ] the tests are passing locally with `cargo test`
11-
- [ ] cookbook renders correctly in `mdbook serve -o`
10+
- [ ] the tests are passing locally with `cargo xtask test all`
1211
- [ ] commits are squashed into one and rebased to latest master
1312
- [ ] PR contains correct "fixes #ISSUE_ID" clause to autoclose the issue on PR merge
1413
- if issue does not exist consider creating it or remove the clause
15-
- [ ] spell check runs without errors `./ci/spellcheck.sh`
16-
- [ ] link check runs without errors `link-checker ./book`
1714
- [ ] non rendered items are in sorted order (links, reference, identifiers, Cargo.toml)
1815
- [ ] links to docs.rs have wildcard version `https://docs.rs/tar/*/tar/struct.Entry.html`
1916
- [ ] example has standard [error handling](https://rust-lang-nursery.github.io/rust-cookbook/about.html#a-note-about-error-handling)
@@ -25,4 +22,4 @@ No worries if anything in these lists is unclear. Just submit the PR and ask awa
2522
### Things to do after submitting PR
2623
- [ ] check if CI is happy with your PR
2724

28-
Thank you for reading, you may now delete this text! Thank you! :smile:
25+
Thank you for reading, you may now delete this text! Thank you! :smile:

CONTRIBUTING.md

+25-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ cd rust-cookbook
2020
Cookbook is built with [mdBook], so install that first with Cargo:
2121

2222
```
23-
cargo install --version 0.3.5 mdbook
23+
cargo install --version 0.4.43 mdbook
2424
```
2525

2626
To build and view the cookbook locally, run:
@@ -44,6 +44,30 @@ To run the cookbook test suite:
4444
cargo test
4545
```
4646

47+
### xtask
48+
49+
To simplify common tasks like testing, building the book, and running linters.
50+
51+
First, ensure you have the required tools installed:
52+
53+
```bash
54+
55+
```
56+
57+
- To run all tests:
58+
59+
```bash
60+
cargo xtask test all
61+
```
62+
63+
- To build the book locally:
64+
65+
```bash
66+
cargo xtask book
67+
```
68+
69+
For more details on available tasks, please check the full [xtask README](./xtask/README.md).
70+
4771
## Linters
4872

4973
The Rust Cookbook comes with link checking and spell checking linters that

ci/dictionary.txt

+3
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,6 @@ XRateLimitReset
346346
YAML
347347
YYYY
348348
zurich
349+
enum
350+
thiserror
351+
tempfile

src/database/postgres/aggregate_data.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ fn main() -> Result<(), Error> {
4040
}
4141
```
4242

43-
[`Museum of Modern Art`]: https://github.com/MuseumofModernArt/collection/blob/master/Artists.csv
43+
[`Museum of Modern Art`]: https://github.com/MuseumofModernArt/collection/blob/main/Artists.csv

src/web/clients.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
| [POST a file to paste-rs][ex-file-post] | [![reqwest-badge]][reqwest] | [![cat-net-badge]][cat-net] |
1414

1515
[ex-url-basic]: clients/requests.html#make-a-http-get-request
16-
[ex-url-header]: web/clients/requests.html#set-custom-headers-and-url-parameters-for-a-rest-request
16+
[ex-url-header]: clients/requests.html#set-custom-headers-and-url-parameters-for-a-rest-request
1717
[ex-rest-custom-params]: clients/requests.html#set-custom-headers-and-url-parameters-for-a-rest-request
1818
[ex-rest-get]: clients/apis.html#query-the-github-api
1919
[ex-rest-head]: clients/apis.html#check-if-an-api-resource-exists

xtask/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "xtask"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]

xtask/README.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# xtask - (Rust Cookbook)
2+
3+
**Rust Dependencies**:
4+
- Make sure you have the required tools installed:
5+
```bash
6+
7+
```
8+
9+
## Available Tasks
10+
11+
### `test`
12+
Run various tests for the project. You can specify individual tests or run them all.
13+
14+
- `cargo`: Run the `cargo test` command for the Rust code.
15+
- `spellcheck`: Run the spellcheck script.
16+
- `link`: Verify links within the project.
17+
- `all`: Run all the tests (default).
18+
19+
**Usage:**
20+
```bash
21+
cargo xtask test [all|cargo|spellcheck|link]
22+
```
23+
24+
### `book`
25+
Build or serve the project's documentation using `mdbook`.
26+
27+
- `build`: Build the book (default).
28+
- `serve`: Serve the book locally and open it in a browser.
29+
30+
**Usage:**
31+
```bash
32+
cargo xtask book [build|serve]
33+
```

xtask/src/main.rs

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
mod tests;
2+
mod mdbook;
3+
4+
use std::path::{Path, PathBuf};
5+
use std::{env, error::Error};
6+
7+
fn main() {
8+
if let Err(e) = try_main() {
9+
eprintln!("{}", e);
10+
std::process::exit(-1);
11+
}
12+
}
13+
14+
fn try_main() -> Result<(), Box<dyn Error>> {
15+
let task = env::args().nth(1);
16+
match task.as_deref() {
17+
Some("test") => {
18+
let sub_task = env::args().nth(2).unwrap_or_else(|| "all".to_string());
19+
tests::run_test(&sub_task)?
20+
}
21+
Some("book") => {
22+
let sub_task = env::args().nth(2).unwrap_or_else(|| "build".to_string());
23+
mdbook::run_book(&sub_task)?
24+
}
25+
_ => print_help(),
26+
}
27+
Ok(())
28+
}
29+
30+
fn project_root() -> PathBuf {
31+
Path::new(&env!("CARGO_MANIFEST_DIR"))
32+
.ancestors()
33+
.nth(1)
34+
.unwrap()
35+
.to_path_buf()
36+
}
37+
38+
fn print_help() {
39+
eprintln!("Available tasks:");
40+
eprintln!(
41+
" test [all|cargo|spellcheck|link] - Run the tests. Use 'all' to run all tests (default), or specify individual tests."
42+
);
43+
eprintln!(
44+
" book [build] - Build the book using mdbook. Default if no subcommand is specified."
45+
);
46+
eprintln!(" book serve - Serve the book using mdbook and open it in a browser.");
47+
eprintln!();
48+
eprintln!("Usage:");
49+
eprintln!(" cargo xtask <task> [subcommand]");
50+
eprintln!();
51+
eprintln!("Examples:");
52+
eprintln!(" cargo xtask test");
53+
eprintln!(" cargo xtask test all");
54+
eprintln!(" cargo xtask test cargo");
55+
eprintln!(" cargo xtask book");
56+
eprintln!(" cargo xtask book serve");
57+
}

xtask/src/mdbook.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use crate::project_root;
2+
use std::{error::Error, process::Command};
3+
4+
pub fn run_book(task: &str) -> Result<(), Box<dyn Error>> {
5+
let args: &[&str] = if task == "serve" { &["--open"] } else { &[] };
6+
7+
execute_mdbook_command(task, args)?;
8+
9+
Ok(())
10+
}
11+
12+
fn execute_mdbook_command(command: &str, additional_args: &[&str]) -> Result<(), Box<dyn Error>> {
13+
check_mdbook_version()?;
14+
15+
let book_dest = project_root().join("book").to_str().unwrap().to_string();
16+
17+
let mut args = vec![command, "--dest-dir", &book_dest];
18+
args.extend_from_slice(additional_args);
19+
20+
let status = Command::new("mdbook")
21+
.current_dir(project_root())
22+
.args(&args)
23+
.status()?;
24+
25+
if !status.success() {
26+
return Err(format!("`mdbook {command}` failed to run successfully!").into());
27+
}
28+
29+
Ok(())
30+
}
31+
32+
fn check_mdbook_version() -> Result<(), Box<dyn Error>> {
33+
let required_version = "0.4.43";
34+
35+
let output = Command::new("mdbook").arg("--version").output()?;
36+
37+
if !output.status.success() {
38+
println!("Error: `mdbook` not found. Please ensure it is installed!");
39+
println!("You can install it using:");
40+
println!(" cargo install mdbook@{required_version}");
41+
return Err(Box::new(std::io::Error::new(
42+
std::io::ErrorKind::NotFound,
43+
"`mdbook` is not installed",
44+
)));
45+
}
46+
47+
let version_output = String::from_utf8_lossy(&output.stdout);
48+
let version_str = version_output.trim();
49+
50+
if !version_str.starts_with(&format!("mdbook {}", required_version)) {
51+
println!(
52+
"Warning: You are using version {version_str} of `mdbook`. Version {required_version} is required."
53+
);
54+
println!(
55+
"Errors may occur if using a different version. Please install version {required_version}:"
56+
57+
);
58+
println!(" cargo install mdbook@{required_version}");
59+
}
60+
61+
Ok(())
62+
}

xtask/src/tests.rs

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use crate::project_root;
2+
use std::error::Error;
3+
use std::process::Command;
4+
5+
pub fn run_test(task: &str) -> Result<(), Box<dyn Error>> {
6+
match task {
7+
"all" => run_all_tests()?,
8+
"cargo" => cargo_test()?,
9+
"spellcheck" => spellcheck()?,
10+
"link" => link_checker()?,
11+
_ => run_all_tests()?,
12+
}
13+
Ok(())
14+
}
15+
16+
fn run_all_tests() -> Result<(), Box<dyn Error>> {
17+
let mut failures = Vec::new();
18+
19+
if cargo_test().is_err() {
20+
failures.push("cargo_test".to_string());
21+
}
22+
23+
if spellcheck().is_err() {
24+
failures.push("spellcheck".to_string());
25+
}
26+
27+
if link_checker().is_err() {
28+
failures.push("link".to_string());
29+
}
30+
31+
if !failures.is_empty() {
32+
println!("\n--- Test Summary ---");
33+
for name in failures {
34+
println!("❌ {name} failed! Re-run with the command:");
35+
println!(" cargo xtask test {name}");
36+
}
37+
} else {
38+
println!("\n🎉 All tests passed!");
39+
}
40+
41+
Ok(())
42+
}
43+
44+
fn cargo_test() -> Result<(), Box<dyn Error>> {
45+
let status = Command::new("cargo")
46+
.current_dir(project_root())
47+
.args(["test", "--package", "rust-cookbook"])
48+
.status()?;
49+
50+
if !status.success() {
51+
return Err("failed to run cargo test!".into());
52+
}
53+
54+
Ok(())
55+
}
56+
57+
fn spellcheck() -> Result<(), Box<dyn Error>> {
58+
let status = Command::new("./ci/spellcheck.sh")
59+
.current_dir(project_root())
60+
.status()?;
61+
62+
if !status.success() {
63+
return Err("failed to run spellcheck!".into());
64+
}
65+
66+
Ok(())
67+
}
68+
69+
fn link_checker() -> Result<(), Box<dyn Error>> {
70+
if Command::new("lychee").arg("--version").status().is_err() {
71+
return Err(
72+
"The `lychee` tool is not installed. Please install it using:\n cargo install [email protected]".into(),
73+
);
74+
}
75+
76+
let book_dir = project_root().join("book");
77+
if !book_dir.is_dir() {
78+
return Err(format!(
79+
"The book directory could not be found in the root directory: {:?}\n\
80+
You can build it using:\n cargo xtask book build",
81+
book_dir
82+
)
83+
.into());
84+
}
85+
86+
let status = Command::new("lychee")
87+
.current_dir(project_root())
88+
.args([
89+
"./book",
90+
"--retry-wait-time",
91+
"20",
92+
"--max-retries",
93+
"3",
94+
"--accept",
95+
"429", // accept 429 (ratelimit) errors as valid
96+
])
97+
.status()?;
98+
99+
if !status.success() {
100+
return Err("Failed to run link checker!".into());
101+
}
102+
103+
Ok(())
104+
}

0 commit comments

Comments
 (0)