Skip to content

Commit cc9a387

Browse files
authored
for-file --json (#80)
* stubbing out teams_for_files_from_codeowners * first try..slow * much faster * bumping crates * extracting separate concerns from runner * updating README with usage examples * DRY * json for-file * fallback error * perf boost * memoizing codeowners * DRY from codeowners
1 parent 9cee8f0 commit cc9a387

File tree

15 files changed

+406
-123
lines changed

15 files changed

+406
-123
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "codeowners"
3-
version = "0.2.16"
3+
version = "0.2.17"
44
edition = "2024"
55

66
[profile.release]

src/cache/file.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub struct GlobalCache {
1717
file_owner_cache: Option<Box<Mutex<HashMap<PathBuf, FileOwnerCacheEntry>>>>,
1818
}
1919

20-
const DEFAULT_CACHE_CAPACITY: usize = 10000;
20+
const DEFAULT_CACHE_CAPACITY: usize = 50000;
2121

2222
impl Caching for GlobalCache {
2323
fn get_file_owner(&self, path: &Path) -> Result<Option<FileOwnerCacheEntry>, Error> {
@@ -62,7 +62,7 @@ impl Caching for GlobalCache {
6262

6363
fn delete_cache(&self) -> Result<(), Error> {
6464
let cache_path = self.get_cache_path();
65-
dbg!("deleting", &cache_path);
65+
tracing::debug!("Deleting cache file: {}", cache_path.display());
6666
fs::remove_file(cache_path).change_context(Error::Io)
6767
}
6868
}

src/cli.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ enum Command {
1717
help = "Find the owner from the CODEOWNERS file and just return the team name and yml path"
1818
)]
1919
from_codeowners: bool,
20+
#[arg(short, long, default_value = "false", help = "Output the result in JSON format")]
21+
json: bool,
2022
name: String,
2123
},
2224

23-
#[clap(about = "Finds code ownership information for a given team ", visible_alias = "t")]
25+
#[clap(about = "Finds code ownership information for a given team", visible_alias = "t")]
2426
ForTeam { name: String },
2527

2628
#[clap(
@@ -113,7 +115,11 @@ pub fn cli() -> Result<RunResult, RunnerError> {
113115
Command::Validate => runner::validate(&run_config, vec![]),
114116
Command::Generate { skip_stage } => runner::generate(&run_config, !skip_stage),
115117
Command::GenerateAndValidate { skip_stage } => runner::generate_and_validate(&run_config, vec![], !skip_stage),
116-
Command::ForFile { name, from_codeowners } => runner::for_file(&run_config, &name, from_codeowners),
118+
Command::ForFile {
119+
name,
120+
from_codeowners,
121+
json,
122+
} => runner::for_file(&run_config, &name, from_codeowners, json),
117123
Command::ForTeam { name } => runner::for_team(&run_config, &name),
118124
Command::DeleteCache => runner::delete_cache(&run_config),
119125
Command::CrosscheckOwners => runner::crosscheck_owners(&run_config),

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn maybe_print_errors(result: RunResult) -> Result<(), RunnerError> {
2626
for msg in result.validation_errors {
2727
println!("{}", msg);
2828
}
29-
process::exit(-1);
29+
process::exit(1);
3030
}
3131

3232
Ok(())

src/ownership.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,21 @@ impl TeamOwnership {
5959
impl Display for FileOwner {
6060
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6161
let sources = if self.sources.is_empty() {
62-
"Unowned".to_string()
62+
"".to_string()
6363
} else {
64-
self.sources
64+
let sources_str = self
65+
.sources
6566
.iter()
6667
.sorted_by_key(|source| source.to_string())
6768
.map(|source| source.to_string())
6869
.collect::<Vec<_>>()
69-
.join("\n- ")
70+
.join("\n- ");
71+
format!("\n- {}", sources_str)
7072
};
7173

7274
write!(
7375
f,
74-
"Team: {}\nGithub Team: {}\nTeam YML: {}\nDescription:\n- {}",
76+
"Team: {}\nGithub Team: {}\nTeam YML: {}\nDescription:{}",
7577
self.team.name, self.team.github_team, self.team_config_file_path, sources
7678
)
7779
}

src/ownership/codeowners_file_parser.rs

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,7 @@ use fast_glob::glob_match;
66
use memoize::memoize;
77
use rayon::prelude::*;
88
use regex::Regex;
9-
use std::{
10-
collections::HashMap,
11-
error::Error,
12-
fs,
13-
io::Error as IoError,
14-
path::{Path, PathBuf},
15-
};
9+
use std::{collections::HashMap, error::Error, fs, io::Error as IoError, path::PathBuf};
1610

1711
use super::file_generator::compare_lines;
1812

@@ -44,16 +38,7 @@ impl Parser {
4438
return Ok(HashMap::new());
4539
}
4640

47-
let codeowners_entries: Vec<(String, String)> =
48-
build_codeowners_lines_in_priority(self.codeowners_file_path.to_string_lossy().into_owned())
49-
.iter()
50-
.map(|line| {
51-
line.split_once(' ')
52-
.map(|(glob, team_name)| (glob.to_string(), team_name.to_string()))
53-
.ok_or_else(|| IoError::new(std::io::ErrorKind::InvalidInput, "Invalid line"))
54-
})
55-
.collect::<Result<_, IoError>>()
56-
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
41+
let codeowners_entries = parse_codeowners_entries(self.codeowners_file_path.to_string_lossy().into_owned());
5742

5843
let teams_by_name = teams_by_github_team_name(self.absolute_team_files_globs());
5944

@@ -71,11 +56,6 @@ impl Parser {
7156
Ok(result)
7257
}
7358

74-
pub fn team_from_file_path(&self, file_path: &Path) -> Result<Option<Team>, Box<dyn Error>> {
75-
let teams = self.teams_from_files_paths(&[file_path.to_path_buf()])?;
76-
Ok(teams.get(file_path.to_string_lossy().into_owned().as_str()).cloned().flatten())
77-
}
78-
7959
fn absolute_team_files_globs(&self) -> Vec<String> {
8060
self.team_file_globs
8161
.iter()
@@ -111,7 +91,6 @@ fn teams_by_github_team_name(team_file_glob: Vec<String>) -> HashMap<String, Tea
11191
teams
11292
}
11393

114-
#[memoize]
11594
fn build_codeowners_lines_in_priority(codeowners_file_path: String) -> Vec<String> {
11695
let codeowners_file = match fs::read_to_string(codeowners_file_path) {
11796
Ok(codeowners_file) => codeowners_file,
@@ -124,6 +103,16 @@ fn build_codeowners_lines_in_priority(codeowners_file_path: String) -> Vec<Strin
124103
stripped_lines_by_priority(&codeowners_file)
125104
}
126105

106+
fn parse_codeowners_entries(codeowners_file_path: String) -> Vec<(String, String)> {
107+
build_codeowners_lines_in_priority(codeowners_file_path)
108+
.iter()
109+
.filter_map(|line| {
110+
line.split_once(' ')
111+
.map(|(glob, team_name)| (glob.to_string(), team_name.to_string()))
112+
})
113+
.collect()
114+
}
115+
127116
#[derive(Debug, Clone, PartialEq, Eq)]
128117
struct Section {
129118
heading: String,

src/ownership/codeowners_query.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,6 @@ use std::path::{Path, PathBuf};
44
use crate::ownership::codeowners_file_parser::Parser;
55
use crate::project::Team;
66

7-
pub(crate) fn team_for_file_from_codeowners(
8-
project_root: &Path,
9-
codeowners_file_path: &Path,
10-
team_file_globs: &[String],
11-
file_path: &Path,
12-
) -> Result<Option<Team>, String> {
13-
let relative_file_path = if file_path.is_absolute() {
14-
crate::path_utils::relative_to_buf(project_root, file_path)
15-
} else {
16-
PathBuf::from(file_path)
17-
};
18-
19-
let parser = Parser {
20-
codeowners_file_path: codeowners_file_path.to_path_buf(),
21-
project_root: project_root.to_path_buf(),
22-
team_file_globs: team_file_globs.to_vec(),
23-
};
24-
25-
parser.team_from_file_path(&relative_file_path).map_err(|e| e.to_string())
26-
}
27-
287
pub(crate) fn teams_for_files_from_codeowners(
298
project_root: &Path,
309
codeowners_file_path: &Path,

src/project.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@ pub enum Error {
158158
impl fmt::Display for Error {
159159
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
160160
match self {
161-
Error::Io => fmt.write_str("Error::Io"),
162-
Error::SerdeYaml => fmt.write_str("Error::SerdeYaml"),
163-
Error::SerdeJson => fmt.write_str("Error::SerdeJson"),
161+
Error::Io => fmt.write_str("IO operation failed"),
162+
Error::SerdeYaml => fmt.write_str("YAML serialization/deserialization failed"),
163+
Error::SerdeJson => fmt.write_str("JSON serialization/deserialization failed"),
164164
}
165165
}
166166
}

src/project_builder.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ impl<'a> ProjectBuilder<'a> {
5252
}
5353
}
5454

55-
#[instrument(level = "debug", skip_all)]
55+
#[instrument(level = "debug", skip_all, fields(base_path = %self.base_path.display()))]
5656
pub fn build(&mut self) -> Result<Project, Error> {
57+
tracing::info!("Starting project build");
5758
let mut builder = WalkBuilder::new(&self.base_path);
5859
builder.hidden(false);
5960
builder.follow_links(false);
@@ -277,6 +278,13 @@ impl<'a> ProjectBuilder<'a> {
277278
.flat_map(|team| vec![(team.name.clone(), team.clone()), (team.github_team.clone(), team.clone())])
278279
.collect();
279280

281+
tracing::info!(
282+
files_count = %project_files.len(),
283+
teams_count = %teams.len(),
284+
packages_count = %packages.len(),
285+
"Project build completed successfully"
286+
);
287+
280288
Ok(Project {
281289
base_path: self.base_path.to_owned(),
282290
files: project_files,

0 commit comments

Comments
 (0)