File Coverage

blib/lib/Dist/Zilla/Plugin/EnsurePrereqsInstalled.pm
Criterion Covered Total %
statement 70 70 100.0
branch 15 18 83.3
condition n/a
subroutine 16 16 100.0
pod 0 5 0.0
total 101 109 92.6


line stmt bran cond sub pod time code
1 7     7   4506732 use strict;
  7         16  
  7         334  
2 7     7   30 use warnings;
  7         11  
  7         377  
3             package Dist::Zilla::Plugin::EnsurePrereqsInstalled;
4             # git description: v0.006-TRIAL-2-gec386c2
5             $Dist::Zilla::Plugin::EnsurePrereqsInstalled::VERSION = '0.007';
6             # ABSTRACT: Ensure at build time that all prereqs, including developer, are satisfied
7             # KEYWORDS: plugin toolchain prerequisites dependencies modules metadata
8             # vim: set ts=8 sw=4 tw=78 et :
9              
10 7     7   28 use Moose;
  7         9  
  7         57  
11             with
12             'Dist::Zilla::Role::BeforeBuild',
13             'Dist::Zilla::Role::AfterBuild',
14             'Dist::Zilla::Role::BeforeRelease';
15              
16 7     7   34888 use CPAN::Meta::Prereqs 2.132830; # for 'merged_requirements'
  7         209  
  7         134  
17 7     7   33 use CPAN::Meta::Requirements;
  7         11  
  7         152  
18 7     7   3766 use CPAN::Meta::Check 0.007 'check_requirements';
  7         13535  
  7         487  
19 7     7   46 use Moose::Util::TypeConstraints;
  7         10  
  7         65  
20 7     7   10590 use namespace::autoclean;
  7         13  
  7         64  
