1 package App
::Codeowners
;
2 # ABSTRACT: A tool for managing CODEOWNERS files
4 use v5
.10
.1; # defined-or
9 use App
::Codeowners
::Formatter
;
10 use App
::Codeowners
::Options
;
11 use App
::Codeowners
::Util
qw(find_codeowners_in_directory run_git git_ls_files git_toplevel);
12 use Color
::ANSI
::Util
0.03 qw(ansifg);
13 use Encode
qw(encode);
17 our $VERSION = '9999.999'; # VERSION
21 App
::Codeowners-
>main(@ARGV);
23 Run the script
and exit; does not return.
29 my $self = bless {}, $class;
31 my $opts = App
::Codeowners
::Options-
>new(@_);
33 my $color = $opts->{color
};
34 local $ENV{NO_COLOR
} = 1 if defined $color && !$color;
36 my $command = $opts->command;
37 my $handler = $self->can("_command_$command")
38 or die "Unknown command: $command\n";
39 $self->$handler($opts);
48 my $toplevel = git_toplevel
('.') or die "Not a git repo\n";
50 my $codeowners_path = find_codeowners_in_directory
($toplevel)
51 or die "No CODEOWNERS file in $toplevel\n";
52 my $codeowners = File
::Codeowners-
>parse_from_filepath($codeowners_path);
54 my ($proc, $cdup) = run_git
(qw{rev-parse --show-cdup});
55 $proc->wait and exit 1;
57 my $show_projects = $opts->{projects
} // scalar @{$codeowners->projects};
59 my $formatter = App
::Codeowners
::Formatter-
>new(
60 format
=> $opts->{format
} || ' * %-50F %O',
64 $opts->{patterns
} ? 'Pattern' : (),
66 $show_projects ? 'Project' : (),
70 my %filter_owners = map { $_ => 1 } @{$opts->{owner
}};
71 my %filter_projects = map { $_ => 1 } @{$opts->{project
}};
72 my %filter_patterns = map { $_ => 1 } @{$opts->{pattern
}};
74 $proc = git_ls_files
('.', $opts->args);
75 while (my $filepath = $proc->next) {
76 my $match = $codeowners->match(path
($filepath)->relative($cdup));
78 for my $owner (@{$match->{owners
}}) {
79 goto ADD_RESULT
if $filter_owners{$owner};
83 if (%filter_patterns) {
84 goto ADD_RESULT
if $filter_patterns{$match->{pattern
} || ''};
87 if (%filter_projects) {
88 goto ADD_RESULT
if $filter_projects{$match->{project
} || ''};
92 $formatter->add_result([
94 $opts->{patterns
} ? $match->{pattern
} : (),
96 $show_projects ? $match->{project
} : (),
99 $proc->wait and exit 1;
102 sub _command_owners
{
106 my $toplevel = git_toplevel
('.') or die "Not a git repo\n";
108 my $codeowners_path = find_codeowners_in_directory
($toplevel)
109 or die "No CODEOWNERS file in $toplevel\n";
110 my $codeowners = File
::Codeowners-
>parse_from_filepath($codeowners_path);
112 my $results = $codeowners->owners($opts->{pattern
});
114 my $formatter = App
::Codeowners
::Formatter-
>new(
115 format
=> $opts->{format
} || '%O',
117 columns
=> [qw(Owner)],
119 $formatter->add_result(map { [$_] } @$results);
122 sub _command_patterns
{
126 my $toplevel = git_toplevel
('.') or die "Not a git repo\n";
128 my $codeowners_path = find_codeowners_in_directory
($toplevel)
129 or die "No CODEOWNERS file in $toplevel\n";
130 my $codeowners = File
::Codeowners-
>parse_from_filepath($codeowners_path);
132 my $results = $codeowners->patterns($opts->{owner
});
134 my $formatter = App
::Codeowners
::Formatter-
>new(
135 format
=> $opts->{format
} || '%T',
137 columns
=> [qw(Pattern)],
139 $formatter->add_result(map { [$_] } @$results);
142 sub _command_projects
{
146 my $toplevel = git_toplevel
('.') or die "Not a git repo\n";
148 my $codeowners_path = find_codeowners_in_directory
($toplevel)
149 or die "No CODEOWNERS file in $toplevel\n";
150 my $codeowners = File
::Codeowners-
>parse_from_filepath($codeowners_path);
152 my $results = $codeowners->projects;
154 my $formatter = App
::Codeowners
::Formatter-
>new(
155 format
=> $opts->{format
} || '%P',
157 columns
=> [qw(Project)],
159 $formatter->add_result(map { [$_] } @$results);
162 sub _command_create
{ goto &_command_update
}
163 sub _command_update
{
167 my ($filepath) = $opts->args;
169 my $path = path
($filepath || '.');
172 die "Does not exist: $path\n" if !$path->parent->exists;
176 $path = find_codeowners_in_directory
($path) || $repopath->child('CODEOWNERS');
179 my $is_new = !$path->is_file;
183 $codeowners = File
::Codeowners-
>new;
184 my $template = <<'END';
185 This file shows mappings between subdirs/files and the individuals and
186 teams who own them. You can read this file yourself or use tools to query it,
187 so you can quickly determine who to speak with or send pull requests to. ❤️
189 Simply write a gitignore pattern followed by one or more names/emails/groups.
192 *.js @harry @javascript-cabal
194 for my $line (split(/\n/, $template)) {
195 $codeowners->append(comment
=> $line);
199 $codeowners = File
::Codeowners-
>parse_from_filepath($path);
203 # if there is a repo we can try to update the list of unowned files
204 my $git_files = git_ls_files
($repopath);
206 $codeowners->clear_unowned;
207 $codeowners->add_unowned(grep { !$codeowners->match($_) } @$git_files);
211 $codeowners->write_to_filepath($path);
212 print STDERR
"Wrote $path\n";