diff --git a/crates/prek/src/cli/mod.rs b/crates/prek/src/cli/mod.rs index 371b16e37..6bbc221af 100644 --- a/crates/prek/src/cli/mod.rs +++ b/crates/prek/src/cli/mod.rs @@ -734,9 +734,10 @@ pub(crate) enum UtilCommand { #[derive(Debug, Args)] pub(crate) struct YamlToTomlArgs { - /// The YAML configuration file to convert. + /// The YAML configuration file to convert. If omitted, discovers + /// `.pre-commit-config.yaml` or `.pre-commit-config.yml` in the current directory. #[arg(value_name = "CONFIG", value_hint = ValueHint::FilePath)] - pub(crate) input: PathBuf, + pub(crate) input: Option, /// Path to write the generated prek.toml file. /// Defaults to `prek.toml` in the same directory as the input file. diff --git a/crates/prek/src/cli/yaml_to_toml.rs b/crates/prek/src/cli/yaml_to_toml.rs index 6e9658d44..e0b8f15b9 100644 --- a/crates/prek/src/cli/yaml_to_toml.rs +++ b/crates/prek/src/cli/yaml_to_toml.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; use owo_colors::OwoColorize; -use prek_consts::PREK_TOML; +use prek_consts::{PRE_COMMIT_CONFIG_YAML, PRE_COMMIT_CONFIG_YML, PREK_TOML}; use toml_edit::{Array, ArrayOfTables, DocumentMut, InlineTable, Table, Value}; use crate::cli::ExitStatus; @@ -12,16 +12,45 @@ use crate::config; use crate::fs::Simplified; use crate::printer::Printer; +/// Resolve the input config path, falling back to `.pre-commit-config.yaml` or +/// `.pre-commit-config.yml` in the current directory. +fn resolve_input(input: Option) -> Result { + if let Some(path) = input { + return Ok(path); + } + + let yaml = Path::new(PRE_COMMIT_CONFIG_YAML); + if yaml.is_file() { + return Ok(yaml.to_path_buf()); + } + + let yml = Path::new(PRE_COMMIT_CONFIG_YML); + if yml.is_file() { + return Ok(yml.to_path_buf()); + } + + anyhow::bail!( + "No `{}` or `{}` found in the current directory\n\n\ + {} Provide a path explicitly: {}", + PRE_COMMIT_CONFIG_YAML.cyan(), + PRE_COMMIT_CONFIG_YML.cyan(), + "hint:".yellow().bold(), + "prek util yaml-to-toml ".cyan() + ); +} + pub(crate) fn yaml_to_toml( - input: &Path, + input: Option, output: Option, force: bool, printer: Printer, ) -> Result { + let input = resolve_input(input)?; + // Validate the input file first. - let _ = config::load_config(input)?; + let _ = config::load_config(&input)?; - let content = fs_err::read_to_string(input)?; + let content = fs_err::read_to_string(&input)?; let value: serde_json::Value = serde_saphyr::from_str(&content)?; let output = output.unwrap_or_else(|| input.parent().unwrap_or(Path::new(".")).join(PREK_TOML)); @@ -65,7 +94,8 @@ pub(crate) fn yaml_to_toml( writeln!( printer.stdout(), - "Written to `{}`", + "Converted `{}` → `{}`", + input.simplified_display().cyan(), output.simplified_display().cyan() )?; diff --git a/crates/prek/src/main.rs b/crates/prek/src/main.rs index 611536b31..21a0d7b02 100644 --- a/crates/prek/src/main.rs +++ b/crates/prek/src/main.rs @@ -401,7 +401,7 @@ async fn run(cli: Cli) -> Result { UtilCommand::YamlToToml(args) => { show_settings!(args); - cli::yaml_to_toml(&args.input, args.output, args.force, printer) + cli::yaml_to_toml(args.input, args.output, args.force, printer) } UtilCommand::GenerateShellCompletion(args) => { show_settings!(args); diff --git a/crates/prek/tests/yaml_to_toml.rs b/crates/prek/tests/yaml_to_toml.rs index f6a1f1218..ea8983e53 100644 --- a/crates/prek/tests/yaml_to_toml.rs +++ b/crates/prek/tests/yaml_to_toml.rs @@ -1,6 +1,6 @@ use assert_fs::assert::PathAssert; use assert_fs::fixture::{FileWriteStr, PathChild}; -use prek_consts::PREK_TOML; +use prek_consts::{PRE_COMMIT_CONFIG_YAML, PRE_COMMIT_CONFIG_YML, PREK_TOML}; use crate::common::{TestContext, cmd_snapshot}; @@ -68,14 +68,14 @@ fn yaml_to_toml_writes_default_output() -> anyhow::Result<()> { context .command() .args(["util", "yaml-to-toml", "config.yaml"]), - @r#" + @" success: true exit_code: 0 ----- stdout ----- - Written to `prek.toml` + Converted `config.yaml` → `prek.toml` ----- stderr ----- - "# + " ); insta::assert_snapshot!(context.read(PREK_TOML), @r#" @@ -180,7 +180,7 @@ fn yaml_to_toml_force_overwrite() -> anyhow::Result<()> { success: true exit_code: 0 ----- stdout ----- - Written to `prek.toml` + Converted `config.yaml` → `prek.toml` ----- stderr ----- " @@ -252,3 +252,133 @@ fn yaml_to_toml_same_output() -> anyhow::Result<()> { Ok(()) } + +#[test] +fn yaml_to_toml_discovers_pre_commit_config_yaml() -> anyhow::Result<()> { + let context = TestContext::new(); + + context + .work_dir() + .child(PRE_COMMIT_CONFIG_YAML) + .write_str(YAML_CONFIG)?; + + cmd_snapshot!( + context.filters(), + context.command().args(["util", "yaml-to-toml"]), + @" + success: true + exit_code: 0 + ----- stdout ----- + Converted `.pre-commit-config.yaml` → `prek.toml` + + ----- stderr ----- + " + ); + + context + .work_dir() + .child(PREK_TOML) + .assert(predicates::path::exists()); + + Ok(()) +} + +#[test] +fn yaml_to_toml_discovers_pre_commit_config_yml() -> anyhow::Result<()> { + let context = TestContext::new(); + + context + .work_dir() + .child(PRE_COMMIT_CONFIG_YML) + .write_str(YAML_CONFIG)?; + + cmd_snapshot!( + context.filters(), + context.command().args(["util", "yaml-to-toml"]), + @" + success: true + exit_code: 0 + ----- stdout ----- + Converted `.pre-commit-config.yml` → `prek.toml` + + ----- stderr ----- + " + ); + + context + .work_dir() + .child(PREK_TOML) + .assert(predicates::path::exists()); + + Ok(()) +} + +#[test] +fn yaml_to_toml_prefers_yaml_over_yml() -> anyhow::Result<()> { + let context = TestContext::new(); + + // Write different content to each file so we can verify which was used. + let yaml_only = indoc::indoc! {r" + repos: + - repo: builtin + hooks: + - id: trailing-whitespace + "}; + let yml_only = indoc::indoc! {r" + repos: + - repo: builtin + hooks: + - id: end-of-file-fixer + "}; + + context + .work_dir() + .child(PRE_COMMIT_CONFIG_YAML) + .write_str(yaml_only)?; + context + .work_dir() + .child(PRE_COMMIT_CONFIG_YML) + .write_str(yml_only)?; + + cmd_snapshot!( + context.filters(), + context.command().args(["util", "yaml-to-toml"]), + @" + success: true + exit_code: 0 + ----- stdout ----- + Converted `.pre-commit-config.yaml` → `prek.toml` + + ----- stderr ----- + " + ); + + // The .yaml file contains trailing-whitespace, the .yml contains end-of-file-fixer. + let output = context.read(PREK_TOML); + assert!( + output.contains("trailing-whitespace"), + "Expected .yaml to be preferred over .yml" + ); + + Ok(()) +} + +#[test] +fn yaml_to_toml_error_when_no_config_found() { + let context = TestContext::new(); + + cmd_snapshot!( + context.filters(), + context.command().args(["util", "yaml-to-toml"]), + @r#" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: No `.pre-commit-config.yaml` or `.pre-commit-config.yml` found in the current directory + + hint: Provide a path explicitly: prek util yaml-to-toml + "# + ); +} diff --git a/docs/cli.md b/docs/cli.md index 0f649809a..63e2415b5 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -914,12 +914,12 @@ Convert a YAML configuration file to prek.toml

Usage

``` -prek util yaml-to-toml [OPTIONS] +prek util yaml-to-toml [OPTIONS] [CONFIG] ```

Arguments

-
CONFIG

The YAML configuration file to convert

+
CONFIG

The YAML configuration file to convert. If omitted, discovers .pre-commit-config.yaml or .pre-commit-config.yml in the current directory

Options