• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wickedOne / gitlab-perl-helpers / 9930703181

14 Jul 2024 09:03PM UTC coverage: 100.0%. Remained the same
9930703181

push

github

web-flow
regex fixes (#30)

- fixed regex bug which caused the script to step out of the teardown
  too early.

2 of 2 new or added lines in 1 file covered. (100.0%)

842 of 842 relevant lines covered (100.0%)

21.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/lib/GPH/Gitlab.pm
1
package GPH::Gitlab;
2

3
use strict;
2✔
4
use warnings FATAL => 'all';
2✔
5

6
sub new {
7
    my ($class, %args) = @_;
17✔
8

9
    (exists($args{owner}) and exists($args{codeowners})) or die "$!";
17✔
10

11
    my $self = {
12
        owner      => $args{owner},
13
        file       => $args{codeowners},
14
        codeowners => undef,
15✔
15
        blacklist  => undef,
16
    };
17

18
    bless $self, $class;
15✔
19

20
    return $self->parseCodeowners(%args);
15✔
21
}
22

23
sub parseCodeowners {
24
    my ($self, %args) = @_;
15✔
25
    my ($fh, %excludes, $default_owners);
15✔
26

27
    open $fh, '<', $args{codeowners} or die "unable to open codeowners file: $!";
15✔
28
    my @lines = <$fh>;
14✔
29
    close($fh);
14✔
30

31
    # build excludes hash for quick lookup
32
    if (exists($args{excludes})) {
14✔
33
        foreach my $item (@{$args{excludes}}) {
9✔
34
            $excludes{$item} = 1;
9✔
35
        }
36
    }
37

38
    foreach (@lines) {
14✔
39
        next if $_ =~ /^#.*/ or $_ =~ /^[\s]?$/;
224✔
40
        my $line = $self->sanitise($_);
168✔
41

42
        if ($line =~ /\]/) {
168✔
43
            $default_owners = ($line =~ /^[\^]?\[[^\]]+\](?:[\[0-9\]]{0,}) (.*)$/) ? $1 : undef;
56✔
44

45
            next;
56✔
46
        }
47

48
        my ($class_path, $owners) = split(/\s/, $line, 2);
112✔
49

50
        next if exists $excludes{$class_path};
112✔
51

52
        $owners = $owners || $default_owners;
94✔
53

54
        next unless defined $owners;
94✔
55

56
        foreach my $owner (split(/\s/, $owners)) {
80✔
57
            next unless $owner =~ /@/;
99✔
58
            if (not exists $self->{codeowners}{$owner}) {
94✔
59
                $self->{codeowners}{$owner} = [];
56✔
60
                $self->{blacklist}{$owner} = [];
56✔
61
            }
62

63
            push(@{$self->{codeowners}{$owner}}, $class_path);
94✔
64

65
            $self->blacklist($class_path);
94✔
66
        }
67
    }
68

69
    return ($self);
14✔
70
}
71

72
sub blacklist {
73
    my ($self, $class_path) = @_;
94✔
74

75
    foreach my $owner (keys %{$self->{codeowners}}) {
94✔
76
        foreach my $path (@{$self->{codeowners}{$owner}}) {
221✔
77
            if ($class_path =~ $path and $class_path ne $path) {
369✔
78
                push(@{$self->{blacklist}{$owner}}, $class_path);
14✔
79
            }
80
        }
81
    }
82

83
    return ($self);
94✔
84
}
85

86
sub sanitise {
87
    my ($self, $line) = @_;
168✔
88

89
    my $pat = quotemeta('/**/* ');
168✔
90
    $line =~ s|$pat|/ |;
168✔
91

92
    return ($line);
168✔
93
}
94

95
sub getPaths {
96
    my $self = shift;
36✔
97

98
    return $self->{codeowners}->{$self->{owner}} || [];
36✔
99
}
100

101
sub getBlacklistPaths {
102
    my $self = shift;
23✔
103

104
    return $self->{blacklist}->{$self->{owner}} || [];
23✔
105
}
106

107
sub getCommaSeparatedPathList {
108
    my $self = shift;
1✔
109

110
    return join(",", @{$self->getPaths()});
1✔
111
}
112

113
sub intersectCommaSeparatedPathList {
114
    my ($self, @paths) = @_;
1✔
115

116
    return join(",", $self->intersect(@paths));
1✔
117
}
118

