Generate the README's --help output automatically via cargo +nightly dev generate-all (#1483)

This commit is contained in:
Reiner Gerecke 2022-12-30 21:06:32 +01:00 committed by GitHub
parent d880ca6cc6
commit c9aa7b9308
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 90 additions and 68 deletions

View file

@ -300,13 +300,14 @@ ruff path/to/code/ --select F401 --select F403
See `ruff --help` for more: See `ruff --help` for more:
<!-- Begin auto-generated cli help. -->
```shell ```shell
Ruff: An extremely fast Python linter. Ruff: An extremely fast Python linter.
Usage: ruff [OPTIONS] [FILES]... Usage: ruff [OPTIONS] [FILES]...
Arguments: Arguments:
[FILES]... [FILES]...
Options: Options:
--config <CONFIG> --config <CONFIG>
@ -381,7 +382,9 @@ Options:
Print help information Print help information
-V, --version -V, --version
Print version information Print version information
``` ```
<!-- End auto-generated cli help. -->
### `pyproject.toml` discovery ### `pyproject.toml` discovery
@ -1669,7 +1672,6 @@ Summary
<!-- Sections automatically generated by `cargo dev generate-options`. --> <!-- Sections automatically generated by `cargo dev generate-options`. -->
<!-- Begin auto-generated options sections. --> <!-- Begin auto-generated options sections. -->
#### [`allowed-confusables`](#allowed-confusables) #### [`allowed-confusables`](#allowed-confusables)
A list of allowed "confusable" Unicode characters to ignore when A list of allowed "confusable" Unicode characters to ignore when

View file

@ -4,7 +4,8 @@ use anyhow::Result;
use clap::Args; use clap::Args;
use crate::{ use crate::{
generate_check_code_prefix, generate_json_schema, generate_options, generate_rules_table, generate_check_code_prefix, generate_cli_help, generate_json_schema, generate_options,
generate_rules_table,
}; };
#[derive(Args)] #[derive(Args)]
@ -27,5 +28,8 @@ pub fn main(cli: &Cli) -> Result<()> {
generate_options::main(&generate_options::Cli { generate_options::main(&generate_options::Cli {
dry_run: cli.dry_run, dry_run: cli.dry_run,
})?; })?;
generate_cli_help::main(&generate_cli_help::Cli {
dry_run: cli.dry_run,
})?;
Ok(()) Ok(())
} }

View file

@ -0,0 +1,34 @@
//! Generate CLI help.
use anyhow::Result;
use clap::{Args, CommandFactory};
use ruff::cli::Cli as MainCli;
use crate::utils::replace_readme_section;
const HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated cli help. -->";
const HELP_END_PRAGMA: &str = "<!-- End auto-generated cli help. -->";
#[derive(Args)]
pub struct Cli {
/// Write the generated help to stdout (rather than to `README.md`).
#[arg(long)]
pub(crate) dry_run: bool,
}
pub fn main(cli: &Cli) -> Result<()> {
let mut cmd = MainCli::command();
let output = cmd.render_help().to_string();
if cli.dry_run {
print!("{output}");
} else {
replace_readme_section(
&format!("```shell\n{output}\n```\n"),
HELP_BEGIN_PRAGMA,
HELP_END_PRAGMA,
)?;
}
Ok(())
}

View file

@ -1,16 +1,13 @@
//! Generate a Markdown-compatible listing of configuration options. //! Generate a Markdown-compatible listing of configuration options.
use std::fs;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use clap::Args; use clap::Args;
use itertools::Itertools; use itertools::Itertools;
use ruff::settings::options::Options; use ruff::settings::options::Options;
use ruff::settings::options_base::{ConfigurationOptions, OptionEntry, OptionField}; use ruff::settings::options_base::{ConfigurationOptions, OptionEntry, OptionField};
use crate::utils::replace_readme_section;
const BEGIN_PRAGMA: &str = "<!-- Begin auto-generated options sections. -->"; const BEGIN_PRAGMA: &str = "<!-- Begin auto-generated options sections. -->";
const END_PRAGMA: &str = "<!-- End auto-generated options sections. -->"; const END_PRAGMA: &str = "<!-- End auto-generated options sections. -->";
@ -95,30 +92,7 @@ pub fn main(cli: &Cli) -> Result<()> {
if cli.dry_run { if cli.dry_run {
print!("{output}"); print!("{output}");
} else { } else {
// Read the existing file. replace_readme_section(&output, BEGIN_PRAGMA, END_PRAGMA)?;
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("Failed to find root directory")
.join("README.md");
let existing = fs::read_to_string(&file)?;
// Extract the prefix.
let index = existing
.find(BEGIN_PRAGMA)
.expect("Unable to find begin pragma");
let prefix = &existing[..index + BEGIN_PRAGMA.len()];
// Extract the suffix.
let index = existing
.find(END_PRAGMA)
.expect("Unable to find end pragma");
let suffix = &existing[index..];
// Write the prefix, new contents, and suffix.
let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?;
write!(f, "{prefix}\n\n")?;
write!(f, "{output}")?;
write!(f, "{suffix}")?;
} }
Ok(()) Ok(())

View file

@ -1,16 +1,13 @@
//! Generate a Markdown-compatible table of supported lint rules. //! Generate a Markdown-compatible table of supported lint rules.
use std::fs;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use clap::Args; use clap::Args;
use itertools::Itertools; use itertools::Itertools;
use ruff::checks::{CheckCategory, CheckCode}; use ruff::checks::{CheckCategory, CheckCode};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::utils::replace_readme_section;
const TABLE_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated sections. -->"; const TABLE_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated sections. -->";
const TABLE_END_PRAGMA: &str = "<!-- End auto-generated sections. -->"; const TABLE_END_PRAGMA: &str = "<!-- End auto-generated sections. -->";
@ -85,32 +82,3 @@ pub fn main(cli: &Cli) -> Result<()> {
Ok(()) Ok(())
} }
fn replace_readme_section(content: &str, begin_pragma: &str, end_pragma: &str) -> Result<()> {
// Read the existing file.
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("Failed to find root directory")
.join("README.md");
let existing = fs::read_to_string(&file)?;
// Extract the prefix.
let index = existing
.find(begin_pragma)
.expect("Unable to find begin pragma");
let prefix = &existing[..index + begin_pragma.len()];
// Extract the suffix.
let index = existing
.find(end_pragma)
.expect("Unable to find end pragma");
let suffix = &existing[index..];
// Write the prefix, new contents, and suffix.
let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?;
writeln!(f, "{prefix}")?;
write!(f, "{content}")?;
write!(f, "{suffix}")?;
Ok(())
}

