File Coverage

blib/lib/CPAN/Mini.pm
Criterion Covered Total %
statement 126 301 41.8
branch 54 174 31.0
condition 6 54 11.1
subroutine 23 48 47.9
pod 15 18 83.3
total 224 595 37.6


line stmt bran cond sub pod time code
1 4     4   58624 use 5.006;
  4         14  
  4         161  
2 4     4   22 use strict;
  4         8  
  4         278  
3 4     4   34 use warnings;
  4         7  
  4         362  
4              
5             package CPAN::Mini;
6             $CPAN::Mini::VERSION = '1.111016';
7             # ABSTRACT: create a minimal mirror of CPAN
8              
9             ## no critic RequireCarping
10              
11             #pod =head1 SYNOPSIS
12             #pod
13             #pod (If you're not going to do something weird, you probably want to look at the
14             #pod L command, instead.)
15             #pod
16             #pod use CPAN::Mini;
17             #pod
18             #pod CPAN::Mini->update_mirror(
19             #pod remote => "http://cpan.mirrors.comintern.su",
20             #pod local => "/usr/share/mirrors/cpan",
21             #pod log_level => 'debug',
22             #pod );
23             #pod
24             #pod =head1 DESCRIPTION
25             #pod
26             #pod CPAN::Mini provides a simple mechanism to build and update a minimal mirror of
27             #pod the CPAN on your local disk. It contains only those files needed to install
28             #pod the newest version of every distribution. Those files are:
29             #pod
30             #pod =for :list
31             #pod * 01mailrc.txt.gz
32             #pod * 02packages.details.txt.gz
33             #pod * 03modlist.data.gz
34             #pod * the last non-developer release of every dist for every author
35             #pod
36             #pod =cut
37              
38 4     4   22 use Carp ();
  4         7  
  4         73  
39              
40 4     4   21 use File::Basename ();
  4         7  
  4         58  
41 4     4   4633 use File::Copy ();
  4         17901  
  4         199  
42 4     4   4021 use File::HomeDir 0.57 (); # Win32 support
  4         29817  
  4         124  
43 4     4   38 use File::Find ();
  4         9  
  4         80  
44 4     4   21 use File::Path 2.04 (); # new API, bugfixes
  4         80  
  4         76  
45 4     4   31 use File::Spec ();
  4         7  
  4         95  
46 4     4   5097 use File::Temp ();
  4         99540  
  4         124  
47              
48 4     4   3979 use URI 1 ();
  4         23073  
  4         126  
49 4     4   8190 use LWP::UserAgent 5 ();
  4         241315  
  4         135  
50              
51 4     4   5968 use Compress::Zlib 1.20 ();
  4         355118  
  4         12988  
