#!/usr/bin/env perl
use strict;
use warnings;
use Carp;
use Pod::Usage qw( pod2usage );
use Getopt::Long qw( :config gnu_getopt );
use English qw( -no_match_vars );
my $VERSION = '0.001';
use 5.012;

# Integrated logging facility
use Log::Log4perl::Tiny qw( :easy :no_extra_logdie_message );
my $level = $ENV{LOGLEVEL} // 'INFO';
Log::Log4perl->easy_init({level => $level, layout => '[%d %-5p] %m%n'});

use FindBin qw< $Bin >;
use lib "$Bin/../lib";
use App::EPAN;

App::EPAN->run(@ARGV);

__END__

=head1 NAME

epan - Exclusive Perl Archive Nook

=head1 VERSION

Ask the version number to the script itself, calling:

   shell$ epan --version


=head1 USAGE

   epan [--usage] [--help] [--man] [--version]

   # "create" insists on *not* finding dirname and creating it
   epan create [-t|--target dirname] Module1 [Module2...]

   # "idx" is how "index" was supposed to be specified
   epan idx [-o|--output filename] [-t|--target dirname]

   # "inject" adds local distribution archives
   epan inject [-t|--target dirname] File1 [File2...]

   # "list-actions" is also "list_actions"
   epan list-actions

   # "list-obsoletes" is also "list_obsoletes"
   epan list-obsoletes [-t|--target dirname]

   # "purge-obsoletes" is also "purge_obsoletes"
   epan purge-obsoletes [-t|--target dirname]

   # "update" is also "add" and "install"
   epan update [-t|--target dirname] Module1 [Module2...]



   # deprecated, don't use this but idx instead
   epan index [-o|--output filename] [directory]


=head1 EXAMPLES

   # collects all what's needed to install Dancer somewhere
   shell$ epan create -t dancer-stuff Dancer

   # regenerate index in ./modules/02packages.details.txt.gz
   shell$ epan idx -t dancer-stuff

   # prints index on standard output, works on /path/to/minicpan
   shell$ epan idx -o - -t /path/to/minicpan


=head1 DESCRIPTION

This program helps you creating and managing an EPAN - a version of the
CPAN that is trimmed down to your needs for installing specific stuff.

To start with an example, suppose you have to install Dancer and a couple
of its plugins in a machine that - for good reasons - is not connected to
the Internet. It's easy to get the distribution files for Dancer and the
plugins... but what about the dependencies? It can easily become
a nightmare, forcing you to go back and forth with new modules as soon as
you discover the need to install them.

Thanks to L<cpanm>, this is quite easier these days: it can actually do
what's needed with a single command:

   # on the machine connected to the Internet or to a minicpan
   $ cpanm -L xxx --scandeps --save-dists dists \
        Dancer Dancer::Plugin::FlashNote ...

which places all the modules in subdirectory C<dists> (thanks to option
C<--save-dists>) with an arrangement similar to what you would expect from
a CPAN mirror.

On the target machine, you still have to make some work - e.g. you should
collect the output from the invocation of cpanm above to figure out the
order to use for installing the distribution files. Additionally, the
directory structure that is generated lacks a proper index file (located
in F<modules/02package.details.txt.gz>) so it would be difficult to use
the normal toolchain.

L<epan> aims at filling up the last mile to get the job done, providing
you with a subdirectory that is ready for deployment, with all the bits in
place to push automation as much as possible. So you can do this:

   # on the machine connected to the Internet or to a minicpan
   $ epan create Dancer Dancer::Plugin::FlashNote ...
   $ tar cvzf epan.tar.gz epan

transfer C<dists.tar.gz> to the target machine and...

   # on the target machine
   $ tar xvzf epan.tar.gz
   $ cd epan
   $ ./install.sh

optionally providing an installation target directory:

   $ ./install.sh /path/to/local/perl

The program C<epan> is actually a unified access point to several
different tools for manipulating your I<exclusive Perl archive nook>. Most
of these commands operate upon a I<target directory> that is where your
EPAN is stored; this can be specified via option C<-t> or its longer
version C<--target>. By default, the target directory is assumed to be
C<epan> in the current directory.

=head2 C<add>, C<install> and C<update>

These commands are synonimous in C<epan>, and all help you pull a module
and its dependencies from a CPAN mirror right into your EPAN, regenerating
the index at the end of the process. The syntax is:

   epan add # or install or update \
      [-t|--target directory]
      Module1 [Module2...]

So, in addition to the common option C<-t> for setting the right target
directory, it accepts a list of module names to install (with their
dependencies).

=head2 C<create> 