119
sub intersect {
120
    my ($self, @paths) = @_;
2✔
121
    my @diff;
2✔
122

123
    foreach my $path (@paths) {
2✔
124
        chomp $path;
6✔
125

126
        next unless $self->match($path);
6✔
127
        next if $self->matchBlacklist($path);
4✔
128

129
        push(@diff, $path);
2✔
130
    }
131

132
    return @diff;
2✔
133
}
134

135
sub match {
136
    my ($self, $path) = @_;
8✔
137

138
    foreach my $owner (@{$self->getPaths()}) {
8✔
139
        return 1 if $path =~ $owner;
12✔
140
    }
141

142
    return 0;
3✔
143
}
144

145
sub matchBlacklist {
146
    my ($self, $path) = @_;
6✔
147

148
    foreach my $owner (@{$self->getBlacklistPaths()}) {
6✔
149
        return 1 if $path =~ $owner;
6✔
150
    }
151

152
    return 0;
3✔
153
}
154

155
1;
156

157
__END__
158

159
=head1 NAME
160

161
GPH::Gitlab - parse and process L<Gitlab|https://about.gitlab.com/> CODEOWNER file
162

163
=head1 SYNOPSIS
164

165
    use GPH::Gitlab;
166

167
    my $gitlab = GPH::Gitlab->new((
168
        owner      => '@teams/alpha',
169
        codeowners => './CODEOWNERS,
170
    ));
171

172
    my $paths $phpmd->getPaths();
173

174
=head1 METHODS
175

176
=over 4
177

178
=item C<< -E<gt>new(%args) >>
179

180
the C<new> method creates a new GPH::Gitlab instance. it takes a hash of options, valid option keys include:
181

182
=over
183

184
=item owner B<(required)>
185

186
code owner name
187

188
=item codeowners B<(required)>
189

190
path to CODEOWNER file
191

192
=item excluded
193

194
list of paths defined in the CODEOWNER file for given owner, but to ignore
195

196
=back
197

198
=item C<< -E<gt>getPaths() >>
199

200
returns array of paths for given codeowner
201

202
=item C<< -E<gt>getBlacklistPaths() >>
203

204
returns array of paths which are blacklisted for given codeowner (based on gitlab's "more specific code owner" principle)
205

206
=item C<< -E<gt>match($path) >>
207

208
match C<$path> with paths defined for given codeowner. returns C<1> on hit, C<0> on miss
209

210
=item C<< -E<gt>matchBlacklist($path) >>
211

212
match C<$path> with blacklisted paths defined for given codeowner. returns C<1> on hit, C<0> on miss
213

214
=item C<< -E<gt>getCommaSeparatedPathList() >>
215

216
returns string of comma separated paths, typically used in C<--filter> options of quality tools
217

218
=item C<< -E<gt>intersect(@paths) >>
219

220
returns intersected array of given C<@paths> and paths defined for given code owner while not defined as blacklisted
221

222
=item C<< -E<gt>intersectCommaSeparatedPathList(@paths) >>
223

224
returns comma separated string of intersected C<@paths>
225

226
=item C<< -E<gt>sanitise($line) >> B<(internal)>
227

228
replace /**/* with a trailing forward slash
229

230
=item C<< -E<gt>blacklist($class_path) >> B<(internal)>
231

232
adds C<$class_path> to blacklists if applicable
233

234
=item C<< -E<gt>parseCodeowners(%args) >> B<(internal)>
235

236
parse CODEOWNERS file. it takes a hash of options, valid option keys include:
237

238
=over
239

240
=item codeowners B<(required)>
241

242
path to CODEOWNER file
243

244
=back
245

246
=back
247

248
=head1 CAVEATS
249

250
currently not all syntax from gitlab's CODEOWNERS file is supported. unsupported at the moment are:
251

252
=over 1
253

254
=item *
255

256
relative & globstar paths (.md )
257

258
=item *
259

260
wildcard default owner (* @default)
261

262
=item *
263

264
escaped pound signs (\#)
265

266
=item *
267

268
single nested paths ('/*')
269

270
=item *
271

272
paths with spaces
273

274
=back
275

276
=head1 AUTHOR
277

278
the GPH::PHPMD module was written by wicliff wolda <wicliff.wolda@gmail.com>
279

280
=head1 COPYRIGHT AND LICENSE
281

282
this library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
283

284
=cut
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc