]> Dogcows Code - chaz/git-codeowners/blob - lib/App/Codeowners.pm
20be9855fa51d7917a683fb6ce8c0321f7c6ff87
[chaz/git-codeowners] / lib / App / Codeowners.pm
1 package App::Codeowners;
2 # ABSTRACT: A tool for managing CODEOWNERS files
3
4 use v5.10.1; # defined-or
5 use utf8;
6 use warnings;
7 use strict;
8
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);
14 use File::Codeowners;
15 use Path::Tiny;
16
17 our $VERSION = '9999.999'; # VERSION
18
19 =method main
20
21 App::Codeowners->main(@ARGV);
22
23 Run the script and exit; does not return.
24
25 =cut
26
27 sub main {
28 my $class = shift;
29 my $self = bless {}, $class;
30
31 my $opts = App::Codeowners::Options->new(@_);
32
33 my $color = $opts->{color};
34 local $ENV{NO_COLOR} = 1 if defined $color && !$color;
35
36 my $command = $opts->command;
37 my $handler = $self->can("_command_$command")
38 or die "Unknown command: $command\n";
39 $self->$handler($opts);
40
41 exit 0;
42 }
43
44 sub _command_show {
45 my $self = shift;
46 my $opts = shift;
47
48 my $toplevel = git_toplevel('.') or die "Not a git repo\n";
49
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);
53
54 my ($proc, $cdup) = run_git(qw{rev-parse --show-cdup});
55 $proc->wait and exit 1;
56
57 my $formatter = App::Codeowners::Formatter->new(
58 format => $opts->{format} || ' * %-50F %O',
59 handle => *STDOUT,
60 columns => [qw(File Owner), $opts->{project} ? 'Project' : ()],
61 );
62
63 $proc = git_ls_files('.', $opts->args);
64 while (my $filepath = $proc->next) {
65 my $match = $codeowners->match(path($filepath)->relative($cdup));
66 $formatter->add_result([
67 $filepath,
68 $match->{owners},
69 $opts->{project} ? $match->{project} : (),
70 ]);
71 }
72 $proc->wait and exit 1;
73 }
74
75 sub _command_owners {
76 my $self = shift;
77 my $opts = shift;
78
79 my $toplevel = git_toplevel('.') or die "Not a git repo\n";
80
81 my $codeowners_path = find_codeowners_in_directory($toplevel)
82 or die "No CODEOWNERS file in $toplevel\n";
83 my $codeowners = File::Codeowners->parse_from_filepath($codeowners_path);
84
85 my $results = $codeowners->owners($opts->{pattern});
86
87 my $formatter = App::Codeowners::Formatter->new(
88 format => $opts->{format} || '%O',
89 handle => *STDOUT,
90 columns => [qw(Owner)],
91 );
92 $formatter->add_result(map { [$_] } @$results);
93 }
94
95 sub _command_patterns {
96 my $self = shift;
97 my $opts = shift;
98
99 my $toplevel = git_toplevel('.') or die "Not a git repo\n";
100
101 my $codeowners_path = find_codeowners_in_directory($toplevel)
102 or die "No CODEOWNERS file in $toplevel\n";
103 my $codeowners = File::Codeowners->parse_from_filepath($codeowners_path);
104
105 my $results = $codeowners->patterns($opts->{owner});
106
107 my $formatter = App::Codeowners::Formatter->new(
108 format => $opts->{format} || '%T',
109 handle => *STDOUT,
110 columns => [qw(Pattern)],
111 );
112 $formatter->add_result(map { [$_] } @$results);
113 }
114
115 sub _command_create { goto &_command_update }
116 sub _command_update {
117 my $self = shift;
118 my $opts = shift;
119
120 my ($filepath) = $opts->args;
121
122 my $path = path($filepath || '.');
123 my $repopath;
124
125 die "Does not exist: $path\n" if !$path->parent->exists;
126
127 if ($path->is_dir) {
128 $repopath = $path;
129 $path = find_codeowners_in_directory($path) || $repopath->child('CODEOWNERS');
130 }
131
132 my $is_new = !$path->is_file;
133
134 my $codeowners;
135 if ($is_new) {
136 $codeowners = File::Codeowners->new;
137 my $template = <<'END';
138 This file shows mappings between subdirs/files and the individuals and
139 teams who own them. You can read this file yourself or use tools to query it,
140 so you can quickly determine who to speak with or send pull requests to. ❤️
141
142 Simply write a gitignore pattern followed by one or more names/emails/groups.
143 Examples:
144 /project_a/** @team1
145 *.js @harry @javascript-cabal
146 END
147 for my $line (split(/\n/, $template)) {
148 $codeowners->append(comment => $line);
149 }
150 }
151 else {
152 $codeowners = File::Codeowners->parse_from_filepath($path);
153 }
154
155 if ($repopath) {
156 # if there is a repo we can try to update the list of unowned files
157 my $git_files = git_ls_files($repopath);
158 if (@$git_files) {
159 $codeowners->clear_unowned;
160 $codeowners->add_unowned(grep { !$codeowners->match($_) } @$git_files);
161 }
162 }
163
164 $codeowners->write_to_filepath($path);
165 print STDERR "Wrote $path\n";
166 }
167
168 1;
This page took 0.039314 seconds and 3 git commands to generate.