provenant/
golden_maintenance.rs1use std::fs;
2use std::path::{Path, PathBuf};
3use std::process::Command;
4
5use anyhow::{Context, Result};
6
7pub fn find_files_with_extension(dir: &Path, extension: &str) -> Result<Vec<PathBuf>> {
8 let mut paths = Vec::new();
9 if !dir.is_dir() {
10 return Ok(paths);
11 }
12
13 fn recurse(dir: &Path, extension: &str, out: &mut Vec<PathBuf>) -> Result<()> {
14 for entry in fs::read_dir(dir)? {
15 let entry = entry?;
16 let path = entry.path();
17 if path.is_dir() {
18 recurse(&path, extension, out)?;
19 } else if path
20 .extension()
21 .and_then(|ext| ext.to_str())
22 .is_some_and(|ext| ext == extension)
23 {
24 out.push(path);
25 }
26 }
27 Ok(())
28 }
29
30 recurse(dir, extension, &mut paths)?;
31 paths.sort();
32 Ok(paths)
33}
34
35pub fn run_prettier(paths: &[PathBuf]) -> Result<()> {
36 if paths.is_empty() {
37 return Ok(());
38 }
39
40 let ignore_path = std::env::temp_dir().join("provenant-empty-prettierignore");
41 if !ignore_path.exists() {
42 fs::write(&ignore_path, "").context("failed to create empty prettier ignore file")?;
43 }
44
45 const CHUNK_SIZE: usize = 100;
46 for chunk in paths.chunks(CHUNK_SIZE) {
47 let mut cmd = Command::new("npm");
48 cmd.args([
49 "exec",
50 "--",
51 "prettier",
52 "--write",
53 "--ignore-path",
54 ignore_path
55 .to_str()
56 .context("temporary prettier ignore path is not valid UTF-8")?,
57 ]);
58 for path in chunk {
59 cmd.arg(path);
60 }
61
62 let output = cmd
63 .output()
64 .context("failed to run `npm exec -- prettier --write`")?;
65
66 if !output.status.success() {
67 let stderr = String::from_utf8_lossy(&output.stderr);
68 anyhow::bail!(
69 "prettier formatting failed (status: {}): {}",
70 output.status,
71 stderr.trim()
72 );
73 }
74 }
75
76 Ok(())
77}