2 # vim:ts=4:shiftwidth=4:expandtab
12 App::RetroPAN - Makes a historic minicpan ⏳
18 my ($author, $dist_name, $url) = find_module_on_date("2011-01-01T00:00:00", "Moose");
22 Uses the MetaCPAN API to find releases made prior to a given date to
23 satisfy your modules' dependencies.
41 This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
45 Dave Lambley <dlambley@cpan.org>
51 use List
::Util qw
/ uniq /;
53 use OrePAN2
::Injector
;
56 use Cpanel
::JSON
::XS qw
/ encode_json decode_json /;
58 our $VERSION = '0.01_02';
60 my $ua = LWP
::UserAgent
->new( keep_alive
=> 2, agent
=> "retropan/$VERSION" );
62 sub find_module_dependencies
{
86 my $req = HTTP
::Request
->new( POST
=> 'https://fastapi.metacpan.org/v1/release/_search', [
87 "Content-Type" => "text/json",
88 "Accept" => "text/json"
91 my $res = $ua->request($req);
92 die $res->status_line if !$res->is_success;
93 my $data = decode_json
($res->decoded_content);
94 my $hit = $data->{hits
}->{hits
}->[0];
96 warn "could not find $au/$dist";
101 grep { !Module
::CoreList
::is_core
($_) }
102 grep { $_ ne "perl" }
103 map { $_->{module
} } @
{ $hit->{_source
}->{dependency
} };
107 sub find_module_on_date
{
108 my ($module, $before) = @_;
110 return if Module
::CoreList
::is_core
($module);
112 # We prefer authorized modules, but can fall back to unauthorized if none
115 "size" => 30, # TODO, keep search open.
117 { "module.authorized" => "desc" },
118 { "version_numified" => "desc" },
126 "module.name" => $module,
131 "maturity" => "released",
135 "range" => { "date" => {"lt" => $before }}
142 my $req = HTTP
::Request
->new( POST
=> 'https://fastapi.metacpan.org/v1/module/_search', [
143 "Content-Type" => "text/json",
144 "Accept" => "text/json"
145 ], encode_json
($q) );
147 my $res = $ua->request($req);
148 die $res->status_line if !$res->is_success;
149 my $data = decode_json
($res->decoded_content);
158 # Some distributions re-release existing modules outside their own
159 # distribution, eg., perl-5.005-minimal-bin-0-arm-linux
160 # We therefore iterate through all modules returned to find the newest
162 foreach my $hit (@
{ $data->{hits
}->{hits
} }) {
163 foreach my $mod (@
{ $hit->{_source
}->{module
} }) {
164 if (($authorized ?
$mod->{authorized
} : 1) && $mod->{name
} eq $module && $mod->{version_numified
} > $version) {
165 $author = $hit->{_source
}->{author
};
166 $release = $hit->{_source
}->{release
};
167 $url = $hit->{_source
}->{download_url
};
168 $version = $mod->{version_numified
};
169 $authorized = $mod->{authorized
};
175 if (!defined $release) {
176 warn "could not find $module before $before";
180 return ($author, $release, $url);
183 sub find_deps_on_date
{
184 my ($before, @modules) = @_;
191 my $mod = pop @modules;
192 next if $done_modules{$mod};
194 my ($au, $dist, $url) = find_module_on_date
($mod, $before);
195 $done_modules{$mod} = 1;
196 next if !defined($au) || !defined($dist);
197 $dist_to_url{"$au/$dist"} = $url;
199 push @modules, find_module_dependencies
($au, $dist);
200 unshift @dists_required, "$au/$dist";
204 [uniq
@dists_required],
210 my ($localdir, $dists_required, $dist_to_url) = @_;
212 my $injector = OrePAN2
::Injector
->new(
213 directory
=> $localdir,
217 foreach my $d (@
{ $dists_required }) {
218 my ($author, $dist) = split(/\//, $d, 2);
220 $dist_to_url->{$d} // die,
228 my $orepan = OrePAN2
::Indexer
->new(
229 directory
=> $localdir,