-
Notifications
You must be signed in to change notification settings - Fork 13.5k
tidy: move rustdoc js stuff into a tidy extra check #142924
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
13f9848
798a0be
a239e1e
16dc058
a2fc310
96169d8
b023137
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,8 @@ use std::path::{Path, PathBuf}; | |
use std::process::Command; | ||
use std::{fmt, fs, io}; | ||
|
||
mod rustdoc_js; | ||
|
||
const MIN_PY_REV: (u32, u32) = (3, 9); | ||
const MIN_PY_REV_STR: &str = "≥3.9"; | ||
|
||
|
@@ -39,19 +41,34 @@ const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements. | |
pub fn check( | ||
root_path: &Path, | ||
outdir: &Path, | ||
librustdoc_path: &Path, | ||
tools_path: &Path, | ||
src_path: &Path, | ||
bless: bool, | ||
extra_checks: Option<&str>, | ||
pos_args: &[String], | ||
bad: &mut bool, | ||
) { | ||
if let Err(e) = check_impl(root_path, outdir, bless, extra_checks, pos_args) { | ||
if let Err(e) = check_impl( | ||
root_path, | ||
outdir, | ||
librustdoc_path, | ||
tools_path, | ||
src_path, | ||
bless, | ||
extra_checks, | ||
pos_args, | ||
) { | ||
tidy_error!(bad, "{e}"); | ||
} | ||
} | ||
|
||
fn check_impl( | ||
root_path: &Path, | ||
outdir: &Path, | ||
librustdoc_path: &Path, | ||
tools_path: &Path, | ||
src_path: &Path, | ||
bless: bool, | ||
extra_checks: Option<&str>, | ||
pos_args: &[String], | ||
|
@@ -65,13 +82,20 @@ fn check_impl( | |
None => vec![], | ||
}; | ||
|
||
// FIXME(lolbinarycat): this is getting complex, we should probably | ||
// have more proper handling, including a warning/error | ||
// for unknown extra check names. | ||
let python_all = lint_args.contains(&"py"); | ||
let python_lint = lint_args.contains(&"py:lint") || python_all; | ||
let python_fmt = lint_args.contains(&"py:fmt") || python_all; | ||
let shell_all = lint_args.contains(&"shell"); | ||
let shell_lint = lint_args.contains(&"shell:lint") || shell_all; | ||
let cpp_all = lint_args.contains(&"cpp"); | ||
let cpp_fmt = lint_args.contains(&"cpp:fmt") || cpp_all; | ||
let js_all = lint_args.contains(&"js"); | ||
let js_lint = js_all || lint_args.contains(&"js:lint"); | ||
let js_typecheck = js_all || lint_args.contains(&"js:typecheck"); | ||
let js_es_check = js_all || lint_args.contains(&"js:es-check"); | ||
|
||
let mut py_path = None; | ||
|
||
|
@@ -224,6 +248,18 @@ fn check_impl( | |
shellcheck_runner(&merge_args(&cfg_args, &file_args_shc))?; | ||
} | ||
|
||
if js_lint { | ||
rustdoc_js::lint(librustdoc_path, tools_path, src_path)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't we say that the checks would be run if the JS files were modified too? Having them never run unless an extra arguments is passed is very inconvenient. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it wasn't too long ago that they were never run locally. i was gonna have a warning for modifying files and not enabling their extra checks.... i suppose i could add a new "auto" value to i would prefer a design which still lets people manually disable these checks if they want. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, if you modify JS files, seems like you do want these checks by default, no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if you don't have the tools installed, and have limited or no network connectivity, but still want to run tidy? I agree that running the tools based on modification is a good default, but there's very little cost to letting people disable it. I think I would want to add 3 new special values, actually:
This would give a robust system that would be easy to add any future checks into, and would also help improve the existing checks, instead of just improving js checks. |
||
} | ||
|
||
if js_typecheck { | ||
rustdoc_js::typecheck(librustdoc_path)?; | ||
} | ||
|
||
if js_es_check { | ||
rustdoc_js::es_check(librustdoc_path)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
//! Tidy check to ensure that rustdoc templates didn't forget a `{# #}` to strip extra whitespace | ||
//! characters. | ||
|
||
use std::ffi::OsStr; | ||
use std::path::{Path, PathBuf}; | ||
use std::process::Command; | ||
|
||
use ignore::DirEntry; | ||
|
||
use crate::walk::walk_no_read; | ||
|
||
fn rustdoc_js_files(librustdoc_path: &Path) -> Vec<PathBuf> { | ||
let mut files = Vec::new(); | ||
walk_no_read( | ||
&[&librustdoc_path.join("html/static/js")], | ||
|path, is_dir| is_dir || !path.extension().is_some_and(|ext| ext == OsStr::new("js")), | ||
&mut |path: &DirEntry| { | ||
files.push(path.path().into()); | ||
}, | ||
); | ||
return files; | ||
} | ||
|
||
fn run_eslint(args: &[PathBuf], config_folder: PathBuf) -> Result<(), super::Error> { | ||
let mut child = Command::new("npx") | ||
.arg("eslint") | ||
.arg("-c") | ||
.arg(config_folder.join(".eslintrc.js")) | ||
.args(args) | ||
.spawn()?; | ||
match child.wait() { | ||
Ok(exit_status) => { | ||
if exit_status.success() { | ||
return Ok(()); | ||
} | ||
Err(super::Error::FailedCheck("eslint command failed")) | ||
} | ||
Err(error) => Err(super::Error::Generic(format!("eslint command failed: {error:?}"))), | ||
} | ||
} | ||
|
||
fn get_eslint_version_inner(global: bool) -> Option<String> { | ||
let mut command = Command::new("npm"); | ||
command.arg("list").arg("--parseable").arg("--long").arg("--depth=0"); | ||
if global { | ||
command.arg("--global"); | ||
} | ||
let output = command.output().ok()?; | ||
let lines = String::from_utf8_lossy(&output.stdout); | ||
lines.lines().find_map(|l| l.split(':').nth(1)?.strip_prefix("eslint@")).map(|v| v.to_owned()) | ||
} | ||
|
||
fn get_eslint_version() -> Option<String> { | ||
get_eslint_version_inner(false).or_else(|| get_eslint_version_inner(true)) | ||
} | ||
|
||
pub(super) fn lint( | ||
librustdoc_path: &Path, | ||
tools_path: &Path, | ||
src_path: &Path, | ||
) -> Result<(), super::Error> { | ||
let eslint_version_path = | ||
src_path.join("ci/docker/host-x86_64/mingw-check-tidy/eslint.version"); | ||
let eslint_version = match std::fs::read_to_string(&eslint_version_path) { | ||
Ok(version) => version.trim().to_string(), | ||
Err(error) => { | ||
eprintln!("failed to read `{}`: {error:?}", eslint_version_path.display()); | ||
return Err(error.into()); | ||
} | ||
}; | ||
// Having the correct `eslint` version installed via `npm` isn't strictly necessary, since we're invoking it via `npx`, | ||
// but this check allows the vast majority that is not working on the rustdoc frontend to avoid the penalty of running | ||
// `eslint` in tidy. See also: https://github.com/rust-lang/rust/pull/142851 | ||
match get_eslint_version() { | ||
Some(version) => { | ||
if version != eslint_version { | ||
// unfortunatly we can't use `Error::Version` here becuse `str::trim` isn't const and | ||
// Version::required must be a static str | ||
return Err(super::Error::Generic(format!( | ||
"⚠️ Installed version of eslint (`{version}`) is different than the \ | ||
one used in the CI (`{eslint_version}`)\n\ | ||
You can install this version using `npm update eslint` or by using \ | ||
`npm install eslint@{eslint_version}`\n | ||
" | ||
))); | ||
} | ||
} | ||
None => { | ||
//eprintln!("`eslint` doesn't seem to be installed. Skipping tidy check for JS files."); | ||
//eprintln!("You can install it using `npm install eslint@{eslint_version}`"); | ||
return Err(super::Error::MissingReq( | ||
"eslint", | ||
"js lint checks", | ||
Some(format!("You can install it using `npm install eslint@{eslint_version}`")), | ||
)); | ||
} | ||
} | ||
let files_to_check = rustdoc_js_files(librustdoc_path); | ||
println!("Running eslint on rustdoc JS files"); | ||
run_eslint(&files_to_check, librustdoc_path.join("html/static"))?; | ||
|
||
run_eslint(&[tools_path.join("rustdoc-js/tester.js")], tools_path.join("rustdoc-js"))?; | ||
run_eslint(&[tools_path.join("rustdoc-gui/tester.js")], tools_path.join("rustdoc-gui"))?; | ||
Ok(()) | ||
} | ||
|
||
pub(super) fn typecheck(librustdoc_path: &Path) -> Result<(), super::Error> { | ||
// use npx to ensure correct version | ||
let mut child = Command::new("npx") | ||
.arg("tsc") | ||
.arg("-p") | ||
.arg(librustdoc_path.join("html/static/js/tsconfig.json")) | ||
.spawn()?; | ||
match child.wait() { | ||
Ok(exit_status) => { | ||
if exit_status.success() { | ||
return Ok(()); | ||
} | ||
Err(super::Error::FailedCheck("tsc command failed")) | ||
} | ||
Err(error) => Err(super::Error::Generic(format!("tsc command failed: {error:?}"))), | ||
} | ||
} | ||
|
||
pub(super) fn es_check(librustdoc_path: &Path) -> Result<(), super::Error> { | ||
let files_to_check = rustdoc_js_files(librustdoc_path); | ||
// use npx to ensure correct version | ||
let mut cmd = Command::new("npx"); | ||
cmd.arg("es-check").arg("es2019"); | ||
for f in files_to_check { | ||
cmd.arg(f); | ||
} | ||
match cmd.spawn()?.wait() { | ||
Ok(exit_status) => { | ||
if exit_status.success() { | ||
return Ok(()); | ||
} | ||
Err(super::Error::FailedCheck("es-check command failed")) | ||
} | ||
Err(error) => Err(super::Error::Generic(format!("es-check command failed: {error:?}"))), | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to separate type checking and eslint? It's essentially the same category of "checking code" to me, at least in the world of JavaScript. Unless it runs for too long, I'd just collapse both into "lint" or "check". When you develop on JS, you'd set your
bootstrap.toml
file to contain both anyway, I imagine.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tsc generally checks correctness, while eslint mostly checks style... thought i suppose if you want fast typechecking, it would be best to run tsc directly...
also calling an extra check "check" feels unhelpful.