#!/usr/bin/perl
use strict;
use warnings;

use File::Basename  qw/basename/;
use Getopt::Long    qw/GetOptions :config gnu_getopt/;
use CPAN::Site      qw/shell install/;

# the server will redirect you to a mirror
use constant
    CPAN_CORE => 'ftp://ftp.cpan.org/pub/CPAN';

#
# Collect options
#

my $lazy;
my $mode;
my $bigcpan_url;
my $mycpan       = $ENV{CPANSITE};

GetOptions
    'cpan=s'     => \$bigcpan_url
  , 'lazy|l!'    => \$lazy
  , 'verbose|v+' => \$mode
  , 'mode|m=s'   => \$mode
  , 'site|s=s'   => \$mycpan
    or exit 1;

defined $lazy or $lazy = 1;

my $action   = (shift @ARGV) || 'shell';
defined $bigcpan_url
   or $bigcpan_url = CPAN_CORE;

if($action eq 'shell')
{   my $version = $CPAN::Site::VERSION || 0;
    print "Entering CPAN::Site $version interactive shell\n";
    shell();
}
elsif($action eq 'install')
{   install(@ARGV);
}
elsif($action =~ m/^\s*install\b/ )   # CPAN.pm compatible
{   ($action, my @args) = split " ", $action;
    install(@args);
}
elsif($action ne 'index' && $action ne 'mirror')
{   die "ERROR: action '$action' is not in use.\n";
}

