Skip to content

Commit 11d9506

Browse files
committed
printing path error
1 parent 1bf9386 commit 11d9506

File tree

4 files changed

+179
-7
lines changed

4 files changed

+179
-7
lines changed

src/ownership.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use self::{
3333
pub struct Ownership {
3434
project: Arc<Project>,
3535
}
36-
36+
#[derive(Debug)]
3737
pub struct FileOwner {
3838
pub team: Team,
3939
pub team_config_file_path: String,

src/ownership/for_file_fast.rs

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ fn load_teams(project_root: &Path, team_file_globs: &[String]) -> std::result::R
123123
match Team::from_team_file_path(path.clone()) {
124124
Ok(team) => teams.push(team),
125125
Err(e) => {
126-
eprintln!("Error parsing team file: {}", e);
126+
eprintln!("Error parsing team file: {}, path: {}", e, path.display());
127127
continue;
128128
}
129129
}
@@ -311,3 +311,152 @@ fn source_priority(source: &Source) -> u8 {
311311
Source::TeamYml => 5,
312312
}
313313
}
314+
315+
#[cfg(test)]
316+
mod tests {
317+
use super::*;
318+
use crate::project::Team;
319+
use std::collections::HashMap;
320+
use tempfile::tempdir;
321+
322+
fn build_config_for_temp(frontend_glob: &str, ruby_glob: &str, vendored_path: &str) -> crate::config::Config {
323+
crate::config::Config {
324+
owned_globs: vec!["**/*".to_string()],
325+
ruby_package_paths: vec![ruby_glob.to_string()],
326+
javascript_package_paths: vec![frontend_glob.to_string()],
327+
team_file_glob: vec!["config/teams/**/*.yml".to_string()],
328+
unowned_globs: vec![],
329+
vendored_gems_path: vendored_path.to_string(),
330+
cache_directory: "tmp/cache/codeowners".to_string(),
331+
}
332+
}
333+
334+
fn team_named(name: &str) -> Team {
335+
Team {
336+
path: Path::new("config/teams/foo.yml").to_path_buf(),
337+
name: name.to_string(),
338+
github_team: format!("@{}Team", name),
339+
owned_globs: vec![],
340+
subtracted_globs: vec![],
341+
owned_gems: vec![],
342+
avoid_ownership: false,
343+
}
344+
}
345+
346+
#[test]
347+
fn test_read_top_of_file_team_parses_at_and_colon_forms() {
348+
let td = tempdir().unwrap();
349+
350+
// @team form
351+
let file_at = td.path().join("at_form.rb");
352+
std::fs::write(&file_at, "# @team Payroll\nputs 'x'\n").unwrap();
353+
assert_eq!(read_top_of_file_team(&file_at), Some("Payroll".to_string()));
354+
355+
// team: form (case-insensitive, allows spaces)
356+
let file_colon = td.path().join("colon_form.rb");
357+
std::fs::write(&file_colon, "# Team: Payments\nputs 'y'\n").unwrap();
358+
assert_eq!(read_top_of_file_team(&file_colon), Some("Payments".to_string()));
359+
}
360+
361+
#[test]
362+
fn test_most_specific_directory_owner_prefers_deeper() {
363+
let td = tempdir().unwrap();
364+
let project_root = td.path();
365+
366+
// Build directories
367+
let deep_dir = project_root.join("a/b/c");
368+
std::fs::create_dir_all(&deep_dir).unwrap();
369+
let mid_dir = project_root.join("a/b");
370+
let top_dir = project_root.join("a");
371+
372+
// Write .codeowner files
373+
std::fs::write(top_dir.join(".codeowner"), "TopTeam").unwrap();
374+
std::fs::write(mid_dir.join(".codeowner"), "MidTeam").unwrap();
375+
std::fs::write(deep_dir.join(".codeowner"), "DeepTeam").unwrap();
376+
377+
// Build teams_by_name
378+
let mut tbn: HashMap<String, Team> = HashMap::new();
379+
for name in ["TopTeam", "MidTeam", "DeepTeam"] {
380+
let t = team_named(name);
381+
tbn.insert(t.name.clone(), t);
382+
}
383+
384+
let rel_file = Path::new("a/b/c/file.rb");
385+
let result = most_specific_directory_owner(project_root, rel_file, &tbn).unwrap();
386+
match result.1 {
387+
Source::Directory(path) => {
388+
assert!(path.ends_with("a/b/c"), "expected deepest directory, got {}", path);
389+
}
390+
_ => panic!("expected Directory source"),
391+
}
392+
assert_eq!(result.0, "DeepTeam");
393+
}
394+
395+
#[test]
396+
fn test_nearest_package_owner_ruby_and_js() {
397+
let td = tempdir().unwrap();
398+
let project_root = td.path();
399+
let config = build_config_for_temp("frontend/**/*", "packs/**/*", "vendored");
400+
401+
// Ruby package
402+
let ruby_pkg = project_root.join("packs/payroll");
403+
std::fs::create_dir_all(&ruby_pkg).unwrap();
404+
std::fs::write(
405+
ruby_pkg.join("package.yml"),
406+
"---\nowner: Payroll\n",
407+
)
408+
.unwrap();
409+
410+
// JS package
411+
let js_pkg = project_root.join("frontend/flow");
412+
std::fs::create_dir_all(&js_pkg).unwrap();
413+
std::fs::write(
414+
js_pkg.join("package.json"),
415+
r#"{"metadata": {"owner": "UX"}}"#,
416+
)
417+
.unwrap();
418+
419+
// Teams map
420+
let mut tbn: HashMap<String, Team> = HashMap::new();
421+
for name in ["Payroll", "UX"] {
422+
let t = team_named(name);
423+
tbn.insert(t.name.clone(), t);
424+
}
425+
426+
// Ruby nearest
427+
let rel_ruby = Path::new("packs/payroll/app/models/thing.rb");
428+
let ruby_owner = nearest_package_owner(project_root, rel_ruby, &config, &tbn).unwrap();
429+
assert_eq!(ruby_owner.0, "Payroll");
430+
match ruby_owner.1 {
431+
Source::Package(pkg_path, glob) => {
432+
assert!(pkg_path.ends_with("packs/payroll/package.yml"));
433+
assert_eq!(glob, "packs/payroll/**/**");
434+
}
435+
_ => panic!("expected Package source for ruby"),
436+
}
437+
438+
// JS nearest
439+
let rel_js = Path::new("frontend/flow/src/index.ts");
440+
let js_owner = nearest_package_owner(project_root, rel_js, &config, &tbn).unwrap();
441+
assert_eq!(js_owner.0, "UX");
442+
match js_owner.1 {
443+
Source::Package(pkg_path, glob) => {
444+
assert!(pkg_path.ends_with("frontend/flow/package.json"));
445+
assert_eq!(glob, "frontend/flow/**/**");
446+
}
447+
_ => panic!("expected Package source for js"),
448+
}
449+
}
450+
451+
#[test]
452+
fn test_vendored_gem_owner() {
453+
let config = build_config_for_temp("frontend/**/*", "packs/**/*", "vendored");
454+
let mut teams: Vec<Team> = vec![team_named("Payroll")];
455+
teams[0].owned_gems = vec!["awesome_gem".to_string()];
456+
457+
let path = Path::new("vendored/awesome_gem/lib/a.rb");
458+
let result = vendored_gem_owner(path, &config, &teams).unwrap();
459+
assert_eq!(result.0, "Payroll");
460+
matches!(result.1, Source::TeamGem);
461+
}
462+
}

src/project.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,7 @@ impl Project {
178178
}
179179

180180
pub fn relative_path<'a>(&'a self, absolute_path: &'a Path) -> &'a Path {
181-
absolute_path
182-
.strip_prefix(&self.base_path)
183-
.expect("Could not generate relative path")
181+
absolute_path.strip_prefix(&self.base_path).unwrap_or(absolute_path)
184182
}
185183

186184
pub fn get_team(&self, name: &str) -> Option<Team> {
@@ -197,3 +195,29 @@ impl Project {
197195
result
198196
}
199197
}
198+
199+
#[cfg(test)]
200+
mod tests {
201+
use super::*;
202+
203+
#[test]
204+
fn test_vendored_gem_by_name_maps_all_gems() {
205+
let vg1 = VendoredGem { path: PathBuf::from("vendored/a"), name: "a".to_string() };
206+
let vg2 = VendoredGem { path: PathBuf::from("vendored/b"), name: "b".to_string() };
207+
let project = Project {
208+
base_path: PathBuf::from("."),
209+
files: vec![],
210+
packages: vec![],
211+
vendored_gems: vec![vg1.clone(), vg2.clone()],
212+
teams: vec![],
213+
codeowners_file_path: PathBuf::from(".github/CODEOWNERS"),
214+
directory_codeowner_files: vec![],
215+
teams_by_name: HashMap::new(),
216+
};
217+
218+
let map = project.vendored_gem_by_name();
219+
assert_eq!(map.len(), 2);
220+
assert_eq!(map.get("a").unwrap().name, vg1.name);
221+
assert_eq!(map.get("b").unwrap().name, vg2.name);
222+
}
223+
}

src/runner.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ pub fn team_for_file_from_codeowners(run_config: &RunConfig, file_path: &str) ->
8787
pub fn team_for_file(run_config: &RunConfig, file_path: &str) -> Result<Option<Team>, Error> {
8888
let config = config_from_path(&run_config.config_path)?;
8989
use crate::ownership::for_file_fast::find_file_owners;
90-
let owners = find_file_owners(&run_config.project_root, &config, std::path::Path::new(file_path))
91-
.map_err(Error::Io)?;
90+
let owners = find_file_owners(&run_config.project_root, &config, std::path::Path::new(file_path)).map_err(Error::Io)?;
9291

9392
Ok(owners.first().map(|fo| fo.team.clone()))
9493
}

0 commit comments

Comments
 (0)