Skip to content

Commit 68a5e19

Browse files
authored
Support executable_name in config and API (#86)
* Add executable_name field to Config with default value * Include executable name in validator errors and messages * Add support for custom executable_name in config * Bump codeowners version to 0.3.1 * Add executable_path to RunConfig and use it in config loading * Simplify executable_path handling by using String directly * Rename executable_path to executable_name throughout code and tests
1 parent 4de6104 commit 68a5e19

File tree

28 files changed

+318
-24
lines changed

28 files changed

+318
-24
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.3.0"
3+
version = "0.3.1"
44
edition = "2024"
55

66
[profile.release]

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,15 @@ codeowners gv --no-cache
213213
- `vendored_gems_path` (default: `'vendored/'`)
214214
- `cache_directory` (default: `'tmp/cache/codeowners'`)
215215
- `ignore_dirs` (default includes: `.git`, `node_modules`, `tmp`, etc.)
216+
- `executable_name` (default: `'codeowners'`): Customize the command name shown in validation error messages. Useful when using `codeowners-rs` via wrappers like the [code_ownership](https://github.com/rubyatscale/code_ownership) Ruby gem.
217+
218+
Example configuration with custom executable name:
219+
220+
```yaml
221+
owned_globs:
222+
- '{app,components,config,frontend,lib,packs,spec}/**/*.{rb,rake,js,jsx,ts,tsx}'
223+
executable_name: 'bin/codeownership' # For Ruby gem wrapper
224+
```
216225

217226
See examples in `tests/fixtures/**/config/` for reference setups.
218227

src/cli.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub fn cli() -> Result<RunResult, RunnerError> {
114114
codeowners_file_path,
115115
project_root,
116116
no_cache: args.no_cache,
117+
executable_name: None,
117118
};
118119

119120
let runner_result = match args.command {

src/config.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ pub struct Config {
2525

2626
#[serde(default = "default_ignore_dirs")]
2727
pub ignore_dirs: Vec<String>,
28+
29+
#[serde(default = "default_executable_name")]
30+
pub executable_name: String,
2831
}
2932

3033
#[allow(dead_code)]
@@ -61,6 +64,10 @@ fn vendored_gems_path() -> String {
6164
"vendored/".to_string()
6265
}
6366

67+
fn default_executable_name() -> String {
68+
"codeowners".to_string()
69+
}
70+
6471
fn default_ignore_dirs() -> Vec<String> {
6572
vec![
6673
".git".to_owned(),
@@ -120,6 +127,40 @@ mod tests {
120127
vec!["frontend/**/node_modules/**/*", "frontend/**/__generated__/**/*"]
121128
);
122129
assert_eq!(config.vendored_gems_path, "vendored/");
130+
assert_eq!(config.executable_name, "codeowners");
131+
Ok(())
132+
}
133+
134+
#[test]
135+
fn test_parse_config_with_custom_executable_name() -> Result<(), Box<dyn Error>> {
136+
let temp_dir = tempdir()?;
137+
let config_path = temp_dir.path().join("config.yml");
138+
let config_str = indoc! {"
139+
---
140+
owned_globs:
141+
- \"**/*.rb\"
142+
executable_name: my-custom-codeowners
143+
"};
144+
fs::write(&config_path, config_str)?;
145+
let config_file = File::open(&config_path)?;
146+
let config: Config = serde_yaml::from_reader(config_file)?;
147+
assert_eq!(config.executable_name, "my-custom-codeowners");
148+
Ok(())
149+
}
150+
151+
#[test]
152+
fn test_executable_name_defaults_when_not_specified() -> Result<(), Box<dyn Error>> {
153+
let temp_dir = tempdir()?;
154+
let config_path = temp_dir.path().join("config.yml");
155+
let config_str = indoc! {"
156+
---
157+
owned_globs:
158+
- \"**/*.rb\"
159+
"};
160+
fs::write(&config_path, config_str)?;
161+
let config_file = File::open(&config_path)?;
162+
let config: Config = serde_yaml::from_reader(config_file)?;
163+
assert_eq!(config.executable_name, "codeowners");
123164
Ok(())
124165
}
125166
}

src/crosscheck.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
ownership::file_owner_resolver::find_file_owners,
77
project::Project,
88
project_builder::ProjectBuilder,
9-
runner::{RunConfig, RunResult, config_from_path, team_for_file_from_codeowners},
9+
runner::{RunConfig, RunResult, config_from_run_config, team_for_file_from_codeowners},
1010
};
1111

1212
pub fn crosscheck_owners(run_config: &RunConfig, cache: &Cache) -> RunResult {
@@ -43,7 +43,7 @@ fn do_crosscheck_owners(run_config: &RunConfig, cache: &Cache) -> Result<Vec<Str
4343
}
4444

4545
fn load_config(run_config: &RunConfig) -> Result<Config, String> {
46-
config_from_path(&run_config.config_path).map_err(|e| e.to_string())
46+
config_from_run_config(run_config).map_err(|e| e.to_string())
4747
}
4848

4949
fn build_project(config: &Config, run_config: &RunConfig, cache: &Cache) -> Result<Project, String> {

src/ownership.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ impl Ownership {
123123
project: self.project.clone(),
124124
mappers: self.mappers(),
125125
file_generator: FileGenerator { mappers: self.mappers() },
126+
executable_name: self.project.executable_name.clone(),
126127
};
127128

128129
validator.validate()

src/ownership/file_owner_resolver.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ mod tests {
293293
vendored_gems_path: vendored_path.to_string(),
294294
cache_directory: "tmp/cache/codeowners".to_string(),
295295
ignore_dirs: vec![],
296+
executable_name: "codeowners".to_string(),
296297
}
297298
}
298299

src/ownership/validator.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ pub struct Validator {
2121
pub project: Arc<Project>,
2222
pub mappers: Vec<Box<dyn Mapper>>,
2323
pub file_generator: FileGenerator,
24+
pub executable_name: String,
2425
}
2526

2627
#[derive(Debug)]
2728
enum Error {
2829
InvalidTeam { name: String, path: PathBuf },
2930
FileWithoutOwner { path: PathBuf },
3031
FileWithMultipleOwners { path: PathBuf, owners: Vec<Owner> },
31-
CodeownershipFileIsStale,
32+
CodeownershipFileIsStale { executable_name: String },
3233
}
3334

3435
#[derive(Debug)]
@@ -130,12 +131,16 @@ impl Validator {
130131
match self.project.get_codeowners_file() {
131132
Ok(current_file) => {
132133
if generated_file != current_file {
133-
vec![Error::CodeownershipFileIsStale]
134+
vec![Error::CodeownershipFileIsStale {
135+
executable_name: self.executable_name.to_string(),
136+
}]
134137
} else {
135138
vec![]
136139
}
137140
}
138-
Err(_) => vec![Error::CodeownershipFileIsStale], // Treat any read error as stale file
141+
Err(_) => vec![Error::CodeownershipFileIsStale {
142+
executable_name: self.executable_name.to_string(),
143+
}],
139144
}
140145
}
141146

@@ -161,13 +166,13 @@ impl Validator {
161166
impl Error {
162167
pub fn category(&self) -> String {
163168
match self {
164-
Error::FileWithoutOwner { path: _ } => "Some files are missing ownership".to_owned(),
165-
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(),
166-
Error::CodeownershipFileIsStale => {
167-
"CODEOWNERS out of date. Run `codeowners generate` to update the CODEOWNERS file".to_owned()
169+
Error::FileWithoutOwner { path: _ } => "Some files are missing ownership".to_owned(),
170+
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(),
171+
Error::CodeownershipFileIsStale { executable_name } => {
172+
format!("CODEOWNERS out of date. Run `{} generate` to update the CODEOWNERS file", executable_name)
173+
}
174+
Error::InvalidTeam { name: _, path: _ } => "Found invalid team annotations".to_owned(),
168175
}
169-
Error::InvalidTeam { name: _, path: _ } => "Found invalid team annotations".to_owned(),
170-
}
171176
}
172177

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

188193
vec![messages.join("\n")]
189194
}
190-
Error::CodeownershipFileIsStale => vec![],
195+
Error::CodeownershipFileIsStale { executable_name: _ } => vec![],
191196
Error::InvalidTeam { name, path } => vec![format!("- {} is referencing an invalid team - '{}'", path.to_string_lossy(), name)],
192197
}
193198
}

src/project.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub struct Project {
1717
pub codeowners_file_path: PathBuf,
1818
pub directory_codeowner_files: Vec<DirectoryCodeownersFile>,
1919
pub teams_by_name: HashMap<String, Team>,
20+
pub executable_name: String,
2021
}
2122

2223
#[derive(Clone, Debug)]
@@ -219,6 +220,7 @@ mod tests {
219220
codeowners_file_path: PathBuf::from(".github/CODEOWNERS"),
220221
directory_codeowner_files: vec![],
221222
teams_by_name: HashMap::new(),
223+
executable_name: "codeowners".to_string(),
222224
};
223225

224226
let map = project.vendored_gem_by_name();

0 commit comments

Comments
 (0)