line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Dist::Zilla::Plugin::AuthorsFromGit; |
2
|
|
|
|
|
|
|
# ABSTRACT: Add per-file per-year copyright info to each Perl document |
3
|
|
|
|
|
|
|
$Dist::Zilla::Plugin::AuthorsFromGit::VERSION = '0.007'; |
4
|
1
|
|
|
1
|
|
2423
|
use Git::Wrapper; |
|
1
|
|
|
|
|
28285
|
|
|
1
|
|
|
|
|
38
|
|
5
|
1
|
|
|
1
|
|
2060
|
use DateTime; |
|
1
|
|
|
|
|
463281
|
|
|
1
|
|
|
|
|
60
|
|
6
|
1
|
|
|
1
|
|
10
|
use List::MoreUtils 0.4 qw(uniq sort_by true); |
|
1
|
|
|
|
|
30
|
|
|
1
|
|
|
|
|
9
|
|
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
770
|
use Moose; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
10
|
|
9
|
|
|
|
|
|
|
with( |
10
|
|
|
|
|
|
|
'Dist::Zilla::Role::FileMunger', |
11
|
|
|
|
|
|
|
'Dist::Zilla::Role::FileFinderUser' => { |
12
|
|
|
|
|
|
|
default_finders => [ ':InstallModules', ':ExecFiles' ], |
13
|
|
|
|
|
|
|
}, |
14
|
|
|
|
|
|
|
); |
15
|
|
|
|
|
|
|
|
16
|
1
|
|
|
1
|
|
6658
|
use namespace::autoclean; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
8
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub getblacklist { |
20
|
0
|
0
|
|
0
|
0
|
|
open ( my $lf, '<', '.copyright-exclude' ) or return ( ); |
21
|
0
|
|
|
|
|
|
my @lines=<$lf>; |
22
|
0
|
|
|
|
|
|
chomp @lines; |
23
|
0
|
|
|
|
|
|
return @lines; |
24
|
|
|
|
|
|
|
}; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub gitauthorlist { |
28
|
0
|
|
|
0
|
0
|
|
my ($file, $git)= @_; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Switching the warning off when the code works nicely is safer than rewriting |
31
|
|
|
|
|
|
|
# the logic to suppress a harmless warning. |
32
|
1
|
|
|
1
|
|
188
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
529
|
|
33
|
|
|
|
|
|
|
|
34
|
0
|
|
|
|
|
|
my @log_lines = $git->RUN('log', '--follow', '-M75%', '--format=%H %at %aN', '--', $file->name); |
35
|
0
|
|
|
|
|
|
my @outputlines; |
36
|
0
|
|
|
|
|
|
push @outputlines, ""; |
37
|
|
|
|
|
|
|
|
38
|
0
|
0
|
|
|
|
|
if (@log_lines) { |
39
|
|
|
|
|
|
|
|
40
|
0
|
|
|
|
|
|
my $earliest_year=3000; |
41
|
0
|
|
|
|
|
|
my $latest_year=0; |
42
|
0
|
|
|
|
|
|
my %authordata; |
43
|
|
|
|
|
|
|
my %authorline; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Get the commit blacklist to ignore |
46
|
0
|
|
|
|
|
|
my @blacklist=getblacklist(); |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# Extract the author data and separate by year |
49
|
0
|
|
|
|
|
|
foreach ( @log_lines ) { |
50
|
|
|
|
|
|
|
|
51
|
0
|
|
|
|
|
|
my @fields=split(/ /,$_,3); |
52
|
0
|
|
|
|
|
|
my $commit=$fields[0]; |
53
|
0
|
|
|
|
|
|
my $when=DateTime->from_epoch(epoch => $fields[1]); |
54
|
0
|
|
|
|
|
|
my $year=$when->year(); |
55
|
0
|
|
|
|
|
|
my $author=$fields[2]; |
56
|
|
|
|
|
|
|
|
57
|
0
|
|
|
0
|
|
|
my $count = true { /$commit/ } @blacklist; |
|
0
|
|
|
|
|
|
|
58
|
0
|
0
|
|
|
|
|
if ( $count >= 1 ) { next; }; |
|
0
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
|
60
|
0
|
0
|
|
|
|
|
if ($year < $earliest_year) { $earliest_year=$year; }; |
|
0
|
|
|
|
|
|
|
61
|
0
|
0
|
|
|
|
|
if ($year > $latest_year) { $latest_year=$year; }; |
|
0
|
|
|
|
|
|
|
62
|
0
|
0
|
|
|
|
|
if ( $author ne "unknown" ) { push(@{$authordata{$year}}, $author); }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
}; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Remove duplicates within a year, sort and transform to string |
66
|
0
|
|
|
|
|
|
foreach my $year (keys %authordata) { |
67
|
|
|
|
|
|
|
|
68
|
0
|
|
|
|
|
|
my @un=uniq(@{$authordata{$year}}); |
|
0
|
|
|
|
|
|
|
69
|
0
|
|
|
0
|
|
|
$authorline{$year}=join(', ',sort_by { $_ } @un); |
|
0
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
}; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Now deduplicate the years |
74
|
0
|
|
|
|
|
|
push @outputlines, " Copyright $earliest_year ".$authorline{$earliest_year}; |
75
|
|
|
|
|
|
|
|
76
|
0
|
|
|
|
|
|
for ( my $year=$earliest_year+1; $year<=$latest_year; $year++) { |
77
|
|
|
|
|
|
|
|
78
|
0
|
0
|
0
|
|
|
|
if ( (defined $authorline{$year}) && (defined $authorline{$year-1}) ) { |
|
|
0
|
|
|
|
|
|
79
|
|
|
|
|
|
|
|
80
|
0
|
0
|
|
|
|
|
if ($authorline{$year-1} eq $authorline{$year}) { |
81
|
|
|
|
|
|
|
|
82
|
0
|
|
|
|
|
|
my $lastline=$outputlines[-1]; |
83
|
0
|
|
|
|
|
|
$lastline=~ s/([0-9]{4})[\- ][0-9 ]{4}/$1-$year/; |
84
|
0
|
|
|
|
|
|
$outputlines[-1]=$lastline; |
85
|
|
|
|
|
|
|
} else { |
86
|
0
|
|
|
|
|
|
push @outputlines, " $year ".$authorline{$year}; |
87
|
|
|
|
|
|
|
}; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
} elsif ( defined $authorline{$year} ) { |
90
|
|
|
|
|
|
|
|
91
|
0
|
|
|
|
|
|
push @outputlines, " $year ".$authorline{$year}; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
}; |
94
|
|
|
|
|
|
|
}; |
95
|
0
|
|
|
|
|
|
push @outputlines, ""; |
96
|
|
|
|
|
|
|
}; |
97
|
|
|
|
|
|
|
|
98
|
1
|
|
|
1
|
|
7
|
use warnings 'uninitialized'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
557
|
|
99
|
|
|
|
|
|
|
|
100
|
0
|
|
|
|
|
|
return @outputlines; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub munge_files { |
104
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
105
|
0
|
|
|
|
|
|
my $myv="git"; |
106
|
0
|
0
|
|
|
|
|
if ( defined $Dist::Zilla::Plugin::AuthorsFromGit::VERSION ) { $myv=$Dist::Zilla::Plugin::AuthorsFromGit::VERSION; }; |
|
0
|
|
|
|
|
|
|
107
|
0
|
|
|
|
|
|
$self->log([ 'extracting Git commit information, plugin version %s', $myv ]); |
108
|
0
|
|
|
|
|
|
my $git = Git::Wrapper->new("."); |
109
|
|
|
|
|
|
|
|
110
|
0
|
|
|
|
|
|
$self->munge_file($_, $git) for @{ $self->found_files }; |
|
0
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub munge_file { |
114
|
0
|
|
|
0
|
0
|
|
my ($self, $file, $git) = @_; |
115
|
|
|
|
|
|
|
|
116
|
0
|
|
|
|
|
|
my @gal=gitauthorlist($file,$git); |
117
|
|
|
|
|
|
|
|
118
|
0
|
|
|
|
|
|
return $self->munge_pod($file, @gal); |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub munge_pod { |
122
|
0
|
|
|
0
|
0
|
|
my ($self, $file, @gal) = @_; |
123
|
|
|
|
|
|
|
|
124
|
0
|
|
|
|
|
|
my @content = split /\n/, $file->content; |
125
|
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
|
require List::Util; |
127
|
0
|
|
|
|
|
|
List::Util->VERSION('1.33'); |
128
|
|
|
|
|
|
|
|
129
|
0
|
|
|
|
|
|
for (0 .. $#content) { |
130
|
0
|
|
|
|
|
|
next until $content[$_] =~ /^=head1 COPYRIGHT AND LICENSE/; |
131
|
|
|
|
|
|
|
|
132
|
0
|
|
|
|
|
|
$_++; # move past the =head1 line itself |
133
|
0
|
|
|
|
|
|
$_++; # and past the subsequent empty line |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# Now we should have a line looking like |
136
|
|
|
|
|
|
|
# |
137
|
|
|
|
|
|
|
# "This software is copyright ... , see the git log." |
138
|
|
|
|
|
|
|
# |
139
|
|
|
|
|
|
|
# The string ", see the git log." is used as magic to trigger the plugin. |
140
|
|
|
|
|
|
|
# We check this format, replace ", see the git log.", |
141
|
|
|
|
|
|
|
# and insert the git information afterwards. |
142
|
|
|
|
|
|
|
|
143
|
0
|
0
|
|
|
|
|
if ($content[$_] =~ /^This software is copyright.*, see the git log\.$/ ) { |
144
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
|
$content[$_] =~ s/, see the git log\.$/; in detail:/; |
146
|
0
|
|
|
|
|
|
splice @content, $_+1, 0, @gal; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
}; |
149
|
|
|
|
|
|
|
|
150
|
0
|
|
|
|
|
|
my $content = join "\n", @content; |
151
|
0
|
0
|
|
|
|
|
$content .= "\n" if length $content; |
152
|
0
|
|
|
|
|
|
$file->content($content); |
153
|
0
|
|
|
|
|
|
return; |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
160
|
|
|
|
|
|
|
1; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
#pod =head1 SEE ALSO |
163
|
|
|
|
|
|
|
#pod |
164
|
|
|
|
|
|
|
#pod L<PkgVersion|Dist::Zilla::Plugin::PodVersion>, |
165
|
|
|
|
|
|
|
#pod L<PkgVersion|Git::Wrapper>, |
166
|
|
|
|
|
|
|
#pod L<PkgVersion|Lab::Measurement> for an application example |
167
|
|
|
|
|
|
|
#pod |
168
|
|
|
|
|
|
|
#pod =cut |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
__END__ |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=pod |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=encoding UTF-8 |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=head1 NAME |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Dist::Zilla::Plugin::AuthorsFromGit - Add per-file per-year copyright info to each Perl document |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head1 VERSION |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
version 0.007 |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=head1 SYNOPSIS |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
In dist.ini, set |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
copyright_holder = the Foo-Bar team, see the git log |
189
|
|
|
|
|
|
|
; [...] |
190
|
|
|
|
|
|
|
[PodWeaver] |
191
|
|
|
|
|
|
|
[AuthorsFromGit] |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
In weaver.ini, set |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
[@NoAuthor] |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
Then a copyright section in each module is created as follows: |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
COPYRIGHT AND LICENSE |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
This software is copyright (c) 2017 by the Foo-Bar team; in detail: |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
Copyright 2014-2015 A. N. Author |
204
|
|
|
|
|
|
|
2016 A. N. Author, O. Th. Erautor |
205
|
|
|
|
|
|
|
2017 O. Th. Erautor |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
with names and years extracted from the Git commit log of the specific module. |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head1 DESCRIPTION |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
This Dist::Zilla plugin is intended for large Perl distributions that have been |
212
|
|
|
|
|
|
|
existing for some time, where maintainership has changed over the years, and |
213
|
|
|
|
|
|
|
where different people have contributed to different parts of the code. It |
214
|
|
|
|
|
|
|
provides a means to acknowledge the contribution of different people to |
215
|
|
|
|
|
|
|
different modules, where it is not possible to resonably list them all in the |
216
|
|
|
|
|
|
|
authors field of the entire distribution. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
This is also to reflect that, independent of the chosen license terms, anyone |
219
|
|
|
|
|
|
|
who contributes nontrivial code to an open source package retains copyright of |
220
|
|
|
|
|
|
|
the contribution. Some legislatures (e.g. Germany) even provide no way of |
221
|
|
|
|
|
|
|
"transferring" copyright, since it is always bound to the natural person who |
222
|
|
|
|
|
|
|
conceived the code. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=head1 USAGE |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
Here, the usage in conjunction with the PodWeaver plugin is described. It should |
227
|
|
|
|
|
|
|
be possible to use this module without it, but I haven't tested that yet. We |
228
|
|
|
|
|
|
|
also assume that your working directory is a Git clone. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Assuming your distribution is called Foo-Bar, in dist.ini, then set |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
copyright_holder = the Foo-Bar team, see the git log |
233
|
|
|
|
|
|
|
; [...] |
234
|
|
|
|
|
|
|
[PodWeaver] |
235
|
|
|
|
|
|
|
[AuthorsFromGit] |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
The precise string ", see the git log" at the end of the copyright_holder line |
238
|
|
|
|
|
|
|
is important since it triggers this plugin. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
In case you do not have a weaver.ini yet, create one with the content |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
[@NoAuthor] |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
This is identical to the default plugin bundle of Pod-Weaver, just that it will |
245
|
|
|
|
|
|
|
not create a separate AUTHORS section. In case you already have a weaver.ini, |
246
|
|
|
|
|
|
|
make sure it does not generate any AUTHORS section. |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
During the build process, Dist::Zilla will then run "git log" for each processed |
249
|
|
|
|
|
|
|
module and extract the list of authors of the module for each year. Then a |
250
|
|
|
|
|
|
|
copyright section in the POD of each module is created as follows: |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
COPYRIGHT AND LICENSE |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
This software is copyright (c) 2017 by the Foo::Bar team; in detail: |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Copyright 2014-2015 A. N. Author |
257
|
|
|
|
|
|
|
2016 A. N. Author, O. Th. Erautor |
258
|
|
|
|
|
|
|
2017 O. Th. Erautor |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=head1 CONFIGURATION |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
Not much. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=head2 Excluding commits |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
In case you want to skip some commits which contain trivial, not |
267
|
|
|
|
|
|
|
copyright-relevant changes ("increase version number", "perltidy"), create |
268
|
|
|
|
|
|
|
a text file named .copyright-exclude in the main distribution directory. It |
269
|
|
|
|
|
|
|
should contain exactly one git commit hash per line, nothing else. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
Use with care, and only add your own commits! |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=head1 KNOWN BUGS |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
There's something fishy with unicode. |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=head1 AUTHOR |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
Andreas K. Huettel <dilfridge@gentoo.org> |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
This software is copyright (c) 2017 by Andreas K. Huettel. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
286
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=cut |