line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/env perl |
2
|
10
|
|
|
10
|
|
9419
|
use 5.010001; |
|
10
|
|
|
|
|
37
|
|
3
|
10
|
|
|
10
|
|
56
|
use warnings; |
|
10
|
|
|
|
|
17
|
|
|
10
|
|
|
|
|
546
|
|
4
|
10
|
|
|
10
|
|
48
|
use strict; |
|
10
|
|
|
|
|
18
|
|
|
10
|
|
|
|
|
258
|
|
5
|
10
|
|
|
10
|
|
8898
|
use utf8; |
|
10
|
|
|
|
|
134
|
|
|
10
|
|
|
|
|
91
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = 'v2.3.6'; |
8
|
|
|
|
|
|
|
|
9
|
10
|
|
|
10
|
|
549
|
use FindBin; |
|
10
|
|
|
|
|
18
|
|
|
10
|
|
|
|
|
926
|
|
10
|
10
|
|
|
10
|
|
5887
|
use lib "$FindBin::Bin/../lib/perl5"; |
|
10
|
|
|
|
|
7901
|
|
|
10
|
|
|
|
|
85
|
|
11
|
10
|
|
|
10
|
|
1660
|
use Path::Tiny; |
|
10
|
|
|
|
|
17
|
|
|
10
|
|
|
|
|
631
|
|
12
|
10
|
|
|
10
|
|
4672
|
use Narada; |
|
10
|
|
|
|
|
25
|
|
|
10
|
|
|
|
|
404
|
|
13
|
10
|
|
|
10
|
|
4889
|
use Narada::Lock qw( exclusive_lock unlock_new shared_lock unlock ); |
|
10
|
|
|
|
|
43
|
|
|
10
|
|
|
|
|
65
|
|
14
|
10
|
|
|
10
|
|
36749
|
use App::migrate; |
|
10
|
|
|
|
|
48427
|
|
|
10
|
|
|
|
|
639
|
|
15
|
10
|
|
|
10
|
|
9572
|
use Getopt::Long qw( GetOptionsFromArray ); |
|
10
|
|
|
|
|
107028
|
|
|
10
|
|
|
|
|
72
|
|
16
|
|
|
|
|
|
|
|
17
|
10
|
|
|
10
|
|
2445
|
use constant INITIAL_VERSION => '0.0.0'; |
|
10
|
|
|
|
|
20
|
|
|
10
|
|
|
|
|
900
|
|
18
|
10
|
|
|
10
|
|
53
|
use constant FULL_BACKUP => path('.backup/full.tar'); |
|
10
|
|
|
|
|
18
|
|
|
10
|
|
|
|
|
115
|
|
19
|
10
|
|
|
10
|
|
1307
|
use constant USAGE => <<'EOUSAGE'; |
|
10
|
|
|
|
|
21
|
|
|
10
|
|
|
|
|
16669
|
|
20
|
|
|
|
|
|
|
Usage: |
21
|
|
|
|
|
|
|
narada-install [--allow-downgrade|-D] [--allow-restore|-R] [-f ] |
22
|
|
|
|
|
|
|
narada-install [-f ] --path|-p ... |
23
|
|
|
|
|
|
|
narada-install --check|-c |
24
|
|
|
|
|
|
|
narada-install --help|-h |
25
|
|
|
|
|
|
|
EOUSAGE |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
my $Last_backup; # OPTIMIZATION delay copying last backup to drop exclusive_lock faster |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
main(@ARGV) if !caller; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
33
|
9
|
|
|
9
|
|
352
|
sub err { die "narada-install: @_\n" }; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub main { |
36
|
|
|
|
|
|
|
## no critic (RequireCarping) |
37
|
62
|
|
|
62
|
|
372449
|
my ($allow_downgrade, $allow_restore, @files) = (0, 0); |
38
|
62
|
|
|
|
|
123
|
my ($is_path, $check, $is_help); |
39
|
0
|
|
|
|
|
0
|
my ($prev_version, $next_version, $path); |
40
|
62
|
100
|
|
|
|
519
|
GetOptionsFromArray(\@_, |
41
|
|
|
|
|
|
|
'D|allow-downgrade' => \$allow_downgrade, |
42
|
|
|
|
|
|
|
'R|allow-restore' => \$allow_restore, |
43
|
|
|
|
|
|
|
'f=s@' => \@files, |
44
|
|
|
|
|
|
|
'p|path' => \$is_path, |
45
|
|
|
|
|
|
|
'c|check=s' => \$check, |
46
|
|
|
|
|
|
|
'h|help' => \$is_help, |
47
|
|
|
|
|
|
|
) or die USAGE; |
48
|
61
|
100
|
|
|
|
43549
|
if ($is_help) { |
49
|
4
|
|
|
|
|
251
|
print USAGE; |
50
|
4
|
|
|
|
|
65
|
return; |
51
|
|
|
|
|
|
|
} |
52
|
57
|
100
|
|
|
|
204
|
if (defined $check) { |
53
|
12
|
100
|
100
|
|
|
260
|
die USAGE if @_ || $is_path || $allow_downgrade || $allow_restore || @files; |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
54
|
7
|
|
|
|
|
378
|
say "Checking $check"; |
55
|
7
|
|
|
|
|
67
|
App::migrate->new->load($check); |
56
|
2
|
|
|
|
|
18171
|
return; |
57
|
|
|
|
|
|
|
} |
58
|
45
|
100
|
|
|
|
137
|
if ($is_path) { |
59
|
7
|
100
|
|
|
|
52
|
die USAGE if @_ <= 2; |
60
|
3
|
|
|
|
|
14
|
$path = [@_]; |
61
|
3
|
|
|
|
|
9
|
$prev_version = $path->[0]; |
62
|
3
|
|
|
|
|
7
|
$next_version = $path->[-1]; |
63
|
3
|
|
|
|
|
6
|
$allow_downgrade= 1; |
64
|
3
|
|
|
|
|
6
|
$allow_restore = 1; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
else { |
67
|
38
|
100
|
|
|
|
213
|
die USAGE if @_ != 1; |
68
|
34
|
|
|
|
|
63
|
$next_version = $_[0]; |
69
|
34
|
|
|
|
|
87
|
$prev_version = INITIAL_VERSION; |
70
|
34
|
100
|
|
|
|
165
|
if (path(q{.})->children(qr/\A(?![.]release\z|[.]backup\z|[.]lock)/ms)) { |
71
|
23
|
|
|
|
|
6703
|
Narada::detect('narada'); |
72
|
23
|
|
|
|
|
100
|
($prev_version) = path('VERSION')->lines({chomp=>1}); |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
} |
75
|
37
|
100
|
|
|
|
7753
|
if ($next_version eq $prev_version) { |
76
|
5
|
|
|
|
|
39
|
return; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
32
|
|
|
|
|
173
|
my $migrate = load($prev_version, $next_version, @files); |
80
|
32
|
|
66
|
|
|
477
|
$path ||= get_path($migrate, $prev_version, $next_version); |
81
|
32
|
|
|
|
|
159
|
check_path($migrate, $path, $allow_downgrade, $allow_restore); |
82
|
31
|
|
|
|
|
160
|
migrate($migrate, $path); |
83
|
|
|
|
|
|
|
|
84
|
25
|
|
|
|
|
5669
|
return; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub load { |
88
|
29
|
|
|
29
|
|
113
|
my ($prev_version, $next_version, @files) = @_; |
89
|
29
|
|
|
|
|
458
|
my $migrate = App::migrate->new; |
90
|
29
|
100
|
|
|
|
617
|
if ($next_version ne INITIAL_VERSION) { |
91
|
14
|
|
|
|
|
78
|
push @files, ".release/$next_version.migrate"; |
92
|
|
|
|
|
|
|
} |
93
|
29
|
100
|
|
|
|
518
|
if (-f ".release/$prev_version.migrate") { |
94
|
20
|
|
|
|
|
77
|
push @files, ".release/$prev_version.migrate"; |
95
|
|
|
|
|
|
|
} |
96
|
29
|
|
|
|
|
93
|
for (@files) { |
97
|
51
|
|
|
|
|
295768
|
say "Loading $_"; |
98
|
51
|
|
|
|
|
384
|
$migrate->load($_); |
99
|
|
|
|
|
|
|
} |
100
|
29
|
|
|
|
|
211347
|
return $migrate; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub get_path { |
104
|
33
|
|
|
33
|
|
1620
|
my ($migrate, $prev_version, $next_version) = @_; |
105
|
33
|
|
|
|
|
204
|
my @paths = $migrate->find_paths($prev_version, $next_version); |
106
|
33
|
100
|
|
|
|
2728
|
if (0 == @paths) { |
|
|
100
|
|
|
|
|
|
107
|
1
|
|
|
|
|
7
|
err "Unable to find migration path from $prev_version to $next_version"; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
elsif (1 != @paths) { |
110
|
|
|
|
|
|
|
err join "\n", |
111
|
|
|
|
|
|
|
'Found more than one upgrade path, run one of these commands to choose a path:', |
112
|
1
|
|
|
|
|
3
|
map {"\tnarada-install --path @{$_}"} @paths; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
10
|
|
113
|
|
|
|
|
|
|
} |
114
|
31
|
|
|
|
|
191
|
return $paths[0]; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub check_path { |
118
|
39
|
|
|
39
|
|
556
|
my ($migrate, $path, $allow_downgrade, $allow_restore) = @_; |
119
|
39
|
100
|
|
|
|
152
|
if ($allow_restore) { |
120
|
15
|
|
|
|
|
37
|
$allow_downgrade = 1; |
121
|
|
|
|
|
|
|
} |
122
|
39
|
|
|
|
|
190
|
for my $step ($migrate->get_steps($path)) { |
123
|
759
|
|
|
|
|
2681
|
my $t = $step->{type}; |
124
|
759
|
|
100
|
|
|
1970
|
my $is_down = $t eq 'downgrade' || $t eq 'after_downgrade'; |
125
|
759
|
100
|
100
|
|
|
2412
|
err 'Downgrade required, use --allow-downgrade to continue' |
126
|
|
|
|
|
|
|
if $is_down && !$allow_downgrade; |
127
|
757
|
100
|
100
|
|
|
1625
|
err 'Restore from backup required, use --allow-restore to continue' |
128
|
|
|
|
|
|
|
if $t eq 'RESTORE' && !$allow_restore; |
129
|
754
|
100
|
|
|
|
1491
|
if ($t eq 'RESTORE') { |
130
|
7
|
|
|
|
|
114
|
my $f = path(".backup/full-$step->{next_version}.tar"); |
131
|
7
|
100
|
|
|
|
437
|
err "Required backup not found: $f" if !$f->is_file; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
} |
134
|
31
|
|
|
|
|
102
|
return; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub migrate { |
138
|
28
|
|
|
28
|
|
65
|
my ($migrate, $path) = @_; |
139
|
|
|
|
|
|
|
|
140
|
28
|
|
|
|
|
653
|
say 'Acquire exclusive lock ...'; |
141
|
28
|
|
|
|
|
256
|
exclusive_lock(); |
142
|
|
|
|
|
|
|
{ |
143
|
28
|
|
|
|
|
196
|
local $ENV{NARADA_SKIP_LOCK} = 1; |
|
28
|
|
|
|
|
1074
|
|
144
|
28
|
|
|
|
|
171
|
$Last_backup = undef; # tests may call main() many times |
145
|
28
|
|
|
|
|
601
|
$migrate->on(BACKUP => \&_backup) |
146
|
|
|
|
|
|
|
->on(RESTORE => \&_restore) |
147
|
|
|
|
|
|
|
->on(VERSION => \&_version) |
148
|
|
|
|
|
|
|
->on(error => \&_error) |
149
|
|
|
|
|
|
|
->run($path); |
150
|
|
|
|
|
|
|
} |
151
|
22
|
|
|
|
|
1218
|
unlock_new(); |
152
|
|
|
|
|
|
|
|
153
|
22
|
|
|
|
|
182
|
shared_lock(); |
154
|
22
|
50
|
33
|
|
|
348
|
if ($Last_backup && !FULL_BACKUP->exists) { |
155
|
22
|
|
|
|
|
642
|
path($Last_backup)->copy(FULL_BACKUP); |
156
|
|
|
|
|
|
|
} |
157
|
22
|
|
|
|
|
18150
|
unlock(); |
158
|
|
|
|
|
|
|
|
159
|
22
|
|
|
|
|
94
|
return; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub ask { |
163
|
0
|
|
|
0
|
|
|
my ($msg, $match) = @_; |
164
|
0
|
|
|
|
|
|
print $msg; |
165
|
|
|
|
|
|
|
# TODO try IO::Prompter |
166
|
|
|
|
|
|
|
## no critic (ProhibitExplicitStdin) |
167
|
0
|
|
|
|
|
|
while () { |
168
|
0
|
|
|
|
|
|
chomp; |
169
|
0
|
0
|
|
|
|
|
return $_ if /$match/ms; |
170
|
0
|
|
|
|
|
|
print $msg; |
171
|
|
|
|
|
|
|
} |
172
|
0
|
|
|
|
|
|
die "Interrupted by EOF\n"; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub _backup { |
176
|
76
|
|
|
76
|
|
9485
|
my ($step) = @_; |
177
|
76
|
100
|
|
|
|
601
|
if ($step->{version} ne INITIAL_VERSION) { |
178
|
66
|
100
|
|
|
|
360
|
if ($Last_backup) { |
179
|
37
|
|
|
|
|
269
|
path($Last_backup)->copy(FULL_BACKUP); |
180
|
|
|
|
|
|
|
} |
181
|
66
|
|
|
|
|
280213
|
my $file = ".backup/full-$step->{version}.tar"; |
182
|
66
|
|
|
|
|
1838
|
say "Backuping to $file"; |
183
|
66
|
100
|
|
|
|
52648286
|
system('narada-backup') == 0 or die "BACKUP failed\n"; |
184
|
62
|
|
|
|
|
875
|
$Last_backup = $file; |
185
|
62
|
|
|
|
|
3527
|
FULL_BACKUP->move($Last_backup); |
186
|
|
|
|
|
|
|
} |
187
|
72
|
|
|
|
|
88639
|
return; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub _restore { |
191
|
9
|
|
|
9
|
|
1220
|
my ($step) = @_; |
192
|
9
|
|
|
|
|
89
|
my $file = ".backup/full-$step->{version}.tar"; |
193
|
9
|
|
|
|
|
277
|
say "Restoring from $file"; |
194
|
9
|
100
|
|
|
|
2957588
|
system('narada-restore', $file) == 0 or die "RESTORE failed\n"; |
195
|
6
|
|
|
|
|
247
|
return; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub _version { |
199
|
72
|
|
|
72
|
|
7102796
|
my ($step) = @_; |
200
|
72
|
|
|
|
|
2560
|
say "Migration to $step->{version} completed"; |
201
|
72
|
100
|
|
|
|
763
|
if ($step->{version} eq INITIAL_VERSION) { |
202
|
13
|
|
|
|
|
790
|
path('VERSION')->remove; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
else { |
205
|
59
|
|
|
|
|
1082
|
path('VERSION')->spew_utf8("$step->{version}\n"); |
206
|
|
|
|
|
|
|
} |
207
|
72
|
|
|
|
|
201364
|
return; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub _error { |
211
|
10
|
|
|
10
|
|
21885
|
say q{}; |
212
|
10
|
|
|
|
|
114
|
say 'YOU NEED TO MANUALLY FIX THIS ISSUE RIGHT NOW'; |
213
|
10
|
|
|
|
|
559
|
my $prompt |
214
|
|
|
|
|
|
|
= "Please choose what to do:\n" |
215
|
|
|
|
|
|
|
. " shell - run $ENV{SHELL} (exit from it to return to this menu)\n" |
216
|
|
|
|
|
|
|
. " continue - continue migration (use if you have fixed this issue)\n" |
217
|
|
|
|
|
|
|
. " restore - interrupt migration and RESTORE previous version from backup\n" |
218
|
|
|
|
|
|
|
. 'Enter action [shell]: ' |
219
|
|
|
|
|
|
|
; |
220
|
10
|
|
|
|
|
61
|
while (1) { |
221
|
10
|
|
|
|
|
292
|
my $ans = lc ask($prompt, qr/\A(?:shell|continue|restore|\s*)\z/msi); |
222
|
10
|
100
|
|
|
|
326
|
if ($ans eq 'restore') { |
|
|
50
|
|
|
|
|
|
223
|
6
|
|
|
|
|
205
|
die "Migration failed\n"; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
elsif ($ans eq 'continue') { |
226
|
4
|
|
|
|
|
35
|
last; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
else { |
229
|
0
|
|
|
|
|
0
|
system $ENV{SHELL}; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
} |
232
|
4
|
|
|
|
|
40
|
return; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
1; # Magic true value required at end of module |
237
|
|
|
|
|
|
|
__END__ |