CPAN-Mini-Inject-1.003/0000755000076500000240000000000014631615434013327 5ustar brianstaffCPAN-Mini-Inject-1.003/bin/0000755000076500000240000000000014631615434014077 5ustar brianstaffCPAN-Mini-Inject-1.003/bin/mcpani0000644000076500000240000001724314631615433015277 0ustar brianstaff#!/usr/bin/perl -w use strict; use Pod::Usage 1.12; use Getopt::Long; use YAML qw( Load ); use CPAN::Mini::Inject; use Env; use File::Slurp 'write_file'; use File::Temp; our $VERSION = '0.37'; our %options = (); sub print_version { printf( "mcpani v%s, using CPAN::Mini::Inject v%s and Perl v%vd\n", $VERSION, $CPAN::Mini::Inject::VERSION, $^V ); } sub chkactions { for my $action ( qw(add update mirror inject) ) { return 1 if ( $options{actionname} eq $action ); } return 0; } sub setsub { $options{actionname} = shift; $options{action} = shift; } sub add { my $mcpi = shift; $mcpi->readlist; $mcpi->add( module => $options{module}, authorid => $options{authorid}, version => $options{version}, file => $options{file} ); if ( $options{verbose} ) { my @added = $mcpi->added_modules; foreach my $added ( @added ){ print "\nAdding File: $added->{file}\n"; print "Author ID: $added->{authorid}\n"; my $modules = $added->{modules}; foreach my $mod ( sort keys %$modules ){ print "Module: $mod\n"; print "Version: $modules->{$mod}\n"; } print "To repository: $mcpi->{config}{repository}\n\n"; } } $mcpi->writelist; } sub update { my $mcpi = shift; mirror( $mcpi ); inject( $mcpi ); } sub mirror { my $mcpi = shift; my %mirroropts; $mirroropts{remote} = $options{remote} if ( defined( $options{remote} ) ); $mirroropts{local} = $options{local} if ( defined( $options{local} ) ); $mirroropts{trace} = $options{verbose} if ( defined( $options{verbose} ) ); $mcpi->update_mirror( %mirroropts ); } sub inject { my $mcpi = shift; print "Injecting modules from $mcpi->{config}{repository}\n" if ( $options{verbose} ); $mcpi->inject( $options{verbose} ); } # MAIN Getopt::Long::Configure( 'no_ignore_case' ); Getopt::Long::Configure( 'bundling' ); GetOptions( 'h|help|?' => sub { pod2usage( { -verbose => 1, -input => \*DATA } ); exit }, 'H|man' => sub { pod2usage( { -verbose => 2, -input => \*DATA } ); exit }, 'V|version' => sub { print_version(); exit; }, 'v|verbose' => \$options{verbose}, 'l|local=s' => \$options{local}, 'r|remote=s' => \$options{remote}, 'p|passive' => \$ENV{FTP_PASSIVE}, 'add' => sub { setsub( 'add', \&add ) }, 'update' => sub { setsub( 'update', \&update ) }, 'mirror' => sub { setsub( 'mirror', \&mirror ) }, 'inject' => sub { setsub( 'inject', \&inject ) }, 'module=s' => \$options{module}, 'authorid=s' => \$options{authorid}, 'modversion=s' => \$options{version}, 'file=s' => \$options{file}, 'all-in-meta' => \$options{'all-in-meta'}, 'signing-key=s' => \$options{'signing_key'}, 'discover-packages' => \$options{'discover-packages'}, ) or exit 1; unless ( defined( $options{action} ) && chkactions() ) { pod2usage( { -verbose => 1, -input => \*DATA } ); exit; } my $mcpi = CPAN::Mini::Inject->new->loadcfg( $options{cfg} )->parsecfg; $CPAN::Checksums::SIGNING_KEY = $options{'signing_key'} if ($options{'signing_key'}); &{ $options{action} }( $mcpi ); __END__ =head1 NAME mcpani -- A command line tool to manage a CPAN Mini Mirror. =head1 SYNOPSIS mcpani [options] < --add | --update | --mirror | --inject > Commands: --add Add a new package to the repository --module Name of the module to add --authorid Author ID of the module --modversion Version number of the module --file tar.gz file of the module --update Update local CPAN mirror and inject modules --mirror Update local CPAN mirror from remote --inject Add modules from repository to CPAN mirror Options: -h, --help This synopsis -H, --man Detailed description -l, --local local location for CPAN::Mini Mirror -r, --remote CPAN mirror to mirror from -p, --passive Enable passive ftp for mirroring. -v, --verbose verbose output -V, --version Version information. --signing-key See CPAN::Checksums $SIGNING_KEY =head1 DESCRIPTION mcpani uses CPAN::Mini to build or update a I CPAN mirror from a I one. It adds two extra features: 1. an additional I of distribution files and related information (author and module versions), separate from the local and remote mirrors, to which you can add your own distribution files. 2. the ability to I the distribution files from your I into a I CPAN mirror. =head1 COMMAND LINE OPTIONS =head2 --add Add a module to the repository for later inclusion in a CPAN Mini mirror. The add command requires the following parameters: =over 4 =item --module This is the name of the module (ie CPAN::Mini::Inject). =item --authorid A CPAN 'like' author ID for the module. The author ID does not need to exist on CPAN. =item --modversion Version number of the module. This must match the version number in the file name. =item --all-in-meta =item --discover-packages L adds all modules found in the file. These options remain for backward compatibility and do nothing. =item --file File name and path of the module. The file name must follow the standard CPAN naming convention (the resulting file from a C). =back Example: mcpani --add --module CPAN::Mini::Inject --authorid SSORICHE --modversion 0.01 --file ./CPAN-Mini-Inject-0.01.tar.gz =head2 --update Update your local CPAN Mini mirror from a CPAN site. Once completed add the modules contained in the repository to it. This is the same as running C followed by C =head2 --mirror Update the local CPAN Mini mirror from CPAN. =head2 --inject Add the repository modules into the CPAN Mini mirror. =head2 -l, --local A local directory to store the CPAN Mini mirror in. Specifying this option overrides the value in the config file. =head2 -r, --remote A CPAN site to create the local CPAN Mini mirror from. =head2 -v, --verbose Display verbose processing information =head2 -V, --version Display version information. =head1 CONFIGURATION FILE F uses a simple configuration file in the following format: local: /www/CPAN remote: ftp://ftp.cpan.org/pub/CPAN ftp://ftp.kernel.org/pub/CPAN repository: /work/mymodules passive: yes dirmode: 0755 Description of options: =over 4 =item * local location to store local CPAN::Mini mirror (*REQUIRED*) =item * remote CPAN site(s) to mirror from. Multiple sites can be listed, with spaces between them. (*REQUIRED*) =item * repository Location to store modules to add to the local CPAN::Mini mirror. =item * passive Enable passive FTP. =item * dirmode Set the permissions of created directories to the specified mode (octal value). The default value is based on the umask (if supported). =back F will search the following four places in order: =over 4 =item * file pointed to by the environment variable MCPANI_CONFIG =item * $HOME/.mcpani/config =item * /usr/local/etc/mcpani =item * /etc/mcpani =back =head1 CURRENT MAINTAINER Christian Walde C<< >> =head1 AUTHOR Shawn Sorichetti C<< >> =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 Copyright & License Copyright 2004 Shawn Sorichetti, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut CPAN-Mini-Inject-1.003/Changes0000644000076500000240000001371014631615433014623 0ustar brianstaffRevision history for CPAN-Mini-Inject 1.003 2024-06-10T08:56:12Z * left mcpani out of EXE_FILES (#3) 1.002 2024-06-07T19:31:11Z * promote to a user version 1.001_02 2024-05-29T00:56:52Z * Adjust the test for unreachable hosts when the tester resolves any host 1.001_01 2024-05-26T12:27:59Z * fix bug in t/update_mirror.t that caused weird issues when the test web server went away (AndyA/CPAN--Mini--Inject#26, from Lukas Märdian) * added --skip_cleanup for updating a mirror (AndyA/CPAN--Mini--Inject#1, from Matt Lanier) * improved some docs (AndyA/CPAN--Mini--Inject#18, from Tim Bunce) * verbose now reports each time a module is injected (AndyA/CPAN--Mini--Inject#21, from Wolfgang Pecho) * repo is now https://github.com/briandfoy/cpan-mini-inject . Added GitHub workflows too. * tests can now be run in parallel. 0.37 2023-06-06 * add a test dependency 0.36 2023-06-06 * implement compatibility with new toolchain cpan_path regime * skip permission tests on cygwin 0.35 2017-07-19 * use Net::EmptyPort to find an empty port to use instead of a hard-coded one 0.34 2017-07-18 Konstantin Yakunin : * sort packages case insensitively 0.33 2013-07-30 Wolfgang Pecho : * allow spaces in configuration file 0.32 2013-04-17 Karen Etheridge : * add link for mcpani Randy Stauner : * Add module name to verbose "injected" message Salve J. Nilsen : * Inject distributions once per file instead of once per module 0.31 2011-10-10 Randy Stauner : * outsource module discovery to Dist::Metadata * moved module discovery from script to CPAN::Mini::Inject 0.30 2011-06-13 * keep just the last added module, even if there was one in 02packages.details.txt before we injected (Paul Driver ) * documentation fix (Randy Stauner ) 0.29 2011-05-15 * Skip tests that are unreliable under a DNS regime where any hostname resolves. * RT 63350 - Anchor the regex to filter old modules * RT 55463 - Don't use .bak for test files * RT 63407 - Win32 fixes related to file locks in tests * Changed tests so they'll be able to deal with read-only dist files 0.27 2010-01-28 All the following are thanks to Jozef Kutej : * --discover-packages option added to parse .pm files inside --file and extract package names andV versions. * allow CHECKSUMS signing. * keep just last added module version in 02packages.details.txt. 0.26 2009-06-15 * Specify minimum File::Path version 0.25 2009-05-29 * Move to GitHub 0.24 2009-05-10 * Made tests use a port (11027) other than 8080 to avoid clashes with existing services. 0.23 2008-10-25 * Rebundled to remove OS X extended attributes that were causing tests to fail. 0.22 2008-06-25 * fixed a bug where authors were being added more than once to 01mailrc.txt.gz 0.21 2008-06-25 * changes for RT bug 17386 by: David Bartle 0.20 2008-06-25 * added a command line option '--all-in-meta' which will parse the META.yml and index every module listed in the 'provides' section * module name and version is now parsed correctly from the filename if relative or absolute paths are specified * changes for RT bug 37093 by: David Bartle 0.18.1 2008-06-25 * Fixed bug number 28363 http://rt.cpan.org/Ticket/Display.html?id=28363 0.18 2005-04-02 * Fixed bug number 11718 involving the Line-Count in 02packages.details.txt.gz not being updated. * Added tests for update_mirror method. * Migrated tests to HTTP::Server::Simple. 0.16 2005-01-08 * Decreased the size of t/read/authors/01mailrc.txt.gz reducing greatly the size of the package. * More problems discovered with exception testing. Moved all exception tests to a single zz.exceptions.t, skip them all if Test::Exception isn't installed. 0.14 2005-01-05 * Added an optional filename parameter to parsecfg. This allows calling parsecfg without having to previously call loadcfg (tests added as required). * Updated Synopsis to not using method chaining, but documented that it is possible. * Updated prereq of CPAN::Mini to version 0.32 * Fixed a warning message if dirmode isn't configured during the inject process. * Fixed update_mirror to call CPAN::Mini->update_mirror directly fixing a problem with CPAN::Mini::Inject and the latest version of CPAN::Mini (0.32). 0.12 2005-01-04 * Tests failed for inject and add on Windows systems. These tests have now been skipped. * Reverted all tests to use eval{}. Using eval "" caused problems on Windows, while eval{} only caused problems on one AIX box. 0.10 2004-12-30 * Fixed dirmode in inject(), add(), writelist(), so that all files created are set with the correct permissions. * Added tests for directory and file modes. 0.08 2004-12-08 * Tests were found to fail on AIX which contained eval{}, all tests updated with eval "". * Added a default value for dirmode in update_mirror based on umask. * Added a dirmode option to the config file. * Fixed regex in mcpani to allow for developer version numbers with add (ie. CPAN-Mini-Inject-0.07_001). * Add a prereq for CPAN::Mini version 0.24. This version contains a bugfix to properly handle dirmode. (Thanks RJBS). 0.06 2004-11-10 * mcpani now parses the file passed to it to determine module name and version. Command line options override the parsing. * loadcfg() croaks if no configuration file is found. 0.04 2004-11-05 * t/readlist.t and t/writelist.t both skip failure tests if the uid is 0. * inject accepts a true parameter to enable verbose mode which lists modules as they are injected. * testremote accepts a true parameter to enable verbose mode which display the site being tested, and which is selected. * Added --passive switch to mcpani to enable passive ftp * Minor updates to perldoc in mcpani * Added CPAN::Mini as a required module to Makefile.PL 0.02 2004-10-31 * First version, released on an unsuspecting world. CPAN-Mini-Inject-1.003/MANIFEST0000644000076500000240000000230314631615434014456 0ustar brianstaffbin/mcpani Changes lib/CPAN/Mini/Inject.pm lib/CPAN/Mini/Inject/Config.pm Makefile.PL MANIFEST MANIFEST.SKIP README.pod t/.mcpani/config t/.mcpani/config_bad t/.mcpani/config_badremote t/.mcpani/config_mcpi t/.mcpani/config_noread t/.mcpani/config_norepo t/.mcpani/config_nowrite t/.mcpani/config_with_whitespaces t/add-multiple.t t/add.t t/exceptions.t t/html/01mailrc.txt.gz t/html/02packages.details.txt.gz t/html/03modlist.data.gz t/html/CHECKSUMS t/html/CPAN-Mini-2.1828.tar.gz t/html/CPAN-Mini-Inject-1.01.tar.gz t/html/index.html t/inject.t t/lib/Local/localserver.pm t/lib/Local/utils.pm t/loadcfg.t t/local/01mailrc.txt.gz.original t/local/CPAN/modules/02packages.details.txt.gz.original t/local/mymodules/CPAN-Mini-0.17.tar.gz t/local/mymodules/CPAN-Mini-Inject-0.01.tar.gz t/local/mymodules/Dist-Metadata-Test-MetaFile-2.2.tar.gz t/local/mymodules/Dist-Metadata-Test-MetaFile-Only.tar.gz t/local/mymodules/not-discoverable.tar.gz t/parsecfg.t t/pod-coverage.t t/pod.t t/private.t t/readlist.t t/testremote.t t/update_mirror.t t/writelist.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) CPAN-Mini-Inject-1.003/t/0000755000076500000240000000000014631615434013572 5ustar brianstaffCPAN-Mini-Inject-1.003/t/pod.t0000644000076500000240000000020114631615433014531 0ustar brianstaffuse Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); CPAN-Mini-Inject-1.003/t/parsecfg.t0000644000076500000240000000267614631615433015563 0ustar brianstaffuse strict; use warnings; use Test::More; use File::Spec::Functions qw(catfile); my $class = 'CPAN::Mini::Inject'; subtest sanity => sub { use_ok $class or BAIL_OUT( "$class did not compile: $@" ); }; subtest 'loadcfg' => sub { my $mcpi = $class->new; isa_ok $mcpi, $class; my $file = catfile qw(t .mcpani config); ok -e $file, "file <$file> exists"; $mcpi->loadcfg( $file ); $mcpi->parsecfg; ok exists $mcpi->{config}, 'config key exists'; is( $mcpi->{config}{local}, 't/local/CPAN' ); is( $mcpi->{config}{remote}, 'http://localhost:11027' ); is( $mcpi->{config}{repository}, 't/local/MYCPAN' ); }; subtest 'no loadcfg' => sub { my $mcpi = $class->new; isa_ok $mcpi, $class; my $file = catfile qw(t .mcpani config); ok -e $file, "file <$file> exists"; $mcpi->parsecfg( $file ); is( $mcpi->{config}{local}, 't/local/CPAN' ); is( $mcpi->{config}{remote}, 'http://localhost:11027' ); is( $mcpi->{config}{repository}, 't/local/MYCPAN' ); }; subtest 'whitespace' => sub { my $mcpi = $class->new; isa_ok $mcpi, $class; my $file = catfile qw(t .mcpani config_with_whitespaces); ok -e $file, "file <$file> exists"; $mcpi->parsecfg( $file ); is( $mcpi->{config}{local}, 't/local/CPAN' ); is( $mcpi->{config}{remote}, 'http://localhost:11027' ); is( $mcpi->{config}{repository}, 't/local/MYCPAN' ); is( $mcpi->{config}{dirmode}, '0775' ); is( $mcpi->{config}{passive}, 'yes' ); }; done_testing(); CPAN-Mini-Inject-1.003/t/inject.t0000644000076500000240000001552514631615433015242 0ustar brianstaffuse strict; use warnings; use Test::More; use File::Path qw(make_path); use File::Copy; use File::Temp (); use File::Basename; use File::Spec::Functions qw(catfile); use Compress::Zlib; use lib qw(t/lib); use Local::utils; my $class = 'CPAN::Mini::Inject'; my $temp_dir = File::Temp::tempdir(CLEANUP=>1); =begin comment C is the URL for the repo from which we'll download latest versions C is our MiniCPAN C is the dir where we will keep the modules to inject =end comment =cut subtest 'sanity' => sub { use_ok $class or BAIL_OUT( "Could not load $class: $@" ); can_ok $class, 'new'; isa_ok $class->new, $class; }; subtest 'setup directories in temp dir' => sub { my @dirs = ( [ qw(modules) ], [ qw(authors) ], [ qw(injects) ], ); foreach my $dir ( @dirs ) { my $path = catfile $temp_dir, @$dir; make_path( $path ); ok -d $path, "Path for <@$dir> exists"; } }; my $t_local = catfile qw(t local); subtest 'check local dir' => sub { ok -d $t_local, 'local directory exists'; }; subtest 'copy initial files' => sub { my $modules_base = catfile $temp_dir, 'modules'; ok -d $modules_base, 'modules dir exists'; my $authors_base = catfile $temp_dir, 'authors'; ok -d $authors_base, 'authors dir exists'; subtest 'packages' => sub { my $file = '02packages.details.txt.gz'; my $destination = catfile $modules_base, $file; my $rc = copy( catfile( $t_local, 'CPAN', 'modules', "$file.original" ), $destination ); ok $rc, 'File::Copy worked'; ok -e $destination, 'Copied packages file to temp_dir'; ok chmod(0666, $destination), 'chmod packages to 0666'; }; subtest 'mailrc' => sub { my $file = '01mailrc.txt.gz'; my $destination = catfile $authors_base, $file; my $rc = copy( catfile( $t_local, "$file.original" ), $destination ); ok $rc, 'File::Copy worked'; ok -e $destination, 'Copied mailrc file to temp_dir'; ok chmod(0666, $destination), 'chmod mailrc to 0666'; }; }; sub get_module_details { my( $dist_sources ) = @_; my @modules = ( { module => 'CPAN::Mini::Inject', authorid => 'SSORICHE', version => '0.01', file => catfile( $dist_sources, 'CPAN-Mini-Inject-0.01.tar.gz' ), }, { module => 'CPAN::Mini::Inject', authorid => 'SSORICHE', version => '0.02', file => catfile( $dist_sources, 'CPAN-Mini-Inject-0.01.tar.gz' ), }, { module => 'CPAN::Mini', authorid => 'RJBS', version => '0.17', file => catfile( $dist_sources, 'CPAN-Mini-0.17.tar.gz' ), }, ); } subtest 'inject the modules' => sub { my $dist_sources = catfile $t_local, 'mymodules'; ok -d $dist_sources, 'Dist sources directory exists'; my @modules = get_module_details( $dist_sources ); my $tmp_config_file; subtest 'make config' => sub { $tmp_config_file = write_config( local => $temp_dir, repository => catfile( $temp_dir, 'injects' ), ); ok -e $tmp_config_file, 'configuration file exists'; }; my $mcpi = $class->new; isa_ok $mcpi, $class; $mcpi = $mcpi->loadcfg( $tmp_config_file )->parsecfg->readlist; foreach my $module ( @modules ) { ok -e $module->{file}, "module file <$module->{file}> exists"; $mcpi = $mcpi->add( %$module ); } subtest 'writelist' => sub { ok $mcpi->writelist, 'inject modules'; }; subtest 'inject' => sub { ok $mcpi->inject( $ENV{TEST_VERBOSE} // 0 ), 'copy modules'; }; subtest 'check the result' => sub { my $authors_dir = catfile $temp_dir, 'authors'; ok -e $authors_dir, 'authors dir exists'; foreach my $module ( @modules ) { subtest "check $module->{file}" => sub { my $author_stub = catfile( $authors_dir, 'id', substr( $module->{authorid}, 0, 1 ), substr( $module->{authorid}, 0, 2 ), $module->{authorid} ); ok -d $author_stub, "author directory $author_stub for $module->{authorid} exists"; is( mode($author_stub), 0775, 'author dir mode is 775' ) if has_modes(); my $module_basename = basename $module->{file}; my $module_path = catfile $author_stub, $module_basename; ok -e $module_path, "$module_basename exists in local"; is( mode($module_path), 0664, 'moduole filr is mode is 664' ) if has_modes(); subtest 'CHECKSUMS' => sub { my $checksums_path = catfile $author_stub, 'CHECKSUMS'; my $rc = ok -e $checksums_path, "CHECKSUMS file for $module->{authorid} exists"; is( mode($checksums_path), 0664, 'checksum file mode is 664' ) if has_modes(); if( $rc ) { my $rc = open my $chk, '<', $checksums_path; my $checksum_text = join "", <$chk>; close $chk; unlike $checksum_text, qr{\Q$authors_dir\E/id}, "root path isn't leaked to checksums"; } else { fail "Can't check CHECKSUMS since it doesn't exist"; } }; }; } }; }; subtest 'packages updated' => sub { my @goodfile = ; my $packages = catfile $temp_dir, 'modules', '02packages.details.txt.gz'; ok -e $packages, 'packages files exists'; ok( my $gzread = gzopen( $packages, 'rb' ), 'opened packages for reading' ); my @packages; my $line; while ( $gzread->gzreadline( $line ) ) { if ( $line =~ /^Written-By:/ ) { push( @packages, "Written-By:\n" ); next; } if ( $line =~ /^Last-Updated:/ ) { push( @packages, "Last-Updated:\n" ); next; } push( @packages, $line ); } $gzread->gzclose; is_deeply( \@goodfile, \@packages, 'got expected packages file data' ); }; subtest 'mailrc updated' => sub { my $mailrc = catfile $temp_dir, 'authors', '01mailrc.txt.gz'; ok -e $mailrc, 'mailrc files exists'; ok( my $gzauthread = gzopen( $mailrc, 'rb' ), 'opened mailrc for reading' ); my %inject_authors = map { $_->{authorid} => 1 } get_module_details(''); my $line; my %found_authors; while ( $gzauthread->gzreadline( $line ) ) { next unless $line =~ /\A alias \h+ ([A-Z]+)/x; $found_authors{$1}++; fail( "Found $1 $found_authors{$1} times" ) if $found_authors{$1} > 1; } $gzauthread->gzclose; foreach my $author ( keys %inject_authors ) { ok exists $found_authors{$author}, "Found $author in $mailrc"; } }; done_testing(); __DATA__ File: 02packages.details.txt URL: http://www.perl.com/CPAN/modules/02packages.details.txt Description: Package names found in directory $CPAN/authors/id/ Columns: package name, version, path Intended-For: Automated fetch routines, namespace documentation. Written-By: Line-Count: 7 Last-Updated: abbreviation 0.02 M/MI/MIYAGAWA/abbreviation-0.02.tar.gz Acme::Code::Police 2.1828 O/OV/OVID/Acme-Code-Police-2.1828.tar.gz BFD 0.31 R/RB/RBS/BFD-0.31.tar.gz CPAN::Mini 0.17 R/RJ/RJBS/CPAN-Mini-0.17.tar.gz CPAN::Mini::Inject 0.02 S/SS/SSORICHE/CPAN-Mini-Inject-0.01.tar.gz CPAN::Nox 1.02 A/AN/ANDK/CPAN-1.76.tar.gz CPANPLUS 0.049 A/AU/AUTRIJUS/CPANPLUS-0.049.tar.gz CPAN-Mini-Inject-1.003/t/update_mirror.t0000644000076500000240000000551314631615433016636 0ustar brianstaffuse strict; use warnings; use Test::More; use CPAN::Mini::Inject; use File::Path; use File::Spec::Functions; use File::Temp (); use lib 't/lib'; use Local::localserver; use Local::utils; $SIG{'INT'} = sub { print "\nCleaning up before exiting\n"; exit 1 }; my $tmp_dir = File::Temp::tempdir( CLEANUP => 1 ); my $tmp_config_file; my $url; my $port; my $pid; subtest 'start local server' => sub { $port = empty_port(); ( $pid ) = start_server($port); diag( "$$: PORT: $port" ) if $ENV{TEST_VERBOSE}; diag( "$$: PID: $pid" ) if $ENV{TEST_VERBOSE}; $url = "http://localhost:$port/"; for( 1 .. 4 ) { my $sleep = $_ * 2; sleep $sleep; diag("Sleeping $sleep seconds waiting for server") if $ENV{TEST_VERBOSE}; last if can_fetch($url); } ok can_fetch($url), "URL $url is available"; }; subtest 'make config' => sub { $tmp_config_file = write_config( local => catfile( qw(t local CPAN) ), remote => $url, repository => $tmp_dir, ); diag("Temp file is <$tmp_config_file>") if $ENV{TEST_VERBOSE}; ok -e $tmp_config_file, " exists"; }; my $mcpi; subtest 'setup' => sub { my $class = 'CPAN::Mini::Inject'; use_ok $class; $mcpi = $class->new; isa_ok $mcpi, $class; }; subtest 'testremote' => sub { $mcpi->loadcfg( $tmp_config_file )->parsecfg; $mcpi->{config}{remote} =~ s/:\d{5}\b/:$port/; ok can_fetch($url), "URL $url is available"; eval { $mcpi->testremote } or print STDERR "testremote died: $@"; ok can_fetch($url), "URL $url is still available"; is( $mcpi->{site}, $url, "Site URL is $url" ); }; subtest 'update mirror' => sub { ok( -e $tmp_dir, 'mirror directory exists' ); ok can_fetch($url), "URL $url is available"; eval { diag( "updating mirror, which can take a couple minutes" ); $mcpi->update_mirror( remote => $url, local => $tmp_dir, trace => 1, log_level => 'info', ); } or print STDERR "update_mirror died: $@"; }; subtest 'mirror state' => sub { ok( -e catfile( $tmp_dir, qw(authors) ), 'authors/ exists' ); ok( -e catfile( $tmp_dir, qw(modules) ), 'modules/ exists' ); ok( -e catfile( $tmp_dir, qw(authors 01mailrc.txt.gz) ), '01mailrc.txt.gz exists' ); ok( -e catfile( $tmp_dir, qw(modules 02packages.details.txt.gz) ), '02packages.details.txt.gz exists' ); ok( -e catfile( $tmp_dir, qw(modules 03modlist.data.gz) ), '03modlist.data.gz exists' ); ok( -e catfile( $tmp_dir, qw(authors id R RJ RJBS CHECKSUMS) ), 'RJBS/CHECKSUMS exists' ); ok( -e catfile( $tmp_dir, qw(authors id R RJ RJBS CPAN-Mini-2.1828.tar.gz) ), 'CPAN-Mini-2.1828.tar.gz exists' ); ok( -e catfile( $tmp_dir, qw(authors id S SS SSORICHE CHECKSUMS) ), 'SSORICHE/CHECKSUMS exists' ); ok( -e catfile( $tmp_dir, qw(authors id S SS SSORICHE CPAN-Mini-Inject-1.01.tar.gz) ), 'CPAN::Mini::Inject exixts' ); }; sleep 1; # allow locks to expire kill( 9, $pid ); done_testing(); CPAN-Mini-Inject-1.003/t/add.t0000644000076500000240000000475414631615433014520 0ustar brianstaffuse strict; use warnings; use Test::More; use File::Path qw(make_path); use File::Spec::Functions qw(catfile); use File::Temp; use lib 't/lib'; use Local::localserver; use Local::utils; my $class = 'CPAN::Mini::Inject'; $SIG{'INT'} = sub { print "\nCleaning up before exiting\n"; exit 1 }; my $temp_dir = File::Temp::tempdir(CLEANUP=>1); subtest 'sanity' => sub { use_ok $class or BAIL_OUT( "Could not load $class: $@" ); can_ok $class, 'new'; isa_ok $class->new, $class; }; my $repo_dir = catfile $temp_dir, 'injects'; subtest 'create directory' => sub { make_path $repo_dir; ok -e $repo_dir, 'repository dir exists' }; my $tmp_config_file = catfile $temp_dir, 'config'; subtest 'make config' => sub { my $fh; if( open $fh, '>', $tmp_config_file ) { print {$fh} <<"HERE"; local: $temp_dir remote : http://localhost:11027 repository: $repo_dir dirmode: 0775 passive: yes HERE close $fh; pass( "created config file" ); } else { fail("Could not create config file. Cannot continue"); done_testing(); exit; } }; subtest 'add' => sub { my $mcpi = $class->new; isa_ok $mcpi, $class; can_ok $mcpi, 'add'; ok -e $tmp_config_file, 'config file exists'; $mcpi->loadcfg( $tmp_config_file )->parsecfg; my $archive_file = 'CPAN-Mini-Inject-0.01.tar.gz'; my $archive_path = catfile qw(t local mymodules), $archive_file; my $module = $class; ok( -e $archive_path, "file <$archive_file> exists" ); my $author = 'SSORICHE'; ok $mcpi->add( module => $module, authorid => $author, version => '0.01', file => $archive_path )->add( module => $module, authorid => $author, version => '0.02', file => $archive_path ), 'adding twice succeeded'; ok exists $mcpi->{modulelist}, 'modulelist key exists'; isa_ok $mcpi->{modulelist}, ref [], 'modulelist value is an array ref'; is scalar @{$mcpi->{modulelist}}, 1, 'modulelist array has one entry'; like $mcpi->{modulelist}[0], qr/\A\Q$module\E/, "modulelist entry has $module"; my $author_path = catfile $repo_dir, qw(authors id), substr( $author, 0, 1 ), substr( $author, 0, 2 ), $author; ok -e $author_path, "author directory for $author exists"; is( mode($author_path), 0775, 'author dir mode is 775' ) if has_modes(); my $repo_archive_path = catfile $author_path, $archive_file; ok -e $repo_archive_path, 'archive exists in repository'; is( mode($repo_archive_path), 0664, 'archive path is mode is 664' ) if has_modes(); ok -r $repo_archive_path, 'archive in repository is readable'; }; done_testing(); CPAN-Mini-Inject-1.003/t/testremote.t0000644000076500000240000000263114631615433016153 0ustar brianstaffuse strict; use warnings; use Test::More; use CPAN::Mini::Inject; use lib 't/lib'; use Local::localserver; $SIG{'INT'} = sub { print "\nCleaning up before exiting\n"; exit 1 }; my $port = empty_port(); my( $pid ) = start_server($port); diag( "$$: PORT: $port" ) if $ENV{TEST_VERBOSE}; diag( "$$: PID: $pid" ) if $ENV{TEST_VERBOSE}; my $url = "http://localhost:$port/"; my $available = 0; for( 1 .. 3 ) { my $sleep = $_ * 2; sleep $sleep; diag("Sleeping $sleep seconds waiting for server") if $ENV{TEST_VERBOSE}; if( can_fetch($url) ) { $available = 1; last; } elsif( ! kill 0, $pid ) { diag("Server pid is gone") if $ENV{TEST_VERBOSE}; last; } } unless( $available ) { fail( "Server never came up" ); done_testing(); exit 1; } ok can_fetch($url), "URL $url is available"; my $mcpi = CPAN::Mini::Inject->new; $mcpi->loadcfg( 't/.mcpani/config' )->parsecfg; $mcpi->{config}{remote} =~ s/:\d{5}\b/:$port/; $mcpi->testremote; is( $mcpi->{site}, $url, "Site URL is $url" ); ok can_fetch($url), "URL $url is available"; $mcpi->loadcfg( 't/.mcpani/config_badremote' )->parsecfg; $mcpi->{config}{remote} =~ s/:\d{5}\b/:$port/; SKIP: { skip 'Test fails with funky DNS providers', 1 if can_fetch( 'http://blahblah' ); # This fails with OpenDNS &c $mcpi->testremote; is( $mcpi->{site}, $url, 'Selects correct remote URL' ); } kill( 9, $pid ); unlink( 't/testconfig' ); done_testing(); CPAN-Mini-Inject-1.003/t/exceptions.t0000644000076500000240000002331214631615433016140 0ustar brianstaffuse strict; use warnings; use Test::More; BEGIN { eval "use Test::Exception"; plan skip_all => "Test::Exception required for exceptions.t" if $@; } use File::Spec::Functions qw(catfile); use File::Path qw(make_path); use File::Temp (); use Socket qw(getaddrinfo); use lib 't/lib'; use Local::utils; my $class = 'CPAN::Mini::Inject'; $SIG{'INT'} = sub { print "\nCleaning up before exiting\n"; exit 1 }; my $temp_dir = File::Temp::tempdir(CLEANUP=>1); subtest 'sanity' => sub { use_ok $class or BAIL_OUT( "Could not load $class: $@" ); can_ok $class, 'new'; isa_ok $class->new, $class; }; subtest 'config problems' => sub { subtest 'no config' => sub { delete local $ENV{HOME}; delete local $ENV{MCPANI_CONFIG}; SKIP: { skip 'Global config file exists. Cannot test no config situation.', 1 if global_config_exists(); my $mcpi = $class->new; isa_ok $mcpi, $class; dies_ok { $mcpi->loadcfg } 'No config file'; } }; subtest 'bad config' => sub { my $tmp_config_file = catfile $temp_dir, 'bad_config'; subtest 'create bad config file' => sub { my $fh; if( open $fh, '>', $tmp_config_file ) { print {$fh} <<'HERE'; # This file is missing a local setting. remote : http://www.cpan.org repository: t/local/MYCPAN passive: yes This line will be ignored HERE ok close($fh), "created bad config file"; } else { fail("could not create config with missing local setting"); } }; ok -e $tmp_config_file, 'bad config with missing local setting file exists'; my $mcpi = $class->new; isa_ok $mcpi, $class; dies_ok { $mcpi->parsecfg( $tmp_config_file ); } 'Missing local setting blows up'; }; subtest 'unreadable' => sub { SKIP: { skip 'User is superuser and can always read', 1 if $< == 0; skip 'User is generally superuser under cygwin and can read', 1 if $^O eq 'cygwin'; my $repo_dir = catfile $temp_dir, 'injects'; ok make_path($repo_dir), "make_path for injects/ succeeded"; my $tmp_config_file = catfile $temp_dir, 'bad_config'; my $fh; if(open $fh, '>', $tmp_config_file) { print {$fh} "Hello"; close $fh; chmod 0111, $tmp_config_file; is( mode($tmp_config_file), 0111, 'mode for config is 0111' ); ok -e $tmp_config_file, 'config file exists'; ok ! -r $tmp_config_file, 'config file is not readable'; } else { fail("Could not create an unreadable file"); } my $mcpi = $class->new; isa_ok $mcpi, $class; dies_ok { $mcpi->parsecfg($tmp_config_file) } 'unreadable file'; like $@, qr/Could not read file/, 'exception has expected message'; chmod 0644, $tmp_config_file; } }; subtest 'no repo config' => sub { my $tmp_config_file = catfile $temp_dir, 'bad_config'; subtest 'create no repo config file' => sub { my $fh; if(open $fh, '>', $tmp_config_file) { print {$fh} "local: t/local/CPAN\nremote: http://www.cpan.org\n"; close $fh; ok -e $tmp_config_file, 'config file exists'; ok -r $tmp_config_file, 'config file is readable'; } else { fail("Could not create no repo config file"); } }; my $mcpi = $class->new; isa_ok $mcpi, $class; lives_ok { $mcpi->parsecfg($tmp_config_file) } 'no repo config file parses'; dies_ok { $mcpi->add( module => 'CPAN::Mini::Inject', authorid => 'SSORICHE', version => '0.01', file => 'test-0.01.tar.gz' ); } 'Missing config repository'; like $@, qr/No repository configured/, 'exception has expected message'; }; subtest 'read-only repo' => sub { SKIP: { skip 'this system does not do file modes', 3 unless has_modes(); my $tmp_config_file = catfile $temp_dir, 'bad_config'; my $repo_dir = catfile $temp_dir, 'read-only-injects'; subtest 'create read-only repo dir' => sub { ok make_path($repo_dir), 'created repo dir'; chmod 0555, $repo_dir; is mode($repo_dir), 0555, 'repo dir has mode 444'; ok ! -w $repo_dir, 'repo dir is not writable'; }; subtest 'create read-only repo config file' => sub { my $fh; if(open $fh, '>', $tmp_config_file) { print {$fh} <<"HERE"; local: $temp_dir remote: http://www.cpan.org repository: $repo_dir HERE close $fh; ok -e $tmp_config_file, 'config file exists'; ok -r $tmp_config_file, 'config file is readable'; } else { fail("Could not create read-only repo config file"); } }; subtest 'try to add to read-only repo' => sub { my $mcpi = $class->new; isa_ok $mcpi, $class; lives_ok { $mcpi->parsecfg($tmp_config_file) } 'read-only repo config file parses'; dies_ok { $mcpi->add( module => 'CPAN::Mini::Inject', authorid => 'SSORICHE', version => '0.01', file => 'test-0.01.tar.gz' ); } 'read-only repository'; like $@, qr/Can not write to repository/, 'exception has expected message'; }; chmod 755, $repo_dir; }; } }; subtest 'add exceptions' => sub { my $repo_dir = catfile $temp_dir, 'injects'; subtest 'create repo dir' => sub { ok make_path($repo_dir), 'created repo dir' unless -d $repo_dir; chmod 0755, $repo_dir; is mode($repo_dir), 0755, 'repo dir has mode 444' if has_modes(); ok -r $repo_dir, 'repo dir is readable'; ok -w $repo_dir, 'repo dir is writable'; }; my $tmp_config_file = catfile $temp_dir, 'good_config'; subtest 'create config file' => sub { my $fh; if(open $fh, '>', $tmp_config_file) { print {$fh} <<"HERE"; local: $temp_dir remote : http://localhost:11027 repository: $repo_dir dirmode: 0775 passive: yes HERE close $fh; ok -e $tmp_config_file, 'config file exists'; ok -r $tmp_config_file, 'config file is readable'; } else { fail("Could not create config file"); } }; my $mcpi = $class->new; isa_ok $mcpi, $class; lives_ok { $mcpi->parsecfg( $tmp_config_file ) } 'parsecfg works'; subtest 'missing file param' => sub { dies_ok { $mcpi->add( module => 'CPAN::Mini::Inject', authorid => 'SSORICHE', version => '0.01' ); } 'Missing add param'; like $@, qr/Required option not specified: file/, 'exception has expected message'; }; subtest 'module file is missing' => sub { dies_ok { $mcpi->add( module => 'CPAN::Mini::Inject', authorid => 'SSORICHE', version => '0.01', file => 'blahblah' ); } 'Module file not readable'; like $@, qr/Can not read module file: blahblah/, 'exception has expected message'; }; subtest 'discoverable' => sub { lives_ok { $mcpi->add( authorid => 'RWSTAUNER', file => 't/local/mymodules/Dist-Metadata-Test-MetaFile-Only.tar.gz' ); } 'Ok without module/version when discoverable'; }; subtest 'not discoverable' => sub { lives_ok { $mcpi->add( module => 'Who::Cares', version => '1', authorid => 'RWSTAUNER', file => 't/local/mymodules/not-discoverable.tar.gz' ); } 'Ok without module/version when specified'; }; subtest 'needs module and version when not discoverable' => sub { dies_ok { $mcpi->add( authorid => 'RWSTAUNER', file => 't/local/mymodules/not-discoverable.tar.gz' ); } 'Dies without module/version when not discoverable'; }; }; subtest 'remote problems' => sub { my $repo_dir = catfile $temp_dir, 'injects'; subtest 'create repo dir' => sub { ok make_path($repo_dir), 'created repo dir' unless -d $repo_dir; chmod 0755, $repo_dir; is mode($repo_dir), 0755, 'repo dir has mode 755' if has_modes(); ok -r $repo_dir, 'repo dir is readable'; ok -w $repo_dir, 'repo dir is writable'; }; subtest 'unreachable remote' => sub { my $unreachable_host = 'com'; my $url = 'http://$host/'; my ($lookup_error, @result) = getaddrinfo $unreachable_host, 'http'; diag( Dumper(\@result) ); use Data::Dumper; SKIP: { plan skip_all => 'bad host resolves, so cannot test that' unless $lookup_error; my $tmp_config_file = catfile $temp_dir, 'good_config'; subtest 'create config file' => sub { my $fh; if(open $fh, '>', $tmp_config_file) { print {$fh} <<"HERE"; local: $temp_dir remote: $url repository: $repo_dir dirmode: 0775 passive: yes HERE close $fh; ok -e $tmp_config_file, 'config file exists'; ok -r $tmp_config_file, 'config file is readable'; } else { fail("Could not create config file"); } }; my $mcpi = $class->new; isa_ok $mcpi, $class; lives_ok { $mcpi->parsecfg( $tmp_config_file ) } 'parsecfg works'; diag "trying to connect to a bad site: this might take a minute"; dies_ok { $mcpi->testremote } 'No reachable site'; like $@, qr/Unable to connect/, 'exception has expected message'; } }; }; # writelist() subtest 'writelist' => sub { SKIP: { skip 'User is superuser and can always write', 1 if $< == 0; skip 'User is generally superuser under cygwin and can write', 1 if $^O eq 'cygwin'; my $repo_dir = catfile $temp_dir, 'injects'; subtest 'create repo dir' => sub { ok make_path($repo_dir), 'created repo dir' unless -d $repo_dir; chmod 0555, $repo_dir; is mode($repo_dir), 0555, 'repo dir has mode 555'; ok -r $repo_dir, 'repo dir is readable'; ok ! -w $repo_dir, 'repo dir is not writable'; }; my $tmp_config_file = catfile $temp_dir, 'config'; subtest 'create config file' => sub { my $fh; if(open $fh, '>', $tmp_config_file) { print {$fh} <<"HERE"; local: $temp_dir remote : http://www.cpan.org repository: $repo_dir HERE close $fh; ok -e $tmp_config_file, 'config file exists'; ok -r $tmp_config_file, 'config file is readable'; } else { fail("Could not create config file"); } }; my $mcpi = $class->new; isa_ok $mcpi, $class; lives_ok { $mcpi->parsecfg( $tmp_config_file ) } 'parsecfg works'; dies_ok { $mcpi->writelist } 'fail write file'; like $@, qr//, 'exception has expected message'; } }; done_testing(); CPAN-Mini-Inject-1.003/t/private.t0000644000076500000240000000127514631615433015435 0ustar brianstaff#!perl use strict; use warnings; use CPAN::Mini::Inject; use Test::More; subtest '_fmtmodule' => sub { my @tests = ( { in => [ 'foo', 'foo.tar.gz', '0.01' ], out => 'foo 0.01 foo.tar.gz', }, { in => [ 'fooIsAModuleWithAReallyLongNameSoLong' . 'InFactThatItScrewsWithTheFormatting', 'foo.tar.gz', '0.01' ], out => 'fooIsAModuleWithAReallyLongNameSoLong' . 'InFactThatItScrewsWithTheFormatting 0.01 foo.tar.gz', }, ); for my $test ( @tests ) { my $got = CPAN::Mini::Inject::_fmtmodule( @{ $test->{in} } ); is $got, $test->{out}, '_fmtmodule'; } }; done_testing(); CPAN-Mini-Inject-1.003/t/writelist.t0000644000076500000240000000455114631615433016011 0ustar brianstaffuse strict; use warnings; use Test::More; use File::Path qw(make_path); use File::Spec::Functions qw(catfile); use File::Temp (); use lib qw(t/lib); use Local::utils; my $class = 'CPAN::Mini::Inject'; $SIG{'INT'} = sub { print "\nCleaning up before exiting\n"; exit 1 }; my $temp_dir = File::Temp::tempdir(CLEANUP=>1); subtest 'sanity' => sub { use_ok $class or BAIL_OUT( "Could not load $class: $@" ); isa_ok $class->new, $class; }; subtest 'setup directories in temp dir' => sub { my @dirs = ( [ qw(modules) ], [ qw(authors) ], [ qw(injects) ], ); foreach my $dir ( @dirs ) { my $path = catfile $temp_dir, @$dir; make_path( $path ); ok -d $path, "Path for <@$dir> exists"; } }; my $modulelist; subtest 'make modulelist' => sub { my $injects_dir = catfile $temp_dir, 'injects'; ok -e $injects_dir, 'injects directory exists'; $modulelist = catfile $injects_dir, 'modulelist'; my $fh; if( open $fh, '>', catfile $modulelist ) { print {$fh} <<'HERE'; CPAN::Checksums 1.016 A/AN/ANDK/CPAN-Checksums-1.016.tar.gz CPAN::Mini 0.18 R/RJ/RJBS/CPAN-Mini-0.18.tar.gz CPANPLUS 0.0499 A/AU/AUTRIJUS/CPANPLUS-0.0499.tar.gz HERE close $fh; } else { fail( "Could not open <$modulelist>: $!" ); } }; subtest 'add to modulelist' => sub { my $tmp_config_file; subtest 'make config' => sub { $tmp_config_file = write_config( local => $temp_dir, repository => catfile( $temp_dir, 'injects' ), ); ok -e $tmp_config_file, 'configuration file exists'; }; my $mcpi = $class->new; isa_ok $mcpi, $class; $mcpi->loadcfg( $tmp_config_file )->parsecfg->readlist; my $module_line = "CPAN::Mini::Inject 0.01 S/SS/SSORICHE/CPAN-Mini-Inject-0.01.tar.gz"; subtest 'modify modulelist' => sub { ok -e $modulelist, "modulelist file exists"; push( @{ $mcpi->{modulelist} }, $module_line ); is( @{ $mcpi->{modulelist} }, 4, 'Updated memory modulelist' ); ok( $mcpi->writelist, 'Write modulelist' ); }; subtest 'check modulelist' => sub { my $other_mcpi = $class->new; isa_ok $other_mcpi, $class; $mcpi->loadcfg( $tmp_config_file )->parsecfg->readlist; is( @{ $mcpi->{modulelist} }, 4, 'Updated memory modulelist' ); my $found = grep { $_ eq $module_line } @{ $mcpi->{modulelist} }; ok $found, "target line is in modulelist"; }; }; done_testing(); CPAN-Mini-Inject-1.003/t/html/0000755000076500000240000000000014631615434014536 5ustar brianstaffCPAN-Mini-Inject-1.003/t/html/index.html0000644000076500000240000000005514631615433016532 0ustar brianstaff

