File Coverage

blib/lib/App/Rakubrew/Update.pm
Criterion Covered Total %
statement 47 144 32.6
branch 0 38 0.0
condition n/a
subroutine 16 19 84.2
pod 0 2 0.0
total 63 203 31.0


line stmt bran cond sub pod time code
1             package App::Rakubrew::Update;
2             require Exporter;
3             our @ISA = qw(Exporter);
4             our @EXPORT = qw();
5              
6 2     2   12 use strict;
  2         3  
  2         60  
7 2     2   8 use warnings;
  2         4  
  2         36  
8 2     2   25 use 5.010;
  2         6  
9 2     2   11 use HTTP::Tinyish;
  2         3  
  2         33  
10 2     2   8 use JSON;
  2         3  
  2         20  
11 2     2   265 use FindBin qw( $RealBin $RealScript );
  2         3  
  2         159  
12 2     2   12 use File::Copy;
  2         4  
  2         81  
13 2     2   10 use File::Spec::Functions qw( catfile catdir );
  2         3  
  2         84  
14 2     2   11 use Fcntl;
  2         3  
  2         455  
15 2     2   1004 use if scalar ($^O =~ /win32/i), 'Win32';
  2         22  
  2         12  
16 2     2   97 use if scalar ($^O =~ /win32/i), 'Win32::Process';
  2         4  
  2         8  
17 2     2   75 use if scalar ($^O =~ /win32/i), 'Win32::ShellQuote';
  2         4  
  2         9  
18              
19 2     2   49 use App::Rakubrew;
  2         4  
  2         35  
20 2     2   9 use App::Rakubrew::Variables;
  2         4  
  2         221  
21 2     2   11 use App::Rakubrew::Config;
  2         4  
  2         122  
22 2     2   10 use App::Rakubrew::Tools;
  2         3  
  2         2085  
