Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
16 changes: 10 additions & 6 deletions src/cli.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -39,19 +39,23 @@ enum Command {
visible_alias = "v"
)]
Validate {
#[arg(help = "Optional list of files to validate ownership for (fast mode for git hooks). Paths are \
resolved relative to the project root; ones that no longer exist are skipped, so a \
changeset that deletes files is not reported as unowned.")]
#[arg(help = "Optional list of files to validate ownership for (for git hooks). Checks ownership of \
just these files, and of packages containing them. Paths are resolved relative to the \
project root, and ones that no longer exist are skipped, so a changeset that deletes \
files is not reported as unowned. Does NOT check whether the CODEOWNERS file itself is \
up to date -- that is a property of the whole file. Run without files, or use \
generate-and-validate, to catch a stale CODEOWNERS.")]
files: Vec<String>,
},

#[clap(about = "Chains both `generate` and `validate` commands.", visible_alias = "gv")]
GenerateAndValidate {
#[arg(long, short, default_value = "false", help = "Skip staging the CODEOWNERS file")]
skip_stage: bool,
#[arg(help = "Optional list of files to validate ownership for (fast mode for git hooks). Paths are \
resolved relative to the project root; ones that no longer exist are skipped, so a \
changeset that deletes files is not reported as unowned.")]
#[arg(help = "Optional list of files to validate ownership for (for git hooks). Checks ownership of \
just these files, and of packages containing them. Paths are resolved relative to the \
project root, and ones that no longer exist are skipped. Staleness is covered \
regardless, since the CODEOWNERS file is regenerated first.")]
files: Vec<String>,
},

Expand Down
26 changes: 23 additions & 3 deletions src/ownership.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@ use mapper::{OwnerMatcher, Source, TeamName};
use std::{
error::Error,
fmt::{self, Display},
path::Path,
path::{Path, PathBuf},
sync::Arc,
};
use tracing::{info, instrument};
Expand DownExpand Up@@ -122,11 +122,31 @@ impl Ownership {
let validator = Validator {
project: self.project.clone(),
mappers: self.mappers(),
file_generator: FileGenerator { mappers: self.mappers() },
executable_name: self.project.executable_name.clone(),
};

validator.validate()
// A second set of mappers, because FileGenerator owns rather than borrows them
// and `Box<dyn Mapper>` is not Clone. Construction is trivial (each `build` just
// stores an Arc); the O(repo) work happens in `owner_matchers`/`entries`.
let file_generator = FileGenerator { mappers: self.mappers() };

validator.validate(&file_generator)
}

/// Like [`Ownership::validate`], but restricted to the supplied project-relative
/// paths. Skips the staleness check, which cannot be scoped — see
/// [`Validator::validate_files`], which also documents the two path lists. Builds no
/// `FileGenerator`, since nothing here generates.
#[instrument(name = "ownership_validate_files", level = "debug", skip_all)]
pub fn validate_files(&self, owned_paths: &[PathBuf], supplied_paths: &[PathBuf]) -> Result<(), ValidatorErrors> {
info!("validating file ownership for {} supplied paths", supplied_paths.len());
let validator = Validator {
project: self.project.clone(),
mappers: self.mappers(),
executable_name: self.project.executable_name.clone(),
};

validator.validate_files(owned_paths, supplied_paths)
}

#[instrument(level = "debug", skip_all)]
Expand Down
Loading
Loading