Skip to content

Commit 803d798

Browse files
authored
Formatting improvements to for_file and validate (#42)
* better formatting for multiple ownership validation * better for file formatting * version 0.2.1 * handling unowned description
1 parent d14ee3d commit 803d798

File tree

7 files changed

+97
-35
lines changed

7 files changed

+97
-35
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.0"
3+
version = "0.2.1"
44
edition = "2021"
55

66
[profile.release]

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ fn cli() -> Result<(), Error> {
133133
_ => {
134134
println!("Error: file is owned by multiple teams!");
135135
for file_owner in file_owners {
136-
println!("\n{}\n", file_owner);
136+
println!("\n{}", file_owner);
137137
}
138138
}
139139
}

src/ownership.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use file_owner_finder::FileOwnerFinder;
2+
use itertools::Itertools;
23
use mapper::{OwnerMatcher, Source, TeamName};
34
use std::{
45
error::Error,
@@ -50,15 +51,20 @@ impl TeamOwnership {
5051

5152
impl Display for FileOwner {
5253
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53-
let sources = self
54-
.sources
55-
.iter()
56-
.map(|source| source.to_string())
57-
.collect::<Vec<String>>()
58-
.join(", ");
54+
let sources = if self.sources.is_empty() {
55+
"Unowned".to_string()
56+
} else {
57+
self.sources
58+
.iter()
59+
.sorted_by_key(|source| source.to_string())
60+
.map(|source| source.to_string())
61+
.collect::<Vec<_>>()
62+
.join("\n- ")
63+
};
64+
5965
write!(
6066
f,
61-
"Team: {}\nTeam YML: {}\nDescription: {}",
67+
"Team: {}\nTeam YML: {}\nDescription:\n- {}",
6268
self.team_name, self.team_config_file_path, sources
6369
)
6470
}
@@ -123,6 +129,7 @@ impl Ownership {
123129
let owners = file_owner_finder.find(Path::new(file_path));
124130
Ok(owners
125131
.iter()
132+
.sorted_by_key(|owner| owner.team_name.to_lowercase())
126133
.map(|owner| match self.project.get_team(&owner.team_name) {
127134
Some(team) => FileOwner {
128135
team_name: owner.team_name.clone(),

src/ownership/validator.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -173,16 +173,20 @@ impl Error {
173173
pub fn messages(&self) -> Vec<String> {
174174
match self {
175175
Error::FileWithoutOwner { path } => vec![format!("- {}", path.to_string_lossy())],
176-
Error::FileWithMultipleOwners { path, owners } => owners
177-
.iter()
178-
.flat_map(|owner| {
179-
owner
180-
.sources
181-
.iter()
182-
.map(|source| format!("- {} (owner: {}, source: {})", path.to_string_lossy(), owner.team_name, &source))
183-
.collect_vec()
184-
})
185-
.collect_vec(),
176+
Error::FileWithMultipleOwners { path, owners } => {
177+
let path_display = path.to_string_lossy();
178+
let mut messages = vec![format!("\n{path_display}")];
179+
180+
owners
181+
.iter()
182+
.sorted_by_key(|owner| owner.team_name.to_lowercase())
183+
.for_each(|owner| {
184+
messages.push(format!(" owner: {}", owner.team_name));
185+
messages.extend(owner.sources.iter().map(|source| format!(" - {source}")));
186+
});
187+
188+
vec![messages.join("\n")]
189+
}
186190
Error::CodeownershipFileIsStale => vec![],
187191
Error::InvalidTeam { name, path } => vec![format!("- {} is referencing an invalid team - '{}'", path.to_string_lossy(), name)],
188192
}

tests/invalid_project_test.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,18 @@ fn test_validate() -> Result<(), Box<dyn Error>> {
1616
CODEOWNERS out of date. Run `codeowners generate` to update the CODEOWNERS file
1717
1818
Code ownership should only be defined for each file in one way. The following files have declared ownership in multiple ways
19-
- gems/payroll_calculator/calculator.rb (owner: Payments, source: Owner annotation at the top of the file)
20-
- gems/payroll_calculator/calculator.rb (owner: Payroll, source: Owner specified in Team YML's `owned_gems`)
21-
- ruby/app/services/multi_owned.rb (owner: Payments, source: Owner annotation at the top of the file)
22-
- ruby/app/services/multi_owned.rb (owner: Payroll, source: Owner specified in `ruby/app/services/.codeowner`)
19+
20+
gems/payroll_calculator/calculator.rb
21+
owner: Payments
22+
- Owner annotation at the top of the file
23+
owner: Payroll
24+
- Owner specified in Team YML's `owned_gems`
25+
26+
ruby/app/services/multi_owned.rb
27+
owner: Payments
28+
- Owner annotation at the top of the file
29+
owner: Payroll
30+
- Owner specified in `ruby/app/services/.codeowner`
2331
2432
Found invalid team annotations
2533
- ruby/app/models/blockchain.rb is referencing an invalid team - 'Web3'
@@ -44,7 +52,9 @@ fn test_for_file() -> Result<(), Box<dyn Error>> {
4452
.stdout(predicate::eq(indoc! {"
4553
Team: Unowned
4654
Team YML: Unowned
47-
Description: \n"})); // trailing whitespace
55+
Description:
56+
- Unowned
57+
"}));
4858
Ok(())
4959
}
5060

@@ -57,17 +67,18 @@ fn test_for_file_multiple_owners() -> Result<(), Box<dyn Error>> {
5767
.arg("ruby/app/services/multi_owned.rb")
5868
.assert()
5969
.success()
60-
.stdout(predicate::str::starts_with("Error: file is owned by multiple teams!"))
61-
// order not static
62-
.stdout(predicate::str::contains(indoc! {"
63-
Team: Payroll
64-
Team YML: config/teams/payroll.yml
65-
Description: Owner specified in `ruby/app/services/.codeowner`
66-
"}))
67-
.stdout(predicate::str::contains(indoc! {"
70+
.stdout(predicate::eq(indoc! {"
71+
Error: file is owned by multiple teams!
72+
6873
Team: Payments
6974
Team YML: config/teams/payments.yml
70-
Description: Owner annotation at the top of the file
75+
Description:
76+
- Owner annotation at the top of the file
77+
78+
Team: Payroll
79+
Team YML: config/teams/payroll.yml
80+
Description:
81+
- Owner specified in `ruby/app/services/.codeowner`
7182
"}));
7283
Ok(())
7384
}

tests/valid_project_test.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,51 @@ fn test_for_file() -> Result<(), Box<dyn Error>> {
4646
.stdout(predicate::eq(indoc! {"
4747
Team: Payroll
4848
Team YML: config/teams/payroll.yml
49-
Description: Owner annotation at the top of the file
49+
Description:
50+
- Owner annotation at the top of the file
5051
"}));
5152
Ok(())
5253
}
5354

55+
#[test]
56+
fn test_for_file_same_team_multiple_ownerships() -> Result<(), Box<dyn Error>> {
57+
Command::cargo_bin("codeowners")?
58+
.arg("--project-root")
59+
.arg("tests/fixtures/valid_project")
60+
.arg("for-file")
61+
.arg("javascript/packages/PayrollFlow/index.tsx")
62+
.assert()
63+
.success()
64+
.stdout(predicate::eq(indoc! {"
65+
Team: Payroll
66+
Team YML: config/teams/payroll.yml
67+
Description:
68+
- Owner annotation at the top of the file
69+
- Owner defined in `javascript/packages/PayrollFlow/package.json` with implicity owned glob: `javascript/packages/PayrollFlow/**/**`
70+
"}));
71+
Ok(())
72+
}
73+
74+
#[test]
75+
fn test_for_file_with_2_ownerships() -> Result<(), Box<dyn Error>> {
76+
Command::cargo_bin("codeowners")?
77+
.arg("--project-root")
78+
.arg("tests/fixtures/valid_project")
79+
.arg("for-file")
80+
.arg("javascript/packages/PayrollFlow/index.tsx")
81+
.assert()
82+
.success()
83+
.stdout(predicate::eq(indoc! {"
84+
Team: Payroll
85+
Team YML: config/teams/payroll.yml
86+
Description:
87+
- Owner annotation at the top of the file
88+
- Owner defined in `javascript/packages/PayrollFlow/package.json` with implicity owned glob: `javascript/packages/PayrollFlow/**/**`
89+
"}));
90+
91+
Ok(())
92+
}
93+
5494
#[test]
5595
fn test_for_team() -> Result<(), Box<dyn Error>> {
5696
Command::cargo_bin("codeowners")?

0 commit comments

Comments
 (0)