23              
24             my $release_index_url = 'https://rakubrew.org/releases';
25             my $download_url_prefix = 'https://rakubrew.org';
26              
27             my %dl_urls = (
28             fatpack => "$download_url_prefix/perl/rakubrew",
29             win => "$download_url_prefix/win/rakubrew.exe",
30             macos => "$download_url_prefix/macos/rakubrew",
31             );
32              
33             sub update {
34 0     0 0   my $quiet = shift;
35              
36             # For par packaged executables the following returns the path and name of
37             # the par packaged file.
38 0           my $current_rakubrew_file = catfile($RealBin, $RealScript);
39              
40             # check whether this is a CPAN installation. Abort if yes.
41 0 0         if ($distro_format eq 'cpan') {
42 0           say STDERR 'Rakubrew was installed via CPAN, use your CPAN client to update.';
43 0           exit 1;
44             }
45              
46 0           my $ht = HTTP::Tinyish->new();
47 0           my $release_index = _download_release_index($ht);
48              
49             # check version
50 0 0         if ($App::Rakubrew::VERSION >= $release_index->{latest}) {
51 0           say 'Rakubrew is up-to-date!';
52 0           exit 0;
53             }
54              
55             # Display changes
56 0 0         if (!$quiet) {
57 0           say "Changes\n";
58 0           say "=======\n";
59 0           for my $change (@{$release_index->{releases}}) {
  0            
60 0 0         next if $change->{version} <= $App::Rakubrew::VERSION;
61 0           say $change->{version} . ':';
62 0           say " $_" for split(/^/, $change->{changes});
63 0           say '';
64             }
65 0           print 'Shall we do the update? [y|N] ';
66 0           my $reply = ;
67 0           chomp $reply;
68 0 0         exit 0 if $reply ne 'y';
69 0           say '';
70             }
71              
72 0 0         mkdir catdir($prefix, 'update') unless (-d catdir($prefix, 'update'));
73 0           my $update_file = catfile($prefix, 'update', $RealScript);
74              
75             # delete RAKUBREW_HOME/update/rakubrew
76 0           unlink $update_file;
77              
78             # download latest to RAKUBREW_HOME/update/rakubrew
79 0           my $res = $ht->get($dl_urls{$distro_format});
80 0 0         unless ($res->{success}) {
81 0           say STDERR "Couldn\'t download update. Error: $res->{status} $res->{reason}";
82 0           exit 1;
83             }
84 0           my $fh;
85 0 0         if (!sysopen($fh, $update_file, O_WRONLY|O_CREAT|O_EXCL, 0777)) {
86 0           say STDERR "Couldn't write update file to $update_file. Aborting update.";
87 0           exit 1;
88             }
89 0           binmode $fh;
90 0           print $fh $res->{content};
91 0           close $fh;
92              
93 0 0         if ($^O =~ /win32/i) {
94             # Windows has no real exec(). In addition all the standard perl
95             # utilities to start processes automatically make the started process
96             # inherit all handles of the parent. This has the effect that it's
97             # impossible in the child to delete the parents executable file even
98             # when the parent has already exited. So we use the lower level
99             # Win32::Process::Create with the 4th argument (inheritHandles) set to 0
100             # to get rid of the handles preventing the deletion of the parent
101             # executable.
102              
103 0           say 'You will now see a command prompt, even though the update process is still running.';
104 0           say 'This is caused by a quirk in Windows\' process handling.';
105 0           say 'Just wait a few seconds until an "Update successful!" message shows up';
106 0           my $ProcessObj;
107 0 0         if (!Win32::Process::Create(
108             $ProcessObj,
109             $update_file,
110             Win32::ShellQuote::quote_native(
111             $update_file,
112             'internal_update',
113             $App::Rakubrew::VERSION,
114             $current_rakubrew_file),
115             0,
116             Win32::Process::NORMAL_PRIORITY_CLASS(),
117             "."
118             )) {
119 0           say STDERR 'Failed to call the downloaded rakubrew executable! Aborting update.';
120 0           exit 1;
121             };
122 0           exit 0;
123             }
124             else {
125 0           { exec($update_file, 'internal_update', $App::Rakubrew::VERSION, $current_rakubrew_file) };
  0            
126 0           say STDERR 'Failed to call the downloaded rakubrew executable! Aborting update.';
127 0           exit 1;
128             }
129             }
130              
131             sub internal_update {
132 0     0 0   my ($old_version, $old_rakubrew_file) = @_;
133              
134 0           my $current_script = catfile($RealBin, $RealScript);
135 0           my $update_file = catfile($prefix, 'update', $RealScript);
136 0 0         if ($update_file ne $current_script) {
137 0           say STDERR "'internal_update' was called on a rakubrew ($current_script) that's not $update_file. That's probably wrong and dangerous. Aborting update.";
138 0           exit 1;
139             }
140              
141             # custom update procedures
142 0 0         if ($old_version < 29) {
143             # Change Github URLs to use the https instead of the git protocol.
144 0           my @repos;
145              
146 0           for my $dir ($git_reference, $versions_dir) {
147 0           opendir(my $dh, $dir);
148 0           push @repos, map({ catdir($dir, $_) } grep({ /^[^.]/ } readdir($dh)));
  0            
  0            
149 0           closedir($dh);
150             }
151 0           push @repos, $zef_dir;
152              
153 0           for my $repo (@repos) {
154 0 0         $repo = catdir($repo, '.git') if -d catdir($repo, '.git');
155 0           my $config_file = catfile($repo, 'config');
156 0 0         if (-f $config_file) {
157 0           print "Updating Github repository URLs in $config_file...";
158 0           my $content = slurp($config_file);
159 0           my $replaced = ($content =~ s|^(\s* url \s* = \s*) git (://github\.com/)|$1https$2|gmx);
160 0 0         if ($replaced) {
161 0           spurt($config_file, $content);
162 0           say "done";
163             }
164             else {
165 0           say "nothing to be done";
166             }
167             }
168             }
169             }
170             #if ($old_version < 2) {
171             # Do update stuff for version 2.
172             #}
173              
174             # copy RAKUBREW_HOME/update/rakubrew to 'path/to/rakubrew'
175 0 0         unlink $old_rakubrew_file or die "Failed to unlink old file: $old_rakubrew_file. Error: $!";
176 0           my $fh;
177 0 0         if (!sysopen($fh, $old_rakubrew_file, O_WRONLY|O_CREAT|O_EXCL, 0777)) {
178 0           say STDERR "Couldn't copy update file to $old_rakubrew_file. Rakubrew is broken now. Try manually copying '$update_file' to '$old_rakubrew_file' to get it fixed again.";
179 0           exit 1;
180             }
181 0           binmode $fh;
182 0 0         if (!copy($update_file, $fh)) {
183 0           close $fh;
184 0           unlink $old_rakubrew_file;
185 0           say STDERR "Couldn't copy update file to $old_rakubrew_file. Rakubrew is broken now. Try manually copying '$update_file' to '$old_rakubrew_file' to get it fixed again.";
186 0           exit 1;
187             }
188 0           close $fh;
189 0           unlink $update_file;
190              
191 0           say 'Update successful!';
192             }
193              
194             sub _download_release_index {
195 0     0     my $ht = shift;
196 0           my $res = $ht->get($release_index_url);
197 0 0         unless ($res->{success}) {
198 0           say STDERR "Couldn\'t fetch release index at $release_index_url. Error: $res->{status} $res->{reason}";
199             ;
200 0           exit 1;
201             }
202 0           return decode_json($res->{content});
203             }
204