Index

CPAN-Mini-Inject-1.003/t/html/CPAN-Mini-Inject-1.01.tar.gz0000644000076500000240000000000514631615433021061 0ustar brianstaffTEST CPAN-Mini-Inject-1.003/t/html/CPAN-Mini-2.1828.tar.gz0000644000076500000240000000000514631615433020032 0ustar brianstaffTEST CPAN-Mini-Inject-1.003/t/html/03modlist.data.gz0000644000076500000240000000006514631615433017626 0ustar brianstaff@B03modlist.data30O,.KI,IKjHKCPAN-Mini-Inject-1.003/t/html/CHECKSUMS0000644000076500000240000000000514631615433016000 0ustar brianstaffTEST CPAN-Mini-Inject-1.003/t/html/01mailrc.txt.gz0000644000076500000240000000006114631615433017322 0ustar brianstaff?B01mailrc.txt30M)J+(KRCPAN-Mini-Inject-1.003/t/html/02packages.details.txt.gz0000644000076500000240000000054414631615433021264 0ustar brianstaffGB02packages.details.txtuAS0{H:2L@M:V_oJudFo_v+]IUn3$uS?5~t+mͧj+5醴5%`d65 dΎL:ϵVmmW07t>aLhnBϬ0n֒P܁-i~/ e˶FCǢg) ɡ6W"iHI fWBz׍ IHGP`Y\@d(s]>EQ!h2@ГxԸs#%ܼ{,I ^tϧw7'wo'}CPAN-Mini-Inject-1.003/t/readlist.t0000644000076500000240000000403014631615433015562 0ustar brianstaffuse strict; use warnings; use File::Path qw(make_path); use File::Spec::Functions qw(catfile); use File::Temp; use Test::More; my $class = 'CPAN::Mini::Inject'; subtest 'sanity' => sub { use_ok $class or BAIL_OUT("$class did not compile: $@"); can_ok $class, 'new'; }; $SIG{'INT'} = sub { print "\nCleaning up before exiting\n"; exit 1 }; my $temp_dir = File::Temp::tempdir(CLEANUP=>1); my $tmp_config_file = catfile $temp_dir, 'config'; my $repo_dir = catfile $temp_dir, 'injects'; subtest 'make repo dir' => sub { make_path $repo_dir; ok -e $repo_dir, "repository directory exists"; }; subtest 'make config' => sub { my $fh; if( open $fh, '>', $tmp_config_file ) { print {$fh} <<"HERE"; local: $temp_dir remote : http://localhost:11027 repository: $repo_dir dirmode: 0775 passive: yes HERE close $fh; pass( "created config file" ); } else { fail("Could not create config file. Cannot continue"); done_testing(); exit; } }; subtest 'make modulelist' => sub { my $modulelist_path = catfile $repo_dir, 'modulelist'; my $fh; if( open $fh, '>', $modulelist_path ) { print {$fh} <<"HERE"; CPAN::Checksums 1.016 A/AN/ANDK/CPAN-Checksums-1.016.tar.gz CPAN::Mini 0.18 R/RJ/RJBS/CPAN-Mini-0.18.tar.gz CPANPLUS 0.0499 A/AU/AUTRIJUS/CPANPLUS-0.0499.tar.gz HERE close $fh; pass( "created modulelist file" ); } else { fail("Could not create modulelist file. Cannot continue"); done_testing(); exit; } }; subtest 'readlist' => sub { my $mcpi = $class->new; isa_ok $mcpi, $class; can_ok $mcpi, 'readlist'; ok -e $tmp_config_file, 'config file exists'; ok $mcpi->loadcfg( $tmp_config_file )->parsecfg, 'parsecfg succeeded'; ok ! exists $mcpi->{modulelist}, "object does not have modulelist key yet"; $mcpi->readlist; ok exists $mcpi->{modulelist}, "object has modulelist key after readlist"; isa_ok $mcpi->{modulelist}, ref [], 'modulelist is an array ref after readlist'; is( @{ $mcpi->{modulelist} }, 3, 'read modulelist' ); }; done_testing(); CPAN-Mini-Inject-1.003/t/local/0000755000076500000240000000000014631615434014664 5ustar brianstaffCPAN-Mini-Inject-1.003/t/local/CPAN/0000755000076500000240000000000014631615434015405 5ustar brianstaffCPAN-Mini-Inject-1.003/t/local/CPAN/modules/0000755000076500000240000000000014631615434017055 5ustar brianstaffCPAN-Mini-Inject-1.003/t/local/CPAN/modules/02packages.details.txt.gz.original0000644000076500000240000000071514631615433025406 0ustar brianstaffX02packages.details.txt}Ao0s#4J(f-V;'_vhAZ:za<󳝅eNJӖWM:*$rU;#cÁִ2 m5kj\ʪўF@F:x54ee~;,k=CZ#}ThZHAfwhRjttas L5R#hdz~ -|J fFK vGL`'[H$Y|w(//V~) .٩ҷQ^52fFʟRL {UY #Gt1$Ue۩׎y #!XVqO`!A9hm>/GIʙGJc,wߝ&㶸/@F#:'CPAN-Mini-Inject-1.003/t/local/mymodules/0000755000076500000240000000000014631615434016702 5ustar brianstaffCPAN-Mini-Inject-1.003/t/local/mymodules/CPAN-Mini-0.17.tar.gz0000644000076500000240000000000014631615433022014 0ustar brianstaffCPAN-Mini-Inject-1.003/t/local/mymodules/Dist-Metadata-Test-MetaFile-2.2.tar.gz0000644000076500000240000000155614631615433025360 0ustar brianstaffX[o0ٿ(Ks$w7֋ItU2U$B9N g; M iyo9sw?n<8۪t羄\\p]\o5IAt(`kp-QO 歓pF͖j aF 5@ƌĔ€pi$Yl jmP*AZg/2wݯb&"Dp '䊂JD0WH'DR1e#,B4>$~cYO>`y} MDAha*UY(JeLlAʴ 3 "ƶPU>`ói0k/fÚAQ@br Ɨax&JDŬF.)Tׄ~.9(մa‘v.V<,l`>?jD(IPH ɽ+fu ^0wu߆ܪ6 00 _(;Tc3' z)҃Rde)ȭtQ1FYBI.=N2JA괒wS {׼ x@>+TWBNFfJ[eUm= =tVq[xwwn(WW2[㍳WU6Ȍson,}:^5ڮo?"nc_ pq<@bp;<+A M bDSPW}Q*b)?/8tNPwmlqh_Qj5|W!K0 FMuЭZj5nCPAN-Mini-Inject-1.003/t/local/mymodules/Dist-Metadata-Test-MetaFile-Only.tar.gz0000644000076500000240000000100114631615433025761 0ustar brianstaffN[k@_9$ 64͛^m]Jm{wt RRe={9s o??\X&oGY/ƛU4\xm&qqXm֓(~azz&LK))c,; .` nO`0Ig%8qYL)wR}_6;ָϏj}W[x)E*` wSu,V`t5F3[{^mVι* C ܈üLŇO_d%5z'0"ߥ4?ٶV0SBT #{4 6a8<T}[peUxS*1QutAW=TF?Kav&,I0Y[c$y~ rCPAN-Mini-Inject-1.003/t/lib/0000755000076500000240000000000014631615434014340 5ustar brianstaffCPAN-Mini-Inject-1.003/t/lib/Local/0000755000076500000240000000000014631615434015372 5ustar brianstaffCPAN-Mini-Inject-1.003/t/lib/Local/localserver.pm0000644000076500000240000000243314631615433020252 0ustar brianstaffuse File::Spec::Functions qw(catfile); use Net::EmptyPort; sub start_server { my( $port ) = @_; my $child_pid = fork; return $child_pid unless $child_pid == 0; require HTTP::Daemon; require HTTP::Date; require HTTP::Status; my $d = HTTP::Daemon->new( LocalPort => $port ) or exit; CONNECTION: while (my $c = $d->accept) { REQUEST: while (my $r = $c->get_request) { my $file = (split m|/|, $r->uri->path)[-1] // 'index.html'; my $path = catfile 't', 'html', $file; if ($r->method eq 'GET') { if( -e $path ) { $c->send_file_response( catfile 't', 'html', $file); } elsif( $path eq 'shutdown' ) { $c->close; undef $c; last CONNECTION; } else { $c->send_error(HTTP::Status::RC_NOT_FOUND()) } } elsif ($r->method eq 'HEAD') { # update_mirror does this if( -e $path ) { my $last_modified = (stat $path)[9]; $c->send_header( 'Last-Modified' => HTTP::Date::time2str($last_modified), 'Content-Length' => (-s $path), ); } else { $c->send_error(HTTP::Status::RC_NOT_FOUND()) } } else { $c->send_error(HTTP::Status::RC_FORBIDDEN()) } } $c->close; undef($c); } exit; } sub can_fetch { require LWP::UserAgent; LWP::UserAgent->new->get( shift )->is_success } 1; CPAN-Mini-Inject-1.003/t/lib/Local/utils.pm0000644000076500000240000000161614631615433017073 0ustar brianstaffuse strict; use warnings; use Carp (); use Data::Dumper; sub global_config_exists { return scalar grep { -r } qw(/usr/local/etc/mcpani /etc/mcpani); } sub has_modes { $^O !~ /^MSWin|^cygwin/ } sub mode { (stat $_[0])[2] & 07777; } sub write_config { my %defaults = qw( local t/local/CPAN remote http://localhost:11027 repository: t/local/MYCPAN dirmode 0775 passive yes ); my %args = (%defaults, @_); my $fh; unless( defined $args{file} ) { ( $fh, $args{file} ) = File::Temp::tempfile(); } unless( defined $fh ) { open $fh, '>', $args{file} or do { Carp::carp "Could not open <$args{file}>: $!"; return; }; } my $contents = <<"HERE"; local: $args{local} remote: $args{remote} repository: $args{repository} dirmode: $args{dirmode} passive: $args{passive} HERE print {$fh} $contents; close $fh; return $args{file}; } 1; CPAN-Mini-Inject-1.003/t/add-multiple.t0000644000076500000240000001253514631615433016345 0ustar brianstaffuse strict; use warnings; use Test::More; use CPAN::Mini::Inject; use File::Basename qw(basename); use File::Copy qw(copy); use File::Path qw(make_path); use File::Spec::Functions qw(catfile); use File::Temp (); use lib qw(t/lib); use Local::utils; my $class = 'CPAN::Mini::Inject'; $SIG{'INT'} = sub { print "\nCleaning up before exiting\n"; exit 1 }; my $temp_dir = File::Temp::tempdir(CLEANUP=>1); subtest 'sanity' => sub { use_ok $class or BAIL_OUT( "Could not load $class: $@" ); isa_ok $class->new, $class; }; subtest 'setup directories in temp dir' => sub { my @dirs = ( [ qw(modules) ], [ qw(authors) ], [ qw(injects) ], ); foreach my $dir ( @dirs ) { my $path = catfile $temp_dir, @$dir; make_path( $path ); ok -d $path, "Path for <@$dir> exists"; } }; my $t_local = catfile qw(t local); subtest 'check local dir' => sub { ok -d $t_local, 'local directory exists'; }; subtest 'copy initial files' => sub { my $modules_base = catfile $temp_dir, 'modules'; ok -d $modules_base, 'modules dir exists'; my $authors_base = catfile $temp_dir, 'authors'; ok -d $authors_base, 'authors dir exists'; subtest 'packages' => sub { my $file = '02packages.details.txt.gz'; my $destination = catfile $modules_base, $file; my $rc = copy( catfile( $t_local, 'CPAN', 'modules', "$file.original" ), $destination ); ok $rc, 'File::Copy worked'; ok -e $destination, 'Copied packages file to temp_dir'; ok chmod(0666, $destination), 'chmod packages to 0666'; }; subtest 'mailrc' => sub { my $file = '01mailrc.txt.gz'; my $destination = catfile $authors_base, $file; my $rc = copy( catfile( $t_local, "$file.original" ), $destination ); ok $rc, 'File::Copy worked'; ok -e $destination, 'Copied mailrc file to temp_dir'; ok chmod(0666, $destination), 'chmod mailrc to 0666'; }; }; sub get_module_details { my( $dist_sources ) = @_; my @modules = ( { module => 'CPAN::Mini::Inject', authorid => 'SSORICHE', version => '0.01', file => catfile( $dist_sources, 'CPAN-Mini-Inject-0.01.tar.gz' ), }, { authorid => 'RWSTAUNER', file => catfile( $dist_sources, 'Dist-Metadata-Test-MetaFile-2.2.tar.gz' ), }, { module => 'Dist::Metadata::Test::MetaFile', authorid => 'RWSTAUNER', version => '2.3', # package versions do not match this file => 't/local/mymodules/Dist-Metadata-Test-MetaFile-2.2.tar.gz' }, { authorid => 'RWSTAUNER', file => 't/local/mymodules/Dist-Metadata-Test-MetaFile-Only.tar.gz' }, ); } subtest 'add modules' => sub { my $dist_sources = catfile $t_local, 'mymodules'; ok -d $dist_sources, 'Dist sources directory exists'; my @modules = get_module_details( $dist_sources ); subtest 'check module sources are there' => sub { foreach my $module ( @modules ) { ok -e $module->{file}, "$module->{file} exists"; } }; my $tmp_config_file; subtest 'make config' => sub { $tmp_config_file = write_config( local => $temp_dir, repository => catfile( $temp_dir, 'injects' ), ); ok -e $tmp_config_file, 'configuration file exists'; }; my $mcpi = $class->new; isa_ok $mcpi, $class; $mcpi->loadcfg( $tmp_config_file )->parsecfg; foreach my $module ( @modules ) { my $basename = basename($module->{file}); subtest $basename => sub { ok $mcpi->add( %$module ), "Added " . $basename; my $auth_path = catfile( substr($module->{authorid}, 0, 1), substr($module->{authorid}, 0, 2), $module->{authorid}, ); is( $mcpi->{authdir}, $auth_path, "author directory <$auth_path> exists in injects repo" ); my $module_path = catfile $temp_dir, 'injects', 'authors', 'id', $auth_path, $basename; ok( -e $module_path, "Added module <$basename> exists" ); ok( -r $module_path, "Added module <$basename> is readable" ); }; } is_deeply( [$mcpi->added_modules], [ { file => 'CPAN-Mini-Inject-0.01.tar.gz', authorid => 'SSORICHE', modules => {'CPAN::Mini::Inject' => '0.01'} }, { file => 'Dist-Metadata-Test-MetaFile-2.2.tar.gz', authorid => 'RWSTAUNER', modules => { 'Dist::Metadata::Test::MetaFile::PM' => '2.0', 'Dist::Metadata::Test::MetaFile' => '2.1' } }, # added twice (bug in usage not in reporting) { file => 'Dist-Metadata-Test-MetaFile-2.2.tar.gz', authorid => 'RWSTAUNER', modules => { 'Dist::Metadata::Test::MetaFile::PM' => '2.0', 'Dist::Metadata::Test::MetaFile' => '2.1' } }, { file => 'Dist-Metadata-Test-MetaFile-Only.tar.gz', authorid => 'RWSTAUNER', modules => {'Dist::Metadata::Test::MetaFile::DiffName' => '0.02'} }, ], 'added_modules returns expected data' ); subtest 'packages entries' => sub { my @expected_lines = ; chomp(@expected_lines); my %expected_lines = map { $_, 1 } grep { /\S/ } @expected_lines; my %Seen; foreach my $line ( @{ $mcpi->{modulelist} } ) { my( $module ) = $line =~ /\A(\S+)/; ok exists $expected_lines{$line}, "Found line for $module"; fail( "Saw $module multiple times" ) if exists $Seen{$module}; $Seen{$module}++; } }; }; done_testing(); __END__ CPAN::Mini::Inject 0.01 S/SS/SSORICHE/CPAN-Mini-Inject-0.01.tar.gz Dist::Metadata::Test::MetaFile::PM 2.0 R/RW/RWSTAUNER/Dist-Metadata-Test-MetaFile-2.2.tar.gz Dist::Metadata::Test::MetaFile 2.1 R/RW/RWSTAUNER/Dist-Metadata-Test-MetaFile-2.2.tar.gz Dist::Metadata::Test::MetaFile::DiffName 0.02 R/RW/RWSTAUNER/Dist-Metadata-Test-MetaFile-Only.tar.gz CPAN-Mini-Inject-1.003/t/pod-coverage.t0000644000076500000240000000024314631615433016330 0ustar brianstaffuse Test::More; eval "use Test::Pod::Coverage 0.08"; plan skip_all => "Test::Pod::Coverage 0.08 required for testing POD coverage" if $@; all_pod_coverage_ok(); CPAN-Mini-Inject-1.003/t/.mcpani/0000755000076500000240000000000014631615434015117 5ustar brianstaffCPAN-Mini-Inject-1.003/t/.mcpani/config_noread0000644000076500000240000000011714631615433017635 0ustar brianstafflocal: t/local/CPAN remote : http://www.cpan.org repository: t/local/WRITEREPO CPAN-Mini-Inject-1.003/t/.mcpani/config0000644000076500000240000000015214631615433016304 0ustar brianstafflocal: t/local/CPAN remote : http://localhost:11027 repository: t/local/MYCPAN dirmode: 0775 passive: yes CPAN-Mini-Inject-1.003/t/.mcpani/config_mcpi0000644000076500000240000000013514631615433017315 0ustar brianstaffremote: http://www.cpan.org http://localhost local: t/local/CPAN repository: t/read/MYCPAN CPAN-Mini-Inject-1.003/t/.mcpani/config_norepo0000644000076500000240000000006114631615433017665 0ustar brianstafflocal: t/local/CPAN remote : http://www.cpan.org CPAN-Mini-Inject-1.003/t/.mcpani/config_bad0000644000076500000240000000020714631615433017113 0ustar brianstaff# This file is missing a local setting. remote : http://www.cpan.org repository: t/local/MYCPAN passive: yes This line will be ignored CPAN-Mini-Inject-1.003/t/.mcpani/config_nowrite0000644000076500000240000000011214631615433020047 0ustar brianstafflocal: t/read/CPAN remote : http://www.cpan.org repository: t/read/MYCPAN CPAN-Mini-Inject-1.003/t/.mcpani/config_with_whitespaces0000644000076500000240000000034714631615433021744 0ustar brianstaff # all config lines with trailing whitespaces local : t/local/CPAN remote : http://localhost:11027 repository : t/local/MYCPAN dirmode : 0775 passive : yes CPAN-Mini-Inject-1.003/t/.mcpani/config_badremote0000644000076500000240000000017214631615433020330 0ustar brianstafflocal: t/local/CPAN remote : http://blahblah http://localhost:11027 repository: t/local/MYCPAN dirmode: 0775 passive: yes CPAN-Mini-Inject-1.003/t/loadcfg.t0000644000076500000240000000403414631615433015356 0ustar brianstaffuse strict; use warnings; use File::Path qw(make_path); use File::Spec::Functions qw(catfile); use File::Temp (); use Test::More; my $class = 'CPAN::Mini::Inject'; $SIG{'INT'} = sub { print "\nCleaning up before exiting\n"; exit 1 }; my $temp_dir = File::Temp::tempdir(CLEANUP=>1); subtest 'sanity' => sub { use_ok $class; can_ok $class, qw(new loadcfg); }; subtest nothing => sub { delete local $ENV{HOME}; delete local $ENV{MCPANI_CONFIG}; my $mcpi = $class->new; isa_ok $mcpi, $class; can_ok $mcpi, qw(loadcfg); my $config_path = catfile $temp_dir, 'nothing-config'; write_config($config_path); ok -e $config_path, 'config path exists'; ok eval { $mcpi->loadcfg( $config_path ); 1 }, 'loadcfg works'; ok exists $mcpi->{cfgfile}, 'cfgfile key exists'; is( $mcpi->{cfgfile}, $config_path ); }; my $mcpani_dir = catfile $temp_dir, '.mcpani'; subtest 'setup .mcpani' => sub { make_path $mcpani_dir; ok -e $mcpani_dir, '.mcpani dir exists'; }; subtest HOME => sub { local $ENV{HOME} = $temp_dir; my $mcpi = $class->new; isa_ok $mcpi, $class; can_ok $mcpi, qw(loadcfg); my $config_path = catfile $mcpani_dir, 'home-config'; write_config($config_path); ok -e $config_path, 'config path exists'; ok eval { $mcpi->loadcfg( $config_path ); 1 }, 'loadcfg works'; ok exists $mcpi->{cfgfile}, 'cfgfile key exists'; is( $mcpi->{cfgfile}, $config_path ); }; subtest MCPANI_CONFIG => sub { local $ENV{MCPANI_CONFIG} = catfile $temp_dir, 'env-config'; my $mcpi = $class->new; isa_ok $mcpi, $class; can_ok $mcpi, qw(loadcfg); my $config_path = $ENV{MCPANI_CONFIG}; write_config($config_path); ok -e $config_path, 'config path exists'; ok eval { $mcpi->loadcfg( $config_path ); 1 }, 'loadcfg works'; ok exists $mcpi->{cfgfile}, 'cfgfile key exists'; is( $mcpi->{cfgfile}, $ENV{MCPANI_CONFIG}, ); }; done_testing(); sub write_config { my( $path ) = @_; open my $fh, '>', $path; print {$fh} <<"HERE"; local: t/local/CPAN remote : http://localhost:11027 repository: t/local/MYCPAN dirmode: 0775 passive: yes HERE close $fh; } CPAN-Mini-Inject-1.003/README.pod0000644000076500000240000001031014631615433014762 0ustar brianstaff=pod =encoding utf8 =for HTML =for HTML =for HTML =for HTML =for HTML Coverage Status =for HTML =for HTML =head1 The CPAN::Mini::Inject module This is the I for the L Perl module. It provides convenience methods to test things about Perl data type instead of their values. You're probably looking at this because you don't know where else to find what you're looking for. Read this once and you might never have to read one again for any Perl module. =head2 Documentation To read about L, look at the embedded documentation in the module itself. Inside the distribution, you can format it with L: % perldoc lib/CPAN/Mini/Inject.pm If you have already installed the module, you can specify the module name instead of the file location: % perldoc CPAN::Mini::Inject You can read the documentation and inspect the meta data at L. The standard module documentation has example uses in the SYNOPSIS section, but you can also look in the I directory (if it's there), or look at the test files in I. =head2 Installation You can install this module with a CPAN client, which will resolve and install the dependencies: % cpan CPAN::Mini::Inject % cpanm CPAN::Mini::Inject You can also install directly from the distribution directory, which will also install the dependencies: % cpan . % cpanm . You could install just this module manually: % perl Makefile.PL % make % make test % make install You probably don't want to do that unless you're fiddling with the module and only want to run the tests without installing anything. =head2 Source location The meta data, such as the source repository and bug tracker, is in I or the I files it creates. You can find that on those CPAN web interfaces, but you can also look at files directly in the source repository: =over 4 =item * L =back If you find a problem, file a ticket in the L: =over 4 =item * L =back =head2 Getting help Although I'm happy to hear from module users in private email, that's the best way for me to forget to do something. Besides the issue trackers, you can find help at L or L, both of which have many competent Perlers who can answer your question, almost in real time. They might not know the particulars of this module, but they can help you diagnose your problem. You might like to read L. =head2 Copyright and License You should have received a I file, but the license is also noted in the module files. About the only thing you can't do is pretend that you wrote code that you didn't. =head2 Good luck! Enjoy, brian d foy, briandfoy@pobox.com =cut CPAN-Mini-Inject-1.003/MANIFEST.SKIP0000644000076500000240000000222314631615433015223 0ustar brianstaff #!start included /usr/local/perls/perl-5.18.1/lib/5.18.1/ExtUtils/MANIFEST.SKIP # Avoid version control files. \bRCS\b \bCVS\b \bSCCS\b ,v$ \B\.svn\b \B\.git\b \B\.gitignore\b \b_darcs\b \B\.cvsignore$ # Avoid VMS specific MakeMaker generated files \bDescrip.MMS$ \bDESCRIP.MMS$ \bdescrip.mms$ # Avoid Makemaker generated and utility files. \bMANIFEST\.bak \bMakefile$ \bblib/ \bMakeMaker-\d \bpm_to_blib\.ts$ \bpm_to_blib$ \bblibdirs\.ts$ # 6.18 through 6.25 generated this # Avoid Module::Build generated and utility files. \bBuild$ \b_build/ \bBuild.bat$ \bBuild.COM$ \bBUILD.COM$ \bbuild.com$ # Avoid temp and backup files. ~$ \.old$ \#$ \b\.# \.bak$ \.tmp$ \.# \.rej$ # Avoid OS-specific files/dirs # Mac OSX metadata \B\.DS_Store # Mac OSX SMB mount metadata files \B\._ # Avoid Devel::Cover and Devel::CoverX::Covered files. \bcover_db\b \bcovered\b # Avoid MYMETA files ^MYMETA\. #!end included /usr/local/perls/perl-5.18.1/lib/5.18.1/ExtUtils/MANIFEST.SKIP \.?appveyor.yml \.releaserc \.lwpcookies ^CPAN-.* \bMANIFEST\s\d \bChanges\s\d \.icloud$ \A\.github\b \.gitattributes\b t/local/WRITEREPO/modulelist ^t/local/CPAN/authors ^t/local/MYCPAN CPAN-Mini-Inject-1.003/META.yml0000664000076500000240000000217614631615434014610 0ustar brianstaff--- abstract: 'Inject modules into a CPAN::Mini mirror.' author: - 'brian d foy ' build_requires: Test::More: '1' configure_requires: ExtUtils::MakeMaker: '6.64' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' license: artistic_2 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: CPAN-Mini-Inject no_index: directory: - t - inc requires: CPAN::Checksums: '2.13' CPAN::Mini: '0.32' Carp: '0' Compress::Zlib: '0' Dist::Metadata: '0.921' File::Basename: '0' File::Copy: '0' File::Path: '2.07' File::Slurp: '0' File::Spec: '2.07' File::Spec::Functions: '0' File::Temp: '0' Getopt::Long: '0' HTTP::Daemon: '0' IO::Zlib: '0' LWP::Simple: '0' Net::EmptyPort: '0' Pod::Usage: '0' YAML: '0' perl: '5.016' resources: bugtracker: https://github.com/briandfoy/cpan-mini-inject/issues homepage: https://github.com/briandfoy/cpan-mini-inject repository: https://github.com/briandfoy/cpan-mini-inject version: '1.003' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' CPAN-Mini-Inject-1.003/lib/0000755000076500000240000000000014631615434014075 5ustar brianstaffCPAN-Mini-Inject-1.003/lib/CPAN/0000755000076500000240000000000014631615434014616 5ustar brianstaffCPAN-Mini-Inject-1.003/lib/CPAN/Mini/0000755000076500000240000000000014631615434015512 5ustar brianstaffCPAN-Mini-Inject-1.003/lib/CPAN/Mini/Inject/0000755000076500000240000000000014631615434016726 5ustar brianstaffCPAN-Mini-Inject-1.003/lib/CPAN/Mini/Inject/Config.pm0000644000076500000240000000665314631615433020502 0ustar brianstaffpackage CPAN::Mini::Inject::Config; use strict; use warnings; use Carp; use File::Spec::Functions qw(rootdir catfile); =head1 NAME CPAN::Mini::Inject::Config - Config for CPAN::Mini::Inject =over 4 =cut our $VERSION = '0.37'; =item C =cut sub new { bless { file => undef }, $_[0] } =item C<< config_file( [FILE] ) >> =cut sub config_file { my ( $self, $file ) = @_; if ( @_ == 2 ) { croak( "Could not read file [$file]!" ) unless -r $file; $self->{file} = $file; } $self->{file}; } =item C<< load_config() >> loadcfg accepts a CPAN::Mini::Inject config file or if not defined will search the following four places in order: =over 4 =item * file pointed to by the environment variable MCPANI_CONFIG =item * $HOME/.mcpani/config =item * /usr/local/etc/mcpani =item * /etc/mcpani =back loadcfg sets the instance variable cfgfile to the file found or undef if none is found. print "$mcpi->{cfgfile}\n"; # /etc/mcpani =cut sub load_config { my $self = shift; my $cfgfile = shift || $self->_find_config; croak 'Unable to find config file' unless $cfgfile; $self->config_file( $cfgfile ); return $cfgfile; } sub _find_config { my ( @files ) = ( $ENV{MCPANI_CONFIG}, ( defined $ENV{HOME} ? catfile( $ENV{HOME}, qw(.mcpani config) ) : () ), catfile( rootdir(), qw(usr local etc mcpani) ), catfile( rootdir(), qw(etc mcpani) ), ); for my $file ( @files ) { next unless defined $file; next unless -r $file; return $file; } return; } =item C<< parse_config() >> parsecfg reads the config file stored in the instance variable cfgfile and creates a hash in config with each setting. $mcpi->{config}{remote} # CPAN sites to mirror from. parsecfg expects the config file in the following format: local: /www/CPAN remote: ftp://ftp.cpan.org/pub/CPAN ftp://ftp.kernel.org/pub/CPAN repository: /work/mymodules passive: yes dirmode: 0755 Description of options: =over 4 =item * local location to store local CPAN::Mini mirror (*REQUIRED*) =item * remote CPAN site(s) to mirror from. Multiple sites can be listed space separated. (*REQUIRED*) =item * repository Location to store modules to add to the local CPAN::Mini mirror. =item * passive Enable passive FTP. =item * dirmode Set the permissions of created directories to the specified mode. The default value is based on umask if supported. =back If either local or remote are not defined parsecfg croaks. =cut sub parse_config { my $self = shift; my $file = shift; my %required = map { $_, 1 } qw(local remote); $self->load_config( $file ) unless $self->config_file; if ( -r $self->config_file ) { open my ( $fh ), "<", $self->config_file or croak( "Could not open config file: $!" ); while ( <$fh> ) { next if /^\s*#/; $self->{$1} = $2 if /^\s*([^:\s]+)\s*:\s*(.*?)\s*$/; delete $required{$1} if defined $required{$1}; } close $fh; croak 'Required parameter(s): ' . join( ' ', keys %required ) . ' missing.' if keys %required; } return $self; } =item C<< get( DIRECTIVE ) >> Return the value for the named configuration directive. =cut sub get { $_[0]->{ $_[1] } } =item C<< set( DIRECTIVE, VALUE ) >> Sets the value for the named configuration directive. =cut sub set { $_[0]->{ $_[1] } = $_[2] } =back =head1 BUGS Report issues to the GitHub queue at https://github.com/briandfoy/cpan-mini-inject/issues =cut 1; CPAN-Mini-Inject-1.003/lib/CPAN/Mini/Inject.pm0000644000076500000240000004312614631615433017271 0ustar brianstaffpackage CPAN::Mini::Inject; use strict; use warnings; use CPAN::Checksums 2.13 qw( updatedir ); use CPAN::Mini; use CPAN::Mini::Inject::Config; use Carp; use Compress::Zlib; use File::Basename; use File::Copy; use File::Path qw( make_path ); use File::Spec; use LWP::Simple; use Dist::Metadata (); =head1 NAME CPAN::Mini::Inject - Inject modules into a CPAN::Mini mirror. =cut our $VERSION = '1.003'; our @ISA = qw( CPAN::Mini ); =head1 SYNOPSIS If you're not going to customize the way CPAN::Mini::Inject works you probably want to look at the L command instead. use CPAN::Mini::Inject; $mcpi=CPAN::Mini::Inject->new; $mcpi->parsecfg('t/.mcpani/config'); $mcpi->add( module => 'CPAN::Mini::Inject', authorid => 'SSORICHE', version => ' 0.01', file => 'mymodules/CPAN-Mini-Inject-0.01.tar.gz' ) $mcpi->writelist; $mcpi->update_mirror; $mcpi->inject; =head1 DESCRIPTION CPAN::Mini::Inject uses CPAN::Mini to build or update a I CPAN mirror from a I one. It adds two extra features: 1. an additional I of distribution files and related information (author and module versions), separate from the local and remote mirrors, to which you can add your own distribution files. 2. the ability to I the distribution files from your I into a I CPAN mirror. =head1 METHODS Each method in CPAN::Mini::Inject returns a CPAN::Mini::Inject object which allows method chaining. For example: my $mcpi=CPAN::Mini::Inject->new; $mcpi->parsecfg ->update_mirror ->inject; A C ISA L. Refer to the L for that module for details of the interface C inherits from it. =over 4 =item C Create a new CPAN::Mini::Inject object. =cut sub new { return bless { config_class => 'CPAN::Mini::Inject::Config' }, $_[0]; } =item C<< config_class( [CLASS] ) >> Returns the name of the class handling the configuration. With an argument, it sets the name of the class to handle the config. To use that, you'll have to call it before you load the configuration. =cut sub config_class { my $self = shift; if ( @_ ) { $self->{config_class} = shift } $self->{config_class}; } =item C<< config >> Returns the configuration object. This object should be from the class returned by C unless you've done something weird. =cut sub config { my $self = shift; if ( @_ ) { $self->{config} = shift } $self->{config}; } =item C<< loadcfg( [FILENAME] ) >> This is a bridge to CPAN::Mini::Inject::Config's loadconfig. It sets the filename for the configuration, or uses one of the defaults. =cut sub loadcfg { my $self = shift; unless ( $self->{config} ) { $self->{config} = $self->config_class->new; } $self->{cfgfile} = $self->{config}->load_config( @_ ); return $self; } =item C<< parsecfg() >> This is a bridge to CPAN::Mini::Inject::Config's parseconfig. =cut sub parsecfg { my $self = shift; unless ( $self->{config} ) { $self->config( $self->config_class->new ); } $self->config->parse_config( @_ ); return $self; } =item C<< site( [SITE] ) >> Returns the CPAN site that CPAN::Mini::Inject chose from the list specified in the C directive. =cut sub site { no warnings; my $self = shift; if ( @_ ) { $self->{site} = shift } $self->{site} || ''; } =item C Test each site listed in the remote parameter of the config file by performing a get on each site in order for authors/01mailrc.txt.gz. The first site to respond successfully is set as the instance variable site. print "$mcpi->{site}\n"; # ftp://ftp.cpan.org/pub/CPAN C accepts an optional parameter to enable verbose mode. =cut sub testremote { my $self = shift; my $verbose = shift; $self->site( undef ) if $self->site; $ENV{FTP_PASSIVE} = 1 if ( $self->config->get( 'passive' ) ); for my $site ( split( /\s+/, $self->config->get( 'remote' ) ) ) { $site .= '/' unless ( $site =~ m/\/$/ ); print "Testing site: $site\n" if ( $verbose ); if ( get( $site . 'authors/01mailrc.txt.gz' ) ) { $self->site( $site ); print "\n$site selected.\n" if ( $verbose ); last; } } croak "Unable to connect to any remote site" unless $self->site; return $self; } =item C This is a subclass of CPAN::Mini. =cut sub update_mirror { my $self = shift; my %options = @_; croak 'Can not write to local: ' . $self->config->get( 'local' ) unless ( -w $self->config->get( 'local' ) ); $ENV{FTP_PASSIVE} = 1 if $self->config->get( 'passive' ); $options{local} ||= $self->config->get( 'local' ); $options{trace} ||= 0; $options{skip_perl} ||= $self->config->get( 'perl' ) || 1; $options{skip_cleanup} ||= $self->config->get( 'skip_cleanup' ) || 0; $self->testremote( $options{trace} ) unless ( $self->site || $options{remote} ); $options{remote} ||= $self->site; $options{dirmode} ||= oct( $self->config->get( 'dirmode' ) || sprintf( '0%o', 0777 & ~umask ) ); CPAN::Mini->update_mirror( %options ); } =item C Add a new module to the repository. The add method copies the module file into the repository with the same structure as a CPAN site. For example CPAN-Mini-Inject-0.01.tar.gz is copied to MYCPAN/authors/id/S/SS/SSORICHE. add creates the required directory structure below the repository. Packages found in the distribution will be added to the module list (for example both C and C will be added to the F file). Packages will be looked for in the C key of the META file if present, otherwise the files in the dist will be searched. See L for more information. =over 4 =item * module The name of the module to add. The distribution file will be searched for modules but you can specify the main one explicitly. =item * authorid CPAN author id. This does not have to be a real author id. =item * version The modules version number. Module names and versions will be determined, but you can specify one explicitly. =item * file The tar.gz of the module. =back add( module => 'Module::Name', authorid => 'AUTHOR', version => 0.01, file => './Module-Name-0.01.tar.gz' ); =cut sub add { my $self = shift; my %options = @_; my $optionchk = _optionchk( \%options, qw/authorid file/ ); croak "Required option not specified: $optionchk" if $optionchk; croak "No repository configured" unless ( $self->config->get( 'repository' ) ); croak "Can not write to repository: " . $self->config->get( 'repository' ) unless ( -w $self->config->get( 'repository' ) ); croak "Can not read module file: $options{file}" unless -r $options{file}; # attempt to guess module and version my $distmeta = Dist::Metadata->new( file => $options{file} ); my $packages = $distmeta->package_versions; # include passed in module and version (prefer discovered version) if ( $options{module} ) { $packages->{ $options{module} } ||= $options{version}; } # if no packages were found we need explicit options if ( !keys %$packages ) { $optionchk = _optionchk( \%options, qw/module version/ ); croak "Required option not specified and no modules were found: $optionchk" if $optionchk; } my $modulefile = basename( $options{file} ); $self->readlist unless exists( $self->{modulelist} ); $options{authorid} = uc( $options{authorid} ); $self->{authdir} = $self->_authordir( $options{authorid}, $self->config->get( 'repository' ) ); my $target = $self->config->get( 'repository' ) . '/authors/id/' . $self->{authdir} . '/' . basename( $options{file} ); copy( $options{file}, dirname( $target ) ) or croak "Copy failed: $!"; $self->_updperms( $target ); { my $mods = join('|', keys %$packages); # remove old versions from the list @{ $self->{modulelist} } = grep { $_ !~ m/\A($mods)\s+/ } @{ $self->{modulelist} }; } # make data available afterwards (since method returns $self) push @{ $self->{added_modules} ||= [] }, { file => $modulefile, authorid => $options{authorid}, modules => $packages }; push( @{ $self->{modulelist} }, map { _fmtmodule( $_, File::Spec::Unix->catfile( File::Spec->splitdir( $self->{authdir} ), $modulefile ), defined($packages->{$_}) ? $packages->{$_} : 'undef' ) } keys %$packages ); return $self; } =item C Returns a list of hash references describing the modules added by this instance. Each hashref will contain C, C, and C. The C entry is a hashref of module names and versions included in the C. The list is cumulative. There will be one entry for each time L was called. This functionality is mostly provided for the included L script to be able to verbosely print all the modules added. =cut sub added_modules { my $self = shift; return @{ $self->{added_modules} ||= [] }; } =item C Insert modules from the repository into the local CPAN::Mini mirror. inject copies each module into the appropriate directory in the CPAN::Mini mirror and updates the CHECKSUMS file. Passing a value to C enables verbose mode, which lists each module as it's injected. =cut sub inject { my $self = shift; my $verbose = shift; my $dirmode = oct( $self->config->get( 'dirmode' ) ) if ( $self->config->get( 'dirmode' ) ); $self->readlist unless ( exists( $self->{modulelist} ) ); my %updatedir; my %already_injected; my %report; for my $modline ( @{ $self->{modulelist} } ) { my ( $module, $version, $file ) = split( /\s+/, $modline ); my $target = $self->config->get( 'local' ) . '/authors/id/' . $file; # collect all modules of a target/file # needed for report push @{ $report{$target} }, $module; next if $already_injected{$module}++; my $source = $self->config->get( 'repository' ) . '/authors/id/' . $file; $updatedir{ dirname( $file ) } = 1; my $tdir = dirname $target; _make_path( $tdir, defined $dirmode ? { mode => $dirmode } : {} ); copy( $source, $tdir ) or croak "Copy $source to $tdir failed: $!"; $self->_updperms( $target ); } # if verbose report target file and the modules it contains if ( $verbose ) { for my $target (keys %report) { my $target_str = "$target ... injected modules : "; my $fmt = '%' . length($target_str) . "s%s\n"; my @modules = @{ $report{$target} }; printf $fmt, $target_str, shift @modules; # first line with target for my $module ( @modules ) { # rest only the module printf $fmt, '', $module; } } } for my $dir ( keys( %updatedir ) ) { my $root = $self->config->get( 'local' ) . "/authors/id"; my $authdir = "$root/$dir"; updatedir( $authdir, $root ); $self->_updperms( "$authdir/CHECKSUMS" ); } $self->updpackages; $self->updauthors; return $self; } =item C Update the CPAN::Mini mirror's modules/02packages.details.txt.gz with the injected module information. =cut sub updpackages { my $self = shift; my @modules = sort( @{ $self->{modulelist} } ); my $infile = $self->_readpkgs; my %packages; # These need to be unique-per-package, with ones that come from the input # file being overridden. for my $line (@$infile, @modules) { my ($pkg) = split(/\s+/, $line, 2); $packages{$pkg} = $line; }; $self->_writepkgs( [ sort { lc $a cmp lc $b } values %packages ] ); } =item C Update the CPAN::Mini mirror's authors/01mailrc.txt.gz with stub information should the author not actually exist on CPAN =cut sub updauthors { my $self = shift; my $repo_authors = $self->_readauthors; my %author_ids_in_repo = map { my ( $id ) = $_ =~ /alias \s+ (\S+)/xms; $id => 1; } @$repo_authors; my @authors; my %authors_added; AUTHOR: for my $modline ( @{ $self->{modulelist} } ) { my ( $module, $version, $file ) = split( /\s+/, $modline ); # extract the author from the path my @dirs = File::Spec->splitdir( $file ); my $author = $dirs[2]; next AUTHOR if defined $author_ids_in_repo{$author}; next AUTHOR if defined $authors_added{$author}; push @$repo_authors, sprintf( 'alias %-10s "Custom Non-CPAN author "', $author ); $authors_added{$author} = 1; } $self->_writeauthors( $repo_authors ); } =item C Load the repository's modulelist. =cut sub _repo_file { File::Spec->catfile( shift->config->get( 'repository' ), @_ ); } sub _modulelist { shift->_repo_file( 'modulelist' ) } sub readlist { my $self = shift; $self->{modulelist} = undef; my $ml = $self->_modulelist; return $self unless -e $ml; open MODLIST, '<', $ml or croak "Can not read module list: $ml ($!)"; while ( ) { chomp; push @{ $self->{modulelist} }, $_; } close MODLIST; return $self; } =item C Write to the repository modulelist. =cut sub writelist { my $self = shift; croak 'Can not write module list: ' . $self->config->get( 'repository' ) . "/modulelist ERROR: $!" unless ( -w $self->{config}{repository} . '/modulelist' || -w $self->{config}{repository} ); return $self unless defined( $self->{modulelist} ); open( MODLIST, '>' . $self->config->get( 'repository' ) . '/modulelist' ); for ( sort( @{ $self->{modulelist} } ) ) { chomp; print MODLIST "$_\n"; } close( MODLIST ); $self->_updperms( $self->config->get( 'repository' ) . '/modulelist' ); return $self; } sub _updperms { my ( $self, $file ) = @_; chmod oct( $self->config->get( 'dirmode' ) ) & 06666, $file if $self->config->get( 'dirmode' ); } sub _optionchk { my ( $options, @list ) = @_; my @missing; for my $option ( @list ) { push @missing, $option unless defined $$options{$option}; } return join ' ', @missing; } sub _make_path { my $um = umask 0; make_path( @_ ); umask $um; } sub _authordir { my ( $self, $author, $dir ) = @_; my @author = ( substr( $author, 0, 1 ), substr( $author, 0, 2 ), $author ); my $dm = $self->config->get( 'dirmode' ); my @new = _make_path( File::Spec->catdir( $dir, 'authors', 'id', @author ), defined $dm ? { mode => oct $dm } : {} ); return return File::Spec->catdir( @author ); } sub _fmtmodule { my ( $module, $file, $version ) = @_; my $fw = 38 - length $version; $fw = length $module if $fw < length $module; return sprintf "%-${fw}s %s %s", $module, $version, $file; } sub _cfg { $_[0]->{config}{ $_[1] } } sub _readpkgs { my $self = shift; my $gzread = gzopen( $self->config->get( 'local' ) . '/modules/02packages.details.txt.gz', 'rb' ) or croak "Cannot open local 02packages.details.txt.gz: $gzerrno"; my $inheader = 1; my @packages; my $package; while ( $gzread->gzreadline( $package ) ) { if ( $inheader ) { $inheader = 0 unless $package =~ /\S/; next; } chomp( $package ); push( @packages, $package ); } $gzread->gzclose; return \@packages; } sub _writepkgs { my $self = shift; my $pkgs = shift; my $gzwrite = gzopen( $self->config->get( 'local' ) . '/modules/02packages.details.txt.gz', 'wb' ) or croak "Can't open local 02packages.details.txt.gz for writing: $gzerrno"; $gzwrite->gzwrite( "File: 02packages.details.txt\n" ); $gzwrite->gzwrite( "URL: http://www.perl.com/CPAN/modules/02packages.details.txt\n" ); $gzwrite->gzwrite( 'Description: Package names found in directory $CPAN/authors/id/' . "\n" ); $gzwrite->gzwrite( "Columns: package name, version, path\n" ); $gzwrite->gzwrite( "Intended-For: Automated fetch routines, namespace documentation.\n" ); $gzwrite->gzwrite( "Written-By: CPAN::Mini::Inject $VERSION\n" ); $gzwrite->gzwrite( "Line-Count: " . scalar( @$pkgs ) . "\n" ); # Last-Updated: Sat, 19 Mar 2005 19:49:10 GMT $gzwrite->gzwrite( "Last-Updated: " . _fmtdate() . "\n\n" ); $gzwrite->gzwrite( "$_\n" ) for ( @$pkgs ); $gzwrite->gzclose; } sub _readauthors { my $self = shift; my $gzread = gzopen( $self->config->get( 'local' ) . '/authors/01mailrc.txt.gz', 'rb' ) or croak "Cannot open " . $self->config->get( 'local' ) . "/authors/01mailrc.txt.gz: $gzerrno"; my @authors; my $author; while ( $gzread->gzreadline( $author ) ) { chomp( $author ); push( @authors, $author ); } $gzread->gzclose; return \@authors; } sub _writeauthors { my $self = shift; my $authors = shift; my $gzwrite = gzopen( $self->config->get( 'local' ) . '/authors/01mailrc.txt.gz', 'wb' ) or croak "Can't open local authors/01mailrc.txt.gz for writing: $gzerrno"; $gzwrite->gzwrite( "$_\n" ) for ( sort @$authors ); $gzwrite->gzclose; } sub _fmtdate { my @date = split( /\s+/, scalar( gmtime ) ); return "$date[0], $date[2] $date[1] $date[4] $date[3] GMT"; } =back =head1 SEE ALSO L =head1 Original Author Shawn Sorichetti, C<< >> =head1 ACKNOWLEDGEMENTS Special thanks to David Bartle, for bringing this module up to date, and resolving the reported bugs. Thanks to Jozef Kutej for numerous patches. =head1 BUGS Report issues to the GitHub queue at https://github.com/briandfoy/cpan-mini-inject/issues =head1 COPYRIGHT AND LICENSE Copyright 2008-2009 Shawn Sorichetti, Andy Armstrong, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; # End of CPAN::Mini::Inject CPAN-Mini-Inject-1.003/Makefile.PL0000644000076500000240000000652314631615433015306 0ustar brianstaffpackage CPAN::Mini::Inject; use strict; use warnings; =encoding utf8 =head1 The build file for CPAN::Mini::Inject This build file is a modulino; it works as both a build script and a module. To build the distribution, run this file normally: % perl Makefile.PL But, it's more interesting than that. You can load it with C and call C to get the data structure it passes to C: my $package = require '/path/to/Makefile.PL'; my $arguments = $package->arguments; Note that C-ing a file makes an entry in C<%INC> for exactly that name. If you try to C another file with the same name, even from a different path, C thinks it has already loaded the file. As such, I recommend you always require the full path to the file. The return value of the C is a package name (in this case, the name of the main module. Use that to call the C method. Even if this distribution needs a higher version of Perl, this bit only needs v5.8. You can play with the data structure with a primitive Perl. =cut use File::Spec::Functions qw(catfile); my $module = __PACKAGE__; ( my $dist = $module ) =~ s/::/-/g; my $github = 'https://github.com/briandfoy/cpan-mini-inject'; my $main_file = catfile( 'lib', split /::/, "$module.pm" ); my %WriteMakefile = ( 'MIN_PERL_VERSION' => '5.016', # from CPAN::Checksums 'NAME' => $module, 'ABSTRACT_FROM' => $main_file, 'VERSION_FROM' => $main_file, 'LICENSE' => 'artistic_2', 'AUTHOR' => 'brian d foy ', 'CONFIGURE_REQUIRES' => { 'ExtUtils::MakeMaker' => '6.64', }, 'BUILD_REQUIRES' => { }, 'TEST_REQUIRES' => { 'Test::More' => '1', }, 'PREREQ_PM' => { 'Carp' => '0', 'Compress::Zlib' => '0', 'CPAN::Checksums' => '2.13', 'CPAN::Mini' => '0.32', 'Dist::Metadata' => '0.921', 'File::Basename' => '0', 'File::Copy' => '0', 'File::Path' => '2.07', 'File::Slurp' => '0', 'File::Spec::Functions' => '0', 'File::Spec' => '2.07', 'File::Temp' => '0', 'Getopt::Long' => '0', 'HTTP::Daemon' => '0', 'IO::Zlib' => '0', 'LWP::Simple' => '0', 'Net::EmptyPort' => '0', 'Pod::Usage' => '0', 'YAML' => '0', }, 'EXE_FILES' => [qw(bin/mcpani)], 'META_MERGE' => { 'meta-spec' => { version => 2 }, resources => { repository => { type => 'git', url => $github, web => $github, }, bugtracker => { web => "$github/issues", }, homepage => $github, }, }, clean => { FILES => "$dist-*" }, ); sub arguments { \%WriteMakefile } do_it() unless caller; sub do_it { require File::Spec; my $MM ='ExtUtils::MakeMaker'; my $MM_version = eval{ "$MM " . $WriteMakefile{'CONFIGURE_REQUIRES'}{'ExtUtils::MakeMaker'} } || "$MM 6.64"; eval "use $MM_version; 1" or die "Could not load $MM_version: $@"; eval "use Test::Manifest 1.21" if -e File::Spec->catfile( qw(t test_manifest) ); my $arguments = arguments(); my $minimum_perl = $arguments->{MIN_PERL_VERSION} || '5.008'; eval "require $minimum_perl;" or die $@; WriteMakefile( %$arguments ); } no warnings; __PACKAGE__; CPAN-Mini-Inject-1.003/META.json0000664000076500000240000000375314631615434014762 0ustar brianstaff{ "abstract" : "Inject modules into a CPAN::Mini mirror.", "author" : [ "brian d foy " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "artistic_2" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "CPAN-Mini-Inject", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : {} }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.64" } }, "runtime" : { "requires" : { "CPAN::Checksums" : "2.13", "CPAN::Mini" : "0.32", "Carp" : "0", "Compress::Zlib" : "0", "Dist::Metadata" : "0.921", "File::Basename" : "0", "File::Copy" : "0", "File::Path" : "2.07", "File::Slurp" : "0", "File::Spec" : "2.07", "File::Spec::Functions" : "0", "File::Temp" : "0", "Getopt::Long" : "0", "HTTP::Daemon" : "0", "IO::Zlib" : "0", "LWP::Simple" : "0", "Net::EmptyPort" : "0", "Pod::Usage" : "0", "YAML" : "0", "perl" : "5.016" } }, "test" : { "requires" : { "Test::More" : "1" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/briandfoy/cpan-mini-inject/issues" }, "homepage" : "https://github.com/briandfoy/cpan-mini-inject", "repository" : { "type" : "git", "url" : "https://github.com/briandfoy/cpan-mini-inject", "web" : "https://github.com/briandfoy/cpan-mini-inject" } }, "version" : "1.003", "x_serialization_backend" : "JSON::PP version 4.16" }