File Coverage

lib/Mojolicious/Plugin/Fondation/Command/fondation.pm
Criterion Covered Total %
statement 79 164 48.1
branch 7 36 19.4
condition 10 23 43.4
subroutine 9 16 56.2
pod 1 1 100.0
total 106 240 44.1


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Fondation::Command::fondation;
2              
3             # ABSTRACT: Fondation orchestration commands -- init, upgrade, refresh
4              
5 1     1   173010 use Mojo::Base 'Mojolicious::Command', -signatures;
  1         1  
  1         11  
6              
7 1     1   1926 use utf8;
  1         2  
  1         4  
8 1     1   25 use Mojo::File 'path';
  1         1  
  1         61  
9 1     1   4 use File::Path qw(remove_tree);
  1         2  
  1         1914  
10              
11             our $VERSION = '0.01';
12              
13             has description => 'Orchestrate Fondation plugins: init, upgrade, refresh';
14             has usage => sub ($self) {
15             <<"USAGE";
16             Usage: APPLICATION fondation COMMAND [OPTIONS]
17              
18             myapp.pl db bootstrap-schema Create the schema class (run once)
19             myapp.pl fondation plan init Preview init steps (dry-run)
20             myapp.pl fondation plan upgrade Preview upgrade steps (dry-run)
21             myapp.pl fondation plan refresh Preview refresh steps (dry-run)
22             myapp.pl fondation init First-time setup for all plugins
23             myapp.pl fondation upgrade Detect drift, upgrade, regenerate
24             myapp.pl fondation refresh Clean all generated artifacts and regenerate
25              
26             USAGE
27             };
28              
29             # ---------------------------------------------------------------------------
30             # Entry point
31             # ---------------------------------------------------------------------------
32              
33 0     0 1 0 sub run ($self, @args) {
  0         0  
  0         0  
  0         0  
34 0         0 my $app = $self->app;
35 0   0     0 my $subcommand = shift @args || '';
36              
37 0 0       0 die $self->usage unless $subcommand;
38              
39 0         0 for ($subcommand) {
40 0 0 0     0 /^plan$/ and return $self->_run_plan($app, shift @args || '');
41 0 0       0 /^init$/ and return $self->_run_init($app);
42 0 0       0 /^upgrade$/ and return $self->_run_upgrade($app);
43 0 0       0 /^refresh$/ and return $self->_run_refresh($app);
44 0         0 die $self->usage;
45             }
46             }
47              
48             # ---------------------------------------------------------------------------
49             # Run a list of steps through $app->commands->run()
50             # ---------------------------------------------------------------------------
51              
52 2     2   3 sub _run_steps ($self, $app, $step_name, $long_name, $steps) {
  2         3  
  2         4  
  2         4  
  2         3  
  2         3  
  2         3  
53 2         5 for my $step (@$steps) {
54 4 50       29 my @cmd = ref $step eq 'ARRAY' ? @$step : ($step);
55 4         8 $app->commands->run(@cmd);
56             }
57             }
58              
59             # ---------------------------------------------------------------------------
60             # Collect steps from all plugins in load order
61             # ---------------------------------------------------------------------------
62              
63 5     5   115 sub _collect_steps ($self, $app, $key) {
  5         7  
  5         9  
  5         9  
  5         8  
64 5         8 my @steps;
65 5         7 for my $long (@{ $app->manager->load_order }) {
  5         22  
66 19         51 my $entry = $app->manager->registry->{$long};
67 19   50     99 my $cfg = $entry->{config} // {};
68 19         25 my $plugin_steps = $cfg->{$key};
69 19 50 66     84 next unless $plugin_steps && ref $plugin_steps eq 'ARRAY' && @$plugin_steps;
      66        
70 5         18 push @steps, { long_name => $long, steps => $plugin_steps };
71             }
72 5         16 return @steps;
73             }
74              
75             # ---------------------------------------------------------------------------
76             # Collect clean targets from all plugins in load order
77             # ---------------------------------------------------------------------------
78              
79 2     2   35 sub _collect_clean ($self, $app) {
  2         3  
  2         5  
  2         2  
80 2         5 my @targets;
81 2         3 for my $long (@{ $app->manager->load_order }) {
  2         10  
82 7         23 my $entry = $app->manager->registry->{$long};
83 7   50     32 my $cfg = $entry->{config} // {};
84 7         11 my $plugin_clean = $cfg->{fondation_clean};
85 7 50 66     24 next unless $plugin_clean && ref $plugin_clean eq 'ARRAY' && @$plugin_clean;
      66        
86 2         9 push @targets, { long_name => $long, targets => $plugin_clean };
87             }
88 2         11 return @targets;
89             }
90              
91             # ---------------------------------------------------------------------------
92             # fondation plan (dry-run preview)
93             # ---------------------------------------------------------------------------
94              
95 0     0   0 sub _run_plan ($self, $app, $subcommand) {
  0         0  
  0         0  
  0         0  
  0         0  
96 0 0       0 die $self->usage unless $subcommand;
97              
98 0         0 for ($subcommand) {
99 0 0       0 /^init$/ and return $self->_show_plan_init($app);
100 0 0       0 /^upgrade$/ and return $self->_show_plan_upgrade($app);
101 0 0       0 /^refresh$/ and return $self->_show_plan_refresh($app);
102 0         0 die $self->usage;
103             }
104             }
105              
106 0     0   0 sub _short_name ($self, $app, $long) {
  0         0  
  0         0  
  0         0  
  0         0  
107 0   0     0 return $app->manager->registry->{$long}{short_name} // $long;
108             }
109              
110 0     0   0 sub _show_plan_init ($self, $app) {
  0         0  
  0         0  
  0         0  
111 0         0 my @plugins = $self->_collect_steps($app, 'fondation_init');
112 0         0 my $total_steps = 0;
113              
114 0         0 for my $p (@plugins) {
115 0         0 my $short = $self->_short_name($app, $p->{long_name});
116 0         0 say "-- $short";
117 0         0 for my $step (@{ $p->{steps} }) {
  0         0  
118 0 0       0 my @cmd = ref $step eq 'ARRAY' ? @$step : ($step);
119 0         0 say " [run] @cmd";
120 0         0 $total_steps++;
121             }
122             }
123              
124 0         0 say "-- Init plan: " . scalar(@plugins) . " plugins, $total_steps steps --";
125             }
126              
127 0     0   0 sub _show_plan_upgrade ($self, $app) {
  0         0  
  0         0  
  0         0  
128 0         0 my @plugins = $self->_collect_steps($app, 'fondation_upgrade');
129 0         0 my $total_steps = 0;
130              
131 0         0 for my $p (@plugins) {
132 0         0 my $short = $self->_short_name($app, $p->{long_name});
133 0         0 say "-- $short";
134 0         0 for my $step (@{ $p->{steps} }) {
  0         0  
135 0 0       0 my @cmd = ref $step eq 'ARRAY' ? @$step : ($step);
136 0         0 say " [run] @cmd";
137 0         0 $total_steps++;
138             }
139             }
140              
141 0         0 say "-- Upgrade plan: " . scalar(@plugins) . " plugins, $total_steps steps --";
142             }
143              
144 0     0   0 sub _show_plan_refresh ($self, $app) {
  0         0  
  0         0  
  0         0  
145             # Phase 1: clean
146 0         0 my @clean = $self->_collect_clean($app);
147              
148 0 0       0 if (@clean) {
149 0         0 say "-- Clean phase --";
150 0         0 for my $p (@clean) {
151 0         0 my $short = $self->_short_name($app, $p->{long_name});
152 0         0 for my $target (@{ $p->{targets} }) {
  0         0  
153 0         0 say " [$short] remove $target";
154             }
155             }
156 0         0 say "";
157             }
158              
159             # Phase 2: init
160 0         0 $self->_show_plan_init($app);
161              
162 0         0 say "-- Refresh plan --";
163             }
164              
165 2     2   54 sub _run_init ($self, $app) {
  2         5  
  2         2  
  2         3  
166 2         7 my @plugins = $self->_collect_steps($app, 'fondation_init');
167              
168 2         4 for my $plugin (@plugins) {
169 2         5 my $short = $app->manager->registry->{$plugin->{long_name}}{short_name};
170 2         52 say "-- $short";
171 2         11 $self->_run_steps($app, 'fondation_init', $plugin->{long_name}, $plugin->{steps});
172             }
173              
174 2         23 say "-- Init complete --";
175             }
176              
177             # ---------------------------------------------------------------------------
178             # fondation upgrade
179             # ---------------------------------------------------------------------------
180              
181 0     0   0 sub _run_upgrade ($self, $app) {
  0         0  
  0         0  
  0         0  
182 0         0 my @plugins = $self->_collect_steps($app, 'fondation_upgrade');
183              
184 0         0 for my $plugin (@plugins) {
185 0         0 my $short = $app->manager->registry->{$plugin->{long_name}}{short_name};
186 0         0 say "-- $short";
187 0         0 $self->_run_steps($app, 'fondation_upgrade', $plugin->{long_name}, $plugin->{steps});
188             }
189              
190 0         0 say "-- Upgrade complete --";
191             }
192              
193             # ---------------------------------------------------------------------------
194             # fondation refresh
195             # ---------------------------------------------------------------------------
196              
197 1     1   1654 sub _run_refresh ($self, $app) {
  1         2  
  1         2  
  1         2  
198 1         4 my $home = $app->home;
199              
200             # Phase 1: clean
201 1         7 my @clean_plugins = $self->_collect_clean($app);
202              
203 1 50       3 if (@clean_plugins) {
204 1         19 say "-- Cleaning generated artifacts --";
205              
206 1         3 for my $plugin (@clean_plugins) {
207 1         4 my $short = $app->manager->registry->{$plugin->{long_name}}{short_name};
208 1         6 for my $target (@{ $plugin->{targets} }) {
  1         60  
209 2         422 my $path = path($home, $target);
210              
211 2 100       61 if (-d $path) {
    50          
212 1         38 say " [$short] Removing $target";
213 1         4 remove_tree($path->to_string, { safe => 0 });
214             }
215             elsif (-f $path) {
216 1         40 say " [$short] Removing $target";
217 1         4 unlink $path->to_string;
218             }
219             }
220             }
221             }
222              
223             # Phase 2: init
224 1         69 $self->_run_init($app);
225              
226 1         8 say "-- Refresh complete --";
227             }
228              
229             1;
230              
231             __END__