File Coverage

blib/lib/App/Rakubrew/Download.pm
Criterion Covered Total %
statement 41 148 27.7
branch 0 68 0.0
condition 0 39 0.0
subroutine 14 22 63.6
pod 0 2 0.0
total 55 279 19.7


line stmt bran cond sub pod time code
1             package App::Rakubrew::Download;
2             require Exporter;
3             our @ISA = qw(Exporter);
4             our @EXPORT = qw();
5              
6 2     2   14 use strict;
  2         4  
  2         58  
7 2     2   10 use warnings;
  2         4  
  2         46  
8 2     2   31 use 5.010;
  2         7  
9 2     2   881 use HTTP::Tinyish;
  2         1982  
  2         55  
10 2     2   1232 use JSON;
  2         24073  
  2         10  
11 2     2   276 use Config;
  2         4  
  2         93  
12 2     2   10 use Cwd qw(cwd);
  2         4  
  2         92  
13 2     2   1271 use IO::Uncompress::Unzip qw( $UnzipError );
  2         94226  
  2         320  
14 2     2   16 use File::Path qw( make_path remove_tree );
  2         4  
  2         204  
15 2     2   1235 use File::Copy::Recursive qw( dirmove );
  2         13987  
  2         138  
16 2     2   15 use File::Spec::Functions qw( updir splitpath catfile catdir );
  2         4  
  2         116  
17 2     2   14 use App::Rakubrew::Variables;
  2         4  
  2         291  
18 2     2   13 use App::Rakubrew::Tools;
  2         4  
  2         137  
19 2     2   14 use App::Rakubrew::VersionHandling;
  2         4  
  2         3362  
