Use separate exit codes for fatal errors vs. lint errors (#2670)

This commit is contained in:
Charlie Marsh 2023-02-08 15:21:15 -05:00 committed by GitHub
parent 81abc5d7d8
commit 6339f8e009
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 50 additions and 12 deletions

View file

@ -627,6 +627,25 @@ configuration.
See the [`isort` documentation](https://pycqa.github.io/isort/docs/configuration/action_comments.html)
for more.
#### Exit codes
By default, Ruff exits with the following status codes:
- `0` if no violations were found, or if all present violations were fixed automatically.
- `1` if violations were found.
- `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an internal error.
This convention mirrors that of tools like ESLint, Prettier, and RuboCop.
Ruff supports two command-line flags that alter its exit code behavior:
- `--exit-zero` will cause Ruff to exit with a status code of `0` even if violations were found.
Note that Ruff will still exit with a status code of `2` if it terminates abnormally.
- `--exit-non-zero-on-fix` will cause Ruff to exit with a status code of `1` if violations were
found, _even if_ all such violations were fixed automatically. Note that the use of
`--exit-non-zero-on-fix` can result in a non-zero exit code even if no violations remain after
autofixing.
<!-- End section: Configuration -->
## Supported Rules

View file

@ -24,7 +24,26 @@ mod iterators;
mod printer;
mod resolve;
fn inner_main() -> Result<ExitCode> {
enum ExitStatus {
/// Linting was successful and there were no linting errors.
Success,
/// Linting was successful but there were linting errors.
Failure,
/// Linting failed.
Error,
}
impl From<ExitStatus> for ExitCode {
fn from(status: ExitStatus) -> Self {
match status {
ExitStatus::Success => ExitCode::from(0),
ExitStatus::Failure => ExitCode::from(1),
ExitStatus::Error => ExitCode::from(2),
}
}
}
fn inner_main() -> Result<ExitStatus> {
let mut args: Vec<_> = std::env::args_os().collect();
// Clap doesn't support default subcommands but we want to run `check` by
@ -82,10 +101,10 @@ quoting the executed command, along with the relevant file contents and `pyproje
Command::Check(args) => return check(args, log_level),
}
Ok(ExitCode::SUCCESS)
Ok(ExitStatus::Success)
}
fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
let (cli, overrides) = args.partition();
// Construct the "default" settings. These are used when no `pyproject.toml`
@ -99,11 +118,11 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
if cli.show_settings {
commands::show_settings(&cli.files, &pyproject_strategy, &overrides)?;
return Ok(ExitCode::SUCCESS);
return Ok(ExitStatus::Success);
}
if cli.show_files {
commands::show_files(&cli.files, &pyproject_strategy, &overrides)?;
return Ok(ExitCode::SUCCESS);
return Ok(ExitStatus::Success);
}
// Extract options that are included in `Settings`, but only apply at the top
@ -163,7 +182,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
eprintln!("Added {modifications} noqa directives.");
}
}
return Ok(ExitCode::SUCCESS);
return Ok(ExitStatus::Success);
}
let printer = Printer::new(&format, &log_level, &autofix, &violations);
@ -264,20 +283,20 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
if !cli.exit_zero {
if cli.diff || fix_only {
if diagnostics.fixed > 0 {
return Ok(ExitCode::FAILURE);
return Ok(ExitStatus::Failure);
}
} else if cli.exit_non_zero_on_fix {
if diagnostics.fixed > 0 || !diagnostics.messages.is_empty() {
return Ok(ExitCode::FAILURE);
return Ok(ExitStatus::Failure);
}
} else {
if !diagnostics.messages.is_empty() {
return Ok(ExitCode::FAILURE);
return Ok(ExitStatus::Failure);
}
}
}
}
Ok(ExitCode::SUCCESS)
Ok(ExitStatus::Success)
}
fn rewrite_legacy_subcommand(cmd: &str) -> &str {
@ -292,13 +311,13 @@ fn rewrite_legacy_subcommand(cmd: &str) -> &str {
#[must_use]
pub fn main() -> ExitCode {
match inner_main() {
Ok(code) => code,
Ok(code) => code.into(),
Err(err) => {
#[allow(clippy::print_stderr)]
{
eprintln!("{}{} {err:?}", "error".red().bold(), ":".bold());
}
ExitCode::FAILURE
ExitStatus::Error.into()
}
}
}