1#!/usr/bin/perl
2use strict;
3use warnings;
4
5use Log::Report       'cpan-site', syntax => 'SHORT';
6use File::Basename    qw/basename/;
7use Getopt::Long      qw/GetOptions :config gnu_getopt/;
8use CPAN::Site::Index qw/cpan_index cpan_mirror/;
9
10# the server will redirect you to a mirror
11use constant CPAN_CORE => 'ftp://ftp.cpan.org/pub/CPAN';
12
13#
14# Collect options
15#
16
17my $lazy;
18my $mode        = 0;
19my $global_cpan = $ENV{CPANSITE_GLOBAL} || CPAN_CORE;
20my $mycpan      = $ENV{CPANSITE_LOCAL}  || $ENV{CPANSITE};
21my $stand_alone = 0;
22my $undefs      = 1;
23my $env_proxy   = 0;
24
25GetOptions
26    'cpan|c=s'   => \$global_cpan
27  , 'lazy|l!'    => \$lazy
28  , 'verbose|v+' => \$mode
29  , 'mode|m=s'   => \$mode
30  , 'site|s=s'   => \$mycpan
31  , 'stand-alone|a!' => \$stand_alone
32  , 'undefs|u!'  => \$undefs
33  , 'env-proxy!' => \$env_proxy
34    or exit 1;
35
36defined $lazy or $lazy = 1;
37
38dispatcher mode => $mode, 'ALL'
39    if $mode;
40
41my $action = shift;
42defined $action
43    or error __x"Missing action. Usage {program} [OPTIONS] ACTION"
44         , program => $0;
45
46if($action eq 'version')
47{   print "$CPAN::Site::Index::VERSION\n";
48    exit 0;
49}
50
51if($action eq 'index')
52{   $mycpan = shift @ARGV if @ARGV;
53    $mycpan
54        or error __x"specify top-directory of your archive as argument";
55
56    $mycpan    =~ s!^file:(?://)?!!;
57    -d $mycpan
58        or fault __x"archive directory '{dir}'", dir => $mycpan;
59
60    cpan_index $mycpan, $global_cpan
61      , lazy      => $lazy
62      , fallback  => !$stand_alone
63      , undefs    => $undefs
64      , env_proxy => $env_proxy;
65    exit 0;
66}
67
68if($action eq 'mirror')
69{   $mycpan
70        or error __x"set CPANSITE_LOCAL in environment or use --site option";
71
72    @ARGV
73        or error __x"list of module names expected";
74
75    cpan_mirror $mycpan, $global_cpan, \@ARGV, env_proxy => $env_proxy;
76    exit 0;
77}
78
79error __x"action '{name}' does not exist (anymore)", name => $action;
80
81__END__
82
83=head1 NAME
84
85cpansite -- extend CPAN with private packages
86
87=head1 SYNOPSIS
88
89 cpansite version
90 cpansite [OPTIONS] index
91 cpansite [OPTIONS] mirror PACKAGE
92
93  OPTIONS:                                      via %ENV:
94    --verbose  -v -vv -vvv --mode=DEBUG
95    --no-lazy       redo everything
96    --cpan <url>    some CPAN mirror            CPANSITE_GLOBAL
97    --env-proxy     read additional proxy settings
98    --site <dir>    local archive directory     CPANSITE_LOCAL
99    --stand-alone   no fallback to global CPAN
100    --no-undefs     do not include "undef" versions in index
101
102=head1 DESCRIPTION
103
104B<WARNING: A lot has changed with the 1.01 release.  Please read more
105about these changes in the file "explain_101.txt" included in the
106distribution.>
107
108The C<cpansite> script is used to create your own CPAN server. The
109logic is implemented in L<CPAN::Site::Index> which you may use directly.
110You only need to install this module on one server in your network.
111
112There are two kinds of local CPANs which can be constructed with this
113software:
114
115=over 4
116
117=item 1. local CPAN with fallback to the global CPAN
118
119When you generate a new index for your local set-up, the default
120behavior is to merge that knowledge with the global CPAN. When you
121install a module on a client, it will first attempt to fetch it from
122your own set-up. If not found, it will automatically continue to
123look at the global CPAN.
124
125=item 2. pure local CPAN, without fallback
126
127When you choose to generate the index without fallback, the installation
128of a module will fail when you do not have a local copy of the module
129in your set-up. You can use the C<mirror> action to collect the latest
130version of a module into your own structure.
131
132=back
133
134=head2 Indexing options
135
136The following options are available with all actions:
137
138=over 4
139
140=item --verbose -v -vv -vvv --mode=DEBUG
141
142Produce verbose output via L<Log::Report>.
143
144=item --site <dir>  or  -s <dir>  or   $CPANSITE_LOCAL
145
146The location of your local CPAN archive set-up.
147
148Example:
149
150  export CPANSITE_LOCAL="/www/websites/cpan.example.com"
151  cpansite index
152
153  cpansite --site $CPANSITE_LOCAL index  # alternative
154
155=item --cpan <url>  or  -c <url>  or   $CPANSITE_GLOBAL
156
157Update the list of "real" CPAN modules regularly (daily or more) from
158this url. By default, C<ftp:///ftp.cpan.org> is addressed which
159redirects to a server close to you.
160
161=item --env-proxy
162
163Let L<LWP::UserAgent> read the proxy settings from environment variables.
164See the according method in that manual page.
165
166=item --stand-alone or  -a
167
168The "real" CPAN list is not included.  For instance, if you have
169downloaded all the releases from CPAN that you need, and you do not want
170unexpected extra downloads.  The downloaded versions will prevail over
171newer releases on CPAN, but you may download modules from the core CPAN
172that you do not expect.
173
174=item --no-lazy     or --lazy  or   -l
175
176Try to avoid redo-ing everything.  By default, the indexer is
177lazy: it will process only new distributions.  When not lazy, all
178distributions on the local disk are processed and a new table is
179created.  The default of this option was reversed with release 1.00
180of C<CPAN::Site>.
181
182=item --no-undefs   or --undefs or -u
183
184Whether to include package names with "undef" version in the packages
185list. Those packages cannot be used for dependencies, so are hardly
186useful but included by default.
187
188=back
189
190=head1 DETAILS
191
192=head2 Configuring the Clients
193
194To get in touch with your own cpan archive, you have to explicitly provide
195an url to it.  Add this to your C<CPAN.pm> configuration file (usually
196F<~/.cpan/CPAN/MyConfig.pm>) option C<urllist>.  B<There is no need to
197install the CPAN::Site software on your clients since release 1.01>.
198
199You probably also want to set the variable C<index_expire> to very short:
200the clients need to reload your local index as soon as possible, and not
201wait a day; just after your new local release is put in your local index,
202it must get visible to your client.
203
204You may also consider to have the CPAN install cache to be cleaned
205by the system.  Certainly when you set the cache size larger (required
206for more complex recursive installations) it is nice to have it removed
207after a (short) while.  Set C<keep_source_where> to a temporary
208directory.
209
210Example for  F<~/.cpan/CPAN/MyConfig.pm>
211
212 $CPAN::Config =
213  { ...
214  , index_expire      => 1/600     # 5 minutes
215  , urllist => [ $MYCPAN_URL, $BIGCPAN_MIRROR ]
216  , keep_source_where => '/tmp/cpan-cache'
217  , build_cache       => 100       # MegaByte cache
218  , ...
219  };
220
221To avoid manually editing the CPAN config file one can also set the
222MYCPAN_URL from the shell:
223
224  cpan> o conf urllist unshift $MYCPAN_URL
225  cpan> o conf index_expire 0.001  # 86 seconds
226  cpan> o conf commit
227
228=head2 Configuring the Server
229
230=head3 Starting your own CPAN
231
232You have to have a ftp or http server running. Create a directory
233where you will distribute the data from, here named C<$MYCPAN>.
234With a web-server, it is adviced to create a virtual host like
235C<cpan.example.com> which has C<$MYCPAN> as DocumentRoot.
236
237Define a fake pause-id (here the demo is MYID), because if you use
238an existing pause-id you clients will start producing warnings about
239missing checksums on files retreived for the public archive.
240
241  MYMODS=$MYCPAN/authors/id/M/MY/MYID
242  mkdir -p $MYMODS
243
244Although CPAN.pm claims to support a directory format of
245C<$MYCPAN/authors/id/MYID>, experience shows that this does not
246work correctly with some recursively dependencies.
247
248=head3 Adding your own modules to the local archive
249
250Put your own modules in C<$MYMODS> and then rerun the indexer.
251
252  mv MyDist-1.00-tar.gz $MYMODS   # local
253  scp MyDist-1.00-tar.gz cpan.example.com:$MYMODS
254
255=head3 Generating an index with fallback
256
257Your own software probably depends on a lot of modules which are
258found on the global CPAN.  And those modules require even more
259modules from CPAN.  By default, your local CPAN index will know
260about all modules which you have yourself plus all module on
261the global CPAN.
262
263The index only contains the last (highest) version of each file
264(which means that each file must contain a version number otherwise the
265text C<undef> is used for version)  In any case, the local packages get
266preference over the global CPAN packages, even when they have a lower
267version number.
268
269With fallback:
270
271 cpansite --site $MYCPAN index
272 cpansite index    # when   CPANSITE_LOCAL=$MYCPAN
273
274The script traverses I<$MYCPAN>F</authors/id> and merges this with the
275I<$MYCPAN>F</global/02packages.details.txt.gz> data, a copy from the
276original CPAN.  It creates a C<CHECKSUMS> file.  The result is a private
277I<$MYCPAN>F</modules/02packages.details.txt.gz> file.
278
279The files F<$MYCPAN/authors/01mailrc.txt.gz> and
280F<$MYCPAN/modules/03modlist.data.gz> are downloaded from CPAN.  This
281will reduce the number of failing retreivals when you start installing
282software.
283
284B<Be warned:> the indexing scans the archive for the same VERSION
285patterns as pause does: do not make too complex expressions in
286those program lines. Only pm files are indexed, not other files,
287like scripts (pl files).
288
289=head3 Generating an index without fallback
290
291When you wish for a controled environment, where all your systems
292run the same versions of the modules, you should disable the fallback
293to the global CPAN.
294
295Without fallback:
296
297 cpansite --site $MYCPAN --stand-alone index
298 cpansite --stand-alone index    # when   CPANSITE_LOCAL=$MYCPAN
299
300The index is now very small.  But when you start installing your software
301on systems, it will start complaining that the module cannot be found on
302CPAN.  Now, add specific distribution versions from the global CPAN to
303your own archive.  See next section.
304
305=head3 Adding distributions from global CPAN to your own
306
307When you want a fixed distribution version to be used on your systems,
308you can manually download them and insert them in the C<$MYCPAN> tree.
309
310However, there is also a simple way to retrieve the most recent version.
311The next example shows how to insert the latest versions of the
312distributions which include the packages Mail::Box and Test::More into
313your local CPAN archive.
314
315 cpansite --site $MYCPAN --cpan $GLOBAL mirror Mail::Box Test::More
316
317 # when CPANSITE_LOCAL=$MYCPAN and CPANSITE_GLOBAL=$GLOBAL
318 cpansite mirror Mail::Box Test::More
319
320=head1 AUTHORS
321
322Mark Overmeer E<lt>markov@cpan.org<gt>.
323
324This project is free software; you can redistribute it and/or modify it
325under the same terms as Perl itself.
326See <http://dev.perl.org/licenses/>
327
328=cut
329