20              
21             my $release_index_url = 'https://rakudo.org/dl/rakudo';
22             my $download_url_prefix = 'https://rakudo.org/dl/rakudo/';
23              
24             sub download_precomp_archive {
25 0     0 0   my ($impl, $ver) = @_;
26              
27 0           my $ht = HTTP::Tinyish->new();
28              
29             my @matching_releases = grep {
30 0 0         $_->{backend} eq $impl && ($ver ? $_->{ver} eq $ver : 1)
  0 0          
31             } _retrieve_releases($ht);
32              
33 0 0         if (!@matching_releases) {
34 0 0         say STDERR 'Couldn\'t find a precomp release for OS: "' . _my_platform() . '", architecture: "' . _my_arch() . '"' . ($ver ? (', version: "' . $ver . '"') : '');
35 0           say STDERR 'You can try building yourself. Use the `rakubrew build` command to do so.';
36 0           exit 1;
37             }
38 0 0 0       if ($ver && @matching_releases > 1) {
39 0           say STDERR 'Multiple releases found for your architecture. Don\'t know what to install. This shouldn\'t happen.';
40 0           exit 1;
41             }
42              
43 0 0         if (!$ver) {
44 0           $ver = $matching_releases[0]->{ver};
45             }
46              
47 0           my $name = "$impl-$ver";
48              
49 0           chdir $versions_dir;
50 0 0         if (-d $name) {
51 0           say STDERR "$name is already installed.";
52 0           exit 1;
53             }
54              
55 0           say 'Downloading ' . $matching_releases[0]->{url};
56 0           my $res = $ht->get($matching_releases[0]->{url});
57 0 0         unless ($res->{success}) {
58 0           say STDERR "Couldn\'t download release. Error: $res->{status} $res->{reason}";
59 0           exit 1;
60             }
61              
62 0           mkdir $name;
63 0           say 'Extracting';
64 0 0         if (_my_platform() eq 'win') {
65 0           _unzip(\($res->{content}), $name);
66             }
67             else {
68 0           _untar($res->{content}, $name);
69             }
70              
71             # Remove top-level rakudo-2020.01 folder and move all files one level up.
72 0           my $back = cwd();
73 0           chdir $name;
74 0           my $rakudo_dir;
75 0 0         opendir(DIR, '.') || die "Can't open directory: $!\n";
76 0           while (my $file = readdir(DIR)) {
77 0 0 0       if (-d $file && $file =~ /^rakudo-/) {
78 0           $rakudo_dir = $file;
79 0           last;
80             }
81             }
82 0           closedir(DIR);
83 0 0         unless ($rakudo_dir) {
84 0           say STDERR "Archive didn't look as expected, aborting. Extracted to: $name";
85 0           exit 1;
86             }
87 0           dirmove($rakudo_dir, '.');
88 0           rmdir($rakudo_dir);
89 0           chdir $back;
90              
91 0           return $name;
92             }
93              
94             sub available_precomp_archives {
95 0     0 0   return _retrieve_releases(HTTP::Tinyish->new());
96             }
97              
98             sub _retrieve_releases {
99 0     0     my $ht = shift;
100 0           my $release_index = _download_release_index($ht);
101             my @matching_releases =
102 0           sort { $b->{ver} cmp $a->{ver} }
103             grep {
104 0           $_->{name} eq 'rakudo'
105             && $_->{type} eq 'archive'
106             && $_->{platform} eq _my_platform()
107             && $_->{arch} eq _my_arch()
108 0 0 0       && $_->{format} eq (_my_platform() eq 'win' ? 'zip' : 'tar.gz')
    0 0        
      0        
109             } @$release_index;
110              
111             # Filter out older build revisions
112             @matching_releases = grep {
113 0           my $this = $_;
  0            
114             not grep {
115 0           +($_->{build_rev}) > +($this->{build_rev})
116             && $_->{name} eq $this->{name}
117             && $_->{type} eq $this->{type}
118             && $_->{platform} eq $this->{platform}
119             && $_->{arch} eq $this->{arch}
120             && $_->{format} eq $this->{format}
121 0 0 0       && $_->{ver} eq $this->{ver};
      0        
      0        
      0        
      0        
122             } @matching_releases;
123             } @matching_releases;
124              
125 0           return @matching_releases;
126             }
127              
128             sub _my_platform {
129 0     0     my %oses = (
130             MSWin32 => 'win',
131             darwin => 'macos',
132             linux => 'linux',
133             openbsd => 'openbsd',
134             );
135 0   0       return $oses{$^O} // $^O;
136             }
137              
138             sub _my_arch {
139             my $arch =
140             $Config{archname} =~ /x64/i ? 'x86_64' :
141             $Config{archname} =~ /x86_64/i ? 'x86_64' :
142             $Config{archname} =~ /amd64/i ? 'x86_64' :
143             $Config{archname} =~ /x86/i ? 'x86' :
144             $Config{archname} =~ /i686/i ? 'x86' :
145             $Config{archname} =~ /darwin/i && `sysctl -n machdep.cpu.brand_string` =~ /Apple/i ? 'arm64' : # MacOS M1 / Apple Silicon
146             $Config{archname} =~ /darwin/i && `sysctl -n machdep.cpu.brand_string` =~ /Intel/i ? 'x86_64' : # MacOS Intel
147             $Config{archname} =~ /aarch64/i ? 'arm64' : # e.g. Raspi >= 2.1 with 64bit OS
148             $Config{archname} =~ /arm-linux-gnueabihf/i ? 'armhf' : # e.g. Raspi >= 2, with 32bit OS
149 0 0 0 0     $Config{archname} =~ /s390x-linux/i ? 's390x' :
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
150             '';
151              
152 0 0         unless ($arch) {
153 0           say STDERR 'Couldn\'t detect system architecture. Current arch is: ' . $Config{archname};
154 0           say STDERR 'Current uname -a is: ' . `uname -a`;
155 0           exit 1;
156             }
157 0           return $arch;
158             }
159              
160             sub _download_release_index {
161 0     0     my $ht = shift;
162 0           my $res = $ht->get($release_index_url);
163 0 0         unless ($res->{success}) {
164 0           say STDERR "Couldn\'t fetch release index at $release_index_url. Error: $res->{status} $res->{reason}";
165 0           exit 1;
166             }
167 0           return decode_json($res->{content});
168             }
169              
170             sub _untar {
171 0     0     my ($data, $target) = @_;
172 0           my $back = cwd();
173 0           chdir $target;
174 0           open (TAR, '| tar -xz');
175 0           binmode(TAR);
176 0           print TAR $data;
177 0           close TAR;
178 0           chdir $back;
179             }
180              
181             sub _unzip {
182 0     0     my ($data_ref, $target) = @_;
183              
184 0           my $zip = IO::Uncompress::Unzip->new($data_ref);
185 0 0         unless ($zip) {
186 0           say STDERR "Reading zip file failed. Error: $UnzipError";
187 0           exit 1;
188             }
189              
190 0           my $status;
191 0           for ($status = 1; $status > 0; $status = $zip->nextStream()) {
192 0           my $header = $zip->getHeaderInfo();
193              
194 0           my ($vol, $path, $file) = splitpath($header->{Name});
195              
196 0 0         if (index($path, updir()) != -1) {
197 0           say STDERR 'Found updirs in zip file, this is bad. Aborting.';
198 0           exit 1;
199             }
200              
201 0           my $target_dir = catdir($target, $path);
202              
203 0 0         unless (-d $target_dir) {
204 0 0         unless (make_path($target_dir)) {
205 0           say STDERR "Failed to create directory $target_dir. Error: $!";
206 0           exit 1;
207             }
208             }
209              
210 0 0         next unless $file;
211              
212 0           my $target_file = catfile($target, $path, $file);
213              
214 0 0         unless (open(FH, '>', $target_file)) {
215 0           say STDERR "Failed to write $target_file. Error: $!";
216 0           exit 1;
217             }
218 0           binmode(FH);
219              
220 0           my $buf;
221 0           while (($status = $zip->read($buf)) > 0) {
222 0           print FH $buf;
223             }
224 0           close FH;
225             }
226              
227 0 0         if ($status < 0) {
228 0           say STDERR "Failed to extract archive. Error: $UnzipError";
229 0           exit 1;
230             }
231             }
232