| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #!perl | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | package App::opan; | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 3 |  |  | 3 |  | 4544 | use strictures 2; | 
|  | 3 |  |  |  |  | 1687 |  | 
|  | 3 |  |  |  |  | 139 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $VERSION = '0.003004'; | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 3 |  |  | 3 |  | 2327 | use Dist::Metadata; | 
|  | 3 |  |  |  |  | 106482 |  | 
|  | 3 |  |  |  |  | 117 |  | 
| 10 | 3 |  |  | 3 |  | 1822 | use File::Open qw(fopen); | 
|  | 3 |  |  |  |  | 4924 |  | 
|  | 3 |  |  |  |  | 215 |  | 
| 11 | 3 |  |  | 3 |  | 1867 | use List::UtilsBy qw(sort_by); | 
|  | 3 |  |  |  |  | 5986 |  | 
|  | 3 |  |  |  |  | 231 |  | 
| 12 | 3 |  |  | 3 |  | 1341 | use IPC::System::Simple qw(capture); | 
|  | 3 |  |  |  |  | 20259 |  | 
|  | 3 |  |  |  |  | 247 |  | 
| 13 | 3 |  |  | 3 |  | 859 | use Mojo::Util qw(monkey_patch); | 
|  | 3 |  |  |  |  | 207776 |  | 
|  | 3 |  |  |  |  | 281 |  | 
| 14 | 3 |  |  | 3 |  | 657 | use Mojo::File qw(path); | 
|  | 3 |  |  |  |  | 29867 |  | 
|  | 3 |  |  |  |  | 164 |  | 
| 15 | 3 |  |  | 3 |  | 1085 | use Import::Into; | 
|  | 3 |  |  |  |  | 5604 |  | 
|  | 3 |  |  |  |  | 8686 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | our %TOKENS = map { $_=>1 } split/:/, $ENV{OPAN_AUTH_TOKENS} || ''; | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | sub cpan_fetch { | 
| 20 | 5 |  |  | 5 |  | 208 | my $app = shift; | 
| 21 | 5 |  |  |  |  | 96 | my $url = Mojo::URL->new(shift)->to_abs($app->cpan_url); | 
| 22 | 5 |  |  |  |  | 5917 | my $tx = $app->ua->get($url); | 
| 23 | 5 | 100 |  |  |  | 766458 | return $tx->res unless my $err = $tx->error; | 
| 24 | 1 |  |  |  |  | 29 | die sprintf "%s %s: %s\n", $tx->req->method, $url, $err->{message}; | 
| 25 |  |  |  |  |  |  | } | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | sub packages_header { | 
| 28 | 18 |  |  | 18 |  | 142 | my ($count) = @_; | 
| 29 | 18 |  |  |  |  | 223 | (my $str = <<"  HEADER") =~ s/^    //mg; | 
| 30 |  |  |  |  |  |  | File:         02packages.details.txt | 
| 31 |  |  |  |  |  |  | Description:  Package names found in directory \$CPAN/authors/id/ | 
| 32 |  |  |  |  |  |  | Columns:      package name, version, path | 
| 33 |  |  |  |  |  |  | Intended-For: Automated fetch routines, namespace documentation. | 
| 34 |  |  |  |  |  |  | Written-By:   App::opan | 
| 35 |  |  |  |  |  |  | Line-Count:   ${count} | 
| 36 | 18 |  |  |  |  | 432 | Last-Updated: ${\scalar gmtime} GMT | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | HEADER | 
| 39 | 18 |  |  |  |  | 534 | return $str; | 
| 40 |  |  |  |  |  |  | } | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | sub extract_provides_from_tarball { | 
| 43 | 5 |  |  | 5 |  | 18 | my ($tarball) = @_; | 
| 44 | 5 |  |  |  |  | 197 | Dist::Metadata->new(file => $tarball)->package_versions; | 
| 45 |  |  |  |  |  |  | } | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | sub pod_section { | 
| 48 | 36 |  |  | 36 |  | 75 | my ($cmd, $for_description) = @_; | 
| 49 | 36 |  |  |  |  | 87 | my $fh = fopen __FILE__; | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 36 |  |  |  |  | 2620 | my $pod = ''; | 
| 52 | 36 | 100 |  |  |  | 693 | while (<$fh>) { /^=head3 $cmd\s*$/ && last } | 
|  | 18972 |  |  |  |  | 54827 |  | 
| 53 | 36 | 50 | 100 |  |  | 122 | while (<$fh>) { /^=head/ && last || ($pod .= $_) } | 
|  | 318 |  |  |  |  | 787 |  | 
| 54 |  |  |  |  |  |  |  | 
| 55 | 36 | 100 |  |  |  | 62 | if ($for_description) { | 
| 56 | 24 | 50 |  |  |  | 225 | $pod = $pod =~ m!\n(\S+.*?\.)(?:\s|$)!s ? $1 : "$0 $cmd --help for more info"; | 
| 57 | 24 |  |  |  |  | 81 | $pod =~ s![\n\r]+! !g; | 
| 58 | 24 |  |  |  |  | 160 | $pod =~ s![\s\.]+$!!; | 
| 59 |  |  |  |  |  |  | } | 
| 60 |  |  |  |  |  |  |  | 
| 61 | 36 |  |  |  |  | 252 | $pod =~ s!\b[CIL]<\/?([^>]+)\>!$1!g; # Remove C<...> pod notation | 
| 62 | 36 |  |  |  |  | 1263 | return $pod; | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | sub provides_to_packages_entries { | 
| 66 | 5 |  |  | 5 |  | 283124 | my ($path, $provides) = @_; | 
| 67 |  |  |  |  |  |  | # <@mst> ok, I officially have no idea what order 02packages is actually in | 
| 68 |  |  |  |  |  |  | # <@rjbs>     $list .= join "", sort {lc $a cmp lc $b} @listing02; | 
| 69 |  |  |  |  |  |  | [ | 
| 70 |  |  |  |  |  |  | map +[ | 
| 71 |  |  |  |  |  |  | $_, defined($provides->{$_}) ? $provides->{$_} : 'undef', $path | 
| 72 | 5 | 50 |  | 4 |  | 159 | ], sort_by { lc } keys %$provides | 
|  | 4 |  |  |  |  | 59 |  | 
| 73 |  |  |  |  |  |  | ] | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | sub entries_from_packages_file { | 
| 77 | 36 |  |  | 36 |  | 57078 | my ($file) = @_; | 
| 78 | 36 |  |  |  |  | 430 | my $fh = fopen $file; | 
| 79 | 36 |  |  |  |  | 5027 | while (my $header = <$fh>) { | 
| 80 | 295 | 100 |  |  |  | 1076 | last if $header =~ /^$/; | 
| 81 |  |  |  |  |  |  | } | 
| 82 | 36 |  |  |  |  | 90 | my @entries; | 
| 83 | 36 |  |  |  |  | 287 | while (my $line = <$fh>) { | 
| 84 | 502592 |  |  |  |  | 752165 | chomp($line); | 
| 85 | 502592 |  |  |  |  | 2666199 | push @entries, [ split /\s+/, $line ]; | 
| 86 |  |  |  |  |  |  | } | 
| 87 | 36 |  |  |  |  | 1324 | return \@entries; | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | sub merge_packages_entries { | 
| 91 | 17 |  |  | 17 |  | 198 | my ($base, $merge_these) = @_; | 
| 92 | 17 | 50 |  |  |  | 84 | return $base unless $merge_these; | 
| 93 | 17 |  |  |  |  | 41 | my @merged; | 
| 94 | 17 |  |  |  |  | 45 | my @to_merge = @$merge_these; | 
| 95 | 17 |  |  |  |  | 165 | foreach my $idx (0..$#$base) { | 
| 96 | 1004822 |  | 66 |  |  | 1620905 | while (@to_merge and lc($to_merge[0][0]) lt lc($base->[$idx][0])) { | 
| 97 | 0 |  |  |  |  | 0 | push @merged, shift @to_merge; | 
| 98 |  |  |  |  |  |  | } | 
| 99 | 1004822 | 100 | 100 |  |  | 2135439 | push @merged, ( | 
| 100 |  |  |  |  |  |  | (@to_merge and $to_merge[0][0] eq $base->[$idx][0]) | 
| 101 |  |  |  |  |  |  | ? shift @to_merge | 
| 102 |  |  |  |  |  |  | : $base->[$idx] | 
| 103 |  |  |  |  |  |  | ); | 
| 104 |  |  |  |  |  |  | } | 
| 105 | 17 |  |  |  |  | 78 | push @merged, @to_merge; | 
| 106 | 17 |  |  |  |  | 138 | return \@merged; | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | sub write_packages_file { | 
| 110 | 18 |  |  | 18 |  | 14646 | my ($file, $entries) = @_; | 
| 111 | 18 |  |  |  |  | 281 | my $fh = fopen $file, 'w'; | 
| 112 | 18 |  |  |  |  | 13545 | print $fh packages_header(scalar @$entries); | 
| 113 |  |  |  |  |  |  | local *_ = sub { | 
| 114 |  |  |  |  |  |  | # mirroring 'sub rewrite02 {' in lib/PAUSE/mldistwatch.pm | 
| 115 |  |  |  |  |  |  | # see http://github.com/andk/pause for the whole thing | 
| 116 | 1004826 |  |  | 1004826 |  | 1636373 | my ($one, $two) = (30, 8); | 
| 117 | 1004826 | 100 |  |  |  | 1538087 | if (length($_[0]) > $one) { | 
| 118 | 498040 |  |  |  |  | 588447 | $one += 8 - length($_[1]); | 
| 119 | 498040 |  |  |  |  | 570290 | $two = length($_[1]); | 
| 120 |  |  |  |  |  |  | } | 
| 121 | 1004826 |  |  |  |  | 3832330 | sprintf "%-${one}s %${two}s  %s\n", @_; | 
| 122 | 18 |  |  |  |  | 371 | }; | 
| 123 | 18 |  |  |  |  | 176 | print $fh _(@$_) for @$entries; | 
| 124 | 18 |  |  |  |  | 9099 | close $fh; | 
| 125 | 18 |  |  |  |  | 374 | path("${file}.gz")->spurt(scalar capture(gzip => -c => $file)); | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub add_dist_to_index { | 
| 129 | 5 |  |  | 5 |  | 581 | my ($index, $dist) = @_; | 
| 130 | 5 |  |  |  |  | 37 | my $existing = entries_from_packages_file($index); | 
| 131 | 5 |  |  |  |  | 42 | my ($path) = $dist =~ m{pans/[a-z]+/dists/(.*)}; | 
| 132 | 5 |  |  |  |  | 177 | write_packages_file( | 
| 133 |  |  |  |  |  |  | $index, | 
| 134 |  |  |  |  |  |  | merge_packages_entries( | 
| 135 |  |  |  |  |  |  | $existing, | 
| 136 |  |  |  |  |  |  | provides_to_packages_entries( | 
| 137 |  |  |  |  |  |  | $path, | 
| 138 |  |  |  |  |  |  | extract_provides_from_tarball($dist) | 
| 139 |  |  |  |  |  |  | ), | 
| 140 |  |  |  |  |  |  | ) | 
| 141 |  |  |  |  |  |  | ); | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub remove_dist_from_index { | 
| 145 | 1 |  |  | 1 |  | 58 | my ($index, $dist) = @_; | 
| 146 | 1 |  |  |  |  | 16 | my $existing = entries_from_packages_file($index); | 
| 147 | 1 |  |  |  |  | 41 | my $exclude = qr/\Q${dist}\E$/; | 
| 148 | 1 |  |  |  |  | 57 | write_packages_file( | 
| 149 |  |  |  |  |  |  | $index, | 
| 150 |  |  |  |  |  |  | [ grep $_->[2] !~ $exclude, @$existing ], | 
| 151 |  |  |  |  |  |  | ); | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | my @pan_names = qw(upstream custom pinset combined nopin); | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | sub do_init { | 
| 157 | 2 |  |  | 2 |  | 14 | my ($app) = @_; | 
| 158 | 2 |  |  |  |  | 15 | path("pans/$_/dists")->make_path for @pan_names; | 
| 159 | 2 |  |  |  |  | 2531 | write_packages_file("pans/$_/index", []) for qw(custom pinset); | 
| 160 | 2 |  |  |  |  | 13693 | do_pull($app); | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | sub do_fetch { | 
| 164 | 2 |  |  | 2 |  | 19 | my ($app) = @_; | 
| 165 | 2 |  |  |  |  | 103 | path('pans/upstream/index.gz')->spurt( | 
| 166 |  |  |  |  |  |  | cpan_fetch($app, 'modules/02packages.details.txt.gz')->body | 
| 167 |  |  |  |  |  |  | ); | 
| 168 | 2 |  |  |  |  | 13350 | path('pans/upstream/index')->spurt( | 
| 169 |  |  |  |  |  |  | scalar capture qw(gzip -dc), 'pans/upstream/index.gz' | 
| 170 |  |  |  |  |  |  | ); | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | sub do_merge { | 
| 174 | 4 |  |  | 4 |  | 83 | my ($app) = @_; | 
| 175 | 4 |  |  |  |  | 68 | my $upstream = entries_from_packages_file('pans/upstream/index'); | 
| 176 | 4 |  |  |  |  | 54 | my $pinset = entries_from_packages_file('pans/pinset/index'); | 
| 177 | 4 |  |  |  |  | 42 | my $custom = entries_from_packages_file('pans/custom/index'); | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 4 |  |  |  |  | 50 | my $nopin = merge_packages_entries($upstream, $custom); | 
| 180 | 4 |  |  |  |  | 43 | write_packages_file('pans/nopin/index', $nopin); | 
| 181 |  |  |  |  |  |  |  | 
| 182 | 4 |  |  |  |  | 1305705 | my $combined = merge_packages_entries( | 
| 183 |  |  |  |  |  |  | $upstream, merge_packages_entries($pinset, $custom) | 
| 184 |  |  |  |  |  |  | ); | 
| 185 | 4 |  |  |  |  | 99 | write_packages_file('pans/combined/index', $combined); | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | sub do_pull { | 
| 189 | 2 |  |  | 2 |  | 35 | my ($app) = @_; | 
| 190 | 2 |  |  |  |  | 104 | do_fetch($app); | 
| 191 | 2 |  |  |  |  | 382172 | do_merge($app); | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | sub do_add { | 
| 195 | 2 |  |  | 2 |  | 25 | my ($app, $path_arg) = @_; | 
| 196 | 2 |  |  |  |  | 30 | my $path = path($path_arg); | 
| 197 | 2 |  |  |  |  | 53 | my $pan_dir = path('pans/custom/dists/M/MY/MY')->make_path; | 
| 198 | 2 | 50 |  |  |  | 818 | $path->copy_to(my $pan_path = $pan_dir->child($path->basename)) | 
| 199 |  |  |  |  |  |  | or die "Failed to copy ${path} into custom pan: $!"; | 
| 200 | 2 |  |  |  |  | 2001 | add_dist_to_index('pans/custom/index', $pan_path); | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | sub do_unadd { | 
| 204 | 0 |  |  | 0 |  | 0 | my ($app, $dist) = @_; | 
| 205 | 0 |  |  |  |  | 0 | remove_dist_from_index('pans/custom/index', $dist); | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | sub do_pin { | 
| 209 | 3 |  |  | 3 |  | 40 | my ($app, $path_arg) = @_; | 
| 210 | 3 | 100 |  |  |  | 38 | $path_arg =~ /^(([A-Z])[A-Z])[A-Z]/ and $path_arg = join('/', $2, $1, $path_arg); | 
| 211 | 3 |  |  |  |  | 14 | my $path = path($path_arg); | 
| 212 | 3 |  |  |  |  | 62 | my $res = cpan_fetch($app, "authors/id/$path"); | 
| 213 | 2 |  |  |  |  | 61 | path("pans/pinset/dists/")->child($path->dirname)->make_path; | 
| 214 | 2 |  |  |  |  | 933 | add_dist_to_index('pans/pinset/index', path("pans/pinset/dists/$path")->spurt($res->body)); | 
| 215 |  |  |  |  |  |  | } | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | sub do_unpin { | 
| 218 | 1 |  |  | 1 |  | 24 | my ($app, $dist) = @_; | 
| 219 | 1 |  |  |  |  | 19 | remove_dist_from_index('pans/pinset/index', $dist); | 
| 220 |  |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | sub generate_purgelist { | 
| 223 | 2 |  |  | 2 |  | 125 | my @list; | 
| 224 | 2 |  |  |  |  | 14 | foreach my $pan (qw(pinset custom)) { | 
| 225 |  |  |  |  |  |  | my %indexed = map +("pans/${pan}/dists/".$_->[2] => 1), | 
| 226 | 4 |  |  |  |  | 9 | @{entries_from_packages_file("pans/${pan}/index")}; | 
|  | 4 |  |  |  |  | 27 |  | 
| 227 | 4 |  |  |  |  | 838 | foreach my $file (sort glob "pans/${pan}/dists/*/*/*/*") { | 
| 228 | 6 | 100 |  |  |  | 55 | push @list, $file unless $indexed{$file}; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  | } | 
| 231 | 2 |  |  |  |  | 308 | return @list; | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | sub do_purgelist { | 
| 235 | 1 |  |  | 1 |  | 33 | print "$_\n" for &generate_purgelist; | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | sub do_purge { | 
| 239 | 1 |  |  | 1 |  | 19 | unlink($_) for &generate_purgelist; | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | sub run_with_server { | 
| 243 | 0 |  |  | 0 |  | 0 | my ($app, $run, $pan, @args) = @_; | 
| 244 | 0 | 0 | 0 |  |  | 0 | unless ( | 
| 245 |  |  |  |  |  |  | defined($pan) and $pan =~ /^--(combined|nopin|autopin)$/ | 
| 246 |  |  |  |  |  |  | ) { | 
| 247 | 0 |  |  |  |  | 0 | unshift @args, grep defined, $pan; | 
| 248 | 0 |  |  |  |  | 0 | $pan = '--combined'; | 
| 249 |  |  |  |  |  |  | } | 
| 250 | 0 |  |  |  |  | 0 | $pan =~ s/^--//; | 
| 251 | 0 |  |  |  |  | 0 | require Mojo::IOLoop::Server; | 
| 252 | 0 |  |  |  |  | 0 | my $port = Mojo::IOLoop::Server->generate_port; | 
| 253 | 0 |  |  |  |  | 0 | my $url = "http://localhost:${port}/"; | 
| 254 | 0 |  |  |  |  | 0 | my $pid = fork(); | 
| 255 | 0 | 0 |  |  |  | 0 | die "fork() fork()ed up: $!" unless defined $pid; | 
| 256 | 0 | 0 |  |  |  | 0 | unless ($pid) { | 
| 257 | 0 | 0 |  |  |  | 0 | $ENV{OPAN_AUTOPIN} = 1 if $pan eq 'autopin'; | 
| 258 | 0 |  |  |  |  | 0 | $app->start(daemon => -l => $url); | 
| 259 | 0 |  |  |  |  | 0 | exit(0); | 
| 260 |  |  |  |  |  |  | } | 
| 261 | 0 |  |  |  |  | 0 | eval { $run->("${url}${pan}", @args) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 262 | 0 |  |  |  |  | 0 | my $err = $@; | 
| 263 | 0 |  |  |  |  | 0 | kill TERM => $pid; | 
| 264 | 0 | 0 |  |  |  | 0 | warn "Run block failed: $err" if $err; | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | sub do_cpanm { | 
| 268 | 0 |  |  | 0 |  | 0 | my ($app, @args) = @_; | 
| 269 |  |  |  |  |  |  | run_with_server($app, sub { | 
| 270 | 0 |  |  | 0 |  | 0 | my ($mirror, @args) = @_; | 
| 271 | 0 |  |  |  |  | 0 | system(cpanm => '--mirror', $mirror, '--mirror-only', @args); | 
| 272 | 0 |  |  |  |  | 0 | }, @args); | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | sub do_carton { | 
| 276 | 0 |  |  | 0 |  | 0 | my ($app, @args) = @_; | 
| 277 |  |  |  |  |  |  | run_with_server($app, sub { | 
| 278 | 0 |  |  | 0 |  | 0 | my ($mirror, @args) = @_; | 
| 279 | 0 |  |  |  |  | 0 | local $ENV{PERL_CARTON_MIRROR} = $mirror; | 
| 280 | 0 |  |  |  |  | 0 | system(carton => @args); | 
| 281 | 0 |  |  |  |  | 0 | }, @args); | 
| 282 |  |  |  |  |  |  | } | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | foreach my $cmd ( | 
| 285 |  |  |  |  |  |  | qw(init fetch add unadd pin unpin merge pull purgelist purge cpanm carton) | 
| 286 |  |  |  |  |  |  | ) { | 
| 287 |  |  |  |  |  |  | my $pkg = "App::opan::Command::${cmd}"; | 
| 288 |  |  |  |  |  |  | my $code = __PACKAGE__->can("do_${cmd}"); | 
| 289 |  |  |  |  |  |  | Mojo::Base->import::into($pkg, 'Mojolicious::Command'); | 
| 290 | 24 |  |  | 24 |  | 597 | monkey_patch $pkg, description => sub { pod_section($cmd, 1) }; | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
|  |  |  |  | 24 |  |  |  | 
| 291 | 12 |  |  | 12 |  | 7252 | monkey_patch $pkg, usage => sub { pod_section($cmd, 0) }; | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
|  |  |  |  | 12 |  |  |  | 
| 292 |  |  |  |  |  |  | monkey_patch $pkg, | 
| 293 | 10 |  |  | 10 |  | 77192 | run => sub { my $self = shift; $code->($self->app, @_) }; | 
|  | 10 |  |  | 10 |  | 74 |  | 
|  |  |  |  | 10 |  |  |  | 
|  |  |  |  | 10 |  |  |  | 
|  |  |  |  | 10 |  |  |  | 
|  |  |  |  | 10 |  |  |  | 
|  |  |  |  | 10 |  |  |  | 
|  |  |  |  | 10 |  |  |  | 
|  |  |  |  | 10 |  |  |  | 
|  |  |  |  | 10 |  |  |  | 
|  |  |  |  | 10 |  |  |  | 
|  |  |  |  | 10 |  |  |  | 
| 294 |  |  |  |  |  |  | } | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 3 |  |  | 3 |  | 1602 | use Mojolicious::Lite; | 
|  | 3 |  |  |  |  | 674326 |  | 
|  | 3 |  |  |  |  | 23 |  | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | Mojo::IOLoop->recurring(600 => sub { do_pull(app); }) if $ENV{OPAN_RECURRING_PULL}; | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | post "/upload" => sub { | 
| 301 |  |  |  |  |  |  | my $c = shift; | 
| 302 |  |  |  |  |  |  | unless ($TOKENS{$c->req->url->to_abs->password || ""}) { | 
| 303 |  |  |  |  |  |  | $c->res->headers->www_authenticate("Basic realm=opan"); | 
| 304 |  |  |  |  |  |  | return $c->render(status => 401, text => "Token missing or not found\n"); | 
| 305 |  |  |  |  |  |  | } | 
| 306 |  |  |  |  |  |  | my $upload = $c->req->upload('dist') || $c->req->upload('pause99_add_uri_httpupload') | 
| 307 |  |  |  |  |  |  | or return $c->render(status => 400, text => "dist file missing\n"); | 
| 308 |  |  |  |  |  |  | my $pan_dir = path('pans/custom/dists/M/MY/MY')->make_path; | 
| 309 |  |  |  |  |  |  | $upload->move_to(my $pan_path = $pan_dir->child($upload->filename)) | 
| 310 |  |  |  |  |  |  | or $c->render( | 
| 311 |  |  |  |  |  |  | status => 500, | 
| 312 |  |  |  |  |  |  | text   => "Failed to move ${upload} into custom pan: $!\n" | 
| 313 |  |  |  |  |  |  | ); | 
| 314 |  |  |  |  |  |  | add_dist_to_index('pans/custom/index', $pan_path); | 
| 315 |  |  |  |  |  |  | do_merge(app); | 
| 316 |  |  |  |  |  |  | $c->render(text => "$pan_path Added to opan\n"); | 
| 317 |  |  |  |  |  |  | }; | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | push(@{app->commands->namespaces}, 'App::opan::Command'); | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | helper cpan_url => sub { Mojo::URL->new($ENV{OPAN_MIRROR} || 'https://www.cpan.org/') }; | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | my $nopin_static = Mojolicious::Static->new( | 
| 324 |  |  |  |  |  |  | paths => [ 'pans/custom/dists' ] | 
| 325 |  |  |  |  |  |  | ); | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | my $pinset_static = Mojolicious::Static->new( | 
| 328 |  |  |  |  |  |  | paths => [ 'pans/pinset/dists' ] | 
| 329 |  |  |  |  |  |  | ); | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | my $combined_static = Mojolicious::Static->new( | 
| 332 |  |  |  |  |  |  | paths => [ 'pans/custom/dists', 'pans/pinset/dists' ] | 
| 333 |  |  |  |  |  |  | ); | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | my $base_static = Mojolicious::Static->new( | 
| 336 |  |  |  |  |  |  | paths => [ 'pans' ] | 
| 337 |  |  |  |  |  |  | ); | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | foreach my $pan (qw(upstream nopin combined pinset custom)) { | 
| 340 |  |  |  |  |  |  | get "/${pan}/modules/02packages.details.txt" => sub { | 
| 341 |  |  |  |  |  |  | $base_static->dispatch($_[0]->stash(path => "${pan}/index")); | 
| 342 |  |  |  |  |  |  | }; | 
| 343 |  |  |  |  |  |  | get "/${pan}/modules/02packages.details.txt.gz" => sub { | 
| 344 |  |  |  |  |  |  | $base_static->dispatch($_[0]->stash(path => "${pan}/index.gz")); | 
| 345 |  |  |  |  |  |  | }; | 
| 346 |  |  |  |  |  |  | } | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | my $serve_upstream = sub { | 
| 349 |  |  |  |  |  |  | my ($c) = @_; | 
| 350 |  |  |  |  |  |  | $c->render_later; | 
| 351 |  |  |  |  |  |  | $c->ua->get( | 
| 352 |  |  |  |  |  |  | $c->cpan_url.'authors/id/'.$c->stash->{dist_path}, | 
| 353 |  |  |  |  |  |  | sub { | 
| 354 |  |  |  |  |  |  | my (undef, $tx) = @_; | 
| 355 |  |  |  |  |  |  | $c->tx->res($tx->res); | 
| 356 |  |  |  |  |  |  | $c->rendered; | 
| 357 |  |  |  |  |  |  | } | 
| 358 |  |  |  |  |  |  | ); | 
| 359 |  |  |  |  |  |  | return; | 
| 360 |  |  |  |  |  |  | }; | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | get '/upstream/authors/id/*dist_path' => $serve_upstream; | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | get '/combined/authors/id/*dist_path' => sub { | 
| 365 |  |  |  |  |  |  | $_[0]->stash(path => $_[0]->stash->{dist_path}); | 
| 366 |  |  |  |  |  |  | $combined_static->dispatch($_[0]) or $serve_upstream->($_[0]); | 
| 367 |  |  |  |  |  |  | }; | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | get '/nopin/authors/id/*dist_path' => sub { | 
| 370 |  |  |  |  |  |  | $_[0]->stash(path => $_[0]->stash->{dist_path}); | 
| 371 |  |  |  |  |  |  | $nopin_static->dispatch($_[0]) or $serve_upstream->($_[0]); | 
| 372 |  |  |  |  |  |  | }; | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | get "/autopin/modules/02packages.details.txt" => sub { | 
| 375 |  |  |  |  |  |  | return $_[0]->render(text => 'Autopin off', status => 404) | 
| 376 |  |  |  |  |  |  | unless $ENV{OPAN_AUTOPIN}; | 
| 377 |  |  |  |  |  |  | $base_static->dispatch($_[0]->stash(path => "nopin/index")); | 
| 378 |  |  |  |  |  |  | }; | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | get "/autopin/modules/02packages.details.txt.gz" => sub { | 
| 381 |  |  |  |  |  |  | return $_[0]->render(text => 'Autopin off', status => 404) | 
| 382 |  |  |  |  |  |  | unless $ENV{OPAN_AUTOPIN}; | 
| 383 |  |  |  |  |  |  | $base_static->dispatch($_[0]->stash(path => "nopin/index.gz")); | 
| 384 |  |  |  |  |  |  | }; | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | get '/autopin/authors/id/*dist_path' => sub { | 
| 387 |  |  |  |  |  |  | return $_[0]->render(text => 'Autopin off', status => 404) | 
| 388 |  |  |  |  |  |  | unless $ENV{OPAN_AUTOPIN}; | 
| 389 |  |  |  |  |  |  | return if $nopin_static->dispatch($_[0]->stash(path => $_[0]->stash->{dist_path})); | 
| 390 |  |  |  |  |  |  | return if eval { | 
| 391 |  |  |  |  |  |  | do_pin(app, $_[0]->stash->{path}); | 
| 392 |  |  |  |  |  |  | $pinset_static->dispatch($_[0]); | 
| 393 |  |  |  |  |  |  | }; | 
| 394 |  |  |  |  |  |  | return $_[0]->render(text => 'Not found', status => 404); | 
| 395 |  |  |  |  |  |  | }; | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | caller() ? app : app->tap(sub { shift->log->level('fatal') })->start; | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | =head1 NAME | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | App::opan - A CPAN overlay for darkpan and pinning purposes | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | Set up an opan (creates a directory tree in C): | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | $ opan init | 
| 408 |  |  |  |  |  |  | $ opan pin MSTROUT/M-1.tar.gz | 
| 409 |  |  |  |  |  |  | $ opan add ./My-Dist-1.23.tar.gz | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | Now, you can start the server: | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | $ opan daemon -l http://localhost:8030/ | 
| 414 |  |  |  |  |  |  | Server available at http://localhost:8030/ | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | Then in another terminal, run one of: | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | $ cpanm --mirror http://localhost:8030/combined/ --mirror-only --installdeps . | 
| 419 |  |  |  |  |  |  | $ PERL_CARTON_MIRROR=http://localhost:8030/combined/ carton install | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | Or, to let opan do that part for you, skip starting the server and run one of: | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | $ opan cpanm --installdeps . | 
| 424 |  |  |  |  |  |  | $ opan carton install | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | Two basic approaches to using this thing. First, if you're using carton, you | 
| 429 |  |  |  |  |  |  | can probably completely ignore the pinning system, so just do: | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | $ opan init | 
| 432 |  |  |  |  |  |  | $ opan add ./My-DarkPan-Dist-1.23.tar.gz | 
| 433 |  |  |  |  |  |  | $ git add pans/; git commit -m 'fresh opan' | 
| 434 |  |  |  |  |  |  | $ opan carton install | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | You can reproduce this install with simply: | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | $ opan carton install --deployment | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | When you want to update to a new version of the cpan index (assuming you | 
| 441 |  |  |  |  |  |  | already have an additional requirement that's too old in your current | 
| 442 |  |  |  |  |  |  | snapshot): | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | $ opan pull | 
| 445 |  |  |  |  |  |  | $ git add pans/; git commit -m 'update pans' | 
| 446 |  |  |  |  |  |  | $ opan carton install | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | Second, if you're not using carton, but you want reproducible installs, you | 
| 449 |  |  |  |  |  |  | can still mostly ignore the pinning system by doing: | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | $ opan init | 
| 452 |  |  |  |  |  |  | $ opan add ./My-DarkPan-Dist-1.23.tar.gz | 
| 453 |  |  |  |  |  |  | $ opan cpanm --autopin --installdeps . | 
| 454 |  |  |  |  |  |  | $ git add pans/; git commit -m 'opan with current version pinning' | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | Your reproducible install is now: | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | $ opan cpanm --installdeps . | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | When you want to update to a new version of the cpan index (assuming you | 
| 461 |  |  |  |  |  |  | already have an additional requirement that's too old in your current | 
| 462 |  |  |  |  |  |  | snapshot): | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | $ opan pull | 
| 465 |  |  |  |  |  |  | $ opan cpanm --autopin --installdeps . | 
| 466 |  |  |  |  |  |  | $ git add pans/; git commit -m 'update pans' | 
| 467 |  |  |  |  |  |  |  | 
| 468 |  |  |  |  |  |  | To update a single dist in this system, the easy route is: | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | $ opan unpin Thingy-1.23.tar.gz | 
| 471 |  |  |  |  |  |  | $ opan cpanm Thingy | 
| 472 |  |  |  |  |  |  | Fetching http://www.cpan.org/authors/id/S/SO/SOMEONE/Thingy-1.25.tar.gz | 
| 473 |  |  |  |  |  |  | ... | 
| 474 |  |  |  |  |  |  | $ opan pin SOMEONE/Thing-1.25.tar.gz | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | This will probably make more sense if you read the L and L | 
| 477 |  |  |  |  |  |  | documentation following before trying to set things up. | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | =head2 Commands | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | =head3 init | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | opan init | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | Creates a C directory with empty indexes for L and L | 
| 486 |  |  |  |  |  |  | and a fresh index for L (i.e. runs L for you at the end | 
| 487 |  |  |  |  |  |  | of initialisation). | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | =head3 fetch | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | opan fetch | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | Fetches 02packages from www.cpan.org into the L PAN. | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | =head3 add | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | opan add Dist-Name-1.23.tar.gz | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | Imports a distribution file into the L PAN under author C. Any | 
| 500 |  |  |  |  |  |  | path parts provided before the filename will be stripped. | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | Support for other authors is pending somebody explaining why that would have | 
| 503 |  |  |  |  |  |  | a point. See L for the command you probably wanted instead. | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | =head3 unadd | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | opan unadd Dist-Name-1.23.tar.gz | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | Looks for a C path in the L PAN index | 
| 510 |  |  |  |  |  |  | and removes the entries. | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | Does not remove the dist file, see L. | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | =head3 pin | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | opan pin AUTHOR/Dist-Name-1.23.tar.gz | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | Fetches the file from the L PAN and adds it to L. | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | =head3 unpin | 
| 521 |  |  |  |  |  |  |  | 
| 522 |  |  |  |  |  |  | opan unpin Dist-Name-1.23.tar.gz | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | Looks for a C path in the L PAN index | 
| 525 |  |  |  |  |  |  | and removes the entries. | 
| 526 |  |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  | Does not remove the dist file, see L. | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | =head3 merge | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | opan merge | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | Rebuilds the L and L PANs' index files. | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | =head3 pull | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | opan pull | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | Does a L and then a L. There's no equivalent for others, | 
| 540 |  |  |  |  |  |  | on the assumption what you'll do is roughly L, L, L, | 
| 541 |  |  |  |  |  |  | L, ... repeat ..., L. | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | =head3 purgelist | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | opan purgelist | 
| 546 |  |  |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | Outputs a list of all non-indexed dists in L and L. | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | =head3 purge | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | opan purge | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | Deletes all files that would have been listed by L. | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | =head3 daemon | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | opan daemon | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | Starts a single process server using L. | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  | =head3 prefork | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | opan prefork | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | Starts a multi-process preforking server using | 
| 566 |  |  |  |  |  |  | L. | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | =head3 get | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | opan get /upstream/modules/02packages.details.txt.gz | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | Runs a request against the opan URL space using L. | 
| 573 |  |  |  |  |  |  |  | 
| 574 |  |  |  |  |  |  | =head3 cpanm | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | opan cpanm --installdeps . | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | Starts a temporary server process and runs cpanm. | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | cpanm --mirror http://localhost:/combined/ --mirror-only | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | Can also be run with one of: | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | opan cpanm --nopin | 
| 585 |  |  |  |  |  |  | opan cpanm --autopin | 
| 586 |  |  |  |  |  |  | opan cpanm --combined | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  | to request a specific PAN. | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | =head3 carton | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | opan carton install | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | Starts a temporary server process and runs carton. | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | PERL_CARTON_MIRROR=http://localhost:/combined/ carton | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | Can also be run with one of: | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | opan carton --nopin | 
| 601 |  |  |  |  |  |  | opan carton --autopin | 
| 602 |  |  |  |  |  |  | opan carton --combined | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | to request a specific PAN. | 
| 605 |  |  |  |  |  |  |  | 
| 606 |  |  |  |  |  |  | =head2 PANs | 
| 607 |  |  |  |  |  |  |  | 
| 608 |  |  |  |  |  |  | =head3 upstream | 
| 609 |  |  |  |  |  |  |  | 
| 610 |  |  |  |  |  |  | 02packages: Fetched from www.cpan.org by the L command. | 
| 611 |  |  |  |  |  |  |  | 
| 612 |  |  |  |  |  |  | Dist files: Fetched from www.cpan.org on-demand. | 
| 613 |  |  |  |  |  |  |  | 
| 614 |  |  |  |  |  |  | =head3 pinset | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | 02packages: Managed by L and L commands. | 
| 617 |  |  |  |  |  |  |  | 
| 618 |  |  |  |  |  |  | Dist files: Fetched from www.cpan.org by L command. | 
| 619 |  |  |  |  |  |  |  | 
| 620 |  |  |  |  |  |  | =head3 custom | 
| 621 |  |  |  |  |  |  |  | 
| 622 |  |  |  |  |  |  | 02packages: Managed by L and L commands. | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | Dist files: Imported from local disk by L command. | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | =head3 combined | 
| 627 |  |  |  |  |  |  |  | 
| 628 |  |  |  |  |  |  | 02packages: Merged from upstream, pinset and custom PANs by L command. | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | Dist files: Fetched from custom, pinset and upstream in that order. | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | =head3 nopin | 
| 633 |  |  |  |  |  |  |  | 
| 634 |  |  |  |  |  |  | 02packages: Merged from upstream and custom PANs by L command. | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | Dist files: Fetched from custom, pinset and upstream in that order. | 
| 637 |  |  |  |  |  |  |  | 
| 638 |  |  |  |  |  |  | =head3 autopin | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | Virtual PAN with no presence on disk. | 
| 641 |  |  |  |  |  |  |  | 
| 642 |  |  |  |  |  |  | Identical to nopin, but fetching a dist from upstream does an implict L. | 
| 643 |  |  |  |  |  |  |  | 
| 644 |  |  |  |  |  |  | Since this can modify your opan config, it's only enabled if the environment | 
| 645 |  |  |  |  |  |  | variable C is set to a true value (calling the L or | 
| 646 |  |  |  |  |  |  | L commands with C<--autopin> sets this for you, because you already | 
| 647 |  |  |  |  |  |  | specified you wanted that). | 
| 648 |  |  |  |  |  |  |  | 
| 649 |  |  |  |  |  |  | =head2 uploads | 
| 650 |  |  |  |  |  |  |  | 
| 651 |  |  |  |  |  |  | To enable the /upload endpoint, set the ENV var OPAN_AUTH_TOKENS to a colon | 
| 652 |  |  |  |  |  |  | separated list of accepted tokens for uploads. This will allow a post with a | 
| 653 |  |  |  |  |  |  | 'file' upload argument, checking http basic auth password against the provided | 
| 654 |  |  |  |  |  |  | auth tokens. | 
| 655 |  |  |  |  |  |  |  | 
| 656 |  |  |  |  |  |  | =head2 recurring pull | 
| 657 |  |  |  |  |  |  |  | 
| 658 |  |  |  |  |  |  | Set ENV OPAN_RECURRING_PULL to a true value to make opan automatically pull | 
| 659 |  |  |  |  |  |  | from upstream every 600 seconds | 
| 660 |  |  |  |  |  |  |  | 
| 661 |  |  |  |  |  |  | =head2 custom upstream | 
| 662 |  |  |  |  |  |  |  | 
| 663 |  |  |  |  |  |  | Set the ENV var OPAN_MIRROR to specify a cpan mirror - the default is | 
| 664 |  |  |  |  |  |  | www.cpan.org. Remember that if you need to temporarily overlay your overlay | 
| 665 |  |  |  |  |  |  | but only for one user, there's nothing stopping you setting OPAN_MIRROR to | 
| 666 |  |  |  |  |  |  | another opan. | 
| 667 |  |  |  |  |  |  |  | 
| 668 |  |  |  |  |  |  | =head1 AUTHOR | 
| 669 |  |  |  |  |  |  |  | 
| 670 |  |  |  |  |  |  | Matt S. Trout (mst) | 
| 671 |  |  |  |  |  |  |  | 
| 672 |  |  |  |  |  |  | =head1 CONTRIBUTORS | 
| 673 |  |  |  |  |  |  |  | 
| 674 |  |  |  |  |  |  | Aaron Crane (arc) | 
| 675 |  |  |  |  |  |  |  | 
| 676 |  |  |  |  |  |  | Marcus Ramburg (marcus) | 
| 677 |  |  |  |  |  |  |  | 
| 678 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | Copyright (c) 2016-2018 the L L and L | 
| 681 |  |  |  |  |  |  | as listed above. | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | =head1 LICENSE | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | This library is free software and may be distributed under the same terms | 
| 686 |  |  |  |  |  |  | as perl itself. | 
| 687 |  |  |  |  |  |  |  | 
| 688 |  |  |  |  |  |  | =cut |