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);
16 our $VERSION = '9999.999'; # VERSION
20 App
::Codeowners-
>main(@ARGV);
22 Run the script
and exit; does not return.
28 my $self = bless {}, $class;
30 my $opts = App
::Codeowners
::Options-
>new(@_);
32 my $color = $opts->{color
};
33 local $ENV{NO_COLOR
} = 1 if defined $color && !$color;
35 my $command = $opts->command;
36 my $handler = $self->can("_command_$command")
37 or die "Unknown command: $command\n";
39 binmode(STDOUT
, ':encoding(UTF-8)');
40 $self->$handler($opts);
49 my $toplevel = git_toplevel
('.') or die "Not a git repo\n";
51 my $codeowners_path = find_codeowners_in_directory
($toplevel)
52 or die "No CODEOWNERS file in $toplevel\n";
53 my $codeowners = File
::Codeowners-
>parse_from_filepath($codeowners_path);
55 my ($proc, $cdup) = run_git
(qw{rev-parse --show-cdup});
56 $proc->wait and exit 1;
58 my $show_projects = $opts->{projects
} // scalar @{$codeowners->projects};
60 my $formatter = App
::Codeowners
::Formatter-
>new(
61 format
=> $opts->{format
} || ' * %-50F %O',
65 $opts->{patterns
} ? 'Pattern' : (),
67 $show_projects ? 'Project' : (),
71 my %filter_owners = map { $_ => 1 } @{$opts->{owner
}};
72 my %filter_projects = map { $_ => 1 } @{$opts->{project
}};
73 my %filter_patterns = map { $_ => 1 } @{$opts->{pattern
}};
75 $proc = git_ls_files
('.', $opts->args);
76 while (my $filepath = $proc->next) {
77 my $match = $codeowners->match(path
($filepath)->relative($cdup));
79 for my $owner (@{$match->{owners
}}) {
80 goto ADD_RESULT
if $filter_owners{$owner};
84 if (%filter_patterns) {
85 goto ADD_RESULT
if $filter_patterns{$match->{pattern
} || ''};
88 if (%filter_projects) {
89 goto ADD_RESULT
if $filter_projects{$match->{project
} || ''};
93 $formatter->add_result([
95 $opts->{patterns
} ? $match->{pattern
} : (),
97 $show_projects ? $match->{project
} : (),
100 $proc->wait and exit 1;
103 sub _command_owners
{
107 my $toplevel = git_toplevel
('.') or die "Not a git repo\n";
109 my $codeowners_path = find_codeowners_in_directory
($toplevel)
110 or die "No CODEOWNERS file in $toplevel\n";
111 my $codeowners = File
::Codeowners-
>parse_from_filepath($codeowners_path);
113 my $results = $codeowners->owners($opts->{pattern
});
115 my $formatter = App
::Codeowners
::Formatter-
>new(
116 format
=> $opts->{format
} || '%O',
118 columns
=> [qw(Owner)],
120 $formatter->add_result(map { [$_] } @$results);
123 sub _command_patterns
{
127 my $toplevel = git_toplevel
('.') or die "Not a git repo\n";
129 my $codeowners_path = find_codeowners_in_directory
($toplevel)
130 or die "No CODEOWNERS file in $toplevel\n";
131 my $codeowners = File
::Codeowners-
>parse_from_filepath($codeowners_path);
133 my $results = $codeowners->patterns($opts->{owner
});
135 my $formatter = App
::Codeowners
::Formatter-
>new(
136 format
=> $opts->{format
} || '%T',
138 columns
=> [qw(Pattern)],
140 $formatter->add_result(map { [$_] } @$results);
143 sub _command_projects
{
147 my $toplevel = git_toplevel
('.') or die "Not a git repo\n";
149 my $codeowners_path = find_codeowners_in_directory
($toplevel)
150 or die "No CODEOWNERS file in $toplevel\n";
151 my $codeowners = File
::Codeowners-
>parse_from_filepath($codeowners_path);
153 my $results = $codeowners->projects;
155 my $formatter = App
::Codeowners
::Formatter-
>new(
156 format
=> $opts->{format
} || '%P',
158 columns
=> [qw(Project)],
160 $formatter->add_result(map { [$_] } @$results);
163 sub _command_create
{ goto &_command_update
}
164 sub _command_update
{
168 my ($filepath) = $opts->args;
170 my $path = path
($filepath || '.');
173 die "Does not exist: $path\n" if !$path->parent->exists;
177 $path = find_codeowners_in_directory
($path) || $repopath->child('CODEOWNERS');
180 my $is_new = !$path->is_file;
184 $codeowners = File
::Codeowners-
>new;
185 my $template = <<'END';
186 This file shows mappings between subdirs/files and the individuals and
187 teams who own them. You can read this file yourself or use tools to query it,
188 so you can quickly determine who to speak with or send pull requests to.
190 Simply write a gitignore pattern followed by one or more names/emails/groups.
193 *.js @harry @javascript-cabal
195 for my $line (split(/\n/, $template)) {
196 $codeowners->append(comment
=> $line);
200 $codeowners = File
::Codeowners-
>parse_from_filepath($path);
204 # if there is a repo we can try to update the list of unowned files
205 my ($proc, @filepaths) = git_ls_files
($repopath);
206 $proc->wait and exit 1;
207 $codeowners->clear_unowned;
208 $codeowners->add_unowned(grep { !$codeowners->match($_) } @filepaths);
211 $codeowners->write_to_filepath($path);
212 print STDERR
"Wrote $path\n";
220 This is the implementation of the F<git-codeowners> command.
222 See L<git-codeowners> for documentation.