File Coverage

blib/lib/Module/Advisor.pm
Criterion Covered Total %
statement 18 78 23.0
branch 0 20 0.0
condition n/a
subroutine 6 18 33.3
pod 0 10 0.0
total 24 126 19.0


line stmt bran cond sub pod time code
1             package Module::Advisor;
2 1     1   23608 use strict;
  1         2  
  1         32  
3 1     1   4 use warnings;
  1         1  
  1         23  
4 1     1   18 use 5.008008;
  1         7  
  1         49  
5             our $VERSION = '0.17';
6 1     1   901 use Module::Metadata;
  1         21499  
  1         39  
7 1     1   1703 use CPAN::Meta::Requirements;
  1         3354  
  1         24  
8 1     1   5 use Carp;
  1         3  
  1         1139  
9              
10             our @SECURITY = (
11             ['Digest' => '< 1.17', 'have a security issue. which could lead to the injection of arbitrary Perl code'],
12             ['Encode' => '< 2.44', 'heap overflow'],
13             ['Encode' => '< 2.49', 'memory leaks'],
14             );
15              
16             our @PERFORMANCE = (
17             ['UNIVERSAL::require', '< 0.11', 'is 400% faster'],
18             );
19              
20             our @BUG = (
21             ['Plack' => '< 0.9982', 'sanity check to remove newlines from headers'],
22             ['Time::Piece' => '< 1.16', 'have timezone related issue'],
23             ['DBD::SQLite' => '< 1.20', 'a lot of bugs.'],
24             ['Mouse' => '>= 1.07, < 1.12', 'Memory leaks in trigger'],
25             ['Text::Xslate' => '< 1.0011', '' bug.'],
26             ['Text::Xslate' => '< 1.5021', 'segv in "render" recursion call'],
27             ['Text::Xslate' => '< 1.6001', 'possibly memory leaks on VM stack frames. see https://github.com/xslate/p5-Text-Xslate/issues/71'],
28             ['Text::Xslate' => '< 2.0005', 'Nested WRAPPER broken https://github.com/xslate/p5-Text-Xslate/issues/79'],
29             ['Furl' => '< 0.39', 'unexpected eof in reading chunked body. It makes busy loop.'],
30             ['AnyEvent::MPRPC' => '< 0.15', 'switch to Data::MessagePack::Stream'],
31             ['Data::MessagePack' => '< 0.46', 'fixed unpacking issue on big-endian system.'],
32             ['Data::MessagePack' => '< 0.39', 'packing float numbers fails on some cases'],
33             ['FCGI::Client' => '< 0.06', 'fixed large packet issue'],
34             ['Starlet' => '< 0.12', 'fix infinite loop when connection is closed while receiving response content'],
35             ['Starman' => '< 0.2014', '$res->[1] is broken after output (This is actualized with Plack::Middleware::AccessLog::Timed) https://github.com/miyagawa/Starman/pull/31'],
36             ['Starman' => '< 0.1006', 'Fixed 100% CPU loop when an unexpected EOF happens'],
37             ['Twiggy' => '< 0.1000', 'busy loop'],
38             ['Teng', '< 0.14', 'fixed deflate bug.'],
39             ['DBIx::Skinny', '< 0.0742', 'txn_scope bug fixed'],
40             ['DBIx::TransactionManager', '< 1.11', 'not execute begin_work at AutoCommit=0.'],
41             ['HTTP::MobileAgent' => '< 0.36', 'new x-up-devcap-multimedia(StandAloneGPS) support'],
42             ['HTTP::MobileAgent' => '< 0.35', 'Updated $HTMLVerMap and $GPSModelsRe in DoCoMo.pm'],
43             ['Encode::JP::Mobile' => '< 0.25', 'resolved FULLWIDTH TILDE issue, etc.'],
44             ['Template' => '< 2.15', 'uri filter does not works properly https://rt.cpan.org/Public/Bug/Display.html?id=19593'],
45             ['HTML::FillInForm::Lite' => '< 1.11', 'HTML5 style tags support'],
46             ['Proc::Daemon' => '< 0.12', 'Init() did not close all filehandles reliably in some cases.'],
47             ['ExclusiveLock::Guard' => '< 0.04', 'change of the file stat timing (measures under high load)'],
48             ['autobox', '< 2.78', 'segv in END block https://rt.cpan.org/Ticket/Display.html?id=80400'],
49              
50             # Broken in specific versions
51             ['Amon2::DBI' => '== 0.31', 'transaction management bug'],
52             ['Math::Random::MT' => '== 1.15', 'rand() took no notice of argument RT #78200'],
53             ['Module::Install' => '== 1.04', 'Broken, http://weblog.bulknews.net/post/33907905561/do-not-ship-modules-with-module-install-1-04'],
54             ['Mouse' => '== 1.04', 'Broken, http://d.hatena.ne.jp/gfx/20130208/1360283357'],
55             ['Plack::Middleware::AxsLog' => '== 0.20', 'Missing \n'],
56             );
57              
58             our @XS = (
59             ['JSON' => 'JSON::XS'],
60             # ['PPI' => 'PPI::XS'], # I think PPI::XS is outdated.
61             ['Plack' => 'HTTP::Parser::XS'],
62             );
63             if ($^O eq 'linux') {
64             push @XS, ['Filesys::Notify::Simple', 'Linux::Inotify2'];
65             }
66              
67             our @FEATURE = (
68             ['Amon2' => '< 3.29', 'JSON hijacking detection.'],
69             ['Log::Minimal' => '< 0.10', 'LM_COLOR'],
70             ['Log::Minimal' => '< 0.08', 'colourful logging'],
71             ['Log::Minimal' => '< 0.03', 'ddf'],
72             ['Proclet' => '< 0.12', 'Proclet::Declare'],
73             ['DBI', '< 1.614' => 'AutoInactiveDestroy'],
74             ['Module::Install::XSUtil', '< 0.44' => 'PUREPERL_ONLY=1'],
75             );
76              
77             our @OPTIONAL_MODULES = (
78             ['LWP', 'LWP::Protocol::https', 'Need to support https'],
79             );
80              
81             # ref. Module::Version
82             sub get_version {
83 0 0   0 0   my $module = shift or croak 'Must get a module name';
84 0           my $metadata = Module::Metadata->new_from_module($module);
85 0 0         $metadata ? $metadata->version : undef;
86             }
87              
88             sub debugf {
89 0     0 0   my $self = shift;
90 0 0         print "# @_\n" if $self->{verbose};
91             }
92              
93             sub new {
94 0     0 0   my $class = shift;
95 0 0         my %args = @_==1 ? %{$_[0]} : @_;
  0            
96 0           bless {%args}, $class;
97             }
98              
99             sub check {
100 0     0 0   my ($self) = @_;
101 0           my $failed = 0;
102 0           $failed += $self->check_security();
103 0           $failed += $self->check_performance();
104 0           $failed += $self->check_bugs();
105 0           $failed += $self->check_xs_installed();
106 0           $failed += $self->check_feature();
107 0           return $failed;
108             }
109              
110             sub matches_version {
111 0     0 0   my($self, $module, $spec, $version) = @_;
112              
113 0           my $requirements = CPAN::Meta::Requirements->new;
114 0           $requirements->add_string_requirement($module, $spec);
115              
116 0           $requirements->accepts_module($module, $version);
117             }
118              
119             sub _check_issue {
120 0     0     my ($self, $patterns, $type_name) = @_;
121 0           my $failed = 0;
122 0           for my $row (@$patterns) {
123 0           my $ver = get_version($row->[0]);
124 0 0         if (defined $ver) {
125 0           $self->debugf("$row->[0] $ver");
126 0 0         if ($self->matches_version($row->[0], $row->[1], $ver)) {
127 0           printf "[$type_name] %s %s %s: %s\n", $row->[0], $ver, $row->[1], $row->[2];
128 0           $failed++;
129             }
130             } else {
131 0           $self->debugf("$row->[0] is not found");
132             }
133             }
134 0           return $failed;
135             }
136              
137             sub check_security {
138 0     0 0   my $self = shift;
139 0           $self->_check_issue(\@SECURITY, 'SECURITY');
140             }
141              
142             sub check_performance {
143 0     0 0   my $self = shift;
144 0           $self->_check_issue(\@PERFORMANCE, 'PERFORMANCE');
145             }
146              
147             sub check_bugs {
148 0     0 0   my $self = shift;
149 0           $self->_check_issue(\@BUG, 'BUG');
150             }
151              
152             sub check_xs_installed {
153 0     0 0   my ($self) = @_;
154 0           my $failed = 0;
155 0           for my $row (@XS) {
156 0           my $pmver = get_version($row->[0]);
157 0 0         if (defined $pmver) {
158 0           my $xsver = get_version($row->[1]);
159 0 0         if (defined $xsver) {
160 0           $self->debugf("$row->[1] is also installed");
161             } else {
162 0           printf "[XS] %s is need to install for better performance for %s\n", $row->[1], $row->[0];
163             }
164             } else {
165 0           $self->debugf("$row->[0] is not installed");
166             }
167             }
168             }
169              
170             sub _check_optional {
171 0     0     my ($self, $rules, $type_name) = @_;
172 0           my $failed = 0;
173 0           for my $row (@$rules) {
174 0           my $pmver = get_version($row->[0]);
175 0 0         if (defined $pmver) {
176 0           my $xsver = get_version($row->[1]);
177 0 0         if (defined $xsver) {
178 0           $self->debugf("$row->[1] is also installed");
179             } else {
180 0           printf "[$type_name] %s => %s: %s\n", $row->[1], $row->[0], $row->[2];
181             }
182             } else {
183 0           $self->debugf("$row->[0] is not installed");
184             }
185             }
186             }
187              
188             sub check_feature {
189 0     0 0   my $self = shift;
190 0           $self->_check_optional(\@OPTIONAL_MODULES, 'OPTIONAL_MODULES');
191             }
192              
193             if (not defined caller(0)) {
194             my $checker = __PACKAGE__->new();
195             $checker->{verbose}++ if $ENV{DEBUG};
196             $checker->check() or print "OK\n";
197             }
198              
199             1;