Add a generate-all step and auto-generate settings.md (#5080)

## Summary

Ensures that `generate-all` generates both the JSON Schema and the
`settings.md` API reference.
This commit is contained in:
Charlie Marsh 2024-07-15 15:58:53 -04:00 committed by GitHub
parent 6275b54d51
commit 41cd4bee58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 287 additions and 25 deletions

View file

@ -2,8 +2,12 @@
//!
//! Based on: <https://github.com/astral-sh/ruff/blob/dc8db1afb08704ad6a788c497068b01edf8b460d/crates/ruff_dev/src/generate_options.rs>
use std::fmt::Write;
use std::path::PathBuf;
use anstream::println;
use anyhow::{bail, Result};
use itertools::Itertools;
use pretty_assertions::StrComparison;
use schemars::JsonSchema;
use serde::Deserialize;
@ -12,6 +16,9 @@ use uv_macros::OptionsMetadata;
use uv_options_metadata::{OptionField, OptionSet, OptionsMetadata, Visit};
use uv_settings::Options as SettingsOptions;
use crate::generate_all::Mode;
use crate::ROOT_DIR;
#[derive(Deserialize, JsonSchema, OptionsMetadata)]
#[serde(deny_unknown_fields)]
#[allow(dead_code)]
@ -25,7 +32,65 @@ struct CombinedOptions {
workspace: WorkspaceOptions,
}
pub(crate) fn generate() -> String {
#[derive(clap::Args)]
pub(crate) struct Args {
/// Write the generated output to stdout (rather than to `settings.md`).
#[arg(long, default_value_t, value_enum)]
pub(crate) mode: Mode,
}
pub(crate) fn main(args: &Args) -> Result<()> {
let reference_string = generate();
let filename = "settings.md";
let reference_path = PathBuf::from(ROOT_DIR).join("docs").join(filename);
match args.mode {
Mode::DryRun => {
println!("{reference_string}");
}
Mode::Check => match fs_err::read_to_string(reference_path) {
Ok(current) => {
if current == reference_string {
println!("Up-to-date: {filename}");
} else {
let comparison = StrComparison::new(&current, &reference_string);
bail!("{filename} changed, please run `cargo dev generate-options-reference`:\n{comparison}");
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
bail!("{filename} not found, please run `cargo dev generate-options-reference`");
}
Err(err) => {
bail!(
"{filename} changed, please run `cargo dev generate-options-reference`:\n{err}"
);
}
},
Mode::Write => {
match fs_err::read_to_string(&reference_path) {
Ok(current) => {
if current == reference_string {
println!("Up-to-date: {filename}");
} else {
println!("Updating: {filename}");
fs_err::write(reference_path, reference_string.as_bytes())?;
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
println!("Updating: {filename}");
fs_err::write(reference_path, reference_string.as_bytes())?;
}
Err(err) => {
bail!("{filename} changed, please run `cargo dev generate-options-reference`:\n{err}");
}
}
}
}
Ok(())
}
fn generate() -> String {
let mut output = String::new();
generate_set(
@ -219,3 +284,24 @@ impl Visit for CollectOptionsVisitor {
self.fields.push((name.to_owned(), field));
}
}
#[cfg(test)]
mod tests {
use std::env;
use anyhow::Result;
use crate::generate_all::Mode;
use super::{main, Args};
#[test]
fn test_generate_options_reference() -> Result<()> {
let mode = if env::var("UV_UPDATE_SCHEMA").as_deref() == Ok("1") {
Mode::Write
} else {
Mode::Check
};
main(&Args { mode })
}
}