elsif($action eq 'index')
{   $mycpan = shift @ARGV if @ARGV;
    $mycpan
        or die "ERROR: specify top-directory of your archive as argument\n";

    $mycpan    =~ s!^file:(?://)?!!;
    -d $mycpan
        or die "ERROR: directory '$mycpan' does not exist\n";

    eval "require CPAN::Site::Index";
    $@ and die "Indexing cannot be done: $@\n";

    CPAN::Site::Index::cpan_index
       ($mycpan, $bigcpan_url, lazy => $lazy, mode => $mode);
}
elsif($action eq 'mirror')
{   $mycpan
        or die "ERROR: set CPANSITE in environment or use --site option\n";

    @ARGV
        or die "ERROR: list of module names expected\n";

    eval "require CPAN::Site::Index";
    $@ and die "Problems with the index: $@\n";

    CPAN::Site::Index::mirror($mycpan, $bigcpan_url, \@ARGV, mode => $mode);
}

exit 0;

__END__

=head1 NAME

cpansite -- extend CPAN with private packages

=head1 SYNOPSIS

 cpansite [OPTIONS] index [MYCPAN]
 cpansite [OPTIONS] install PACKAGE
 cpansite [OPTIONS] [shell]

  Indexing options:
    --verbose  -v -vv -vvv --mode=DEBUG
    --no-lazy         redo everything
    --cpan <url>      some CPAN mirror

=head1 DESCRIPTION

The C<cpansite> script can be used (on the server) to create the required
extended CPAN index and (on the clients) to install those modules.
Before it can be used, you need to do some minor configuring, explained
in the L</DETAILS> chapter, below in this manual page.

Without any arguments, a CPAN install B<shell> is started.  Besides,
an abbreviation for B<install> is available.  Other uses for the
C<CPAN.pm> module are only supported in the traditional way:

  perl -MCPAN::Site 'something'

Not all versions of CPAN.pm work correctly; you may need to upgrade that
first.

=head2 General options

The following options are available in all modes:

=over 4

=item $CPANSITE

The CPANSITE environment variable contains a list of white-space
seperated urls which will be added before the list of url in the
personal CPAN.pm configuration file F<.cpan/CPAN/MyConfig.pm>

Example:

  export CPANSITE="http://mycpan.example.com/local"
  perl -MCPAN::Site -e shell
  cpansite shell              # alternative

=back

=head2 Indexing options (mode B<index>)

In "index mode", this script generates the required index for your own
local CPAN additions.  See the L</DETAILS> head1 below.  You can either
specify a directory which contains your cpan tree, or an environment
variable named C<CPANSITE>.

Additional options for "index" mode:

=over 4

=item --verbose -v -vv -vvv --mode=DEBUG

Produce verbose output.

=item --no-lazy --lazy -l

Try to avoid redo-ing everything.  By default, the indexer is
lazy: it will process only new distributions.  When not lazy, all
distributions on the local disk are processed and a new table is
created.  The default of this option reversed with release 1.00 of
C<CPAN::Site>.

=item --cpan <url>

Update the list of "real" CPAN modules regularly (daily) from this
url.  By default, the slow but most up-to-date CPAN source at
C<ftp.cpan.org> is used.

When this flag is explicitly empty
  cpansite --cpan '' index

then the "real" CPAN list is not included.  For instance, if you have
downloaded all the releases from CPAN that you need, and you do not want
unexpected extra downloads.  The downloaded versions will prevail over
newer releases on CPAN, but you may download modules from the core CPAN
that you do not expect.

=back

=head1 DETAILS

Alex Efros contributed a shorter description of how to get things to
work.  You can find this in the CPAN::Site manual page.

=head2 Configuration

=head3 Configuring the clients

To get in touch with your own cpan archive, you have to provide
an url to it.  Either add this to your C<CPAN.pm> configuration
file (usually F<.cpan/CPAN/MyConfig.pm>) option C<urllist> or
set the environment variable C<CPANSITE>.

You probably also want to set the variable C<index_expire> to very short:
the clients need to reload your local index as soon as possible, and not
wait a day; just after your new local release is put in your local index,
it must get visible to your client.

You may also consider to have the CPAN install cache to be cleaned
by the system.  Certainly when you set the cache size larger (required
for more complex recursive installations) it is nice to have it removed
after a (short) while.  Set C<keep_source_where> to a temporary
directory.

Example for  F<.cpan/CPAN/MyConfig.pm>

 $CPAN::Config =
  { ...
  , index_expire => 1/600    # 5 minutes
  , urllist => [ MYCPAN_URL, 'ftp://ftp.cpan.org/pub/CPAN' ]
  , keep_source_where => '/tmp/cpan-cache'
  , build_cache => 100       # MegaByte cache
  , ...
  };

To avoid manually editing the CPAN config file one can also set the
MYCPAN_URL from the shell:

  cpan> o conf urllist unshift MYCPAN_URL
  cpan> o conf commit

=head3 Generating an index

Where the local index is the sole search list used at the client site,
it will contain both your own packages and all of the other packages
listed on CPAN.  You will need to rerun the indexing on regular basis
(for instance one a day) to keep in sync with CPAN, for instance with
cron.

The output will only contain the last (highest) version of each file
(which means that each file must contain a version number otherwise the
text C<undef> is used for version)  In any case, the local packages get
preference over the global CPAN packages, even when they have a lower
version number.

Example:

 MYCPAN=/location/of/my/data/on/disk
 cpansite -vl index $MYCPAN

The script traverses I<$MYCPAN>F</authors/id> and merges this
with the I<$MYCPAN>F</modules/02packages.details.txt.gz> data,
a copy from the original CPAN.  It creates a C<CHECKSUMS> file, and
README files from each tar-ball.  The result is a
I<$MYCPAN>F</site/02packages.details.txt.gz> file.

The files F<$MYCPAN/authors/01mailrc.txt.gz> and
F<$MYCPAN/modules/03modlist.data.gz> are downloaded from CPAN.  This
will reduce the number of failing retreivals when you start installing
software.

=head3 Adding your own modules to the local archive

Define a fake pause-id (here the demo is MYID), because if you use
an existing pause-id you clients will start producing warnings about
missing checksums on files retreived for the public archive.

  mkdir -p $MYCPAN/authors/id/M/MY/MYID
  mv MyDist-1.00-tar.gz $MYCPAN/authors/id/M/MY/MYID
  cpansite -v index $MYCPAN

Although CPAN.pm claims to support a directory format of
C<$MYCPAN/authors/id/MYID>, experience shows that this does not
work correctly with some recursively dependencies.

=head1 SEE ALSO

CPAN::Site(3pm)

=head1 AUTHORS

Mark Overmeer E<lt>perl@overmeer.netE<gt>.
Original implementation by Ulrich Pfeifer E<lt>pfeifer@wait.deE<gt>.

=cut
