1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-12-21 14:35:47 +02:00

tests: Migrate create_slide.list.sh into cargo xtask function. (#2957)

The new xtask function makes the helper code
- more readable
- more reliable due to better error checking
- be in the same place as other helper functions
- and more aligned to the skillset relevant for contributing in this
repository.

The shell script grew and was not readable for everyone anymore without
deeper knowledge.

mitigates #2941 in a more reliable way but does still not fully fix the
root cause
This commit is contained in:
michael-kerscher
2025-10-29 18:15:21 +01:00
committed by GitHub
parent a5e68972c2
commit d1899de233
6 changed files with 134 additions and 95 deletions

View File

@@ -21,9 +21,10 @@
use anyhow::{Context, Result, anyhow};
use clap::{Parser, Subcommand};
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};
use walkdir::WalkDir;
fn main() -> Result<()> {
execute_task()
@@ -54,6 +55,13 @@ enum Task {
#[arg(short, long)]
dir: Option<PathBuf>,
},
/// (Re)creates the slides.list.ts file based on the given book html
/// directory.
CreateSlideList {
/// The book html directory
#[arg(short, long)]
dir: PathBuf,
},
/// Tests all included Rust snippets.
RustTests,
/// Starts a web server with the course.
@@ -85,6 +93,7 @@ fn execute_task() -> Result<()> {
match cli.task {
Task::InstallTools { binstall } => install_tools(binstall),
Task::WebTests { dir } => run_web_tests(dir),
Task::CreateSlideList { dir } => create_slide_list(dir),
Task::RustTests => run_rust_tests(),
Task::Serve { language, output } => start_web_server(language, output),
Task::Build { language, output } => build(language, output),
@@ -191,13 +200,7 @@ fn run_web_tests(dir: Option<PathBuf>) -> Result<()> {
if let Some(d) = &absolute_dir {
println!("Refreshing slide lists...");
let refresh_slides_script = Path::new("tests")
.join("src")
.join("slides")
.join("create-slide.list.sh");
let mut cmd = Command::new(&refresh_slides_script);
cmd.current_dir(workspace_dir).arg(d);
run_command(&mut cmd)?;
create_slide_list(d.clone())?;
}
let tests_dir = workspace_dir.join("tests");
@@ -210,6 +213,125 @@ fn run_web_tests(dir: Option<PathBuf>) -> Result<()> {
run_command(&mut cmd)
}
// Creates a list of .html slides from the html directory containing the
// index.html to check the slides.
// - CI environment: Only modified files are listed
// - Otherwise: All existing html files
fn create_slide_list(html_directory: PathBuf) -> Result<()> {
let workspace_dir = Path::new(env!("CARGO_WORKSPACE_DIR"));
let tests_dir = workspace_dir.join("tests");
// Check if the provided directory is correct
if !html_directory.join("index.html").exists() {
return Err(anyhow!(
"Could not find index.html in {}. Please check if the correct directory is used (e.g. book/html).",
html_directory.display()
));
}
// These special slides are not checked against the style guide
let exclude_paths = [
"exercise.html",
"solution.html",
"toc.html",
"print.html",
"404.html",
"glossary.html",
"index.html",
"course-structure.html",
]
.map(PathBuf::from);
// Collect the files relevant for evaluation.
// - CI environment variable is set: all modified markdown files in the src/
// directory
// - all html files in the provided directory otherwise
let candidate_slides: Vec<PathBuf> = if env::var("CI").is_ok() {
println!("CI environment detected, checking only modified slides.");
// GITHUB_BASE_REF is available in PRs. Default to 'main' for other CI
// contexts.
let base_ref = env::var("GITHUB_BASE_REF").unwrap_or("main".to_string());
let mut cmd = Command::new("git");
cmd.arg("diff")
.arg("--name-only")
.arg(format!("{}...", base_ref))
.arg("--")
// Retrieve all modified files in the src directory.
// Pathspec syntax: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-pathspec
// `*` can match path separators, thus matches also files in
// subdirectories
.arg("src/*.md");
println!("> {cmd:?}");
let output = cmd.output().context("Failed to run git diff")?;
String::from_utf8(output.stdout)?
.lines()
.map(|line| {
let path = Path::new(line);
// We know the path starts with "src/" because of the pathspec in the
// `git diff` command, and we need it relative to the html base
// directory
let stripped_path = path.strip_prefix("src").unwrap();
let mut html_path = stripped_path.to_path_buf();
// replace the .md extension with .html
html_path.set_extension("html");
html_path
})
.collect()
} else {
println!("Local environment, checking all slides.");
WalkDir::new(&html_directory)
.into_iter()
.filter_map(|e| e.ok())
// only files with .html extension
.filter(|e| e.path().extension().is_some_and(|ext| ext == "html"))
// relative path inside the html directory
.map(|e| e.path().strip_prefix(&html_directory).unwrap().to_path_buf())
.collect()
};
// Filter the candidate slides
let mut slides = Vec::new();
for slide in candidate_slides {
// Skip excluded files
if exclude_paths.iter().any(|exclude_path| slide.ends_with(exclude_path)) {
continue;
}
// Test if the html files actually exist
let full_path = html_directory.join(&slide);
if !full_path.exists() {
continue;
}
// Optimization: check if these are redirection html files and skip these
let content = fs::read_to_string(&full_path)
.with_context(|| format!("Failed to read slide: {}", slide.display()))?;
if content.contains("Redirecting to...") {
continue;
}
slides.push(slide);
}
if env::var("CI").is_ok() {
println!("The following slides have been modified and will be checked:");
for slide in &slides {
println!("{}", slide.display());
}
}
// Write the file list into a .ts file that can be read by the JS based webtest
let output_path = tests_dir.join("src").join("slides").join("slides.list.ts");
let mut output_content = "export const slides = [\n".to_string();
for slide in slides {
output_content.push_str(&format!(" \"{}\",\n", slide.display()));
}
output_content.push_str("];\n");
fs::write(&output_path, output_content)
.with_context(|| format!("Failed to write to {}", output_path.display()))?;
Ok(())
}
fn run_rust_tests() -> Result<()> {
println!("Running rust tests...");
let workspace_root = Path::new(env!("CARGO_WORKSPACE_DIR"));