View file

@ -13,6 +13,7 @@
pub mod generate_all; pub mod generate_all;
pub mod generate_check_code_prefix; pub mod generate_check_code_prefix;
pub mod generate_cli_help;
pub mod generate_json_schema; pub mod generate_json_schema;
pub mod generate_options; pub mod generate_options;
pub mod generate_rules_table; pub mod generate_rules_table;
@ -20,3 +21,4 @@ pub mod print_ast;
pub mod print_cst; pub mod print_cst;
pub mod print_tokens; pub mod print_tokens;
pub mod round_trip; pub mod round_trip;
mod utils;

View file

@ -14,8 +14,8 @@
use anyhow::Result; use anyhow::Result;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use ruff_dev::{ use ruff_dev::{
generate_all, generate_check_code_prefix, generate_json_schema, generate_options, generate_all, generate_check_code_prefix, generate_cli_help, generate_json_schema,
generate_rules_table, print_ast, print_cst, print_tokens, round_trip, generate_options, generate_rules_table, print_ast, print_cst, print_tokens, round_trip,
}; };
#[derive(Parser)] #[derive(Parser)]
@ -38,6 +38,8 @@ enum Commands {
GenerateRulesTable(generate_rules_table::Cli), GenerateRulesTable(generate_rules_table::Cli),
/// Generate a Markdown-compatible listing of configuration options. /// Generate a Markdown-compatible listing of configuration options.
GenerateOptions(generate_options::Cli), GenerateOptions(generate_options::Cli),
/// Generate CLI help.
GenerateCliHelp(generate_cli_help::Cli),
/// Print the AST for a given Python file. /// Print the AST for a given Python file.
PrintAST(print_ast::Cli), PrintAST(print_ast::Cli),
/// Print the LibCST CST for a given Python file. /// Print the LibCST CST for a given Python file.
@ -56,6 +58,7 @@ fn main() -> Result<()> {
Commands::GenerateJSONSchema(args) => generate_json_schema::main(args)?, Commands::GenerateJSONSchema(args) => generate_json_schema::main(args)?,
Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?, Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?,
Commands::GenerateOptions(args) => generate_options::main(args)?, Commands::GenerateOptions(args) => generate_options::main(args)?,
Commands::GenerateCliHelp(args) => generate_cli_help::main(args)?,
Commands::PrintAST(args) => print_ast::main(args)?, Commands::PrintAST(args) => print_ast::main(args)?,
Commands::PrintCST(args) => print_cst::main(args)?, Commands::PrintCST(args) => print_cst::main(args)?,
Commands::PrintTokens(args) => print_tokens::main(args)?, Commands::PrintTokens(args) => print_tokens::main(args)?,

35
ruff_dev/src/utils.rs Normal file
View file

@ -0,0 +1,35 @@
use std::fs;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
use anyhow::Result;
pub fn replace_readme_section(content: &str, begin_pragma: &str, end_pragma: &str) -> Result<()> {
// Read the existing file.
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("Failed to find root directory")
.join("README.md");
let existing = fs::read_to_string(&file)?;
// Extract the prefix.
let index = existing
.find(begin_pragma)
.expect("Unable to find begin pragma");
let prefix = &existing[..index + begin_pragma.len()];
// Extract the suffix.
let index = existing
.find(end_pragma)
.expect("Unable to find end pragma");
let suffix = &existing[index..];
// Write the prefix, new contents, and suffix.
let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?;
writeln!(f, "{prefix}")?;
write!(f, "{content}")?;
write!(f, "{suffix}")?;
Ok(())
}