| File: | blib/lib/App/Project/Doctor/Check/Meta.pm |
| Coverage: | 97.3% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package App::Project::Doctor::Check::Meta; | |||||
| 2 | ||||||
| 3 | 2 2 2 | 1867 3 28 | use strict; | |||
| 4 | 2 2 2 | 2 2 57 | use warnings; | |||
| 5 | 2 2 2 | 5 2 8 | use autodie qw(:all); | |||
| 6 | ||||||
| 7 | 2 2 2 | 4237 3 6 | use parent -norequire, 'App::Project::Doctor::Check::Base'; | |||
| 8 | ||||||
| 9 | 2 2 2 | 69 2 63 | use Carp qw(croak carp); | |||
| 10 | 2 2 2 | 3 2 504 | use Readonly; | |||
| 11 | ||||||
| 12 | our $VERSION = '0.02'; | |||||
| 13 | ||||||
| 14 | Readonly::Array my @REQUIRED_FIELDS => qw(name version author abstract license); | |||||
| 15 | Readonly::Array my @META_FILES => qw(META.json META.yml MYMETA.json MYMETA.yml); | |||||
| 16 | ||||||
| 17 | 2 | 186 | sub name { 'META' } | |||
| 18 | 1 | 2 | sub description { 'META.yml or META.json is present, parseable, and complete.' } | |||
| 19 | 2 | 4 | sub can_fix { 0 } | |||
| 20 | 2 | 4 | sub order { 30 } | |||
| 21 | ||||||
| 22 | sub check { | |||||
| 23 | 6 | 8 | my ($self, $ctx) = @_; | |||
| 24 | 6 | 9 | croak 'check requires an App::Project::Doctor::Context' unless ref $ctx; | |||
| 25 | ||||||
| 26 | 6 | 7 | my @findings; | |||
| 27 | ||||||
| 28 | 6 24 | 13 37 | my ($meta_file) = grep { $ctx->has_file($_) } @META_FILES; | |||
| 29 | ||||||
| 30 | 6 | 13 | unless ($meta_file) { | |||
| 31 | 3 | 7 | push @findings, _f( | |||
| 32 | severity => 'warning', | |||||
| 33 | message => 'No META.{yml,json} -- run your builder to generate one.', | |||||
| 34 | detail => 'CPAN indexers require META to discover name and version.', | |||||
| 35 | ); | |||||
| 36 | # Fall back: at least confirm a builder file exists. | |||||
| 37 | 3 | 7 | unless ($ctx->builder_file) { | |||
| 38 | 1 | 2 | push @findings, _f( | |||
| 39 | severity => 'error', | |||||
| 40 | message => 'No Makefile.PL, Build.PL, or dist.ini found.', | |||||
| 41 | ); | |||||
| 42 | } | |||||
| 43 | 3 | 17 | return @findings; | |||
| 44 | } | |||||
| 45 | ||||||
| 46 | 3 | 6 | require CPAN::Meta; | |||
| 47 | 3 3 | 4 3 | my $meta = eval { CPAN::Meta->load_file($ctx->abs_path($meta_file)) }; | |||
| 48 | 3 | 151 | if ($@) { | |||
| 49 | 1 | 2 | return _f( | |||
| 50 | severity => 'error', | |||||
| 51 | message => "Failed to parse $meta_file -- file may be malformed.", | |||||
| 52 | file => $meta_file, | |||||
| 53 | ); | |||||
| 54 | } | |||||
| 55 | ||||||
| 56 | 2 2 | 3 5 | my %data = %{ $meta->as_struct }; | |||
| 57 | 2 | 8 | for my $field (@REQUIRED_FIELDS) { | |||
| 58 | 10 | 44 | next if defined $data{$field} && length $data{$field}; | |||
| 59 | 1 | 5 | push @findings, _f( | |||
| 60 | severity => 'error', | |||||
| 61 | message => "META field '$field' is missing or empty in $meta_file.", | |||||
| 62 | file => $meta_file, | |||||
| 63 | ); | |||||
| 64 | } | |||||
| 65 | ||||||
| 66 | 2 | 7 | unless (@findings) { | |||
| 67 | 1 | 3 | push @findings, _f( | |||
| 68 | severity => 'pass', | |||||
| 69 | message => "$meta_file is valid and all required fields are present.", | |||||
| 70 | ); | |||||
| 71 | } | |||||
| 72 | ||||||
| 73 | 2 | 7 | return @findings; | |||
| 74 | } | |||||
| 75 | ||||||
| 76 | sub _f { | |||||
| 77 | 7 | 11 | require App::Project::Doctor::Finding; | |||
| 78 | 7 | 15 | return App::Project::Doctor::Finding->new(check_name => 'META', @_); | |||
| 79 | } | |||||
| 80 | ||||||
| 81 | 1; | |||||
| 82 | ||||||