File Coverage

blib/lib/Dist/Zilla/Plugin/AuthorsFromGit.pm
Criterion Covered Total %
statement 21 93 22.5
branch 0 24 0.0
condition 0 3 0.0
subroutine 7 14 50.0
pod 0 5 0.0
total 28 139 20.1


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