52              
53             #pod =method update_mirror
54             #pod
55             #pod CPAN::Mini->update_mirror(
56             #pod remote => "http://cpan.mirrors.comintern.su",
57             #pod local => "/usr/share/mirrors/cpan",
58             #pod force => 0,
59             #pod log_level => 'debug',
60             #pod );
61             #pod
62             #pod This is the only method that need be called from outside this module. It will
63             #pod update the local mirror with the files from the remote mirror.
64             #pod
65             #pod If called as a class method, C creates an ephemeral CPAN::Mini
66             #pod object on which other methods are called. That object is used to store mirror
67             #pod location and state.
68             #pod
69             #pod This method returns the number of files updated.
70             #pod
71             #pod The following options are recognized:
72             #pod
73             #pod =begin :list
74             #pod
75             #pod * C
76             #pod
77             #pod This is the local file path where the mirror will be written or updated.
78             #pod
79             #pod * C
80             #pod
81             #pod This is the URL of the CPAN mirror from which to work. A reasonable default
82             #pod will be picked by default. A list of CPAN mirrors can be found at
83             #pod L
84             #pod
85             #pod * C
86             #pod
87             #pod Generally an octal number, this option sets the permissions of created
88             #pod directories. It defaults to 0711.
89             #pod
90             #pod * C
91             #pod
92             #pod If true, the C method will allow all extra files to be mirrored.
93             #pod
94             #pod * C
95             #pod
96             #pod If true, CPAN::Mini will not try to remove source control files during
97             #pod cleanup. See C for details.
98             #pod
99             #pod * C
100             #pod
101             #pod If true, this option will cause CPAN::Mini to read the entire module list and
102             #pod update anything out of date, even if the module list itself wasn't out of date
103             #pod on this run.
104             #pod
105             #pod * C
106             #pod
107             #pod If true, CPAN::Mini will skip the major language distributions: perl, parrot,
108             #pod and ponie. It will also skip embperl, sybperl, bioperl, and kurila.
109             #pod
110             #pod * C
111             #pod
112             #pod This defines the minimum level of message to log: debug, info, warn, or fatal
113             #pod
114             #pod * C
115             #pod
116             #pod If true, CPAN::Mini will warn with status messages on errors. (default: true)
117             #pod
118             #pod * C
119             #pod
120             #pod This options provides a set of rules for filtering paths. If a distribution
121             #pod matches one of the rules in C, it will not be mirrored. A regex
122             #pod rule is matched if the path matches the regex; a code rule is matched if the
123             #pod code returns 1 when the path is passed to it. For example, the following
124             #pod setting would skip all distributions from RJBS and SUNGO:
125             #pod
126             #pod path_filters => [
127             #pod qr/RJBS/,
128             #pod sub { $_[0] =~ /SUNGO/ }
129             #pod ]
130             #pod
131             #pod * C
132             #pod
133             #pod This option provides a set of rules for filtering modules. It behaves like
134             #pod path_filters, but acts only on module names. (Since most modules are in
135             #pod distributions with more than one module, this setting will probably be less
136             #pod useful than C.) For example, this setting will skip any
137             #pod distribution containing only modules with the word "Acme" in them:
138             #pod
139             #pod module_filters => [ qr/Acme/i ]
140             #pod
141             #pod * C
142             #pod
143             #pod This option should be an arrayref of extra files in the remote CPAN to mirror
144             #pod locally.
145             #pod
146             #pod * C
147             #pod
148             #pod If this option is true, CPAN::Mini will not try delete unmirrored files when it
149             #pod has finished mirroring
150             #pod
151             #pod * C
152             #pod
153             #pod If offline, CPAN::Mini will not attempt to contact remote resources.
154             #pod
155             #pod * C
156             #pod
157             #pod If true, no connection cache will be established. This is mostly useful as a
158             #pod workaround for connection cache failures.
159             #pod
160             #pod =end :list
161             #pod
162             #pod =cut
163              
164             sub update_mirror {
165 0     0 1 0 my $self = shift;
166 0 0       0 $self = $self->new(@_) unless ref $self;
167              
168 0 0       0 unless ($self->{offline}) {
169 0         0 my $local = $self->{local};
170              
171 0         0 $self->log("Updating $local");
172 0         0 $self->log("Mirroring from $self->{remote}");
173 0         0 $self->log("=" x 63);
174              
175 0 0       0 die "local mirror target $local is not writable" unless -w $local;
176              
177             # mirrored tracks the already done, keyed by filename
178             # 1 = local-checked, 2 = remote-mirrored
179 0         0 $self->mirror_indices;
180              
181 0 0 0     0 return unless $self->{force} or $self->{changes_made};
182              
183             # mirror all the files
184 0         0 $self->_mirror_extras;
185 0         0 $self->mirror_file($_, 1) for @{ $self->_get_mirror_list };
  0         0  
186              
187             # install indices after files are mirrored in case we're interrupted
188             # so indices will seem new again when continuing
189 0         0 $self->_install_indices;
190              
191 0         0 $self->_write_out_recent;
192              
193             # eliminate files we don't need
194 0 0       0 $self->clean_unmirrored unless $self->{skip_cleanup};
195             }
196              
197 0         0 return $self->{changes_made};
198             }
199              
200 0     0   0 sub _recent { $_[0]->{recent}{ $_[1] } = 1 }
201              
202             sub _write_out_recent {
203 0     0   0 my ($self) = @_;
204 0 0       0 return unless my @keys = keys %{ $self->{recent} };
  0         0  
205              
206 0         0 my $recent = File::Spec->catfile($self->{local}, 'RECENT');
207 0 0       0 open my $recent_fh, '>', $recent or die "can't open $recent for writing: $!";
208              
209 0         0 for my $file (sort keys %{ $self->{recent} }) {
  0         0  
210 0 0       0 print {$recent_fh} "$file\n" or die "can't write to $recent: $!";
  0         0  
211             }
212              
213 0 0       0 die "error closing $recent: $!" unless close $recent_fh;
214 0         0 return;
215             }
216              
217             sub _get_mirror_list {
218 0     0   0 my $self = shift;
219              
220 0         0 my %mirror_list;
221              
222             # now walk the packages list
223 0         0 my $details = File::Spec->catfile(
224             $self->_scratch_dir,
225             qw(modules 02packages.details.txt.gz)
226             );
227              
228 0 0       0 my $gz = Compress::Zlib::gzopen($details, "rb")
229             or die "Cannot open details: $Compress::Zlib::gzerrno";
230              
231 0         0 my $inheader = 1;
232 0         0 my $file_ok = 0;
233 0         0 while ($gz->gzreadline($_) > 0) {
234 0 0       0 if ($inheader) {
235 0 0       0 if (/\S/) {
236 0         0 my ($header, $value) = split /:\s*/, $_, 2;
237 0         0 chomp $value;
238 0 0 0     0 if ($header eq 'File'
      0        
239             and ($value eq '02packages.details.txt'
240             or $value eq '02packages.details.txt.gz')) {
241 0         0 $file_ok = 1;
242             }
243             } else {
244 0         0 $inheader = 0;
245             }
246              
247 0         0 next;
248             }
249              
250 0 0       0 die "02packages.details.txt file is not a valid index\n"
251             unless $file_ok;
252              
253 0         0 my ($module, $version, $path) = split;
254 0 0       0 next if $self->_filter_module({
255             module => $module,
256             version => $version,
257             path => $path,
258             });
259              
260 0         0 $mirror_list{"authors/id/$path"}++;
261             }
262              
263 0         0 return [ sort keys %mirror_list ];
264             }
265              
266             #pod =method new
267             #pod
268             #pod my $minicpan = CPAN::Mini->new;
269             #pod
270             #pod This method constructs a new CPAN::Mini object. Its parameters are described
271             #pod above, under C.
272             #pod
273             #pod =cut
274              
275             sub new {
276 9     9 1 15 my $class = shift;
277 9         49 my %defaults = (
278             changes_made => 0,
279             dirmode => 0711, ## no critic Zero
280             errors => 1,
281             mirrored => {},
282             log_level => 'info',
283             );
284              
285 9         69 my $self = bless { %defaults, @_ } => $class;
286              
287 9 50       36 $self->{dirmode} = $defaults{dirmode} unless defined $self->{dirmode};
288              
289 9         21 $self->{recent} = {};
290              
291 9 50       25 Carp::croak "no local mirror supplied" unless $self->{local};
292              
293 9 50       29 substr($self->{local}, 0, 1, $class->__homedir)
294             if substr($self->{local}, 0, 1) eq q{~};
295              
296 9 50 33     357 Carp::croak "local mirror path exists but is not a directory"
297             if (-e $self->{local})
298             and not(-d $self->{local});
299              
300 9 50       131 unless (-e $self->{local}) {
301 0         0 File::Path::mkpath(
302             $self->{local},
303             {
304             verbose => $self->{log_level} eq 'debug',
305             mode => $self->{dirmode},
306             },
307             );
308             }
309              
310 9 50       186 Carp::croak "no write permission to local mirror" unless -w $self->{local};
311              
312 9 50       22 Carp::croak "no remote mirror supplied" unless $self->{remote};
313              
314 9 50       40 $self->{remote} = "$self->{remote}/" if substr($self->{remote}, -1) ne '/';
315              
316 9         104 my $version = $class->VERSION;
317 9 50       34 $version = 'v?' unless defined $version;
318              
319 9 50       91 $self->{__lwp} = LWP::UserAgent->new(
    50          
320             agent => "$class/$version",
321             env_proxy => 1,
322             ($self->{no_conn_cache} ? () : (keep_alive => 5)),
323             ($self->{timeout} ? (timeout => $self->{timeout}) : ()),
324             );
325              
326 9 50       14296 unless ($self->{offline}) {
327 0         0 my $test_uri = URI->new_abs(
328             'modules/02packages.details.txt.gz',
329             $self->{remote},
330             )->as_string;
331              
332             Carp::croak "unable to contact the remote mirror"
333 0 0       0 unless eval { $self->__lwp->head($test_uri)->is_success };
  0         0  
334             }
335              
336 9         77 return $self;
337             }
338              
339 0     0   0 sub __lwp { $_[0]->{__lwp} }
340              
341             #pod =method mirror_indices
342             #pod
343             #pod $minicpan->mirror_indices;
344             #pod
345             #pod This method updates the index files from the CPAN.
346             #pod
347             #pod =cut
348              
349             sub _fixed_mirrors {
350 0     0   0 qw(
351             authors/01mailrc.txt.gz
352             modules/02packages.details.txt.gz
353             modules/03modlist.data.gz
354             );
355             }
356              
357             sub _scratch_dir {
358 0     0   0 my ($self) = @_;
359              
360 0   0     0 $self->{scratch} ||= File::Temp::tempdir(CLEANUP => 1);
361 0         0 return $self->{scratch};
362             }
363              
364             sub mirror_indices {
365 0     0 1 0 my $self = shift;
366              
367 0         0 $self->_make_index_dirs($self->_scratch_dir);
368              
369 0         0 for my $path ($self->_fixed_mirrors) {
370 0         0 my $local_file = File::Spec->catfile($self->{local}, split m{/}, $path);
371 0         0 my $scratch_file = File::Spec->catfile(
372             $self->_scratch_dir,
373             split(m{/}, $path),
374             );
375              
376 0         0 File::Copy::copy($local_file, $scratch_file);
377              
378 0         0 utime((stat $local_file)[ 8, 9 ], $scratch_file);
379              
380 0         0 $self->mirror_file($path, undef, { to_scratch => 1 });
381             }
382             }
383              
384             sub _mirror_extras {
385 0     0   0 my $self = shift;
386              
387 0         0 for my $path (@{ $self->{also_mirror} }) {
  0         0  
388 0         0 $self->mirror_file($path, undef);
389             }
390             }
391              
392             sub _make_index_dirs {
393 0     0   0 my ($self, $base_dir, $dir_mode, $trace) = @_;
394 0   0     0 $base_dir ||= $self->_scratch_dir;
395 0 0       0 $dir_mode = 0711 if !defined $dir_mode; ## no critic Zero
396 0 0       0 $trace = 0 if !defined $trace;
397              
398 0         0 for my $index ($self->_fixed_mirrors) {
399 0         0 my $dir = File::Basename::dirname($index);
400 0         0 my $needed = File::Spec->catdir($base_dir, $dir);
401 0         0 File::Path::mkpath($needed, { verbose => $trace, mode => $dir_mode });
402 0 0       0 die "couldn't create $needed: $!" unless -d $needed;
403             }
404             }
405              
406             sub _install_indices {
407 0     0   0 my $self = shift;
408              
409 0         0 $self->_make_index_dirs(
410             $self->{local},
411             $self->{dirmode},
412             $self->{log_level} eq 'debug',
413             );
414              
415 0         0 for my $file ($self->_fixed_mirrors) {
416 0         0 my $local_file = File::Spec->catfile($self->{local}, split m{/}, $file);
417              
418 0         0 unlink $local_file;
419              
420 0         0 File::Copy::copy(
421             File::Spec->catfile($self->_scratch_dir, split m{/}, $file),
422             $local_file,
423             );
424              
425 0         0 $self->{mirrored}{$local_file} = 1;
426             }
427             }
428              
429             #pod =method mirror_file
430             #pod
431             #pod $minicpan->mirror_file($path, $skip_if_present)
432             #pod
433             #pod This method will mirror the given file from the remote to the local mirror,
434             #pod overwriting any existing file unless C<$skip_if_present> is true.
435             #pod
436             #pod =cut
437              
438             sub mirror_file {
439 0     0 1 0 my ($self, $path, $skip_if_present, $arg) = @_;
440              
441 0   0     0 $arg ||= {};
442              
443             # full URL
444 0 0       0 my $remote_uri = eval { $path->isa('URI') }
  0         0  
445             ? $path
446             : URI->new_abs($path, $self->{remote})->as_string;
447              
448             # native absolute file
449 0 0       0 my $local_file = File::Spec->catfile(
450             $arg->{to_scratch} ? $self->_scratch_dir : $self->{local},
451             split m{/}, $path
452             );
453              
454 0         0 my $checksum_might_be_up_to_date = 1;
455              
456 0 0 0     0 if ($skip_if_present and -f $local_file) {
    0 0        
457             ## upgrade to checked if not already
458 0   0     0 $self->{mirrored}{$local_file} ||= 1;
459             } elsif (($self->{mirrored}{$local_file} || 0) < 2) {
460             ## upgrade to full mirror
461 0         0 $self->{mirrored}{$local_file} = 2;
462              
463 0         0 File::Path::mkpath(
464             File::Basename::dirname($local_file),
465             {
466             verbose => $self->{log_level} eq 'debug',
467             mode => $self->{dirmode},
468             },
469             );
470              
471 0         0 $self->log($path, { no_nl => 1 });
472 0         0 my $res = $self->{__lwp}->mirror($remote_uri, $local_file);
473              
474 0 0       0 if ($res->is_success) {
    0          
475 0 0       0 utime undef, undef, $local_file if $arg->{update_times};
476 0         0 $checksum_might_be_up_to_date = 0;
477 0         0 $self->_recent($path);
478 0         0 $self->log(" ... updated");
479 0         0 $self->{changes_made}++;
480             } elsif ($res->code != 304) { # not modified
481 0         0 $self->log(" ... resulted in an HTTP error with status " . $res->code);
482 0         0 $self->log_warn("$remote_uri: " . $res->status_line);
483 0         0 return;
484             } else {
485 0         0 $self->log(" ... up to date");
486             }
487             }
488              
489 0 0       0 if ($path =~ m{^authors/id}) { # maybe fetch CHECKSUMS
490 0         0 my $checksum_path
491             = URI->new_abs("CHECKSUMS", $remote_uri)->rel($self->{remote})->as_string;
492              
493 0 0       0 if ($path ne $checksum_path) {
494 0         0 $self->mirror_file($checksum_path, $checksum_might_be_up_to_date);
495             }
496             }
497             }
498              
499             #pod =begin devel
500             #pod
501             #pod =method _filter_module
502             #pod
503             #pod next
504             #pod if $self->_filter_module({ module => $foo, version => $foo, path => $foo });
505             #pod
506             #pod This method holds the filter chain logic. C takes an optional
507             #pod set of filter parameters. As C encounters a distribution, it
508             #pod calls this method to figure out whether or not it should be downloaded. The
509             #pod user provided filters are taken into account. Returns 1 if the distribution is
510             #pod filtered (to be skipped). Returns 0 if the distribution is to not filtered
511             #pod (not to be skipped).
512             #pod
513             #pod =end devel
514             #pod
515             #pod =cut
516              
517             sub __do_filter {
518 45     45   77 my ($self, $filter, $file) = @_;
519 45 100       139 return unless $filter;
520              
521 39 100       96 if (ref($filter) eq 'ARRAY') {
522 11         24 for (@$filter) {
523 25 100       58 return 1 if $self->__do_filter($_, $file);
524             }
525 4         33 return;
526             }
527              
528 28 100       60 if (ref($filter) eq 'CODE') {
529 6         17 return $filter->($file);
530             } else {
531 22         167 return $file =~ $filter;
532             }
533             }
534              
535             sub _filter_module {
536 19     19   64 my $self = shift;
537 19         25 my $args = shift;
538              
539 19 100       58 if ($self->{skip_perl}) {
540 6 100       56 return 1 if $args->{path} =~ m{/(?:emb|syb|bio)?perl-\d}i;
541 3 100       27 return 1 if $args->{path} =~ m{/(?:parrot|ponie)-\d}i;
542 1 50       7 return 1 if $args->{path} =~ m{/(?:kurila)-\d}i;
543 1 50       7 return 1 if $args->{path} =~ m{/\bperl-?5\.004}i;
544 1 50       6 return 1 if $args->{path} =~ m{/\bperl_mlb\.zip}i;
545             }
546              
547 14 100       95 return 1 if $self->__do_filter($self->{path_filters}, $args->{path});
548 6 50       23 return 1 if $self->__do_filter($self->{module_filters}, $args->{module});
549 6         28 return 0;
550             }
551              
552             #pod =method file_allowed
553             #pod
554             #pod next unless $minicpan->file_allowed($filename);
555             #pod
556             #pod This method returns true if the given file is allowed to exist in the local
557             #pod mirror, even if it isn't one of the required mirror files.
558             #pod
559             #pod By default, only dot-files are allowed. If the C option is true,
560             #pod all files are allowed.
561             #pod
562             #pod =cut
563              
564             sub file_allowed {
565 0     0 1 0 my ($self, $file) = @_;
566 0 0       0 return 1 if $self->{exact_mirror};
567              
568             # It's a cheap hack, but it gets the job done.
569 0 0       0 return 1 if $file eq File::Spec->catfile($self->{local}, 'RECENT');
570              
571 0 0       0 return (substr(File::Basename::basename($file), 0, 1) eq q{.}) ? 1 : 0;
572             }
573              
574             #pod =method clean_unmirrored
575             #pod
576             #pod $minicpan->clean_unmirrored;
577             #pod
578             #pod This method looks through the local mirror's files. If it finds a file that
579             #pod neither belongs in the mirror nor is allowed (see the C method),
580             #pod C is called on the file.
581             #pod
582             #pod If you set C to a true value, then this doesn't clean
583             #pod up files that belong to source control systems. Currently this ignores:
584             #pod
585             #pod .cvs .cvsignore
586             #pod .svn .svnignore
587             #pod .git .gitignore
588             #pod
589             #pod Send patches for other source control files that you would like to have added.
590             #pod
591             #pod =cut
592              
593             my %Source_control_files;
594             BEGIN {
595 4     4   17 %Source_control_files = map { $_ => 1 }
  24         8985  
596             qw(.cvs .svn .git .cvsignore .svnignore .gitignore);
597             }
598              
599             sub clean_unmirrored {
600 0     0 1 0 my $self = shift;
601              
602             File::Find::find sub {
603 0     0   0 my $file = File::Spec->canonpath($File::Find::name); ## no critic Package
604 0         0 my $basename = File::Basename::basename( $file );
605              
606 0 0 0     0 if (
607             $self->{ignore_source_control}
608             and exists $Source_control_files{$basename}
609             ) {
610 0         0 $File::Find::prune = 1;
611 0         0 return;
612             }
613              
614 0 0 0     0 return unless (-f $file and not $self->{mirrored}{$file});
615 0 0       0 return if $self->file_allowed($file);
616              
617 0         0 $self->clean_file($file);
618              
619 0         0 }, $self->{local};
620             }
621              
622             #pod =method clean_file
623             #pod
624             #pod $minicpan->clean_file($filename);
625             #pod
626             #pod This method, called by C, deletes the named file. It returns
627             #pod true if the file is successfully unlinked. Otherwise, it returns false.
628             #pod
629             #pod =cut
630              
631             sub clean_file {
632 0     0 1 0 my ($self, $file) = @_;
633              
634 0 0       0 unless (unlink $file) {
635 0         0 $self->log_warn("$file cannot be removed: $!");
636 0         0 return;
637             }
638              
639 0         0 $self->log("$file removed");
640              
641 0         0 return 1;
642             }
643              
644             #pod =method log_warn
645             #pod
646             #pod =method log
647             #pod
648             #pod =method log_debug
649             #pod
650             #pod $minicpan->log($message);
651             #pod
652             #pod This will log (print) the given message unless the log level is too low.
653             #pod
654             #pod C, which logs at the I level, may also be called as C for
655             #pod backward compatibility reasons.
656             #pod
657             #pod =cut
658              
659             sub log_level {
660 7 50   7 0 3364 return $_[0]->{log_level} if ref $_[0];
661 0         0 return 'info';
662             }
663              
664             sub log_unconditionally {
665 0     0 0 0 my ($self, $message, $arg) = @_;
666 0   0     0 $arg ||= {};
667              
668 0 0       0 print($message, $arg->{no_nl} ? () : "\n");
669             }
670              
671             sub log_warn {
672 0 0   0 1 0 return if $_[0]->log_level eq 'fatal';
673 0         0 $_[0]->log_unconditionally($_[1], $_[2]);
674             }
675              
676             sub log {
677 0 0   0 1 0 return unless $_[0]->log_level =~ /\A(?:info|debug)\z/;
678 0         0 $_[0]->log_unconditionally($_[1], $_[2]);
679             }
680              
681             sub trace {
682 0     0 0 0 my $self = shift;
683 0         0 $self->log(@_);
684             }
685              
686             sub log_debug {
687 0     0 1 0 my ($self, @rest) = @_;
688 0 0       0 return unless $_[0]->log_level eq 'debug';
689 0         0 $_[0]->log_unconditionally($_[1], $_[2]);
690             }
691              
692             #pod =method read_config
693             #pod
694             #pod my %config = CPAN::Mini->read_config(\%options);
695             #pod
696             #pod This routine returns a set of arguments that can be passed to CPAN::Mini's
697             #pod C or C methods. It will look for a file called
698             #pod F<.minicpanrc> in the user's home directory as determined by
699             #pod L.
700             #pod
701             #pod =cut
702              
703             sub __homedir {
704 18     18   18 my ($class) = @_;
705              
706 18   33     63 my $homedir = File::HomeDir->my_home || $ENV{HOME};
707              
708 18 50       94 Carp::croak "couldn't determine your home directory! set HOME env variable"
709             unless defined $homedir;
710              
711 18         181 return $homedir;
712             }
713              
714             sub __homedir_configfile {
715 18     18   26 my ($class) = @_;
716 18         40 my $default = File::Spec->catfile($class->__homedir, '.minicpanrc');
717             }
718              
719             sub __default_configfile {
720 0     0   0 my ($self) = @_;
721              
722 0         0 (my $pm_loc = $INC{'CPAN/Mini.pm'}) =~ s/Mini\.pm\z//;
723 0         0 File::Spec->catfile($pm_loc, 'minicpan.conf');
724             }
725              
726             sub read_config {
727 9     9 1 17 my ($class, $options) = @_;
728              
729 9         27 my $config_file = $class->config_file($options);
730              
731 9 100       34 return unless defined $config_file;
732              
733             # This is ugly, but lets us respect -qq for now even before we have an
734             # object. I think a better fix is warranted. -- rjbs, 2010-03-04
735 2 50 50     13 $class->log("Using config from $config_file")
736             if ($options->{log_level}||'info') =~ /\A(?:warn|fatal)\z/;
737              
738 2 50       8 substr($config_file, 0, 1, $class->__homedir)
739             if substr($config_file, 0, 1) eq q{~};
740              
741 2 50       31 return unless -e $config_file;
742              
743 2 50       71 open my $config_fh, '<', $config_file
744             or die "couldn't open config file $config_file: $!";
745              
746 2         3 my %config;
747 2         4 my %is_multivalue = map {; $_ => 1 }
  6         16  
748             qw(also_mirror module_filters path_filters);
749              
750 2         10 $config{$_} = [] for keys %is_multivalue;
751              
752 2         33 while (<$config_fh>) {
753 2         6 chomp;
754 2 50       9 next if /\A\s*\Z/sm;
755              
756 2 50       14 if (/\A(\w+):\s*(\S.*?)\s*\Z/sm) {
757 2         6 my ($key, $value) = ($1, $2);
758              
759 2 50       5 if ($is_multivalue{ $key }) {
760 0         0 push @{ $config{$key} }, split /\s+/, $value;
  0         0  
761             } else {
762 2         17 $config{ $key } = $value;
763             }
764             }
765             }
766              
767 2         5 for (qw(also_mirror)) {
768 2         3 $config{$_} = [ grep { length } @{ $config{$_} } ];
  0         0  
  2         9  
769             }
770              
771 2         5 for (qw(module_filters path_filters)) {
772 4         5 $config{$_} = [ map { qr/$_/ } @{ $config{$_} } ];
  0         0  
  4         11  
773             }
774              
775 2         7 for (keys %is_multivalue) {
776 6 50       7 delete $config{$_} unless @{ $config{$_} };
  6         21  
777             }
778              
779 2         36 return %config;
780             }
781              
782             #pod =method config_file
783             #pod
784             #pod my $config_file = CPAN::Mini->config_file( { options } );
785             #pod
786             #pod This routine returns the config file name. It first looks at for the
787             #pod C setting, then the C environment
788             #pod variable, then the default F<~/.minicpanrc>, and finally the
789             #pod F. It uses the first defined value it finds.
790             #pod If the filename it selects does not exist, it returns false.
791             #pod
792             #pod OPTIONS is an optional hash reference of the C config hash.
793             #pod
794             #pod =cut
795              
796             sub config_file {
797 16     16 1 7032 my ($class, $options) = @_;
798              
799 16         22 my $config_file = do {
800 16 100       22 if (defined eval { $options->{config_file} }) {
  16 100       94  
    100          
    100          
801 1         3 $options->{config_file};
802             } elsif (defined $ENV{CPAN_MINI_CONFIG}) {
803 3         5 $ENV{CPAN_MINI_CONFIG};
804             } elsif (defined $class->__homedir_configfile) {
805 10         25 $class->__homedir_configfile;
806             } elsif (defined $class->__default_configfile) {
807 1         10 $class->__default_configfile;
808             } else {
809 1         29 ();
810             }
811             };
812              
813             return (
814 16 100 100     418 (defined $config_file && -e $config_file)
815             ? $config_file
816             : ()
817             );
818             }
819              
820             #pod =head2 remote_from
821             #pod
822             #pod my $remote = CPAN::Mini->remote_from( $remote_from, $orig_remote, $quiet );
823             #pod
824             #pod This routine take an string argument and turn it into a method
825             #pod call to handle to retrieve the a cpan mirror url from a source.
826             #pod Currently supported methods:
827             #pod
828             #pod cpan - fetch the first mirror from your CPAN.pm config
829             #pod cpanplus - fetch the first mirror from your CPANPLUS.pm config
830             #pod
831             #pod =cut
832              
833             sub remote_from {
834 0     0 1   my ( $class, $remote_from, $orig_remote, $quiet ) = @_;
835              
836 0           my $method = lc "remote_from_" . $remote_from;
837              
838 0 0         Carp::croak "unknown remote_from value: $remote_from"
839             unless $class->can($method);
840              
841 0           my $new_remote = $class->$method;
842              
843 0 0 0       warn "overriding '$orig_remote' with '$new_remote' via $method\n"
844             if !$quiet && $orig_remote;
845              
846 0           return $new_remote;
847             }
848              
849             #pod =head2 remote_from_cpan
850             #pod
851             #pod my $remote = CPAN::Mini->remote_from_cpan;
852             #pod
853             #pod This routine loads your CPAN.pm config and returns the first mirror in mirror
854             #pod list. You can set this as your default by setting remote_from:cpan in your
855             #pod F<.minicpanrc> file.
856             #pod
857             #pod =cut
858              
859             sub remote_from_cpan {
860 0     0 1   my ($self) = @_;
861              
862             Carp::croak "unable find a CPAN, maybe you need to install it"
863 0 0         unless eval { require CPAN; 1 };
  0            
  0            
864              
865 0           CPAN::HandleConfig::require_myconfig_or_config();
866              
867 0 0         Carp::croak "unable to find mirror list in your CPAN config"
868             unless exists $CPAN::Config->{urllist};
869              
870 0 0 0       Carp::croak "unable to find first mirror url in your CPAN config"
871             unless ref( $CPAN::Config->{urllist} ) eq 'ARRAY' && $CPAN::Config->{urllist}[0];
872              
873 0           return $CPAN::Config->{urllist}[0];
874             }
875              
876             #pod =head2 remote_from_cpanplus
877             #pod
878             #pod my $remote = CPAN::Mini->remote_from_cpanplus;
879             #pod
880             #pod This routine loads your CPANPLUS.pm config and returns the first mirror in
881             #pod mirror list. You can set this as your default by setting remote_from:cpanplus
882             #pod in your F<.minicpanrc> file.
883             #pod
884             #pod =cut
885              
886             sub remote_from_cpanplus {
887 0     0 1   my ($self) = @_;
888              
889             Carp::croak "unable find a CPANPLUS, maybe you need to install it"
890 0 0         unless eval { require CPANPLUS::Backend };
  0            
891              
892 0           my $cb = CPANPLUS::Backend->new;
893 0           my $hosts = $cb->configure_object->get_conf('hosts');
894              
895 0 0         Carp::croak "unable to find mirror list in your CPANPLUS config"
896             unless $hosts;
897              
898 0 0 0       Carp::croak "unable to find first mirror in your CPANPLUS config"
899             unless ref($hosts) eq 'ARRAY' && $hosts->[0];
900              
901 0           my $url_parts = $hosts->[0];
902 0   0       return $url_parts->{scheme} . "://" . $url_parts->{host} . ( $url_parts->{path} || '' );
903             }
904              
905             #pod =head1 SEE ALSO
906             #pod
907             #pod Randal Schwartz's original article on minicpan, here:
908             #pod
909             #pod http://www.stonehenge.com/merlyn/LinuxMag/col42.html
910             #pod
911             #pod L, which provides the C method, which performs
912             #pod the same task as this module.
913             #pod
914             #pod =head1 THANKS
915             #pod
916             #pod Thanks to David Dyck for letting me know about my stupid documentation errors.
917             #pod
918             #pod Thanks to Roy Fulbright for finding an obnoxious bug on Win32.
919             #pod
920             #pod Thanks to Shawn Sorichetti for fixing a stupid octal-number-as-string bug.
921             #pod
922             #pod Thanks to sungo for implementing the filters, so I can finally stop mirroring
923             #pod bioperl, and Robert Rothenberg for suggesting adding coderef rules.
924             #pod
925             #pod Thanks to Adam Kennedy for noticing and complaining about a lot of stupid
926             #pod little design decisions.
927             #pod
928             #pod Thanks to Michael Schwern and Jason Kohles, for pointing out missing
929             #pod documentation.
930             #pod
931             #pod Thanks to David Golden for some important bugfixes and refactoring.
932             #pod
933             #pod =cut
934              
935             1;
936              
937             __END__