File Coverage

lib/Test/PAUSE/Permissions.pm
Criterion Covered Total %
statement 15 98 15.3
branch 0 48 0.0
condition 0 40 0.0
subroutine 5 11 45.4
pod 1 1 100.0
total 21 198 10.6


line stmt bran cond sub pod time code
1             package Test::PAUSE::Permissions;
2              
3 1     1   59118 use strict;
  1         2  
  1         22  
4 1     1   3 use warnings;
  1         1  
  1         22  
5 1     1   386 use parent 'Exporter';
  1         223  
  1         4  
6 1     1   37 use Test::More;
  1         1  
  1         4  
7 1     1   531 use Parse::LocalDistribution;
  1         51707  
  1         845  
8              
9             our $VERSION = '0.07';
10              
11             our @EXPORT = (@Test::More::EXPORT, qw/all_permissions_ok/);
12              
13             sub all_permissions_ok {
14 0 0   0 1   my ($author, $opts) = ref $_[0] ? (undef, @_) : @_;
15 0   0       $opts ||= {};
16              
17 0 0         plan skip_all => 'Set RELEASE_TESTING environmental variable to test this.' unless $ENV{RELEASE_TESTING};
18              
19             # Get your id from .pause
20 0   0       $author ||= _get_pause_user();
21              
22 0 0         plan skip_all => "Can't determine who is going to release." unless $author;
23              
24             # Get authority from META
25 0   0       my $meta_authority ||= _get_authority_in_meta();
26              
27 0 0         local $Parse::PMFile::ALLOW_DEV_VERSION = 1 if $opts->{dev};
28              
29             # Get packages (respecting no_index)
30 0           my $provides = Parse::LocalDistribution->new->parse();
31              
32             # Get maintainers from 06perms or MetaCPAN
33 0   0       my $use_metacpan = $opts->{metacpan} || $ENV{TEST_PAUSE_PERMISSIONS_METACPAN};
34 0           my $maintainers = _get_maintainers($use_metacpan, [keys %$provides]);
35              
36             # Iterate
37 0           my $saw_errors;
38 0           my @authorities = grep $_, $author, $meta_authority;
39 0           my %new_packages;
40 0           my %dist_maintainers = map {uc $_ => 1} @authorities;
  0            
41             SKIP:
42 0           for my $package (keys %$provides) {
43 0   0       my $authority = uc($meta_authority || $author || '');
44              
45 0           my $module_maintainers = $maintainers->{$package};
46              
47 0 0         if (!$module_maintainers) {
48 0           pass "$package: no one has permissions ($authority should have the first come)";
49 0           $new_packages{$package} = 1;
50 0           next;
51             }
52 0           $dist_maintainers{uc $_} = 1 for @$module_maintainers;
53              
54             # Author should have permissions, regardless of the authority
55 0 0         if (grep { uc $_ eq uc $author } @$module_maintainers) {
  0            
56 0           pass "$package: $author has a permission";
57             }
58             else {
59 0           fail "$package: maintained by ".join ', ', @$module_maintainers;
60 0           $saw_errors = 1;
61             }
62              
63             # $AUTHORITY has no effect in PAUSE.
64             # just see if $AUTHORITY matches x_authority for information
65 0 0         if ($meta_authority) {
66 0           my $file_authority = _get_authority_in_file($package, $provides->{$package});
67 0 0 0       if ($file_authority && $file_authority ne $meta_authority) {
68             # XXX: should fail?
69 0           diag "$package: \$AUTHORITY ($file_authority) doesn't match x_authority ($meta_authority)";
70             }
71             }
72             }
73              
74             # There are several known IDs that won't maintain any package
75 0           delete $dist_maintainers{$_} for qw/ADOPTME HANDOFF NEEDHELP LOCAL/;
76              
77             # GH #3: Adding a new module to an established distribution maintained by a large group may cause
78             # an annoying permission problem.
79 0 0 0       if (
      0        
      0        
80             !$saw_errors # having errors already means there's someone (ie. you) who can't upload it
81             and %new_packages # no problem if no new module is added
82             and (keys %new_packages < keys %$provides) # no problem if everything is new
83             and (keys %dist_maintainers > @authorities) # no problem if maintainers are few and everyone gets permissions
84             ) {
85 0           delete $dist_maintainers{$_} for @authorities;
86 0           my $message = "Some of the maintainers of this distribution (@{[sort keys %dist_maintainers]}) won't have permissions for the following package(s): @{[sort keys %new_packages]}.";
  0            
  0            
87 0 0         if ($opts->{strict}) {
88 0           fail $message;
89             } else {
90 0           diag "[WARNING] $message\n(This may or may not be a problem, depending on the policy of your team.)";
91             }
92             }
93              
94 0           done_testing;
95             }
96              
97             sub _get_pause_user {
98             # Get authority from ~/.pause
99 0     0     require Config::Identity::PAUSE;
100 0           my %config = Config::Identity::PAUSE->load;
101 0           return $config{user};
102             }
103              
104             sub _get_authority_in_meta {
105             # Get authority from META
106 0     0     my $meta = _parse_meta();
107 0 0 0       if ($meta && $meta->{x_authority}) {
108 0           my $authority = $meta->{x_authority};
109 0           $authority =~ s/^cpan://i;
110 0           return $authority;
111             }
112             }
113              
114             sub _parse_meta {
115 0     0     for my $file (qw/META.json META.yml/) {
116 0 0 0       next unless -f $file && -r _;
117 0           my $meta = Parse::CPAN::Meta->load_file($file);
118 0 0 0       return $meta if $meta && ref $meta eq ref {};
119             }
120             }
121              
122             sub _get_authority_in_file {
123 0     0     my ($package, $package_info) = @_;
124 0           my $file = $package_info->{infile};
125 0 0 0       return unless $file && -f $file && -r _;
      0        
126              
127 0 0         open my $fh, '<', $file or return;
128 0           my $in_pod = 0;
129 0           while(<$fh>) {
130 0 0         last if /__(DATA|END)__/;
131 0 0         $in_pod = /^=(?!cut?)/ ? 1 : /^=cut/ ? 0 : $in_pod;
    0          
132 0 0         next if $in_pod;
133              
134 0 0         if (/\$(?:${package}::)?AUTHORITY\s*=.+?(?i:cpan):([A-Za-z0-9]+)/) {
135 0           return $1;
136             }
137             }
138             }
139              
140             sub _get_maintainers {
141 0     0     my ($use_metacpan, $modules) = @_;
142 0 0         if ($use_metacpan) {
143 0           eval { require PAUSE::Permissions::MetaCPAN };
  0            
144 0 0         die "To use MetaCPAN permission API, "
145             . "you need to install PAUSE::Permissions::MetaCPAN, $@" if $@;
146 0           my $perms = PAUSE::Permissions::MetaCPAN->new->get(modules => $modules);
147 0           my %maintainers;
148 0           for my $module (@$modules) {
149             $maintainers{$module} = $perms->{$module}
150 0 0         ? [ $perms->{$module}{owner}, @{$perms->{$module}{co_maintainers}} ] : undef;
  0            
151             }
152 0           \%maintainers;
153             } else {
154 0           require PAUSE::Permissions;
155 0           my $file = PAUSE::Permissions->new;
156 0           my %maintainers;
157 0           for my $module (@$modules) {
158 0           my $mp = $file->module_permissions($module);
159 0 0         $maintainers{$module} = $mp ? [$mp->all_maintainers] : undef;
160             }
161 0           \%maintainers;
162             }
163             }
164              
165             1;
166              
167             __END__