summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2014-08-08 22:52:09 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2014-08-08 22:52:09 +0000
commit2a051e40a3fc09bba24c335060e8df327d313e55 (patch)
tree4ab817c1ef19de5ef30213d9accb8786b0b69302
downloadCPAN-Mini-tarball-master.tar.gz
-rw-r--r--Changes232
-rw-r--r--LICENSE379
-rw-r--r--MANIFEST21
-rw-r--r--MANIFEST.SKIP2
-rw-r--r--META.json512
-rw-r--r--META.yml376
-rw-r--r--Makefile.PL83
-rw-r--r--README15
-rw-r--r--bin/minicpan185
-rw-r--r--dist.ini13
-rw-r--r--lib/CPAN/Mini.pm1311
-rw-r--r--lib/CPAN/Mini/App.pm209
-rw-r--r--t/00-load.t7
-rw-r--r--t/000-report-versions-tiny.t86
-rw-r--r--t/app.t132
-rw-r--r--t/config-file.t108
-rw-r--r--t/filter.t157
-rw-r--r--xt/fake.t52
-rw-r--r--xt/release/changes_has_content.t41
-rw-r--r--xt/release/pod-syntax.t6
20 files changed, 3927 insertions, 0 deletions
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..e91ca8f
--- /dev/null
+++ b/Changes
@@ -0,0 +1,232 @@
+Revision history for CPAN-Mini
+
+1.111016 2014-08-08 18:52:05-04:00 America/New_York
+ - cope with a difference in the 02packages headers made by Pinto
+ (thanks, Fabrice Gabolde)
+
+1.111015 2013-12-13 08:17:02 America/New_York
+ update repo and bugtracker
+
+1.111014 2013-11-15 19:17:39 America/New_York
+ prune overly-aggressively-set prereqs; we do not require CPANPLUS!
+
+1.111013 2013-04-13 14:02:58 Europe/London
+ make www.cpan.org the default remote!
+
+ link to the list of mirrors, too (thanks, SYSMON!)
+
+ pass all options from command line, not just known ones (thanks,
+ Jeffrey Thalhammer, who waited a very very long time for me to apply
+ this!)
+
+ add --remote-from option to get remote from CPAN or CPANPLUS config
+ (thanks, Jeff Bisbee, who also waited a long time)
+
+ expand leading "~" in config file name (suggested by justincase)
+
+ die if local target isn't writable (suggested by SARGIE)
+
+1.111012 2013-03-28 16:32:58 America/New_York
+ when testing, mock my_home harder (thanks, David Golden!)
+
+1.111011 2013-02-07 16:40:23 America/New_York
+ check that the received 02packages seems at least remotely plausible
+
+1.111010 2012-10-24 10:46:35 America/New_York
+ fix the old ->trace method, broken in 1.111004
+
+1.111009 2012-08-27 21:32:39 America/New_York
+ config options that once had to be specified as multiple entries
+ on one line may now be given as repeated entries; these are
+ also_mirror, module_filters, and path_filters
+
+1.111008 2011-12-23 13:36:41 America/New_York
+ don't fail tests when the outside env has set CPAN_MINI_CONFIG
+ (thanks, Stephen Thirwall!)
+
+1.111007 2011-05-12 08:21:55 America/New_York
+ run the test minicpan in offline mode to avoid tester errors
+
+1.111006 2011-04-30 14:21:22 America/New_York
+ set the default log_level much earlier, to avoid an undef warning
+
+1.111005 2011-04-29 11:14:10 America/New_York
+ no code changes, this release is just intended to be indexed
+
+1.111004 2011-04-26 08:57:00 America/New_York
+ overhaul logging: there is now a log_level
+
+ delay creation of scratch dir until needed
+
+ a bunch of tests added for switch and config processing
+
+1.111003 2011-04-18 14:17:36 Europe/Amsterdam
+ add tools for building a fake cpan and an xt test for testing online
+
+1.111002 2011-04-16 18:20:43 Europe/Amsterdam
+
+ test for online-ness by HEAD-ing 02packages, not the remote root
+
+1.111001 2011-02-10 19:34:38 America/New_York
+ restore six-digit-mantissa versioning to help downstream packagers
+
+1.111 2011-02-05 15:13:17 America/New_York
+ add --timeout (thanks, Gabor Szabo!)
+
+1.110 2011-01-27 21:48:34 America/New_York
+ if the config file does not exist, do not die or spew warnings; this
+ fixes RT #57388
+
+1.100630 2010-03-04 07:57:34 America/New_York
+ suppress "using config..." during setup with -qq
+
+1.100593 2010-02-28 16:58:34 America/New_York
+ fix a previously unreached bit of code re: basename
+
+1.100592 2010-02-28 15:15:36 America/New_York
+ fix insanely stupid typo; need more tests!
+
+1.100591 2010-02-28 15:11:03 America/New_York
+ add an option to skip source code control files (thanks, brian d foy)
+
+1.100590 2009-02-28
+ add -C switch to allow alternate config file (thanks, brian d foy)
+ getopt switches are now correctly case-sensitive (thanks, brian)
+ improve subclassability for config file and output (thanks, brian)
+ switch to Dist::Zilla for release management
+
+0.576 2009-01-16
+ add repo to metadata
+
+0.575 2009-01-12
+ add no_conn_cache argument
+
+0.574 2008-11-26
+ fix broken prereq declaration in Makefile.PL
+
+0.573 2008-11-25
+ switch to new File::Path API, do not suffer undef dirmodes
+ write a RECENT file of the files mirrored in the latest run
+
+0.572 2008-11-04
+ add 'use File::HomeDir' to CPAN/Mini.pm (thanks DAGOLDEN)
+ improve handling of trailing whitespace in config (thanks ANK)
+
+0.571 2008-05-23
+ set LWP::UserAgent's env_proxy option to use proxy (RT #36124 from
+ IFOMICHEV)
+
+ allow skip_cleanup in config
+
+0.570 2008-05-01
+ offline mode now (correctly) means that the remote is not checked for
+ availability (RT #35563)
+
+0.569 2008-04-30
+ massive speed improvements by caching connection to remote mirror
+ add offline mode (by request of ADAMK)
+ add default config file location (by request of ADAMK)
+
+0.568 2008-03-05
+ [ no code changes ]
+ fix distribution to remove resource forks (ugh!)
+
+0.567 2008-02-05
+ BUGFIX: actually respect -c option
+ internal refactoring to make subclassing easier (DAGOLDEN)
+ bring code formatting inline with other (code (simply)) code
+
+0.566 2008-01-21
+ do not mirror "also_mirror" files twice (thanks DAGOLDEN)
+
+0.565 2007-11-08
+ move guts of minicpan command to ::App
+ CPANTS tweaks
+ switch to Module::Install
+
+0.564 2007-10-31
+ tweak packaging for CPANTS
+
+0.563 2007-10-16
+ MAJOR BUG FIX: mirror files in ./modules
+ introduced in 0.561, this bug only affected new mirrors, so anyone
+ who had been using it before that would not have noticed
+ BUG FIX: don't be so pedantic about requiring that remote end in /
+
+0.562 2007-07-04
+ fix skip_perl to continue to skip a perl-like dist
+
+0.561 2007-07-03
+ initially mirror indices to a scratch space, so that the indices in
+ the minicpan are not replaced until all referenced files are in place
+
+ when skipping perls, also skip: kurila, perl_mlb
+
+0.552 2006-12-01
+ documentation fixes
+
+0.551 2006-11-13
+ packaging improvements
+
+0.550 2006-08-08
+ add tilde expansion for homedir in local mirror specification
+ move configuration reading into CPAN::Mini
+ document a few previously-undocumented things
+ documentation cleanup
+ added unused-by-script option to use current mtime for indices
+ (this helps CPANPLUS do the right thing)
+
+0.500 2006-07-11
+ we no longer need File::HomeDir::Win32 on Windows
+ provide the also_mirror option to get other static files
+
+0.40 2005-11-04
+ remove force option to trace
+ create local mirror if needed
+ notice if local mirror -e && ! -d
+ cleared out stupid /\A\s+\z/ lines
+
+0.38 2005-10-13 00:05
+ more intelligently divide cleanup tasks (isn't ADAMK great?)
+ move arg validity check constructor (to silence ADAMK)
+ add some more checks for validity (stolen from ADAMK)
+ add errors option and -qq commandline option for it
+
+0.36 2005-01-06 18:40
+ code refs can be passed to _filters, which were slightly refactored
+
+0.32 2004-12-31 15:45
+ added an old alpha binary for perl to the perls to skip
+
+0.30 2004-12-28 10:00
+ added a "new" method for construction
+ update_mirror can act as class or instance method
+ added clean_file method
+
+0.26 2004-12-02 15:05
+ require version 5.6 of perl in Makefile.PL
+
+0.24 2004-11-29 14:30
+ dirmode is correctly octalized (thanks SSORICHE)
+ sungo's *_filters patch
+
+0.20 2004-09-28 10:20
+ added config file
+ added file_allowed (to override cleanup)
+ the -d option, long documented, now works
+
+0.18 2004-09-21 20:15
+ canonpath File::Find::name to avoid horrible Win32 bug
+ added -v to print version of CPAN::Mini
+
+0.16 2004-09-07 21:50
+ added -d to set mode for created dirs
+
+0.14 2004-08-28 17:05
+ uses Pod::Usage
+ now skips ponie and parrot (not just perl)
+ -p option to override the above skipping
+ "seen_changes" attribute and return value added
+
+0.10 2004-08-26 10:50
+ initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c08394d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,379 @@
+This software is copyright (c) 2004 by Ricardo SIGNES.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+Terms of the Perl programming language system itself
+
+a) the GNU General Public License as published by the Free
+ Software Foundation; either version 1, or (at your option) any
+ later version, or
+b) the "Artistic License"
+
+--- The GNU General Public License, Version 1, February 1989 ---
+
+This software is Copyright (c) 2004 by Ricardo SIGNES.
+
+This is free software, licensed under:
+
+ The GNU General Public License, Version 1, February 1989
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 1, February 1989
+
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The license agreements of most software companies try to keep users
+at the mercy of those companies. By contrast, our General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. The
+General Public License applies to the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+You can use it for your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Specifically, the General Public License is designed to make
+sure that you have the freedom to give away or sell copies of free
+software, that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of a such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must tell them their rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License. The
+"Program", below, refers to any such program or work, and a "work based
+on the Program" means either the Program or any work containing the
+Program or a portion of it, either verbatim or with modifications. Each
+licensee is addressed as "you".
+
+ 1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this
+General Public License and to the absence of any warranty; and give any
+other recipients of the Program a copy of this General Public License
+along with the Program. You may charge a fee for the physical act of
+transferring a copy.
+
+ 2. You may modify your copy or copies of the Program or any portion of
+it, and copy and distribute such modifications under the terms of Paragraph
+1 above, provided that you also do the following:
+
+ a) cause the modified files to carry prominent notices stating that
+ you changed the files and the date of any change; and
+
+ b) cause the whole of any work that you distribute or publish, that
+ in whole or in part contains the Program or any part thereof, either
+ with or without modifications, to be licensed at no charge to all
+ third parties under the terms of this General Public License (except
+ that you may choose to grant warranty protection to some or all
+ third parties, at your option).
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use
+ in the simplest and most usual way, to print or display an
+ announcement including an appropriate copyright notice and a notice
+ that there is no warranty (or else, saying that you provide a
+ warranty) and that users may redistribute the program under these
+ conditions, and telling the user how to view a copy of this General
+ Public License.
+
+ d) You may charge a fee for the physical act of transferring a
+ copy, and you may at your option offer warranty protection in
+ exchange for a fee.
+
+Mere aggregation of another independent work with the Program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other work under the scope of these terms.
+
+ 3. You may copy and distribute the Program (or a portion or derivative of
+it, under Paragraph 2) in object code or executable form under the terms of
+Paragraphs 1 and 2 above provided that you also do one of the following:
+
+ a) accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ b) accompany it with a written offer, valid for at least three
+ years, to give any third party free (except for a nominal charge
+ for the cost of distribution) a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ c) accompany it with the information you received as to where the
+ corresponding source code may be obtained. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form alone.)
+
+Source code for a work means the preferred form of the work for making
+modifications to it. For an executable file, complete source code means
+all the source code for all modules it contains; but, as a special
+exception, it need not include source code for modules which are standard
+libraries that accompany the operating system on which the executable
+file runs, or for standard header files or definitions files that
+accompany that operating system.
+
+ 4. You may not copy, modify, sublicense, distribute or transfer the
+Program except as expressly provided under this General Public License.
+Any attempt otherwise to copy, modify, sublicense, distribute or transfer
+the Program is void, and will automatically terminate your rights to use
+the Program under this License. However, parties who have received
+copies, or rights to use copies, from you under this General Public
+License will not have their licenses terminated so long as such parties
+remain in full compliance.
+
+ 5. By copying, distributing or modifying the Program (or any work based
+on the Program) you indicate your acceptance of this license to do so,
+and all its terms and conditions.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these
+terms and conditions. You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein.
+
+ 7. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of the license which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+the license, you may choose any version ever published by the Free Software
+Foundation.
+
+ 8. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to humanity, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+ To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19xx name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than `show w' and `show
+c'; they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ program `Gnomovision' (a program to direct compilers to make passes
+ at assemblers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+--- The Artistic License 1.0 ---
+
+This software is Copyright (c) 2004 by Ricardo SIGNES.
+
+This is free software, licensed under:
+
+ The Artistic License 1.0
+
+The Artistic License
+
+Preamble
+
+The intent of this document is to state the conditions under which a Package
+may be copied, such that the Copyright Holder maintains some semblance of
+artistic control over the development of the package, while giving the users of
+the package the right to use and distribute the Package in a more-or-less
+customary fashion, plus the right to make reasonable modifications.
+
+Definitions:
+
+ - "Package" refers to the collection of files distributed by the Copyright
+ Holder, and derivatives of that collection of files created through
+ textual modification.
+ - "Standard Version" refers to such a Package if it has not been modified,
+ or has been modified in accordance with the wishes of the Copyright
+ Holder.
+ - "Copyright Holder" is whoever is named in the copyright or copyrights for
+ the package.
+ - "You" is you, if you're thinking about copying or distributing this Package.
+ - "Reasonable copying fee" is whatever you can justify on the basis of media
+ cost, duplication charges, time of people involved, and so on. (You will
+ not be required to justify it to the Copyright Holder, but only to the
+ computing community at large as a market that must bear the fee.)
+ - "Freely Available" means that no fee is charged for the item itself, though
+ there may be fees involved in handling the item. It also means that
+ recipients of the item may redistribute it under the same conditions they
+ received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications derived
+from the Public Domain or from the Copyright Holder. A Package modified in such
+a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided that
+you insert a prominent notice in each changed file stating how and when you
+changed that file, and provided that you do at least ONE of the following:
+
+ a) place your modifications in the Public Domain or otherwise make them
+ Freely Available, such as by posting said modifications to Usenet or an
+ equivalent medium, or placing the modifications on a major archive site
+ such as ftp.uu.net, or by allowing the Copyright Holder to include your
+ modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation or organization.
+
+ c) rename any non-standard executables so the names do not conflict with
+ standard executables, which must also be provided, and provide a separate
+ manual page for each non-standard executable that clearly documents how it
+ differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or executable
+form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library files,
+ together with instructions (in the manual page or equivalent) on where to
+ get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of the Package
+ with your modifications.
+
+ c) accompany any non-standard executables with their corresponding Standard
+ Version executables, giving the non-standard executables non-standard
+ names, and clearly documenting the differences in manual pages (or
+ equivalent), together with instructions on where to get the Standard
+ Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package. You may charge any fee you choose for support of this Package. You
+may not charge a fee for this Package itself. However, you may distribute this
+Package in aggregate with other (possibly commercial) programs as part of a
+larger (possibly commercial) software distribution provided that you do not
+advertise this Package as a product of your own.
+
+6. The scripts and library files supplied as input to or produced as output
+from the programs of this Package do not automatically fall under the copyright
+of this Package, but belong to whomever generated them, and may be sold
+commercially, and may be aggregated with this Package.
+
+7. C or perl subroutines supplied by you and linked into this Package shall not
+be considered part of this Package.
+
+8. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+The End
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..0d05514
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,21 @@
+# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.020.
+Changes
+LICENSE
+MANIFEST
+MANIFEST.SKIP
+META.json
+META.yml
+Makefile.PL
+README
+bin/minicpan
+dist.ini
+lib/CPAN/Mini.pm
+lib/CPAN/Mini/App.pm
+t/00-load.t
+t/000-report-versions-tiny.t
+t/app.t
+t/config-file.t
+t/filter.t
+xt/fake.t
+xt/release/changes_has_content.t
+xt/release/pod-syntax.t
diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP
new file mode 100644
index 0000000..d04795b
--- /dev/null
+++ b/MANIFEST.SKIP
@@ -0,0 +1,2 @@
+misc
+fakecpan
diff --git a/META.json b/META.json
new file mode 100644
index 0000000..b1c4854
--- /dev/null
+++ b/META.json
@@ -0,0 +1,512 @@
+{
+ "abstract" : "create a minimal mirror of CPAN",
+ "author" : [
+ "Ricardo SIGNES <rjbs@cpan.org>",
+ "Randal Schwartz <merlyn@stonehenge.com>"
+ ],
+ "dynamic_config" : 0,
+ "generated_by" : "Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.141520",
+ "license" : [
+ "perl_5"
+ ],
+ "meta-spec" : {
+ "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
+ "version" : "2"
+ },
+ "name" : "CPAN-Mini",
+ "prereqs" : {
+ "configure" : {
+ "requires" : {
+ "ExtUtils::MakeMaker" : "0"
+ }
+ },
+ "develop" : {
+ "requires" : {
+ "Test::Pod" : "1.41",
+ "version" : "0.9901"
+ }
+ },
+ "runtime" : {
+ "requires" : {
+ "Carp" : "0",
+ "Compress::Zlib" : "1.20",
+ "File::Basename" : "0",
+ "File::Copy" : "0",
+ "File::Find" : "0",
+ "File::HomeDir" : "0.57",
+ "File::Path" : "2.04",
+ "File::Spec" : "0",
+ "File::Temp" : "0",
+ "Getopt::Long" : "0",
+ "LWP::UserAgent" : "5",
+ "Pod::Usage" : "1.00",
+ "URI" : "1",
+ "perl" : "5.006",
+ "strict" : "0",
+ "warnings" : "0"
+ }
+ },
+ "test" : {
+ "requires" : {
+ "Test::More" : "0.96"
+ }
+ }
+ },
+ "release_status" : "stable",
+ "resources" : {
+ "bugtracker" : {
+ "web" : "https://github.com/rjbs/CPAN-Mini/issues"
+ },
+ "homepage" : "https://github.com/rjbs/CPAN-Mini",
+ "repository" : {
+ "type" : "git",
+ "url" : "https://github.com/rjbs/CPAN-Mini.git",
+ "web" : "https://github.com/rjbs/CPAN-Mini"
+ }
+ },
+ "version" : "1.111016",
+ "x_Dist_Zilla" : {
+ "perl" : {
+ "version" : "5.021002"
+ },
+ "plugins" : [
+ {
+ "class" : "Dist::Zilla::Plugin::Git::GatherDir",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::GatherDir" : {
+ "include_untracked" : "0"
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@RJBS/Git::GatherDir",
+ "version" : "2.023"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed",
+ "name" : "@RJBS/CheckPrereqsIndexed",
+ "version" : "0.012"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::CheckExtraTests",
+ "name" : "@RJBS/CheckExtraTests",
+ "version" : "0.022"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::PromptIfStale",
+ "config" : {
+ "Dist::Zilla::Plugin::PromptIfStale" : {
+ "check_all_plugins" : 0,
+ "check_all_prereqs" : 0,
+ "modules" : [
+ "Dist::Zilla::PluginBundle::RJBS"
+ ],
+ "phase" : "build",
+ "skip" : []
+ }
+ },
+ "name" : "@RJBS/RJBS-Outdated",
+ "version" : "0.024"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::PromptIfStale",
+ "config" : {
+ "Dist::Zilla::Plugin::PromptIfStale" : {
+ "check_all_plugins" : "1",
+ "check_all_prereqs" : 0,
+ "modules" : [],
+ "phase" : "release",
+ "skip" : []
+ }
+ },
+ "name" : "@RJBS/CPAN-Outdated",
+ "version" : "0.024"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::PruneCruft",
+ "name" : "@RJBS/@Filter/PruneCruft",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ManifestSkip",
+ "name" : "@RJBS/@Filter/ManifestSkip",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::MetaYAML",
+ "name" : "@RJBS/@Filter/MetaYAML",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::License",
+ "name" : "@RJBS/@Filter/License",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Readme",
+ "name" : "@RJBS/@Filter/Readme",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ExecDir",
+ "name" : "@RJBS/@Filter/ExecDir",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ShareDir",
+ "name" : "@RJBS/@Filter/ShareDir",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Manifest",
+ "name" : "@RJBS/@Filter/Manifest",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::TestRelease",
+ "name" : "@RJBS/@Filter/TestRelease",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ConfirmRelease",
+ "name" : "@RJBS/@Filter/ConfirmRelease",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::UploadToCPAN",
+ "name" : "@RJBS/@Filter/UploadToCPAN",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::MakeMaker",
+ "config" : {
+ "Dist::Zilla::Role::TestRunner" : {
+ "default_jobs" : 9
+ }
+ },
+ "name" : "@RJBS/MakeMaker",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::AutoPrereqs",
+ "name" : "@RJBS/AutoPrereqs",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::NextVersion",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::NextVersion" : {
+ "first_version" : "0.001",
+ "version_by_branch" : "0",
+ "version_regexp" : "(?^:^([0-9]+\\.[0-9]+)$)"
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@RJBS/Git::NextVersion",
+ "version" : "2.023"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::PkgVersion",
+ "name" : "@RJBS/PkgVersion",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::MetaConfig",
+ "name" : "@RJBS/MetaConfig",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::MetaJSON",
+ "name" : "@RJBS/MetaJSON",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::NextRelease",
+ "name" : "@RJBS/NextRelease",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::ChangesHasContent",
+ "name" : "@RJBS/Test::ChangesHasContent",
+ "version" : "0.006"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::PodSyntaxTests",
+ "name" : "@RJBS/PodSyntaxTests",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ReportVersions::Tiny",
+ "name" : "@RJBS/ReportVersions::Tiny",
+ "version" : "1.10"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Prereqs",
+ "config" : {
+ "Dist::Zilla::Plugin::Prereqs" : {
+ "phase" : "test",
+ "type" : "requires"
+ }
+ },
+ "name" : "@RJBS/TestMoreWithSubtests",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::PodWeaver",
+ "config" : {
+ "Dist::Zilla::Plugin::PodWeaver" : {
+ "config_plugins" : [
+ "@RJBS"
+ ],
+ "finder" : [
+ ":InstallModules",
+ ":ExecFiles"
+ ],
+ "plugins" : [
+ {
+ "class" : "Pod::Weaver::Plugin::EnsurePod5",
+ "name" : "@CorePrep/EnsurePod5",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Plugin::H1Nester",
+ "name" : "@CorePrep/H1Nester",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Plugin::SingleEncoding",
+ "name" : "@RJBS/SingleEncoding",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Name",
+ "name" : "@RJBS/Name",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Version",
+ "name" : "@RJBS/Version",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Region",
+ "name" : "@RJBS/Prelude",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Generic",
+ "name" : "@RJBS/Synopsis",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Generic",
+ "name" : "@RJBS/Description",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Generic",
+ "name" : "@RJBS/Overview",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Generic",
+ "name" : "@RJBS/Stability",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Collect",
+ "name" : "Attributes",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Collect",
+ "name" : "Methods",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Collect",
+ "name" : "Functions",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Leftovers",
+ "name" : "@RJBS/Leftovers",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Region",
+ "name" : "@RJBS/postlude",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Authors",
+ "name" : "@RJBS/Authors",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Legal",
+ "name" : "@RJBS/Legal",
+ "version" : "4.006"
+ },
+ {
+ "class" : "Pod::Weaver::Plugin::Transformer",
+ "name" : "@RJBS/List",
+ "version" : "4.006"
+ }
+ ]
+ }
+ },
+ "name" : "@RJBS/PodWeaver",
+ "version" : "4.005"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::GithubMeta",
+ "name" : "@RJBS/GithubMeta",
+ "version" : "0.46"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Check",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Check" : {
+ "untracked_files" : "die"
+ },
+ "Dist::Zilla::Role::Git::DirtyFiles" : {
+ "allow_dirty" : [
+ "dist.ini",
+ "Changes"
+ ],
+ "allow_dirty_match" : [],
+ "changelog" : "Changes"
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@RJBS/@Git/Check",
+ "version" : "2.023"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Commit",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Commit" : {
+ "add_files_in" : [],
+ "commit_msg" : "v%v%n%n%c",
+ "time_zone" : "local"
+ },
+ "Dist::Zilla::Role::Git::DirtyFiles" : {
+ "allow_dirty" : [
+ "dist.ini",
+ "Changes"
+ ],
+ "allow_dirty_match" : [],
+ "changelog" : "Changes"
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@RJBS/@Git/Commit",
+ "version" : "2.023"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Tag",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Tag" : {
+ "branch" : null,
+ "signed" : 0,
+ "tag" : "1.111016",
+ "tag_format" : "%v",
+ "tag_message" : "v%v",
+ "time_zone" : "local"
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@RJBS/@Git/Tag",
+ "version" : "2.023"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Push",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Push" : {
+ "push_to" : [
+ "origin :",
+ "github :"
+ ],
+ "remotes_must_exist" : 0
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@RJBS/@Git/Push",
+ "version" : "2.023"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::RemovePrereqs",
+ "config" : {
+ "Dist::Zilla::Plugin::RemovePrereqs" : {
+ "modules_to_remove" : [
+ "CPAN",
+ "CPANPLUS::Backend"
+ ]
+ }
+ },
+ "name" : "RemovePrereqs",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":InstallModules",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":IncModules",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":TestFiles",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":ExecFiles",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":ShareFiles",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":MainModule",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":AllFiles",
+ "version" : "5.020"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":NoFiles",
+ "version" : "5.020"
+ }
+ ],
+ "zilla" : {
+ "class" : "Dist::Zilla::Dist::Builder",
+ "config" : {
+ "is_trial" : "0"
+ },
+ "version" : "5.020"
+ }
+ }
+}
+
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..2e672f9
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,376 @@
+---
+abstract: 'create a minimal mirror of CPAN'
+author:
+ - 'Ricardo SIGNES <rjbs@cpan.org>'
+ - 'Randal Schwartz <merlyn@stonehenge.com>'
+build_requires:
+ Test::More: '0.96'
+configure_requires:
+ ExtUtils::MakeMaker: '0'
+dynamic_config: 0
+generated_by: 'Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.141520'
+license: perl
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: '1.4'
+name: CPAN-Mini
+requires:
+ Carp: '0'
+ Compress::Zlib: '1.20'
+ File::Basename: '0'
+ File::Copy: '0'
+ File::Find: '0'
+ File::HomeDir: '0.57'
+ File::Path: '2.04'
+ File::Spec: '0'
+ File::Temp: '0'
+ Getopt::Long: '0'
+ LWP::UserAgent: '5'
+ Pod::Usage: '1.00'
+ URI: '1'
+ perl: '5.006'
+ strict: '0'
+ warnings: '0'
+resources:
+ bugtracker: https://github.com/rjbs/CPAN-Mini/issues
+ homepage: https://github.com/rjbs/CPAN-Mini
+ repository: https://github.com/rjbs/CPAN-Mini.git
+version: '1.111016'
+x_Dist_Zilla:
+ perl:
+ version: '5.021002'
+ plugins:
+ -
+ class: Dist::Zilla::Plugin::Git::GatherDir
+ config:
+ Dist::Zilla::Plugin::Git::GatherDir:
+ include_untracked: '0'
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@RJBS/Git::GatherDir'
+ version: '2.023'
+ -
+ class: Dist::Zilla::Plugin::CheckPrereqsIndexed
+ name: '@RJBS/CheckPrereqsIndexed'
+ version: '0.012'
+ -
+ class: Dist::Zilla::Plugin::CheckExtraTests
+ name: '@RJBS/CheckExtraTests'
+ version: '0.022'
+ -
+ class: Dist::Zilla::Plugin::PromptIfStale
+ config:
+ Dist::Zilla::Plugin::PromptIfStale:
+ check_all_plugins: 0
+ check_all_prereqs: 0
+ modules:
+ - Dist::Zilla::PluginBundle::RJBS
+ phase: build
+ skip: []
+ name: '@RJBS/RJBS-Outdated'
+ version: '0.024'
+ -
+ class: Dist::Zilla::Plugin::PromptIfStale
+ config:
+ Dist::Zilla::Plugin::PromptIfStale:
+ check_all_plugins: '1'
+ check_all_prereqs: 0
+ modules: []
+ phase: release
+ skip: []
+ name: '@RJBS/CPAN-Outdated'
+ version: '0.024'
+ -
+ class: Dist::Zilla::Plugin::PruneCruft
+ name: '@RJBS/@Filter/PruneCruft'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::ManifestSkip
+ name: '@RJBS/@Filter/ManifestSkip'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::MetaYAML
+ name: '@RJBS/@Filter/MetaYAML'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::License
+ name: '@RJBS/@Filter/License'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::Readme
+ name: '@RJBS/@Filter/Readme'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::ExecDir
+ name: '@RJBS/@Filter/ExecDir'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::ShareDir
+ name: '@RJBS/@Filter/ShareDir'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::Manifest
+ name: '@RJBS/@Filter/Manifest'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::TestRelease
+ name: '@RJBS/@Filter/TestRelease'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::ConfirmRelease
+ name: '@RJBS/@Filter/ConfirmRelease'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::UploadToCPAN
+ name: '@RJBS/@Filter/UploadToCPAN'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::MakeMaker
+ config:
+ Dist::Zilla::Role::TestRunner:
+ default_jobs: 9
+ name: '@RJBS/MakeMaker'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::AutoPrereqs
+ name: '@RJBS/AutoPrereqs'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::Git::NextVersion
+ config:
+ Dist::Zilla::Plugin::Git::NextVersion:
+ first_version: '0.001'
+ version_by_branch: '0'
+ version_regexp: (?^:^([0-9]+\.[0-9]+)$)
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@RJBS/Git::NextVersion'
+ version: '2.023'
+ -
+ class: Dist::Zilla::Plugin::PkgVersion
+ name: '@RJBS/PkgVersion'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::MetaConfig
+ name: '@RJBS/MetaConfig'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::MetaJSON
+ name: '@RJBS/MetaJSON'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::NextRelease
+ name: '@RJBS/NextRelease'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::Test::ChangesHasContent
+ name: '@RJBS/Test::ChangesHasContent'
+ version: '0.006'
+ -
+ class: Dist::Zilla::Plugin::PodSyntaxTests
+ name: '@RJBS/PodSyntaxTests'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::ReportVersions::Tiny
+ name: '@RJBS/ReportVersions::Tiny'
+ version: '1.10'
+ -
+ class: Dist::Zilla::Plugin::Prereqs
+ config:
+ Dist::Zilla::Plugin::Prereqs:
+ phase: test
+ type: requires
+ name: '@RJBS/TestMoreWithSubtests'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::PodWeaver
+ config:
+ Dist::Zilla::Plugin::PodWeaver:
+ config_plugins:
+ - '@RJBS'
+ finder:
+ - ':InstallModules'
+ - ':ExecFiles'
+ plugins:
+ -
+ class: Pod::Weaver::Plugin::EnsurePod5
+ name: '@CorePrep/EnsurePod5'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Plugin::H1Nester
+ name: '@CorePrep/H1Nester'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Plugin::SingleEncoding
+ name: '@RJBS/SingleEncoding'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Name
+ name: '@RJBS/Name'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Version
+ name: '@RJBS/Version'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Region
+ name: '@RJBS/Prelude'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Generic
+ name: '@RJBS/Synopsis'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Generic
+ name: '@RJBS/Description'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Generic
+ name: '@RJBS/Overview'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Generic
+ name: '@RJBS/Stability'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Collect
+ name: Attributes
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Collect
+ name: Methods
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Collect
+ name: Functions
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Leftovers
+ name: '@RJBS/Leftovers'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Region
+ name: '@RJBS/postlude'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Authors
+ name: '@RJBS/Authors'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Section::Legal
+ name: '@RJBS/Legal'
+ version: '4.006'
+ -
+ class: Pod::Weaver::Plugin::Transformer
+ name: '@RJBS/List'
+ version: '4.006'
+ name: '@RJBS/PodWeaver'
+ version: '4.005'
+ -
+ class: Dist::Zilla::Plugin::GithubMeta
+ name: '@RJBS/GithubMeta'
+ version: '0.46'
+ -
+ class: Dist::Zilla::Plugin::Git::Check
+ config:
+ Dist::Zilla::Plugin::Git::Check:
+ untracked_files: die
+ Dist::Zilla::Role::Git::DirtyFiles:
+ allow_dirty:
+ - dist.ini
+ - Changes
+ allow_dirty_match: []
+ changelog: Changes
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@RJBS/@Git/Check'
+ version: '2.023'
+ -
+ class: Dist::Zilla::Plugin::Git::Commit
+ config:
+ Dist::Zilla::Plugin::Git::Commit:
+ add_files_in: []
+ commit_msg: v%v%n%n%c
+ time_zone: local
+ Dist::Zilla::Role::Git::DirtyFiles:
+ allow_dirty:
+ - dist.ini
+ - Changes
+ allow_dirty_match: []
+ changelog: Changes
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@RJBS/@Git/Commit'
+ version: '2.023'
+ -
+ class: Dist::Zilla::Plugin::Git::Tag
+ config:
+ Dist::Zilla::Plugin::Git::Tag:
+ branch: ~
+ signed: 0
+ tag: '1.111016'
+ tag_format: '%v'
+ tag_message: v%v
+ time_zone: local
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@RJBS/@Git/Tag'
+ version: '2.023'
+ -
+ class: Dist::Zilla::Plugin::Git::Push
+ config:
+ Dist::Zilla::Plugin::Git::Push:
+ push_to:
+ - 'origin :'
+ - 'github :'
+ remotes_must_exist: 0
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@RJBS/@Git/Push'
+ version: '2.023'
+ -
+ class: Dist::Zilla::Plugin::RemovePrereqs
+ config:
+ Dist::Zilla::Plugin::RemovePrereqs:
+ modules_to_remove:
+ - CPAN
+ - CPANPLUS::Backend
+ name: RemovePrereqs
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':InstallModules'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':IncModules'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':TestFiles'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':ExecFiles'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':ShareFiles'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':MainModule'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':AllFiles'
+ version: '5.020'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':NoFiles'
+ version: '5.020'
+ zilla:
+ class: Dist::Zilla::Dist::Builder
+ config:
+ is_trial: '0'
+ version: '5.020'
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..ca5bb37
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,83 @@
+
+# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.020.
+use strict;
+use warnings;
+
+use 5.006;
+
+use ExtUtils::MakeMaker ;
+
+
+
+my %WriteMakefileArgs = (
+ "ABSTRACT" => "create a minimal mirror of CPAN",
+ "AUTHOR" => "Ricardo SIGNES <rjbs\@cpan.org>, Randal Schwartz <merlyn\@stonehenge.com>",
+ "CONFIGURE_REQUIRES" => {
+ "ExtUtils::MakeMaker" => 0
+ },
+ "DISTNAME" => "CPAN-Mini",
+ "EXE_FILES" => [
+ "bin/minicpan"
+ ],
+ "LICENSE" => "perl",
+ "NAME" => "CPAN::Mini",
+ "PREREQ_PM" => {
+ "Carp" => 0,
+ "Compress::Zlib" => "1.20",
+ "File::Basename" => 0,
+ "File::Copy" => 0,
+ "File::Find" => 0,
+ "File::HomeDir" => "0.57",
+ "File::Path" => "2.04",
+ "File::Spec" => 0,
+ "File::Temp" => 0,
+ "Getopt::Long" => 0,
+ "LWP::UserAgent" => 5,
+ "Pod::Usage" => "1.00",
+ "URI" => 1,
+ "strict" => 0,
+ "warnings" => 0
+ },
+ "TEST_REQUIRES" => {
+ "Test::More" => "0.96"
+ },
+ "VERSION" => "1.111016",
+ "test" => {
+ "TESTS" => "t/*.t"
+ }
+);
+
+
+my %FallbackPrereqs = (
+ "Carp" => 0,
+ "Compress::Zlib" => "1.20",
+ "File::Basename" => 0,
+ "File::Copy" => 0,
+ "File::Find" => 0,
+ "File::HomeDir" => "0.57",
+ "File::Path" => "2.04",
+ "File::Spec" => 0,
+ "File::Temp" => 0,
+ "Getopt::Long" => 0,
+ "LWP::UserAgent" => 5,
+ "Pod::Usage" => "1.00",
+ "Test::More" => "0.96",
+ "URI" => 1,
+ "strict" => 0,
+ "warnings" => 0
+);
+
+
+unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
+ delete $WriteMakefileArgs{TEST_REQUIRES};
+ delete $WriteMakefileArgs{BUILD_REQUIRES};
+ $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs;
+}
+
+delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
+ unless eval { ExtUtils::MakeMaker->VERSION(6.52) };
+
+WriteMakefile(%WriteMakefileArgs);
+
+
+
diff --git a/README b/README
new file mode 100644
index 0000000..d6b538c
--- /dev/null
+++ b/README
@@ -0,0 +1,15 @@
+
+
+This archive contains the distribution CPAN-Mini,
+version 1.111016:
+
+ create a minimal mirror of CPAN
+
+This software is copyright (c) 2004 by Ricardo SIGNES.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+
+This README file was generated by Dist::Zilla::Plugin::Readme v5.020.
+
diff --git a/bin/minicpan b/bin/minicpan
new file mode 100644
index 0000000..66f4e65
--- /dev/null
+++ b/bin/minicpan
@@ -0,0 +1,185 @@
+#!/usr/bin/perl -w
+use strict;
+use warnings;
+use CPAN::Mini::App;
+CPAN::Mini::App->run;
+
+# PODNAME: minicpan
+# ABSTRACT: uses CPAN::Mini to create or update a local mirror
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+minicpan - uses CPAN::Mini to create or update a local mirror
+
+=head1 VERSION
+
+version 1.111016
+
+=head1 SYNOPSIS
+
+ minicpan [options]
+
+ Options
+ -l LOCAL - where is the local minicpan? (required)
+ -r REMOTE - where is the remote cpan mirror? (required)
+ -d 0### - permissions (numeric) to use when creating directories
+ -f - check all directories, even if indices are unchanged
+ -p - mirror perl, ponie, and parrot distributions
+ --debug - run in debug mode (print even banal messages)
+ -q - run in quiet mode (don't print status)
+ -qq - run in silent mode (don't even print warnings)
+ -c CLASS - what class to use to mirror (default: CPAN::Mini)
+ -C FILE - what config file to use (default: ~/.minicpanrc)
+ -h - print help and exit
+ -v - print version and exit
+ -x - build an exact mirror, getting even normally disallowed files
+ -t SEC - timeout in sec. Defaults to 180 sec
+ --offline - operate in offline mode (generally: do nothing)
+ --log-level - provide a log level; instead of --debug, -q, or -qq
+ --remote-from TYPE - cpan remote from 'cpan' or 'cpanplus' configs
+
+=head1 DESCRIPTION
+
+This simple shell script just updates (or creates) a miniature CPAN mirror as
+described in CPAN::Mini.
+
+=head1 CONFIGURATION FILE
+
+By default, C<minicpan> will read a configuration file to get configuration
+information. The file is a simple set of names and values, as in the following
+example:
+
+ local: /home/rjbs/mirrors/minicpan/
+ remote: http://your.favorite.cpan/cpan/
+ exact_mirror: 1
+
+C<minicpan> tries to find a configuration file through the following process.
+It takes the first defined it finds:
+
+=over 4
+
+=item * Use the value specified by C<-C> on the command line
+
+=item * Use the value in the C<CPAN_MINI_CONFIG> environment variable
+
+=item * Use F<~/.minicpanrc>
+
+=item * Use F<CPAN/Mini/minicpan.conf>
+
+=back
+
+If the selected file does not exist, C<minicpan> does not keep looking.
+
+You can override this process with a C<config_file> method in your subclass.
+
+See C<CPAN::Mini> for a full listing of available options.
+
+=head1 TO DO
+
+Improve command-line options.
+
+=head1 SEE ALSO
+
+Randal Schwartz's original article, which can be found here:
+
+ http://www.stonehenge.com/merlyn/LinuxMag/col42.html
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Ricardo SIGNES <rjbs@cpan.org>
+
+=item *
+
+Randal Schwartz <merlyn@stonehenge.com>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2004 by Ricardo SIGNES.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
+__END__
+
+#pod =head1 SYNOPSIS
+#pod
+#pod minicpan [options]
+#pod
+#pod Options
+#pod -l LOCAL - where is the local minicpan? (required)
+#pod -r REMOTE - where is the remote cpan mirror? (required)
+#pod -d 0### - permissions (numeric) to use when creating directories
+#pod -f - check all directories, even if indices are unchanged
+#pod -p - mirror perl, ponie, and parrot distributions
+#pod --debug - run in debug mode (print even banal messages)
+#pod -q - run in quiet mode (don't print status)
+#pod -qq - run in silent mode (don't even print warnings)
+#pod -c CLASS - what class to use to mirror (default: CPAN::Mini)
+#pod -C FILE - what config file to use (default: ~/.minicpanrc)
+#pod -h - print help and exit
+#pod -v - print version and exit
+#pod -x - build an exact mirror, getting even normally disallowed files
+#pod -t SEC - timeout in sec. Defaults to 180 sec
+#pod --offline - operate in offline mode (generally: do nothing)
+#pod --log-level - provide a log level; instead of --debug, -q, or -qq
+#pod --remote-from TYPE - cpan remote from 'cpan' or 'cpanplus' configs
+#pod
+#pod =head1 DESCRIPTION
+#pod
+#pod This simple shell script just updates (or creates) a miniature CPAN mirror as
+#pod described in CPAN::Mini.
+#pod
+#pod =head1 CONFIGURATION FILE
+#pod
+#pod By default, C<minicpan> will read a configuration file to get configuration
+#pod information. The file is a simple set of names and values, as in the following
+#pod example:
+#pod
+#pod local: /home/rjbs/mirrors/minicpan/
+#pod remote: http://your.favorite.cpan/cpan/
+#pod exact_mirror: 1
+#pod
+#pod C<minicpan> tries to find a configuration file through the following process.
+#pod It takes the first defined it finds:
+#pod
+#pod =over 4
+#pod
+#pod =item * Use the value specified by C<-C> on the command line
+#pod
+#pod =item * Use the value in the C<CPAN_MINI_CONFIG> environment variable
+#pod
+#pod =item * Use F<~/.minicpanrc>
+#pod
+#pod =item * Use F<CPAN/Mini/minicpan.conf>
+#pod
+#pod =back
+#pod
+#pod If the selected file does not exist, C<minicpan> does not keep looking.
+#pod
+#pod You can override this process with a C<config_file> method in your subclass.
+#pod
+#pod See C<CPAN::Mini> for a full listing of available options.
+#pod
+#pod =head1 TO DO
+#pod
+#pod Improve command-line options.
+#pod
+#pod =head1 SEE ALSO
+#pod
+#pod Randal Schwartz's original article, which can be found here:
+#pod
+#pod http://www.stonehenge.com/merlyn/LinuxMag/col42.html
+#pod
+#pod =cut
diff --git a/dist.ini b/dist.ini
new file mode 100644
index 0000000..829bb89
--- /dev/null
+++ b/dist.ini
@@ -0,0 +1,13 @@
+name = CPAN-Mini
+author = Ricardo SIGNES <rjbs@cpan.org>
+author = Randal Schwartz <merlyn@stonehenge.com>
+license = Perl_5
+copyright_year = 2004
+copyright_holder = Ricardo SIGNES
+
+[@RJBS]
+version = 1
+
+[RemovePrereqs]
+remove = CPAN
+remove = CPANPLUS::Backend
diff --git a/lib/CPAN/Mini.pm b/lib/CPAN/Mini.pm
new file mode 100644
index 0000000..9569082
--- /dev/null
+++ b/lib/CPAN/Mini.pm
@@ -0,0 +1,1311 @@
+use 5.006;
+use strict;
+use warnings;
+
+package CPAN::Mini;
+$CPAN::Mini::VERSION = '1.111016';
+# ABSTRACT: create a minimal mirror of CPAN
+
+## no critic RequireCarping
+
+#pod =head1 SYNOPSIS
+#pod
+#pod (If you're not going to do something weird, you probably want to look at the
+#pod L<minicpan> command, instead.)
+#pod
+#pod use CPAN::Mini;
+#pod
+#pod CPAN::Mini->update_mirror(
+#pod remote => "http://cpan.mirrors.comintern.su",
+#pod local => "/usr/share/mirrors/cpan",
+#pod log_level => 'debug',
+#pod );
+#pod
+#pod =head1 DESCRIPTION
+#pod
+#pod CPAN::Mini provides a simple mechanism to build and update a minimal mirror of
+#pod the CPAN on your local disk. It contains only those files needed to install
+#pod the newest version of every distribution. Those files are:
+#pod
+#pod =for :list
+#pod * 01mailrc.txt.gz
+#pod * 02packages.details.txt.gz
+#pod * 03modlist.data.gz
+#pod * the last non-developer release of every dist for every author
+#pod
+#pod =cut
+
+use Carp ();
+
+use File::Basename ();
+use File::Copy ();
+use File::HomeDir 0.57 (); # Win32 support
+use File::Find ();
+use File::Path 2.04 (); # new API, bugfixes
+use File::Spec ();
+use File::Temp ();
+
+use URI 1 ();
+use LWP::UserAgent 5 ();
+
+use Compress::Zlib 1.20 ();
+
+#pod =method update_mirror
+#pod
+#pod CPAN::Mini->update_mirror(
+#pod remote => "http://cpan.mirrors.comintern.su",
+#pod local => "/usr/share/mirrors/cpan",
+#pod force => 0,
+#pod log_level => 'debug',
+#pod );
+#pod
+#pod This is the only method that need be called from outside this module. It will
+#pod update the local mirror with the files from the remote mirror.
+#pod
+#pod If called as a class method, C<update_mirror> creates an ephemeral CPAN::Mini
+#pod object on which other methods are called. That object is used to store mirror
+#pod location and state.
+#pod
+#pod This method returns the number of files updated.
+#pod
+#pod The following options are recognized:
+#pod
+#pod =begin :list
+#pod
+#pod * C<local>
+#pod
+#pod This is the local file path where the mirror will be written or updated.
+#pod
+#pod * C<remote>
+#pod
+#pod This is the URL of the CPAN mirror from which to work. A reasonable default
+#pod will be picked by default. A list of CPAN mirrors can be found at
+#pod L<http://www.cpan.org/SITES.html>
+#pod
+#pod * C<dirmode>
+#pod
+#pod Generally an octal number, this option sets the permissions of created
+#pod directories. It defaults to 0711.
+#pod
+#pod * C<exact_mirror>
+#pod
+#pod If true, the C<files_allowed> method will allow all extra files to be mirrored.
+#pod
+#pod * C<ignore_source_control>
+#pod
+#pod If true, CPAN::Mini will not try to remove source control files during
+#pod cleanup. See C<clean_unmirrored> for details.
+#pod
+#pod * C<force>
+#pod
+#pod If true, this option will cause CPAN::Mini to read the entire module list and
+#pod update anything out of date, even if the module list itself wasn't out of date
+#pod on this run.
+#pod
+#pod * C<skip_perl>
+#pod
+#pod If true, CPAN::Mini will skip the major language distributions: perl, parrot,
+#pod and ponie. It will also skip embperl, sybperl, bioperl, and kurila.
+#pod
+#pod * C<log_level>
+#pod
+#pod This defines the minimum level of message to log: debug, info, warn, or fatal
+#pod
+#pod * C<errors>
+#pod
+#pod If true, CPAN::Mini will warn with status messages on errors. (default: true)
+#pod
+#pod * C<path_filters>
+#pod
+#pod This options provides a set of rules for filtering paths. If a distribution
+#pod matches one of the rules in C<path_filters>, it will not be mirrored. A regex
+#pod rule is matched if the path matches the regex; a code rule is matched if the
+#pod code returns 1 when the path is passed to it. For example, the following
+#pod setting would skip all distributions from RJBS and SUNGO:
+#pod
+#pod path_filters => [
+#pod qr/RJBS/,
+#pod sub { $_[0] =~ /SUNGO/ }
+#pod ]
+#pod
+#pod * C<module_filters>
+#pod
+#pod This option provides a set of rules for filtering modules. It behaves like
+#pod path_filters, but acts only on module names. (Since most modules are in
+#pod distributions with more than one module, this setting will probably be less
+#pod useful than C<path_filters>.) For example, this setting will skip any
+#pod distribution containing only modules with the word "Acme" in them:
+#pod
+#pod module_filters => [ qr/Acme/i ]
+#pod
+#pod * C<also_mirror>
+#pod
+#pod This option should be an arrayref of extra files in the remote CPAN to mirror
+#pod locally.
+#pod
+#pod * C<skip_cleanup>
+#pod
+#pod If this option is true, CPAN::Mini will not try delete unmirrored files when it
+#pod has finished mirroring
+#pod
+#pod * C<offline>
+#pod
+#pod If offline, CPAN::Mini will not attempt to contact remote resources.
+#pod
+#pod * C<no_conn_cache>
+#pod
+#pod If true, no connection cache will be established. This is mostly useful as a
+#pod workaround for connection cache failures.
+#pod
+#pod =end :list
+#pod
+#pod =cut
+
+sub update_mirror {
+ my $self = shift;
+ $self = $self->new(@_) unless ref $self;
+
+ unless ($self->{offline}) {
+ my $local = $self->{local};
+
+ $self->log("Updating $local");
+ $self->log("Mirroring from $self->{remote}");
+ $self->log("=" x 63);
+
+ die "local mirror target $local is not writable" unless -w $local;
+
+ # mirrored tracks the already done, keyed by filename
+ # 1 = local-checked, 2 = remote-mirrored
+ $self->mirror_indices;
+
+ return unless $self->{force} or $self->{changes_made};
+
+ # mirror all the files
+ $self->_mirror_extras;
+ $self->mirror_file($_, 1) for @{ $self->_get_mirror_list };
+
+ # install indices after files are mirrored in case we're interrupted
+ # so indices will seem new again when continuing
+ $self->_install_indices;
+
+ $self->_write_out_recent;
+
+ # eliminate files we don't need
+ $self->clean_unmirrored unless $self->{skip_cleanup};
+ }
+
+ return $self->{changes_made};
+}
+
+sub _recent { $_[0]->{recent}{ $_[1] } = 1 }
+
+sub _write_out_recent {
+ my ($self) = @_;
+ return unless my @keys = keys %{ $self->{recent} };
+
+ my $recent = File::Spec->catfile($self->{local}, 'RECENT');
+ open my $recent_fh, '>', $recent or die "can't open $recent for writing: $!";
+
+ for my $file (sort keys %{ $self->{recent} }) {
+ print {$recent_fh} "$file\n" or die "can't write to $recent: $!";
+ }
+
+ die "error closing $recent: $!" unless close $recent_fh;
+ return;
+}
+
+sub _get_mirror_list {
+ my $self = shift;
+
+ my %mirror_list;
+
+ # now walk the packages list
+ my $details = File::Spec->catfile(
+ $self->_scratch_dir,
+ qw(modules 02packages.details.txt.gz)
+ );
+
+ my $gz = Compress::Zlib::gzopen($details, "rb")
+ or die "Cannot open details: $Compress::Zlib::gzerrno";
+
+ my $inheader = 1;
+ my $file_ok = 0;
+ while ($gz->gzreadline($_) > 0) {
+ if ($inheader) {
+ if (/\S/) {
+ my ($header, $value) = split /:\s*/, $_, 2;
+ chomp $value;
+ if ($header eq 'File'
+ and ($value eq '02packages.details.txt'
+ or $value eq '02packages.details.txt.gz')) {
+ $file_ok = 1;
+ }
+ } else {
+ $inheader = 0;
+ }
+
+ next;
+ }
+
+ die "02packages.details.txt file is not a valid index\n"
+ unless $file_ok;
+
+ my ($module, $version, $path) = split;
+ next if $self->_filter_module({
+ module => $module,
+ version => $version,
+ path => $path,
+ });
+
+ $mirror_list{"authors/id/$path"}++;
+ }
+
+ return [ sort keys %mirror_list ];
+}
+
+#pod =method new
+#pod
+#pod my $minicpan = CPAN::Mini->new;
+#pod
+#pod This method constructs a new CPAN::Mini object. Its parameters are described
+#pod above, under C<update_mirror>.
+#pod
+#pod =cut
+
+sub new {
+ my $class = shift;
+ my %defaults = (
+ changes_made => 0,
+ dirmode => 0711, ## no critic Zero
+ errors => 1,
+ mirrored => {},
+ log_level => 'info',
+ );
+
+ my $self = bless { %defaults, @_ } => $class;
+
+ $self->{dirmode} = $defaults{dirmode} unless defined $self->{dirmode};
+
+ $self->{recent} = {};
+
+ Carp::croak "no local mirror supplied" unless $self->{local};
+
+ substr($self->{local}, 0, 1, $class->__homedir)
+ if substr($self->{local}, 0, 1) eq q{~};
+
+ Carp::croak "local mirror path exists but is not a directory"
+ if (-e $self->{local})
+ and not(-d $self->{local});
+
+ unless (-e $self->{local}) {
+ File::Path::mkpath(
+ $self->{local},
+ {
+ verbose => $self->{log_level} eq 'debug',
+ mode => $self->{dirmode},
+ },
+ );
+ }
+
+ Carp::croak "no write permission to local mirror" unless -w $self->{local};
+
+ Carp::croak "no remote mirror supplied" unless $self->{remote};
+
+ $self->{remote} = "$self->{remote}/" if substr($self->{remote}, -1) ne '/';
+
+ my $version = $class->VERSION;
+ $version = 'v?' unless defined $version;
+
+ $self->{__lwp} = LWP::UserAgent->new(
+ agent => "$class/$version",
+ env_proxy => 1,
+ ($self->{no_conn_cache} ? () : (keep_alive => 5)),
+ ($self->{timeout} ? (timeout => $self->{timeout}) : ()),
+ );
+
+ unless ($self->{offline}) {
+ my $test_uri = URI->new_abs(
+ 'modules/02packages.details.txt.gz',
+ $self->{remote},
+ )->as_string;
+
+ Carp::croak "unable to contact the remote mirror"
+ unless eval { $self->__lwp->head($test_uri)->is_success };
+ }
+
+ return $self;
+}
+
+sub __lwp { $_[0]->{__lwp} }
+
+#pod =method mirror_indices
+#pod
+#pod $minicpan->mirror_indices;
+#pod
+#pod This method updates the index files from the CPAN.
+#pod
+#pod =cut
+
+sub _fixed_mirrors {
+ qw(
+ authors/01mailrc.txt.gz
+ modules/02packages.details.txt.gz
+ modules/03modlist.data.gz
+ );
+}
+
+sub _scratch_dir {
+ my ($self) = @_;
+
+ $self->{scratch} ||= File::Temp::tempdir(CLEANUP => 1);
+ return $self->{scratch};
+}
+
+sub mirror_indices {
+ my $self = shift;
+
+ $self->_make_index_dirs($self->_scratch_dir);
+
+ for my $path ($self->_fixed_mirrors) {
+ my $local_file = File::Spec->catfile($self->{local}, split m{/}, $path);
+ my $scratch_file = File::Spec->catfile(
+ $self->_scratch_dir,
+ split(m{/}, $path),
+ );
+
+ File::Copy::copy($local_file, $scratch_file);
+
+ utime((stat $local_file)[ 8, 9 ], $scratch_file);
+
+ $self->mirror_file($path, undef, { to_scratch => 1 });
+ }
+}
+
+sub _mirror_extras {
+ my $self = shift;
+
+ for my $path (@{ $self->{also_mirror} }) {
+ $self->mirror_file($path, undef);
+ }
+}
+
+sub _make_index_dirs {
+ my ($self, $base_dir, $dir_mode, $trace) = @_;
+ $base_dir ||= $self->_scratch_dir;
+ $dir_mode = 0711 if !defined $dir_mode; ## no critic Zero
+ $trace = 0 if !defined $trace;
+
+ for my $index ($self->_fixed_mirrors) {
+ my $dir = File::Basename::dirname($index);
+ my $needed = File::Spec->catdir($base_dir, $dir);
+ File::Path::mkpath($needed, { verbose => $trace, mode => $dir_mode });
+ die "couldn't create $needed: $!" unless -d $needed;
+ }
+}
+
+sub _install_indices {
+ my $self = shift;
+
+ $self->_make_index_dirs(
+ $self->{local},
+ $self->{dirmode},
+ $self->{log_level} eq 'debug',
+ );
+
+ for my $file ($self->_fixed_mirrors) {
+ my $local_file = File::Spec->catfile($self->{local}, split m{/}, $file);
+
+ unlink $local_file;
+
+ File::Copy::copy(
+ File::Spec->catfile($self->_scratch_dir, split m{/}, $file),
+ $local_file,
+ );
+
+ $self->{mirrored}{$local_file} = 1;
+ }
+}
+
+#pod =method mirror_file
+#pod
+#pod $minicpan->mirror_file($path, $skip_if_present)
+#pod
+#pod This method will mirror the given file from the remote to the local mirror,
+#pod overwriting any existing file unless C<$skip_if_present> is true.
+#pod
+#pod =cut
+
+sub mirror_file {
+ my ($self, $path, $skip_if_present, $arg) = @_;
+
+ $arg ||= {};
+
+ # full URL
+ my $remote_uri = eval { $path->isa('URI') }
+ ? $path
+ : URI->new_abs($path, $self->{remote})->as_string;
+
+ # native absolute file
+ my $local_file = File::Spec->catfile(
+ $arg->{to_scratch} ? $self->_scratch_dir : $self->{local},
+ split m{/}, $path
+ );
+
+ my $checksum_might_be_up_to_date = 1;
+
+ if ($skip_if_present and -f $local_file) {
+ ## upgrade to checked if not already
+ $self->{mirrored}{$local_file} ||= 1;
+ } elsif (($self->{mirrored}{$local_file} || 0) < 2) {
+ ## upgrade to full mirror
+ $self->{mirrored}{$local_file} = 2;
+
+ File::Path::mkpath(
+ File::Basename::dirname($local_file),
+ {
+ verbose => $self->{log_level} eq 'debug',
+ mode => $self->{dirmode},
+ },
+ );
+
+ $self->log($path, { no_nl => 1 });
+ my $res = $self->{__lwp}->mirror($remote_uri, $local_file);
+
+ if ($res->is_success) {
+ utime undef, undef, $local_file if $arg->{update_times};
+ $checksum_might_be_up_to_date = 0;
+ $self->_recent($path);
+ $self->log(" ... updated");
+ $self->{changes_made}++;
+ } elsif ($res->code != 304) { # not modified
+ $self->log(" ... resulted in an HTTP error with status " . $res->code);
+ $self->log_warn("$remote_uri: " . $res->status_line);
+ return;
+ } else {
+ $self->log(" ... up to date");
+ }
+ }
+
+ if ($path =~ m{^authors/id}) { # maybe fetch CHECKSUMS
+ my $checksum_path
+ = URI->new_abs("CHECKSUMS", $remote_uri)->rel($self->{remote})->as_string;
+
+ if ($path ne $checksum_path) {
+ $self->mirror_file($checksum_path, $checksum_might_be_up_to_date);
+ }
+ }
+}
+
+#pod =begin devel
+#pod
+#pod =method _filter_module
+#pod
+#pod next
+#pod if $self->_filter_module({ module => $foo, version => $foo, path => $foo });
+#pod
+#pod This method holds the filter chain logic. C<update_mirror> takes an optional
+#pod set of filter parameters. As C<update_mirror> encounters a distribution, it
+#pod calls this method to figure out whether or not it should be downloaded. The
+#pod user provided filters are taken into account. Returns 1 if the distribution is
+#pod filtered (to be skipped). Returns 0 if the distribution is to not filtered
+#pod (not to be skipped).
+#pod
+#pod =end devel
+#pod
+#pod =cut
+
+sub __do_filter {
+ my ($self, $filter, $file) = @_;
+ return unless $filter;
+
+ if (ref($filter) eq 'ARRAY') {
+ for (@$filter) {
+ return 1 if $self->__do_filter($_, $file);
+ }
+ return;
+ }
+
+ if (ref($filter) eq 'CODE') {
+ return $filter->($file);
+ } else {
+ return $file =~ $filter;
+ }
+}
+
+sub _filter_module {
+ my $self = shift;
+ my $args = shift;
+
+ if ($self->{skip_perl}) {
+ return 1 if $args->{path} =~ m{/(?:emb|syb|bio)?perl-\d}i;
+ return 1 if $args->{path} =~ m{/(?:parrot|ponie)-\d}i;
+ return 1 if $args->{path} =~ m{/(?:kurila)-\d}i;
+ return 1 if $args->{path} =~ m{/\bperl-?5\.004}i;
+ return 1 if $args->{path} =~ m{/\bperl_mlb\.zip}i;
+ }
+
+ return 1 if $self->__do_filter($self->{path_filters}, $args->{path});
+ return 1 if $self->__do_filter($self->{module_filters}, $args->{module});
+ return 0;
+}
+
+#pod =method file_allowed
+#pod
+#pod next unless $minicpan->file_allowed($filename);
+#pod
+#pod This method returns true if the given file is allowed to exist in the local
+#pod mirror, even if it isn't one of the required mirror files.
+#pod
+#pod By default, only dot-files are allowed. If the C<exact_mirror> option is true,
+#pod all files are allowed.
+#pod
+#pod =cut
+
+sub file_allowed {
+ my ($self, $file) = @_;
+ return 1 if $self->{exact_mirror};
+
+ # It's a cheap hack, but it gets the job done.
+ return 1 if $file eq File::Spec->catfile($self->{local}, 'RECENT');
+
+ return (substr(File::Basename::basename($file), 0, 1) eq q{.}) ? 1 : 0;
+}
+
+#pod =method clean_unmirrored
+#pod
+#pod $minicpan->clean_unmirrored;
+#pod
+#pod This method looks through the local mirror's files. If it finds a file that
+#pod neither belongs in the mirror nor is allowed (see the C<file_allowed> method),
+#pod C<clean_file> is called on the file.
+#pod
+#pod If you set C<ignore_source_control> to a true value, then this doesn't clean
+#pod up files that belong to source control systems. Currently this ignores:
+#pod
+#pod .cvs .cvsignore
+#pod .svn .svnignore
+#pod .git .gitignore
+#pod
+#pod Send patches for other source control files that you would like to have added.
+#pod
+#pod =cut
+
+my %Source_control_files;
+BEGIN {
+ %Source_control_files = map { $_ => 1 }
+ qw(.cvs .svn .git .cvsignore .svnignore .gitignore);
+}
+
+sub clean_unmirrored {
+ my $self = shift;
+
+ File::Find::find sub {
+ my $file = File::Spec->canonpath($File::Find::name); ## no critic Package
+ my $basename = File::Basename::basename( $file );
+
+ if (
+ $self->{ignore_source_control}
+ and exists $Source_control_files{$basename}
+ ) {
+ $File::Find::prune = 1;
+ return;
+ }
+
+ return unless (-f $file and not $self->{mirrored}{$file});
+ return if $self->file_allowed($file);
+
+ $self->clean_file($file);
+
+ }, $self->{local};
+}
+
+#pod =method clean_file
+#pod
+#pod $minicpan->clean_file($filename);
+#pod
+#pod This method, called by C<clean_unmirrored>, deletes the named file. It returns
+#pod true if the file is successfully unlinked. Otherwise, it returns false.
+#pod
+#pod =cut
+
+sub clean_file {
+ my ($self, $file) = @_;
+
+ unless (unlink $file) {
+ $self->log_warn("$file cannot be removed: $!");
+ return;
+ }
+
+ $self->log("$file removed");
+
+ return 1;
+}
+
+#pod =method log_warn
+#pod
+#pod =method log
+#pod
+#pod =method log_debug
+#pod
+#pod $minicpan->log($message);
+#pod
+#pod This will log (print) the given message unless the log level is too low.
+#pod
+#pod C<log>, which logs at the I<info> level, may also be called as C<trace> for
+#pod backward compatibility reasons.
+#pod
+#pod =cut
+
+sub log_level {
+ return $_[0]->{log_level} if ref $_[0];
+ return 'info';
+}
+
+sub log_unconditionally {
+ my ($self, $message, $arg) = @_;
+ $arg ||= {};
+
+ print($message, $arg->{no_nl} ? () : "\n");
+}
+
+sub log_warn {
+ return if $_[0]->log_level eq 'fatal';
+ $_[0]->log_unconditionally($_[1], $_[2]);
+}
+
+sub log {
+ return unless $_[0]->log_level =~ /\A(?:info|debug)\z/;
+ $_[0]->log_unconditionally($_[1], $_[2]);
+}
+
+sub trace {
+ my $self = shift;
+ $self->log(@_);
+}
+
+sub log_debug {
+ my ($self, @rest) = @_;
+ return unless $_[0]->log_level eq 'debug';
+ $_[0]->log_unconditionally($_[1], $_[2]);
+}
+
+#pod =method read_config
+#pod
+#pod my %config = CPAN::Mini->read_config(\%options);
+#pod
+#pod This routine returns a set of arguments that can be passed to CPAN::Mini's
+#pod C<new> or C<update_mirror> methods. It will look for a file called
+#pod F<.minicpanrc> in the user's home directory as determined by
+#pod L<File::HomeDir|File::HomeDir>.
+#pod
+#pod =cut
+
+sub __homedir {
+ my ($class) = @_;
+
+ my $homedir = File::HomeDir->my_home || $ENV{HOME};
+
+ Carp::croak "couldn't determine your home directory! set HOME env variable"
+ unless defined $homedir;
+
+ return $homedir;
+}
+
+sub __homedir_configfile {
+ my ($class) = @_;
+ my $default = File::Spec->catfile($class->__homedir, '.minicpanrc');
+}
+
+sub __default_configfile {
+ my ($self) = @_;
+
+ (my $pm_loc = $INC{'CPAN/Mini.pm'}) =~ s/Mini\.pm\z//;
+ File::Spec->catfile($pm_loc, 'minicpan.conf');
+}
+
+sub read_config {
+ my ($class, $options) = @_;
+
+ my $config_file = $class->config_file($options);
+
+ return unless defined $config_file;
+
+ # This is ugly, but lets us respect -qq for now even before we have an
+ # object. I think a better fix is warranted. -- rjbs, 2010-03-04
+ $class->log("Using config from $config_file")
+ if ($options->{log_level}||'info') =~ /\A(?:warn|fatal)\z/;
+
+ substr($config_file, 0, 1, $class->__homedir)
+ if substr($config_file, 0, 1) eq q{~};
+
+ return unless -e $config_file;
+
+ open my $config_fh, '<', $config_file
+ or die "couldn't open config file $config_file: $!";
+
+ my %config;
+ my %is_multivalue = map {; $_ => 1 }
+ qw(also_mirror module_filters path_filters);
+
+ $config{$_} = [] for keys %is_multivalue;
+
+ while (<$config_fh>) {
+ chomp;
+ next if /\A\s*\Z/sm;
+
+ if (/\A(\w+):\s*(\S.*?)\s*\Z/sm) {
+ my ($key, $value) = ($1, $2);
+
+ if ($is_multivalue{ $key }) {
+ push @{ $config{$key} }, split /\s+/, $value;
+ } else {
+ $config{ $key } = $value;
+ }
+ }
+ }
+
+ for (qw(also_mirror)) {
+ $config{$_} = [ grep { length } @{ $config{$_} } ];
+ }
+
+ for (qw(module_filters path_filters)) {
+ $config{$_} = [ map { qr/$_/ } @{ $config{$_} } ];
+ }
+
+ for (keys %is_multivalue) {
+ delete $config{$_} unless @{ $config{$_} };
+ }
+
+ return %config;
+}
+
+#pod =method config_file
+#pod
+#pod my $config_file = CPAN::Mini->config_file( { options } );
+#pod
+#pod This routine returns the config file name. It first looks at for the
+#pod C<config_file> setting, then the C<CPAN_MINI_CONFIG> environment
+#pod variable, then the default F<~/.minicpanrc>, and finally the
+#pod F<CPAN/Mini/minicpan.conf>. It uses the first defined value it finds.
+#pod If the filename it selects does not exist, it returns false.
+#pod
+#pod OPTIONS is an optional hash reference of the C<CPAN::Mini> config hash.
+#pod
+#pod =cut
+
+sub config_file {
+ my ($class, $options) = @_;
+
+ my $config_file = do {
+ if (defined eval { $options->{config_file} }) {
+ $options->{config_file};
+ } elsif (defined $ENV{CPAN_MINI_CONFIG}) {
+ $ENV{CPAN_MINI_CONFIG};
+ } elsif (defined $class->__homedir_configfile) {
+ $class->__homedir_configfile;
+ } elsif (defined $class->__default_configfile) {
+ $class->__default_configfile;
+ } else {
+ ();
+ }
+ };
+
+ return (
+ (defined $config_file && -e $config_file)
+ ? $config_file
+ : ()
+ );
+}
+
+#pod =head2 remote_from
+#pod
+#pod my $remote = CPAN::Mini->remote_from( $remote_from, $orig_remote, $quiet );
+#pod
+#pod This routine take an string argument and turn it into a method
+#pod call to handle to retrieve the a cpan mirror url from a source.
+#pod Currently supported methods:
+#pod
+#pod cpan - fetch the first mirror from your CPAN.pm config
+#pod cpanplus - fetch the first mirror from your CPANPLUS.pm config
+#pod
+#pod =cut
+
+sub remote_from {
+ my ( $class, $remote_from, $orig_remote, $quiet ) = @_;
+
+ my $method = lc "remote_from_" . $remote_from;
+
+ Carp::croak "unknown remote_from value: $remote_from"
+ unless $class->can($method);
+
+ my $new_remote = $class->$method;
+
+ warn "overriding '$orig_remote' with '$new_remote' via $method\n"
+ if !$quiet && $orig_remote;
+
+ return $new_remote;
+}
+
+#pod =head2 remote_from_cpan
+#pod
+#pod my $remote = CPAN::Mini->remote_from_cpan;
+#pod
+#pod This routine loads your CPAN.pm config and returns the first mirror in mirror
+#pod list. You can set this as your default by setting remote_from:cpan in your
+#pod F<.minicpanrc> file.
+#pod
+#pod =cut
+
+sub remote_from_cpan {
+ my ($self) = @_;
+
+ Carp::croak "unable find a CPAN, maybe you need to install it"
+ unless eval { require CPAN; 1 };
+
+ CPAN::HandleConfig::require_myconfig_or_config();
+
+ Carp::croak "unable to find mirror list in your CPAN config"
+ unless exists $CPAN::Config->{urllist};
+
+ Carp::croak "unable to find first mirror url in your CPAN config"
+ unless ref( $CPAN::Config->{urllist} ) eq 'ARRAY' && $CPAN::Config->{urllist}[0];
+
+ return $CPAN::Config->{urllist}[0];
+}
+
+#pod =head2 remote_from_cpanplus
+#pod
+#pod my $remote = CPAN::Mini->remote_from_cpanplus;
+#pod
+#pod This routine loads your CPANPLUS.pm config and returns the first mirror in
+#pod mirror list. You can set this as your default by setting remote_from:cpanplus
+#pod in your F<.minicpanrc> file.
+#pod
+#pod =cut
+
+sub remote_from_cpanplus {
+ my ($self) = @_;
+
+ Carp::croak "unable find a CPANPLUS, maybe you need to install it"
+ unless eval { require CPANPLUS::Backend };
+
+ my $cb = CPANPLUS::Backend->new;
+ my $hosts = $cb->configure_object->get_conf('hosts');
+
+ Carp::croak "unable to find mirror list in your CPANPLUS config"
+ unless $hosts;
+
+ Carp::croak "unable to find first mirror in your CPANPLUS config"
+ unless ref($hosts) eq 'ARRAY' && $hosts->[0];
+
+ my $url_parts = $hosts->[0];
+ return $url_parts->{scheme} . "://" . $url_parts->{host} . ( $url_parts->{path} || '' );
+}
+
+#pod =head1 SEE ALSO
+#pod
+#pod Randal Schwartz's original article on minicpan, here:
+#pod
+#pod http://www.stonehenge.com/merlyn/LinuxMag/col42.html
+#pod
+#pod L<CPANPLUS::Backend>, which provides the C<local_mirror> method, which performs
+#pod the same task as this module.
+#pod
+#pod =head1 THANKS
+#pod
+#pod Thanks to David Dyck for letting me know about my stupid documentation errors.
+#pod
+#pod Thanks to Roy Fulbright for finding an obnoxious bug on Win32.
+#pod
+#pod Thanks to Shawn Sorichetti for fixing a stupid octal-number-as-string bug.
+#pod
+#pod Thanks to sungo for implementing the filters, so I can finally stop mirroring
+#pod bioperl, and Robert Rothenberg for suggesting adding coderef rules.
+#pod
+#pod Thanks to Adam Kennedy for noticing and complaining about a lot of stupid
+#pod little design decisions.
+#pod
+#pod Thanks to Michael Schwern and Jason Kohles, for pointing out missing
+#pod documentation.
+#pod
+#pod Thanks to David Golden for some important bugfixes and refactoring.
+#pod
+#pod =cut
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+CPAN::Mini - create a minimal mirror of CPAN
+
+=head1 VERSION
+
+version 1.111016
+
+=head1 SYNOPSIS
+
+(If you're not going to do something weird, you probably want to look at the
+L<minicpan> command, instead.)
+
+ use CPAN::Mini;
+
+ CPAN::Mini->update_mirror(
+ remote => "http://cpan.mirrors.comintern.su",
+ local => "/usr/share/mirrors/cpan",
+ log_level => 'debug',
+ );
+
+=head1 DESCRIPTION
+
+CPAN::Mini provides a simple mechanism to build and update a minimal mirror of
+the CPAN on your local disk. It contains only those files needed to install
+the newest version of every distribution. Those files are:
+
+=over 4
+
+=item *
+
+01mailrc.txt.gz
+
+=item *
+
+02packages.details.txt.gz
+
+=item *
+
+03modlist.data.gz
+
+=item *
+
+the last non-developer release of every dist for every author
+
+=back
+
+=head1 METHODS
+
+=head2 update_mirror
+
+ CPAN::Mini->update_mirror(
+ remote => "http://cpan.mirrors.comintern.su",
+ local => "/usr/share/mirrors/cpan",
+ force => 0,
+ log_level => 'debug',
+ );
+
+This is the only method that need be called from outside this module. It will
+update the local mirror with the files from the remote mirror.
+
+If called as a class method, C<update_mirror> creates an ephemeral CPAN::Mini
+object on which other methods are called. That object is used to store mirror
+location and state.
+
+This method returns the number of files updated.
+
+The following options are recognized:
+
+=over 4
+
+=item *
+
+C<local>
+
+This is the local file path where the mirror will be written or updated.
+
+=item *
+
+C<remote>
+
+This is the URL of the CPAN mirror from which to work. A reasonable default
+will be picked by default. A list of CPAN mirrors can be found at
+L<http://www.cpan.org/SITES.html>
+
+=item *
+
+C<dirmode>
+
+Generally an octal number, this option sets the permissions of created
+directories. It defaults to 0711.
+
+=item *
+
+C<exact_mirror>
+
+If true, the C<files_allowed> method will allow all extra files to be mirrored.
+
+=item *
+
+C<ignore_source_control>
+
+If true, CPAN::Mini will not try to remove source control files during
+cleanup. See C<clean_unmirrored> for details.
+
+=item *
+
+C<force>
+
+If true, this option will cause CPAN::Mini to read the entire module list and
+update anything out of date, even if the module list itself wasn't out of date
+on this run.
+
+=item *
+
+C<skip_perl>
+
+If true, CPAN::Mini will skip the major language distributions: perl, parrot,
+and ponie. It will also skip embperl, sybperl, bioperl, and kurila.
+
+=item *
+
+C<log_level>
+
+This defines the minimum level of message to log: debug, info, warn, or fatal
+
+=item *
+
+C<errors>
+
+If true, CPAN::Mini will warn with status messages on errors. (default: true)
+
+=item *
+
+C<path_filters>
+
+This options provides a set of rules for filtering paths. If a distribution
+matches one of the rules in C<path_filters>, it will not be mirrored. A regex
+rule is matched if the path matches the regex; a code rule is matched if the
+code returns 1 when the path is passed to it. For example, the following
+setting would skip all distributions from RJBS and SUNGO:
+
+ path_filters => [
+ qr/RJBS/,
+ sub { $_[0] =~ /SUNGO/ }
+ ]
+
+=item *
+
+C<module_filters>
+
+This option provides a set of rules for filtering modules. It behaves like
+path_filters, but acts only on module names. (Since most modules are in
+distributions with more than one module, this setting will probably be less
+useful than C<path_filters>.) For example, this setting will skip any
+distribution containing only modules with the word "Acme" in them:
+
+ module_filters => [ qr/Acme/i ]
+
+=item *
+
+C<also_mirror>
+
+This option should be an arrayref of extra files in the remote CPAN to mirror
+locally.
+
+=item *
+
+C<skip_cleanup>
+
+If this option is true, CPAN::Mini will not try delete unmirrored files when it
+has finished mirroring
+
+=item *
+
+C<offline>
+
+If offline, CPAN::Mini will not attempt to contact remote resources.
+
+=item *
+
+C<no_conn_cache>
+
+If true, no connection cache will be established. This is mostly useful as a
+workaround for connection cache failures.
+
+=back
+
+=head2 new
+
+ my $minicpan = CPAN::Mini->new;
+
+This method constructs a new CPAN::Mini object. Its parameters are described
+above, under C<update_mirror>.
+
+=head2 mirror_indices
+
+ $minicpan->mirror_indices;
+
+This method updates the index files from the CPAN.
+
+=head2 mirror_file
+
+ $minicpan->mirror_file($path, $skip_if_present)
+
+This method will mirror the given file from the remote to the local mirror,
+overwriting any existing file unless C<$skip_if_present> is true.
+
+=head2 file_allowed
+
+ next unless $minicpan->file_allowed($filename);
+
+This method returns true if the given file is allowed to exist in the local
+mirror, even if it isn't one of the required mirror files.
+
+By default, only dot-files are allowed. If the C<exact_mirror> option is true,
+all files are allowed.
+
+=head2 clean_unmirrored
+
+ $minicpan->clean_unmirrored;
+
+This method looks through the local mirror's files. If it finds a file that
+neither belongs in the mirror nor is allowed (see the C<file_allowed> method),
+C<clean_file> is called on the file.
+
+If you set C<ignore_source_control> to a true value, then this doesn't clean
+up files that belong to source control systems. Currently this ignores:
+
+ .cvs .cvsignore
+ .svn .svnignore
+ .git .gitignore
+
+Send patches for other source control files that you would like to have added.
+
+=head2 clean_file
+
+ $minicpan->clean_file($filename);
+
+This method, called by C<clean_unmirrored>, deletes the named file. It returns
+true if the file is successfully unlinked. Otherwise, it returns false.
+
+=head2 log_warn
+
+=head2 log
+
+=head2 log_debug
+
+ $minicpan->log($message);
+
+This will log (print) the given message unless the log level is too low.
+
+C<log>, which logs at the I<info> level, may also be called as C<trace> for
+backward compatibility reasons.
+
+=head2 read_config
+
+ my %config = CPAN::Mini->read_config(\%options);
+
+This routine returns a set of arguments that can be passed to CPAN::Mini's
+C<new> or C<update_mirror> methods. It will look for a file called
+F<.minicpanrc> in the user's home directory as determined by
+L<File::HomeDir|File::HomeDir>.
+
+=head2 config_file
+
+ my $config_file = CPAN::Mini->config_file( { options } );
+
+This routine returns the config file name. It first looks at for the
+C<config_file> setting, then the C<CPAN_MINI_CONFIG> environment
+variable, then the default F<~/.minicpanrc>, and finally the
+F<CPAN/Mini/minicpan.conf>. It uses the first defined value it finds.
+If the filename it selects does not exist, it returns false.
+
+OPTIONS is an optional hash reference of the C<CPAN::Mini> config hash.
+
+=begin devel
+
+=method _filter_module
+
+ next
+ if $self->_filter_module({ module => $foo, version => $foo, path => $foo });
+
+This method holds the filter chain logic. C<update_mirror> takes an optional
+set of filter parameters. As C<update_mirror> encounters a distribution, it
+calls this method to figure out whether or not it should be downloaded. The
+user provided filters are taken into account. Returns 1 if the distribution is
+filtered (to be skipped). Returns 0 if the distribution is to not filtered
+(not to be skipped).
+
+=end devel
+
+=head2 remote_from
+
+ my $remote = CPAN::Mini->remote_from( $remote_from, $orig_remote, $quiet );
+
+This routine take an string argument and turn it into a method
+call to handle to retrieve the a cpan mirror url from a source.
+Currently supported methods:
+
+ cpan - fetch the first mirror from your CPAN.pm config
+ cpanplus - fetch the first mirror from your CPANPLUS.pm config
+
+=head2 remote_from_cpan
+
+ my $remote = CPAN::Mini->remote_from_cpan;
+
+This routine loads your CPAN.pm config and returns the first mirror in mirror
+list. You can set this as your default by setting remote_from:cpan in your
+F<.minicpanrc> file.
+
+=head2 remote_from_cpanplus
+
+ my $remote = CPAN::Mini->remote_from_cpanplus;
+
+This routine loads your CPANPLUS.pm config and returns the first mirror in
+mirror list. You can set this as your default by setting remote_from:cpanplus
+in your F<.minicpanrc> file.
+
+=head1 SEE ALSO
+
+Randal Schwartz's original article on minicpan, here:
+
+ http://www.stonehenge.com/merlyn/LinuxMag/col42.html
+
+L<CPANPLUS::Backend>, which provides the C<local_mirror> method, which performs
+the same task as this module.
+
+=head1 THANKS
+
+Thanks to David Dyck for letting me know about my stupid documentation errors.
+
+Thanks to Roy Fulbright for finding an obnoxious bug on Win32.
+
+Thanks to Shawn Sorichetti for fixing a stupid octal-number-as-string bug.
+
+Thanks to sungo for implementing the filters, so I can finally stop mirroring
+bioperl, and Robert Rothenberg for suggesting adding coderef rules.
+
+Thanks to Adam Kennedy for noticing and complaining about a lot of stupid
+little design decisions.
+
+Thanks to Michael Schwern and Jason Kohles, for pointing out missing
+documentation.
+
+Thanks to David Golden for some important bugfixes and refactoring.
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Ricardo SIGNES <rjbs@cpan.org>
+
+=item *
+
+Randal Schwartz <merlyn@stonehenge.com>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2004 by Ricardo SIGNES.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/lib/CPAN/Mini/App.pm b/lib/CPAN/Mini/App.pm
new file mode 100644
index 0000000..29b4c69
--- /dev/null
+++ b/lib/CPAN/Mini/App.pm
@@ -0,0 +1,209 @@
+use strict;
+use warnings;
+
+package CPAN::Mini::App;
+$CPAN::Mini::App::VERSION = '1.111016';
+# ABSTRACT: the guts of the minicpan command
+
+#pod =head1 SYNOPSIS
+#pod
+#pod #!/usr/bin/perl
+#pod use CPAN::Mini::App;
+#pod CPAN::Mini::App->run;
+#pod
+#pod =cut
+
+use CPAN::Mini;
+use File::HomeDir;
+use File::Spec;
+use Getopt::Long qw(:config no_ignore_case);
+use Pod::Usage 1.00;
+
+sub _display_version {
+ my $class = shift;
+ no strict 'refs';
+ print "minicpan",
+ ($class ne 'CPAN::Mini' ? ' (from CPAN::Mini)' : q{}),
+ ", powered by $class ", $class->VERSION, "\n\n";
+ exit;
+}
+
+#pod =method run
+#pod
+#pod This method is called by F<minicpan> to do all the work. Don't rely on what it
+#pod does just yet.
+#pod
+#pod =cut
+
+sub _validate_log_level {
+ my ($class, $level) = @_;
+ return $level if $level =~ /\A(?:fatal|warn|debug|info)\z/;
+ die "unknown logging level: $level\n";
+}
+
+sub run {
+ my ($class) = @_;
+
+ my $minicpan = $class->initialize_minicpan;
+
+ $minicpan->update_mirror;
+}
+
+sub initialize_minicpan {
+ my ($class) = @_;
+
+ my $version;
+
+ my %commandline;
+
+ my @option_spec = $class->_option_spec();
+ GetOptions(\%commandline, @option_spec) or pod2usage(2);
+
+ # These two options will cause the program to exit before finishing ->run
+ pod2usage(1) if $commandline{help};
+ $version = 1 if $commandline{version};
+
+ # How noisy should we be?
+ my $debug = $commandline{debug};
+ my $log_level = $commandline{log_level};
+ my $quiet = $commandline{qq} ? 2 : $commandline{quiet};
+
+ die "can't mix --debug, --log-level, and --debug\n"
+ if defined($quiet) + defined($debug) + defined($log_level) > 1;
+
+ # Set log_level accordingly
+ $quiet ||= 0;
+ $log_level = $debug ? 'debug'
+ : $quiet == 1 ? 'warn'
+ : $quiet >= 2 ? 'fatal'
+ : $log_level ? $log_level
+ : undef;
+
+ my %config = CPAN::Mini->read_config({
+ log_level => 'info',
+ %commandline
+ });
+
+ $config{class} ||= 'CPAN::Mini';
+
+ # Override config with commandline options
+ %config = (%config, %commandline);
+
+ $config{log_level} = $log_level || $config{log_level} || 'info';
+ $class->_validate_log_level($config{log_level});
+
+ eval "require $config{class}";
+ die $@ if $@;
+
+ _display_version($config{class}) if $version;
+
+ if ($config{remote_from} && ! $config{remote}) {
+ $config{remote} = $config{class}->remote_from(
+ $config{remote_from},
+ $config{remote},
+ $config{quiet},
+ );
+ }
+
+ $config{remote} ||= 'http://www.cpan.org/';
+
+ pod2usage(2) unless $config{local} and $config{remote};
+
+ $|++;
+
+ # Convert dirmode string to a real octal value, if given
+ $config{dirmode} = oct $config{dirmode} if $config{dirmode};
+
+ # Turn the 'perl' option into 'skip_perl', for backward compatibility
+ $config{skip_perl} = not delete $config{perl};
+
+ return $config{class}->new(%config);
+}
+
+sub _option_spec {
+ return qw<
+ class|c=s
+ help|h
+ version|v
+ quiet|q+
+ qq
+ debug
+ log_level|log-level=s
+ local|l=s
+ remote|r=s
+ dirmode|d=s
+ offline
+ force|f
+ perl
+ exact_mirror|x
+ timeout|t=i
+ config_file|config|C=s
+ remote-from=s
+ >;
+}
+
+#pod =head1 SEE ALSO
+#pod
+#pod Randal Schwartz's original article, which can be found here:
+#pod
+#pod http://www.stonehenge.com/merlyn/LinuxMag/col42.html
+#pod
+#pod =cut
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+CPAN::Mini::App - the guts of the minicpan command
+
+=head1 VERSION
+
+version 1.111016
+
+=head1 SYNOPSIS
+
+ #!/usr/bin/perl
+ use CPAN::Mini::App;
+ CPAN::Mini::App->run;
+
+=head1 METHODS
+
+=head2 run
+
+This method is called by F<minicpan> to do all the work. Don't rely on what it
+does just yet.
+
+=head1 SEE ALSO
+
+Randal Schwartz's original article, which can be found here:
+
+ http://www.stonehenge.com/merlyn/LinuxMag/col42.html
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Ricardo SIGNES <rjbs@cpan.org>
+
+=item *
+
+Randal Schwartz <merlyn@stonehenge.com>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2004 by Ricardo SIGNES.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
diff --git a/t/00-load.t b/t/00-load.t
new file mode 100644
index 0000000..8a560de
--- /dev/null
+++ b/t/00-load.t
@@ -0,0 +1,7 @@
+#!perl
+
+use Test::More tests => 1;
+
+use_ok( 'CPAN::Mini' );
+
+diag( "Testing CPAN::Mini $CPAN::Mini::VERSION" );
diff --git a/t/000-report-versions-tiny.t b/t/000-report-versions-tiny.t
new file mode 100644
index 0000000..e25334d
--- /dev/null
+++ b/t/000-report-versions-tiny.t
@@ -0,0 +1,86 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+# This is a relatively nice way to avoid Test::NoWarnings breaking our
+# expectations by adding extra tests, without using no_plan. It also helps
+# avoid any other test module that feels introducing random tests, or even
+# test plans, is a nice idea.
+our $success = 0;
+END { $success && done_testing; }
+
+# List our own version used to generate this
+my $v = "\nGenerated by Dist::Zilla::Plugin::ReportVersions::Tiny v1.10\n";
+
+eval { # no excuses!
+ # report our Perl details
+ my $want = '5.006';
+ $v .= "perl: $] (wanted $want) on $^O from $^X\n\n";
+};
+defined($@) and diag("$@");
+
+# Now, our module version dependencies:
+sub pmver {
+ my ($module, $wanted) = @_;
+ $wanted = " (want $wanted)";
+ my $pmver;
+ eval "require $module;";
+ if ($@) {
+ if ($@ =~ m/Can't locate .* in \@INC/) {
+ $pmver = 'module not found.';
+ } else {
+ diag("${module}: $@");
+ $pmver = 'died during require.';
+ }
+ } else {
+ my $version;
+ eval { $version = $module->VERSION; };
+ if ($@) {
+ diag("${module}: $@");
+ $pmver = 'died during VERSION check.';
+ } elsif (defined $version) {
+ $pmver = "$version";
+ } else {
+ $pmver = '<undef>';
+ }
+ }
+
+ # So, we should be good, right?
+ return sprintf('%-45s => %-10s%-15s%s', $module, $pmver, $wanted, "\n");
+}
+
+eval { $v .= pmver('Carp','any version') };
+eval { $v .= pmver('Compress::Zlib','1.20') };
+eval { $v .= pmver('ExtUtils::MakeMaker','any version') };
+eval { $v .= pmver('File::Basename','any version') };
+eval { $v .= pmver('File::Copy','any version') };
+eval { $v .= pmver('File::Find','any version') };
+eval { $v .= pmver('File::HomeDir','0.57') };
+eval { $v .= pmver('File::Path','2.04') };
+eval { $v .= pmver('File::Spec','any version') };
+eval { $v .= pmver('File::Temp','any version') };
+eval { $v .= pmver('Getopt::Long','any version') };
+eval { $v .= pmver('LWP::UserAgent','5') };
+eval { $v .= pmver('Pod::Usage','1.00') };
+eval { $v .= pmver('Test::More','0.96') };
+eval { $v .= pmver('URI','1') };
+eval { $v .= pmver('strict','any version') };
+eval { $v .= pmver('warnings','any version') };
+
+
+# All done.
+$v .= <<'EOT';
+
+Thanks for using my code. I hope it works for you.
+If not, please try and include this output in the bug report.
+That will help me reproduce the issue and solve your problem.
+
+EOT
+
+diag($v);
+ok(1, "we really didn't test anything, just reporting data");
+$success = 1;
+
+# Work around another nasty module on CPAN. :/
+no warnings 'once';
+$Template::Test::NO_FLUSH = 1;
+exit 0;
diff --git a/t/app.t b/t/app.t
new file mode 100644
index 0000000..8b278ac
--- /dev/null
+++ b/t/app.t
@@ -0,0 +1,132 @@
+use strict;
+use warnings;
+use Test::More;
+
+use CPAN::Mini::App;
+use File::Spec;
+use File::Temp qw(tempdir);
+
+my $TARGET = tempdir(CLEANUP => 1);
+my @LR_ARGS = (qw(--offline -r http://example.tld/cpan -l), $TARGET);
+
+delete $ENV{CPAN_MINI_CONFIG};
+
+{
+ no warnings 'redefine';
+ *File::HomeDir::my_home = sub { $ENV{HOME} };
+}
+
+sub config_dir {
+ my ($config) = @_;
+
+ my $tempdir = tempdir(CLEANUP => 1);
+
+ return $tempdir unless defined $config;
+
+ my $filename = File::Spec->catfile($tempdir, '.minicpanrc');
+ open my $config_fh, '>', $filename or die "can't write to $filename: $!";
+
+ for my $key (keys %$config) {
+ print {$config_fh} "$key: $config->{$key}\n";
+ }
+
+ close $config_fh or die "error closing $filename: $!";
+
+ return $tempdir;
+}
+
+subtest "defaults" => sub {
+ local $ENV{HOME} = config_dir;
+ local @ARGV = @LR_ARGS;
+
+ my $minicpan = CPAN::Mini::App->initialize_minicpan;
+ isa_ok($minicpan, 'CPAN::Mini');
+
+ is($minicpan->log_level, 'info', "default log level is info");
+};
+
+subtest "--debug" => sub {
+ local $ENV{HOME} = config_dir;
+ local @ARGV = (qw(--debug), @LR_ARGS);
+
+ my $minicpan = CPAN::Mini::App->initialize_minicpan;
+ isa_ok($minicpan, 'CPAN::Mini');
+
+ is($minicpan->log_level, 'debug', "--debug to get log level debug");
+};
+
+subtest "config: log_level" => sub {
+ local $ENV{HOME} = config_dir({ log_level => 'debug' });
+ local @ARGV = @LR_ARGS;
+
+ my $minicpan = CPAN::Mini::App->initialize_minicpan;
+ isa_ok($minicpan, 'CPAN::Mini');
+
+ is($minicpan->log_level, 'debug', "debug from config file");
+};
+
+subtest "--debug overrides config" => sub {
+ local $ENV{HOME} = config_dir({ log_level => 'fatal' });
+ local @ARGV = (qw(--debug), @LR_ARGS);
+
+ my $minicpan = CPAN::Mini::App->initialize_minicpan;
+ isa_ok($minicpan, 'CPAN::Mini');
+
+ is($minicpan->log_level, 'debug', "--debug overrides config file");
+};
+
+subtest "--log-level" => sub {
+ local $ENV{HOME} = config_dir;
+ local @ARGV = (qw(--log-level debug), @LR_ARGS);
+
+ my $minicpan = CPAN::Mini::App->initialize_minicpan;
+ isa_ok($minicpan, 'CPAN::Mini');
+
+ is($minicpan->log_level, 'debug', "--debug to get log level debug");
+};
+
+subtest "only one log-level-like switch allowed" => sub {
+ for my $combo (
+ [ qw(--debug -q) ],
+ [ qw(--debug --log-level debug) ],
+ ) {
+ local $ENV{HOME} = config_dir;
+ local @ARGV = (@$combo, @LR_ARGS);
+
+ my $minicpan = eval { CPAN::Mini::App->initialize_minicpan };
+ like($@, qr/can't mix/, "can't use @$combo together");
+ };
+};
+
+for my $switch (qw(-qq --qq)) {
+ subtest "extra quiet with $switch" => sub {
+ local $ENV{HOME} = config_dir;
+ local @ARGV = ($switch, @LR_ARGS);
+
+ my $minicpan = CPAN::Mini::App->initialize_minicpan;
+ isa_ok($minicpan, 'CPAN::Mini');
+
+ is($minicpan->log_level, 'fatal', "$switch gets us log level 'fatal'");
+ };
+}
+
+
+subtest "-perl switch" => sub {
+
+ local $ENV{HOME} = config_dir;
+ local @ARGV = @LR_ARGS;
+
+ my $minicpan = CPAN::Mini::App->initialize_minicpan;
+ isa_ok($minicpan, 'CPAN::Mini');
+ is($minicpan->{skip_perl}, 1, "'skip_perl' is true without -perl switch");
+
+
+ local @ARGV = ('-perl', @LR_ARGS);
+ $minicpan = CPAN::Mini::App->initialize_minicpan;
+ isa_ok($minicpan, 'CPAN::Mini');
+ is($minicpan->{skip_perl}, q{}, "'skip_perl' is false with -perl switch");
+};
+
+done_testing;
+
+1;
diff --git a/t/config-file.t b/t/config-file.t
new file mode 100644
index 0000000..4d476a6
--- /dev/null
+++ b/t/config-file.t
@@ -0,0 +1,108 @@
+#!perl
+
+use warnings;
+use strict;
+
+use Test::More tests => 18;
+
+use File::Basename;
+
+my $class = 'CPAN::Mini';
+
+use_ok($class);
+can_ok($class, 'config_file');
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# command line option case
+{
+ my $filename = 'Changes';
+ ok(-e $filename, "file name [$filename] exists");
+
+ local $ENV{CPAN_MINI_CONFIG} = 'Buster';
+ my $options = { config_file => $filename, };
+
+ is($class->config_file($options),
+ $filename, 'selects config file name from command line');
+}
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# environment variable case
+{
+ my $filename = $0;
+ ok(-e $filename, "file name [$filename] exists");
+
+ local $ENV{CPAN_MINI_CONFIG} = $filename;
+
+ is($class->config_file, $filename,
+ 'selects config file name from environment with no args');
+ is($class->config_file({}),
+ $filename, 'selects config file name from environment with empty hash ref');
+ is($class->config_file('trash'),
+ $filename, 'selects config file name from environment with non-ref arg');
+}
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# default case
+# this is the case where there is a ~/.minicpanrc
+{
+
+ my $filename = 'Changes';
+ ok(-e $filename, "file name [$filename] exists");
+
+ {
+ no strict 'refs';
+ no warnings 'redefine';
+
+ *{"${class}::__homedir_configfile"} = sub { $filename };
+ is($class->__homedir_configfile,
+ $filename, "__homedir_configfile returns mocked name");
+ }
+
+ local $ENV{CPAN_MINI_CONFIG} = undef;
+
+ is($class->config_file, $filename, 'selects default config file name');
+}
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# last ditch case
+# this is the case wehre there is no ~/.minicpanrc
+{
+ local $ENV{CPAN_MINI_CONFIG} = undef;
+ my $is_there_filename = 'Changes';
+ ok(-e $is_there_filename, "file name [$is_there_filename] does exist");
+
+ {
+ no strict 'refs';
+ no warnings 'redefine';
+
+ *{"${class}::__homedir_configfile"} = sub { undef };
+ is($class->__homedir_configfile,
+ undef, "__homedir_configfile returns mocked name");
+ *{"${class}::__default_configfile"} = sub { $is_there_filename };
+ is($class->__default_configfile,
+ $is_there_filename, "__default_configfile returns mocked name");
+ }
+
+ is($class->config_file, $is_there_filename,
+ 'selects default config file name');
+}
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# everything failed case
+{
+ local $ENV{CPAN_MINI_CONFIG} = undef;
+
+ {
+ no strict 'refs';
+ no warnings 'redefine';
+
+ *{"${class}::__homedir_configfile"} = sub { undef };
+ is($class->__homedir_configfile,
+ undef, "__homedir_configfile returns mocked name");
+ *{"${class}::__default_configfile"} = sub { undef };
+ is($class->__default_configfile,
+ undef, "__default_configfile returns mocked name");
+ }
+
+ is($class->config_file, undef, 'returns undef when no config file is found');
+}
diff --git a/t/filter.t b/t/filter.t
new file mode 100644
index 0000000..46333fa
--- /dev/null
+++ b/t/filter.t
@@ -0,0 +1,157 @@
+#!perl
+
+use warnings;
+use strict;
+
+use Test::More tests => 19;
+
+use CPAN::Mini;
+
+my $self = {
+ changes_made => 1,
+ force => 1,
+};
+
+bless $self, "CPAN::Mini";
+################################################
+# skip_perl
+
+$self->{skip_perl} = 1;
+
+ok($self->_filter_module({
+ module => 'perl',
+ version => '0.01',
+ path => '/perl-0.01.tar.gz',
+}), "perl distro skip check");
+
+ok($self->_filter_module({
+ module => 'bioperl',
+ version => '0.01',
+ path => '/bioperl-0.01.tar.gz',
+}), "bioperl distro skip check");
+
+ok($self->_filter_module({
+ module => 'embperl',
+ version => '0.01',
+ path => '/embperl-0.01.tar.gz',
+}), "embperl distro skip check");
+
+ok(!$self->_filter_module({
+ module => 'notperl',
+ version => '0.01',
+ path => '/POE-0.01.tar.gz',
+}), "POE distro not-skip check");
+
+ok($self->_filter_module({
+ module => 'ponie',
+ version => '0.01',
+ path => '/ponie-0.01.tar.gz',
+}), "ponie distro skip check");
+
+ok($self->_filter_module({
+ module => 'parrot',
+ version => '0.01',
+ path => '/parrot-0.01.tar.gz',
+}), "parrot distro skip check");
+
+delete $self->{skip_perl};
+
+ok(!$self->_filter_module({
+ module => 'perl',
+ version => '0.01',
+ path => '/perl-0.01.tar.gz',
+}), "perl distro no-skip check");
+
+################################################
+# path_filters
+
+$self->{path_filters} = qr/skipme/;
+
+ok($self->_filter_module({
+ module => 'skipme',
+ version => '0.01',
+ path => '/skipme-0.01.tar.gz',
+}), "path_filters skip check skipme 1");
+
+ok(!$self->_filter_module({
+ module => 'noskip',
+ version => '0.01',
+ path => '/noskip-0.01.tar.gz',
+}), "path_filters no-skip check");
+
+$self->{path_filters} = [
+ qr/skipme/,
+ qr/burnme/,
+ sub { return $_[0] =~ /subskip/ }
+];
+
+ok($self->_filter_module({
+ module => 'skipme',
+ version => '0.01',
+ path => '/skipme-0.01.tar.gz',
+}), "path_filters skip check skipme 2");
+
+ok($self->_filter_module({
+ module => 'burnme',
+ version => '0.01',
+ path => '/burnme-0.01.tar.gz',
+}), "path_filters skip check burnme");
+
+ok($self->_filter_module({
+ module => 'submod',
+ version => '0.01',
+ path => '/subskip-0.01.tar.gz',
+}), "path_filters skip check (by sub)");
+
+ok(!$self->_filter_module({
+ module => 'noskip',
+ version => '0.01',
+ path => '/noskip-0.01.tar.gz',
+}), "path_filters no-skip check");
+
+################################################
+# module_filters
+
+$self->{module_filters} = qr/skipme/;
+
+ok($self->_filter_module({
+ module => 'skipme',
+ version => '0.01',
+ path => '/skipme-0.01.tar.gz',
+}), "module_filters skip check skipme 1");
+
+ok(!$self->_filter_module({
+ module => 'noskip',
+ version => '0.01',
+ path => '/noskip-0.01.tar.gz',
+}), "module_filters no-skip check");
+
+$self->{module_filters} = [
+ qr/skipme/,
+ qr/burnme/,
+ sub { $_[0] =~ /submod/ }
+];
+
+ok($self->_filter_module({
+ module => 'skipme',
+ version => '0.01',
+ path => '/skipme-0.01.tar.gz',
+}), "module_filters skip check skipme 2");
+
+ok($self->_filter_module({
+ module => 'burnme',
+ version => '0.01',
+ path => '/burnme-0.01.tar.gz',
+}), "module_filters skip check burnme");
+
+ok($self->_filter_module({
+ module => 'submod',
+ version => '0.01',
+ path => '/subskip-0.01.tar.gz',
+}), "module_filters skip check (by sub)");
+
+ok(!$self->_filter_module({
+ module => 'noskip',
+ version => '0.01',
+ path => '/noskip-0.01.tar.gz',
+}), "module_filters no-skip check");
diff --git a/xt/fake.t b/xt/fake.t
new file mode 100644
index 0000000..1427b46
--- /dev/null
+++ b/xt/fake.t
@@ -0,0 +1,52 @@
+#!perl
+use strict;
+use warnings;
+
+use File::Find::Rule;
+use File::Spec;
+use File::Temp qw(tempdir);
+use CPAN::Mini;
+
+use Test::More;
+
+my $tempdir = tempdir(CLEANUP => 1);
+
+CPAN::Mini->update_mirror(
+ remote => "http://fakecpan.org/fake/minicpan/1.001/cpan",
+ local => $tempdir,
+ log_level => 'fatal',
+);
+
+pass("performed initial mirror");
+
+CPAN::Mini->update_mirror(
+ remote => "http://fakecpan.org/fake/minicpan/1.002/cpan",
+ local => $tempdir,
+ log_level => 'fatal',
+);
+
+pass("performed mirror update");
+
+my @files = File::Find::Rule->file->in($tempdir);
+$_ = File::Spec->abs2rel($_, $tempdir) for @files;
+
+my @want = qw(
+ RECENT
+ authors/01mailrc.txt.gz
+ authors/id/O/OP/OPRIME/Bug-Gold-9.001.tar.gz
+ authors/id/O/OP/OPRIME/CHECKSUMS
+ authors/id/O/OP/OPRIME/XForm-Rollout-1.00.tar.gz
+ authors/id/X/XY/XYZZY/CHECKSUMS
+ authors/id/X/XY/XYZZY/Hall-MtKing-0.01.tar.gz
+ authors/id/X/XY/XYZZY/Y-2.tar.gz
+ modules/02packages.details.txt.gz
+ modules/03modlist.data.gz
+);
+
+is_deeply(
+ [ sort @files ],
+ [ sort @want ],
+ "we end up with just the files we expect",
+);
+
+done_testing;
diff --git a/xt/release/changes_has_content.t b/xt/release/changes_has_content.t
new file mode 100644
index 0000000..c942c33
--- /dev/null
+++ b/xt/release/changes_has_content.t
@@ -0,0 +1,41 @@
+#!perl
+
+use Test::More tests => 2;
+
+note 'Checking Changes';
+my $changes_file = 'Changes';
+my $newver = '1.111016';
+my $trial_token = '-TRIAL';
+
+SKIP: {
+ ok(-e $changes_file, "$changes_file file exists")
+ or skip 'Changes is missing', 1;
+
+ ok(_get_changes($newver), "$changes_file has content for $newver");
+}
+
+done_testing;
+
+# _get_changes copied and adapted from Dist::Zilla::Plugin::Git::Commit
+# by Jerome Quelin
+sub _get_changes
+{
+ my $newver = shift;
+
+ # parse changelog to find commit message
+ open(my $fh, '<', $changes_file) or die "cannot open $changes_file: $!";
+ my $changelog = join('', <$fh>);
+ close $fh;
+
+ my @content =
+ grep { /^$newver(?:$trial_token)?(?:\s+|$)/ ... /^\S/ } # from newver to un-indented
+ split /\n/, $changelog;
+ shift @content; # drop the version line
+
+ # drop unindented last line and trailing blank lines
+ pop @content while ( @content && $content[-1] =~ /^(?:\S|\s*$)/ );
+
+ # return number of non-blank lines
+ return scalar @content;
+}
+
diff --git a/xt/release/pod-syntax.t b/xt/release/pod-syntax.t
new file mode 100644
index 0000000..f0468f1
--- /dev/null
+++ b/xt/release/pod-syntax.t
@@ -0,0 +1,6 @@
+#!perl
+# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests.
+use Test::More;
+use Test::Pod 1.41;
+
+all_pod_files_ok();