Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "codeowners"
version = "0.3.0"
version = "0.3.1"
edition = "2024"

[profile.release]
Expand Down
41 changes: 41 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub struct Config {

#[serde(default = "default_ignore_dirs")]
pub ignore_dirs: Vec<String>,

#[serde(default = "default_executable_name")]
pub executable_name: String,
}

#[allow(dead_code)]
Expand Down Expand Up @@ -61,6 +64,10 @@ fn vendored_gems_path() -> String {
"vendored/".to_string()
}

fn default_executable_name() -> String {
"codeowners".to_string()
}

fn default_ignore_dirs() -> Vec<String> {
vec![
".cursor".to_owned(),
Expand Down Expand Up @@ -121,6 +128,40 @@ mod tests {
vec!["frontend/**/node_modules/**/*", "frontend/**/__generated__/**/*"]
);
assert_eq!(config.vendored_gems_path, "vendored/");
assert_eq!(config.executable_name, "codeowners");
Ok(())
}

#[test]
fn test_parse_config_with_custom_executable_name() -> Result<(), Box<dyn Error>> {
let temp_dir = tempdir()?;
let config_path = temp_dir.path().join("config.yml");
let config_str = indoc! {"
---
owned_globs:
- \"**/*.rb\"
executable_name: my-custom-codeowners
"};
fs::write(&config_path, config_str)?;
let config_file = File::open(&config_path)?;
let config: Config = serde_yaml::from_reader(config_file)?;
assert_eq!(config.executable_name, "my-custom-codeowners");
Ok(())
}

#[test]
fn test_executable_name_defaults_when_not_specified() -> Result<(), Box<dyn Error>> {
let temp_dir = tempdir()?;
let config_path = temp_dir.path().join("config.yml");
let config_str = indoc! {"
---
owned_globs:
- \"**/*.rb\"
"};
fs::write(&config_path, config_str)?;
let config_file = File::open(&config_path)?;
let config: Config = serde_yaml::from_reader(config_file)?;
assert_eq!(config.executable_name, "codeowners");
Ok(())
}
}
1 change: 1 addition & 0 deletions src/ownership.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ impl Ownership {
project: self.project.clone(),
mappers: self.mappers(),
file_generator: FileGenerator { mappers: self.mappers() },
executable_name: self.project.executable_name.clone(),
};

validator.validate()
Expand Down
1 change: 1 addition & 0 deletions src/ownership/file_owner_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ mod tests {
vendored_gems_path: vendored_path.to_string(),
cache_directory: "tmp/cache/codeowners".to_string(),
ignore_dirs: vec![],
executable_name: "codeowners".to_string(),
}
}

Expand Down
25 changes: 15 additions & 10 deletions src/ownership/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ pub struct Validator {
pub project: Arc<Project>,
pub mappers: Vec<Box<dyn Mapper>>,
pub file_generator: FileGenerator,
pub executable_name: String,
}

#[derive(Debug)]
enum Error {
InvalidTeam { name: String, path: PathBuf },
FileWithoutOwner { path: PathBuf },
FileWithMultipleOwners { path: PathBuf, owners: Vec<Owner> },
CodeownershipFileIsStale,
CodeownershipFileIsStale { executable_name: String },
}

#[derive(Debug)]
Expand Down Expand Up @@ -130,12 +131,16 @@ impl Validator {
match self.project.get_codeowners_file() {
Ok(current_file) => {
if generated_file != current_file {
vec![Error::CodeownershipFileIsStale]
vec![Error::CodeownershipFileIsStale {
executable_name: self.executable_name.to_string(),
}]
} else {
vec![]
}
}
Err(_) => vec![Error::CodeownershipFileIsStale], // Treat any read error as stale file
Err(_) => vec![Error::CodeownershipFileIsStale {
executable_name: self.executable_name.to_string(),
}],
}
}

Expand All @@ -161,13 +166,13 @@ impl Validator {
impl Error {
pub fn category(&self) -> String {
match self {
Error::FileWithoutOwner { path: _ } => "Some files are missing ownership".to_owned(),
Error::FileWithMultipleOwners { path: _, owners: _ } => "Code ownership should only be defined for each file in one way. The following files have declared ownership in multiple ways".to_owned(),
Error::CodeownershipFileIsStale => {
"CODEOWNERS out of date. Run `codeowners generate` to update the CODEOWNERS file".to_owned()
Error::FileWithoutOwner { path: _ } => "Some files are missing ownership".to_owned(),
Error::FileWithMultipleOwners { path: _, owners: _ } => "Code ownership should only be defined for each file in one way. The following files have declared ownership in multiple ways".to_owned(),
Error::CodeownershipFileIsStale { executable_name } => {
format!("CODEOWNERS out of date. Run `{} generate` to update the CODEOWNERS file", executable_name)
}
Error::InvalidTeam { name: _, path: _ } => "Found invalid team annotations".to_owned(),
}
Error::InvalidTeam { name: _, path: _ } => "Found invalid team annotations".to_owned(),
}
}

pub fn messages(&self) -> Vec<String> {
Expand All @@ -187,7 +192,7 @@ impl Error {

vec![messages.join("\n")]
}
Error::CodeownershipFileIsStale => vec![],
Error::CodeownershipFileIsStale { executable_name: _ } => vec![],
Error::InvalidTeam { name, path } => vec![format!("- {} is referencing an invalid team - '{}'", path.to_string_lossy(), name)],
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Project {
pub codeowners_file_path: PathBuf,
pub directory_codeowner_files: Vec<DirectoryCodeownersFile>,
pub teams_by_name: HashMap<String, Team>,
pub executable_name: String,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -219,6 +220,7 @@ mod tests {
codeowners_file_path: PathBuf::from(".github/CODEOWNERS"),
directory_codeowner_files: vec![],
teams_by_name: HashMap::new(),
executable_name: "codeowners".to_string(),
};

let map = project.vendored_gem_by_name();
Expand Down
1 change: 1 addition & 0 deletions src/project_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ impl<'a> ProjectBuilder<'a> {
codeowners_file_path: self.codeowners_file_path.to_path_buf(),
directory_codeowner_files: directory_codeowners,
teams_by_name,
executable_name: self.config.executable_name.clone(),
})
}
}
Expand Down
32 changes: 32 additions & 0 deletions tests/custom_executable_name_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use predicates::prelude::*;
use std::error::Error;

mod common;
use common::OutputStream;
use common::run_codeowners;

#[test]
fn test_validate_uses_custom_executable_name() -> Result<(), Box<dyn Error>> {
run_codeowners(
"custom_executable_name",
&["validate"],
false,
OutputStream::Stdout,
predicate::str::contains("CODEOWNERS out of date. Run `my-custom-tool generate` to update the CODEOWNERS file"),
)?;
Ok(())
}

#[test]
fn test_validate_default_executable_name() -> Result<(), Box<dyn Error>> {
// This tests the invalid_project which doesn't specify executable_name
// and should use the default "codeowners"
run_codeowners(
"invalid_project",
&["validate"],
false,
OutputStream::Stdout,
predicate::str::contains("CODEOWNERS out of date. Run `codeowners generate` to update the CODEOWNERS file"),
)?;
Ok(())
}
3 changes: 3 additions & 0 deletions tests/fixtures/custom_executable_name/.github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This is a stale CODEOWNERS file - it's intentionally wrong to trigger the error
ruby/app/payments/bar.rb @PaymentTeam

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
owned_globs:
- "**/*.rb"
team_file_glob:
- config/teams/**/*.yml
executable_name: my-custom-tool

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: Payments
github:
team: "@PaymentTeam"
owned_globs:
- ruby/app/payments/**

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# A file owned by Payments
class Foo
end