-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathcli.rs
More file actions
135 lines (115 loc) · 4.55 KB
/
cli.rs
File metadata and controls
135 lines (115 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use clap::{Parser, Subcommand};
use codeowners::runner::RunConfig;
use codeowners::runner::{self, Error as RunnerError, RunResult};
use error_stack::{Result, ResultExt};
use path_clean::PathClean;
use std::path::{Path, PathBuf};
#[derive(Subcommand, Debug)]
#[command(version)]
enum Command {
#[clap(about = "Finds the owner of a given file.", visible_alias = "f")]
ForFile {
#[arg(
short,
long,
default_value = "false",
help = "Find the owner from the CODEOWNERS file and just return the team name and yml path"
)]
from_codeowners: bool,
#[arg(short, long, default_value = "false", help = "Output the result in JSON format")]
json: bool,
name: String,
},
#[clap(about = "Finds code ownership information for a given team", visible_alias = "t")]
ForTeam { name: String },
#[clap(
about = "Generate the CODEOWNERS file and save it to '--codeowners-file-path'.",
visible_alias = "g"
)]
Generate {
#[arg(long, short, default_value = "false", help = "Skip staging the CODEOWNERS file")]
skip_stage: bool,
},
#[clap(
about = "Validate the validity of the CODEOWNERS file. A validation failure will exit with a failure code and a detailed output of the validation errors.",
visible_alias = "v"
)]
Validate {
#[arg(help = "Optional list of files to validate ownership for (fast mode for git hooks)")]
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)")]
files: Vec<String>,
},
#[clap(about = "Delete the cache file.", visible_alias = "d")]
DeleteCache,
#[clap(about = "Compare the CODEOWNERS file to the for-file command.", hide = true)]
CrosscheckOwners,
}
/// A CLI to validate and generate Github's CODEOWNERS file.
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[command(subcommand)]
command: Command,
/// Path for the CODEOWNERS file.
#[arg(long, default_value = "./.github/CODEOWNERS")]
codeowners_file_path: PathBuf,
/// Path for the configuration file
#[arg(long, default_value = "./config/code_ownership.yml")]
config_path: PathBuf,
/// Path for the root of the project
#[arg(long, default_value = ".")]
project_root: PathBuf,
/// Run without the cache (good for CI, testing)
#[arg(long)]
no_cache: bool,
}
impl Args {
fn absolute_project_root(&self) -> Result<PathBuf, RunnerError> {
self.project_root.canonicalize().change_context(RunnerError::Io(format!(
"Can't canonicalize project root: {}",
&self.project_root.to_string_lossy()
)))
}
fn absolute_config_path(&self) -> Result<PathBuf, RunnerError> {
Ok(self.absolute_path(&self.config_path)?.clean())
}
fn absolute_codeowners_path(&self) -> Result<PathBuf, RunnerError> {
Ok(self.absolute_path(&self.codeowners_file_path)?.clean())
}
fn absolute_path(&self, path: &Path) -> Result<PathBuf, RunnerError> {
Ok(self.absolute_project_root()?.join(path))
}
}
pub fn cli() -> Result<RunResult, RunnerError> {
let args = Args::parse();
let config_path = args.absolute_config_path()?;
let codeowners_file_path = args.absolute_codeowners_path()?;
let project_root = args.absolute_project_root()?;
let run_config = RunConfig {
config_path,
codeowners_file_path,
project_root,
no_cache: args.no_cache,
executable_name: None,
};
let runner_result = match args.command {
Command::Validate { files } => runner::validate(&run_config, files),
Command::Generate { skip_stage } => runner::generate(&run_config, !skip_stage),
Command::GenerateAndValidate { files, skip_stage } => runner::generate_and_validate(&run_config, files, !skip_stage),
Command::ForFile {
name,
from_codeowners,
json,
} => runner::for_file(&run_config, &name, from_codeowners, json),
Command::ForTeam { name } => runner::for_team(&run_config, &name),
Command::DeleteCache => runner::delete_cache(&run_config),
Command::CrosscheckOwners => runner::crosscheck_owners(&run_config),
};
Ok(runner_result)
}