21              
22             sub mvp_aliases {
23             +{
24 8     8 0 1057 type => 'prereq_types',
25             relationship => 'prereq_types',
26             relation => 'prereq_types',
27             }
28             }
29 8     8 0 25742 sub mvp_multivalue_args { qw(prereq_types) }
30              
31             has prereq_types => (
32             isa => 'ArrayRef[Str]',
33             lazy => 1,
34             default => sub { ['requires'] },
35             traits => ['Array'],
36             handles => { prereq_types => 'elements' },
37             );
38              
39             has build_phase => (
40             is => 'ro', isa => enum([qw(build release)]),
41             lazy => 1,
42             default => 'build',
43             );
44              
45             # there is nothing in this plugin that should affect the outcome of the build
46             # -- its configuration is not significant.
47             #around dump_config => sub { };
48              
49             sub before_build
50             {
51 8     8 0 670758 my $self = shift;
52              
53 8         48 $self->_check_authordeps;
54             }
55              
56             sub after_build
57             {
58 6     6 0 246470 my $self = shift;
59              
60 6 100       266 return if $self->build_phase ne 'build';
61 5         25 $self->_check_prereqs;
62             }
63              
64             sub before_release
65             {
66 1     1 0 97569 my $self = shift;
67              
68 1 50       43 return if $self->build_phase ne 'release';
69 1         4 $self->_check_prereqs;
70             }
71              
72             sub _check_authordeps
73             {
74 8     8   13 my $self = shift;
75              
76 8         42 $self->log_debug('checking that all authordeps are satisfied...');
77 8 100       3341 if (my $unsatisfied = $self->_get_authordeps)
78             {
79 2         6136 $self->log_fatal(join "\n",
80             'Unsatisfied authordeps:',
81             $unsatisfied,
82             'To remedy, do: cpanm ' . join(' ', split("\n", $unsatisfied)),
83             );
84             }
85             }
86              
87             sub _check_prereqs
88             {
89 6     6   14 my $self = shift;
90              
91 6         37 $self->log_debug("checking that all prereqs are satisfied...");
92              
93             # this is safe to request, since we only run this method in phases after
94             # prereqs and distmeta has been calculated
95 6         2056 my $distmeta = $self->zilla->distmeta;
96              
97 6 50       232 $self->log('dynamic_config is set: make sure you put all possible prereqs into develop prereqs so your tests are complete!')
98             if $distmeta->{dynamic_config};
99              
100 6         10 my @prereq_phases = keys %{$distmeta->{prereqs}};
  6         29  
101 6         157 my $prereqs = $self->zilla->prereqs->cpan_meta_prereqs;
102              
103             # returns: { module name => diagnostic, ... }
104 7         49 my $requires_result = check_requirements(
105 6         624 $prereqs->merged_requirements(\@prereq_phases, [ grep { $_ ne 'conflicts' } $self->prereq_types ]),
106             'requires',
107             );
108              
109 6 100       40238 if (my @unsatisfied = sort grep { defined $requires_result->{$_} } keys %$requires_result)
  8         49  
110             {
111 8         24 $self->log_fatal(join "\n",
112             'Unsatisfied prerequisites:',
113 8         52 (map { ' ' . $requires_result->{$_} } @unsatisfied),
114 3 100       8 'To remedy, do: cpanm ' . join(' ', grep { $_ ne 'perl' } @unsatisfied),
115             ( defined $requires_result->{perl} ? 'And update your perl!' : () ),
116             );
117             }
118              
119 3         12 my $conflicts_result = check_requirements(
120             $prereqs->merged_requirements(\@prereq_phases, ['conflicts']),
121             'conflicts',
122             );
123 3 100       12704 if (my @conflicts = sort grep { defined $conflicts_result->{$_} } keys %$conflicts_result)
  1         9  
124             {
125 1         12 $self->log_fatal(join "\n",
126             'Conflicts found:',
127 1         2 (map { ' ' . $conflicts_result->{$_} } @conflicts),
128             'To remedy, do: pm-uninstall ' . join(' ', @conflicts),
129             );
130             }
131              
132              
133 2 100       9 if (my $x_breaks = $distmeta->{x_breaks})
134             {
135 1         3 $self->log_debug('checking x_breaks...');
136              
137 1         247 my $reqs = CPAN::Meta::Requirements->new;
138 1         16 $reqs->add_string_requirement($_, $x_breaks->{$_}) foreach keys %$x_breaks;
139              
140 1         126 my $result = check_requirements($reqs, 'conflicts');
141              
142 1 50       12401 if (my @breaks = sort grep { defined $result->{$_} } keys %$result)
  1         8  
143             {
144 1         9 $self->log_fatal(join "\n",
145             'Breakages found:',
146 1         2 (map { ' ' . $result->{$_} } @breaks),
147             'To remedy, do: cpanm ' . join(' ', @breaks),
148             );
149             }
150             }
151             }
152              
153             sub _get_authordeps
154             {
155 8     8   16 my $self = shift;
156              
157 8         4487 require Dist::Zilla::Util::AuthorDeps;
158 8         5733 require Path::Class;
159 8         43 Dist::Zilla::Util::AuthorDeps::format_author_deps(
160             Dist::Zilla::Util::AuthorDeps::extract_author_deps(
161             Path::Class::dir('.'), # ugh!
162             1, # --missing
163             ),
164             (), # --versions
165             );
166             }
167              
168             __PACKAGE__->meta->make_immutable;
169              
170             __END__
171              
172             =pod
173              
174             =encoding UTF-8
175              
176             =head1 NAME
177              
178             Dist::Zilla::Plugin::EnsurePrereqsInstalled - Ensure at build time that all prereqs, including developer, are satisfied
179              
180             =head1 VERSION
181              
182             version 0.007
183              
184             =head1 SYNOPSIS
185              
186             In your F<dist.ini>:
187              
188             [EnsurePrereqsInstalled]
189              
190             =head1 DESCRIPTION
191              
192             This is a L<Dist::Zilla> plugin that verifies, during the C<dzil build>
193             process, that all required prerequisites are satisfied, including developer
194             prereqs. If any prerequisites are missing, the build is aborted.
195              
196             =for stopwords Authordeps
197              
198             Authordeps (developer prerequisites that can be extracted directly from
199             F<dist.ini>) are always checked at the start of the build. This would be
200             equivalent to calling C<dzil authordeps --missing>.
201              
202             All prerequisites are fetched from the distribution near the end of the build
203             and a final validation check is performed at that time (unless C<build_phase>
204             is C<release>, in which case the check is delayed until just prior to
205             performing the release).
206              
207             Only 'requires', 'conflicts' and 'x_breaks' prerequisites are checked (by
208             default); other types (e.g. 'recommends' and 'suggests' are ignored).
209              
210             All prerequisite phases are checked: configure, build, test, runtime, develop
211             (and any custom x_ keys that may also be present, given adequate toolchain
212             support).
213              
214             =head1 BACKGROUND
215              
216             This plugin was written for a distribution that does some fiddly work during
217             file munging time that required the installation of a module, specified as an
218             C<< ; authordep Module::Name >> in F<dist.ini>. When the module is missing, an ugly exception
219             is printed, without a clear explanation that this module was a developer
220             prerequisite that ought to have been installed first.
221              
222             It is this author's opinion that this check out to be performed by
223             L<Dist::Zilla> itself, rather than leaving it to an optional plugin.
224              
225             =for Pod::Coverage mvp_aliases mvp_multivalue_args before_build after_build before_release
226              
227             =head1 CONFIGURATION OPTIONS
228              
229             =head2 type (or relationship, prereq_type)
230              
231             [EnsurePrereqsInstalled]
232             type = requires
233             type = recommends
234              
235             Indicate what relationship type(s) of prereqs are checked (such as requires, recommends, suggests).
236             Defaults to 'requires'; can be used more than once. (Note that 'conflicts'
237             and 'x_breaks' prereqs are always checked and this cannot be disabled.)
238              
239             =head2 build_phase
240              
241             [EnsurePrereqsInstalled]
242             build_phase = release
243              
244             Indicates what L<Dist::Zilla> phase to perform the check at - either build
245             (default) or release.
246              
247             =head1 POTENTIAL FEATURES
248              
249             ...if anyone has an interest:
250              
251             =over 4
252              
253             =item *
254              
255             option to exclude modules from being checked
256              
257             =item *
258              
259             option to prompt to continue instead of dying on unsatisfied prereqs
260              
261             =item *
262              
263             option for different treatment (warn? prompt?) for recommended, suggested prereqs
264              
265             =back
266              
267             =head1 SUPPORT
268              
269             =for stopwords irc
270              
271             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-Plugin-EnsurePrereqsInstalled>
272             (or L<bug-Dist-Zilla-Plugin-EnsurePrereqsInstalled@rt.cpan.org|mailto:bug-Dist-Zilla-Plugin-EnsurePrereqsInstalled@rt.cpan.org>).
273             I am also usually active on irc, as 'ether' at C<irc.perl.org>.
274              
275             =head1 SEE ALSO
276              
277             These plugins all do somewhat similar and overlapping things, but are all useful in their own way:
278              
279             =over 4
280              
281             =item *
282              
283             L<CPAN::Meta::Spec/Prereq Spec>
284              
285             =item *
286              
287             L<Dist::Zilla::Plugin::PromptIfStale>
288              
289             =item *
290              
291             L<Dist::Zilla::Plugin::CheckPrereqsIndexed>
292              
293             =item *
294              
295             L<Dist::Zilla::Plugin::Test::ReportPrereqs>
296              
297             =item *
298              
299             L<Dist::Zilla::Plugin::Test::CheckDeps>
300              
301             =item *
302              
303             L<Dist::Zilla::Plugin::Test::CheckBreaks>
304              
305             =back
306              
307             =head1 AUTHOR
308              
309             Karen Etheridge <ether@cpan.org>
310              
311             =head1 COPYRIGHT AND LICENSE
312              
313             This software is copyright (c) 2014 by Karen Etheridge.
314              
315             This is free software; you can redistribute it and/or modify it under
316             the same terms as the Perl 5 programming language system itself.
317              
318             =cut