This command is almost the same as C<add> and its aliases, with the
exception that the target directory MUST NOT already exist when called.

=head2 C<index>

Regenerate the index so that tools like C<cpanm> are happy about what they
find and treat your target directory as a real CPAN sort-of mirror. The
syntax is the following:

   epan index [-t|--target dirname]

Note that other commands (e.g. C<add> or C<create>) already do the
indexing. This command can be useful when you have a starting base (i.e.
a compound of modules coming from CPAN and your own distribution) already
arranged in the right directory tree, but you need to generate an index.
For example, this happens when you collect some distribution files using
C<cpanm>:

   shell$ cpanminus -L xxx --save-dists dists Mod1 Mod2...

because it saves the needed distributions in C<dists> but it does not
generate the index. The same happens when using C<carton>.

In these cases, if you want to prepare a pack of modules to carry with your
application, you can do like this:

   $ figure_out_modules > modlist
   $ cpanm -L xxx --save-dists dists $(<modlist)
   $ epan idx -t dists
   $ tar cvf dists.tar dists

then carry dists.tar with you, at which point you can:

   $ cpanm --mirror file://$YOURPATH --mirror-only Mod1 Mod2 ...

=head2 C<inject>

If you have some local distribution files, e.g. generated by yourself and
not (yet) uploaded to CPAN, you can inject them into a local EPAN. The
syntax is straightforward:

   epan inject \ 
      [-a|--author author-name] \
      [-t|--target dirname] File1 [File2...]

=head2 C<list-actions> and C<list_actions>

Prints out the list of available commands.

=head2 C<list-obsoletes> and C<list_obsoletes>

   epan list-obsoletes [-t|--target dirname]

Prints out a list of obsolete distributions in the EPAN. A distribution is
considered I<obsolete> if there is a newer corresponding version in the
EPAN. E.g. suppose that you work on C<Acme::Whatever> and inject version
C<0.2>:

   epan inject Acme-Whatever-0.2.tar.gz
   # ...

then you work on it some more time, and inject version C<0.3>:

   epan inject Acme-Whatever-0.2.tar.gz
   # ...

Now your EPAN contains two distribution packages for C<Acme::Whatever>,
one for release C<0.2> (which is the obsolete one) and one for the newest
version C<0.3>.

=head2 C<purge-obsoletes> and C<purge_obsoletes>

   epan purge-obsoletes [-t|--target dirname]

Remove (purge) obsolete distribution packages from the EPAN.

=head1 OPTIONS

The following options are supported, even though not all actions use them
all:

=over

=item -1 | -m | --mailrc

path to the file C<01mailrc.txt.gz>, defaults to
C<authors/01mailrc.txt.gz> inside the target directory

=item -2 | -o | --output | --package-details

path to the file for C<02packages.details.txt.gz>, defaults to
C<modules/02packages.details.txt.gz> inside the target directory. Yes, you
can use C<-> with the I<usual> meaning.

=item -3 | -l | --modlist | --modlist-data

path to the file C<03modlist.data.gz>, defaults to
C<modules/03modlist.data.gz> inside the target directory.

=item -a | --author author-name

module author to use when doing injection of local distribution packages

=item --help

print a somewhat more verbose help, showing usage, this description of
the options and some examples from the synopsis.

=item --man

print out the full documentation for the script.

=item -t | --target dirname

set the directory of the root for the EPAN to work on. Defaults to the
sub-directory C<epan> in the current directory. This option applies to all
commands except C<list-actions> and C<index>.

=item --usage

print a concise usage line and exit.

=item --version

print the version of the script.

=back

=head1 CONFIGURATION AND ENVIRONMENT

epan requires no configuration files. The following environment variable
is honored:

=over

=item C<EPAN_AUTHOR>

set the name of the I<pause account> to use for indexing. Defaults to
C<LOCAL>. It is overridden by C<--author>.

=back


=head1 DEPENDENCIES

Runs on perl 5.012, adapt it if you want to run on something older :-)

The following non-core modules are used:

=over

=item *

B<< Dist::Metadata >>

=item *

B<< Path::Class >>

=item *

B<< File::Find::Rule >>

=item *

B<< Log::Log4perl::Tiny >>

=back

=head1 BUGS AND LIMITATIONS

No bugs have been reported.

Please report any bugs or feature requests through http://rt.cpan.org/


=head1 AUTHOR

Flavio Poletti C<polettix@cpan.org>


=head1 LICENCE AND COPYRIGHT

Copyright (C) 2011-2021 by Flavio Poletti C<polettix@cpan.org>.

This script is free software; you can redistribute it and/or
modify it under the terms of the Artistic License 2.0.

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.

=cut
