Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions crates/prek/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf>,

/// Path to write the generated prek.toml file.
/// Defaults to `prek.toml` in the same directory as the input file.
Expand Down
40 changes: 35 additions & 5 deletions crates/prek/src/cli/yaml_to_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,53 @@ 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;
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<PathBuf>) -> Result<PathBuf> {
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 <CONFIG>".cyan()
);
}

pub(crate) fn yaml_to_toml(
input: &Path,
input: Option<PathBuf>,
output: Option<PathBuf>,
force: bool,
printer: Printer,
) -> Result<ExitStatus> {
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));
Expand Down Expand Up @@ -65,7 +94,8 @@ pub(crate) fn yaml_to_toml(

writeln!(
printer.stdout(),
"Written to `{}`",
"Converted `{}` → `{}`",
input.simplified_display().cyan(),
output.simplified_display().cyan()
)?;

Expand Down
2 changes: 1 addition & 1 deletion crates/prek/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
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);
Expand Down
140 changes: 135 additions & 5 deletions crates/prek/tests/yaml_to_toml.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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#"
Expand Down Expand Up @@ -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 -----
"
Expand Down Expand Up @@ -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 <CONFIG>
"#
);
}
4 changes: 2 additions & 2 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -914,12 +914,12 @@ Convert a YAML configuration file to prek.toml
<h3 class="cli-reference">Usage</h3>

```
prek util yaml-to-toml [OPTIONS] <CONFIG>
prek util yaml-to-toml [OPTIONS] [CONFIG]
```

<h3 class="cli-reference">Arguments</h3>

<dl class="cli-reference"><dt id="prek-util-yaml-to-toml--input"><a href="#prek-util-yaml-to-toml--input"><code>CONFIG</code></a></dt><dd><p>The YAML configuration file to convert</p>
<dl class="cli-reference"><dt id="prek-util-yaml-to-toml--input"><a href="#prek-util-yaml-to-toml--input"><code>CONFIG</code></a></dt><dd><p>The YAML configuration file to convert. If omitted, discovers <code>.pre-commit-config.yaml</code> or <code>.pre-commit-config.yml</code> in the current directory</p>
</dd></dl>

<h3 class="cli-reference">Options</h3>
Expand Down
Loading