|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Kaliop\eZMigrationBundle\Command; |
|
4
|
|
|
|
|
5
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
|
6
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
|
7
|
|
|
use Symfony\Component\Console\Input\InputOption; |
|
8
|
|
|
use Kaliop\eZMigrationBundle\API\Value\Migration; |
|
9
|
|
|
use Kaliop\eZMigrationBundle\API\Value\MigrationDefinition; |
|
10
|
|
|
use Symfony\Component\Console\Helper\Table; |
|
11
|
|
|
|
|
12
|
|
|
/** |
|
13
|
|
|
* Command to display the status of migrations. |
|
14
|
|
|
* |
|
15
|
|
|
* @todo add option to skip displaying already executed migrations |
|
16
|
|
|
*/ |
|
17
|
|
|
class StatusCommand extends AbstractCommand |
|
18
|
|
|
{ |
|
19
|
|
|
const STATUS_INVALID = -1; |
|
20
|
|
|
|
|
21
|
|
|
protected function configure() |
|
22
|
|
|
{ |
|
23
|
|
|
$this->setName('kaliop:migration:status') |
|
24
|
|
|
->setDescription('View the status of all (or a set of) migrations.') |
|
25
|
|
|
->addOption('path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, "The directory or file to load the migration definitions from") |
|
26
|
|
|
->addOption('summary', null, InputOption::VALUE_NONE, "Only print summary information") |
|
27
|
|
|
->addOption('todo', null, InputOption::VALUE_NONE, "Only print list of migrations to execute (full path to each)") |
|
28
|
|
|
->setHelp(<<<EOT |
|
29
|
|
|
The <info>kaliop:migration:status</info> command displays the status of all available migrations: |
|
30
|
|
|
|
|
31
|
|
|
<info>./ezpublish/console kaliop:migration:status</info> |
|
32
|
|
|
|
|
33
|
|
|
You can optionally specify the path to migration versions with <info>--path</info>: |
|
34
|
|
|
|
|
35
|
|
|
<info>./ezpublish/console kaliop:migrations:status --path=/path/to/bundle/version_directory --path=/path/to/bundle/version_directory/single_migration_file</info> |
|
36
|
|
|
EOT |
|
37
|
|
|
); |
|
38
|
|
|
} |
|
39
|
|
|
|
|
40
|
|
|
public function execute(InputInterface $input, OutputInterface $output) |
|
41
|
|
|
{ |
|
42
|
|
|
$migrationsService = $this->getMigrationService(); |
|
43
|
|
|
|
|
44
|
|
|
$migrationDefinitions = $migrationsService->getMigrationsDefinitions($input->getOption('path')); |
|
45
|
|
|
$migrations = $migrationsService->getMigrations(); |
|
46
|
|
|
|
|
47
|
|
|
if (!count($migrationDefinitions) && !count($migrations)) { |
|
48
|
|
|
$output->writeln('<info>No migrations found</info>'); |
|
49
|
|
|
return; |
|
50
|
|
|
} |
|
51
|
|
|
|
|
52
|
|
|
// create a unique ist of all migrations (coming from db) and definitions (coming from disk) |
|
53
|
|
|
$index = array(); |
|
54
|
|
|
foreach ($migrationDefinitions as $migrationDefinition) { |
|
55
|
|
|
$index[$migrationDefinition->name] = array('definition' => $migrationDefinition); |
|
56
|
|
|
} |
|
57
|
|
|
foreach ($migrations as $migration) { |
|
58
|
|
|
if (isset($index[$migration->name])) { |
|
59
|
|
|
$index[$migration->name]['migration'] = $migration; |
|
60
|
|
|
} else { |
|
61
|
|
|
$index[$migration->name] = array('migration' => $migration); |
|
62
|
|
|
|
|
63
|
|
|
// no definition, but a migration is there. Check if the definition sits elsewhere on disk than we expect it to be... |
|
64
|
|
|
// q: what if we have a loader which does not work with is_file? Could we remove this check? |
|
65
|
|
|
if ($migration->path != '' && is_file($migration->path)) { |
|
66
|
|
|
try { |
|
67
|
|
|
$migrationDefinitionCollection = $migrationsService->getMigrationsDefinitions(array($migration->path)); |
|
68
|
|
|
if (count($migrationDefinitionCollection)) { |
|
69
|
|
|
$index[$migration->name]['definition'] = reset($migrationDefinitionCollection); |
|
70
|
|
|
} |
|
71
|
|
|
} catch (\Exception $e) { |
|
72
|
|
|
/// @todo one day we should be able to limit the kind of exceptions we have to catch here... |
|
73
|
|
|
} |
|
74
|
|
|
} |
|
75
|
|
|
} |
|
76
|
|
|
} |
|
77
|
|
|
ksort($index); |
|
78
|
|
|
|
|
79
|
|
|
if (!$input->getOption('summary') && !$input->getOption('todo')) { |
|
80
|
|
|
if (count($index) > 50000) { |
|
81
|
|
|
$output->writeln("WARNING: printing the status table might take a while as it contains many rows. Please wait..."); |
|
82
|
|
|
} |
|
83
|
|
|
$output->writeln("\n <info>==</info> All Migrations\n"); |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
$summary = array( |
|
87
|
|
|
self::STATUS_INVALID => array('Invalid', 0), |
|
88
|
|
|
Migration::STATUS_TODO => array('To do', 0), |
|
89
|
|
|
Migration::STATUS_STARTED => array('Started', 0), |
|
90
|
|
|
Migration::STATUS_DONE => array('Done', 0), |
|
91
|
|
|
Migration::STATUS_SUSPENDED => array('Suspended', 0), |
|
92
|
|
|
Migration::STATUS_FAILED => array('Failed', 0), |
|
93
|
|
|
Migration::STATUS_SKIPPED => array('Skipped', 0), |
|
94
|
|
|
Migration::STATUS_PARTIALLY_DONE => array('Partially done', 0), |
|
95
|
|
|
); |
|
96
|
|
|
$data = array(); |
|
97
|
|
|
|
|
98
|
|
|
$i = 1; |
|
99
|
|
|
foreach ($index as $name => $value) { |
|
100
|
|
|
if (!isset($value['migration'])) { |
|
101
|
|
|
$migrationDefinition = $migrationsService->parseMigrationDefinition($value['definition']); |
|
102
|
|
|
$notes = ''; |
|
103
|
|
|
if ($migrationDefinition->status != MigrationDefinition::STATUS_PARSED) { |
|
104
|
|
|
$notes = '<error>' . $migrationDefinition->parsingError . '</error>'; |
|
105
|
|
|
$summary[self::STATUS_INVALID][1]++; |
|
106
|
|
|
} else { |
|
107
|
|
|
$summary[Migration::STATUS_TODO][1]++; |
|
108
|
|
|
} |
|
109
|
|
|
if ($input->getOption('todo')) { |
|
110
|
|
|
$data[] = $migrationDefinition->path; |
|
111
|
|
|
} else { |
|
112
|
|
|
$data[] = array( |
|
113
|
|
|
$i++, |
|
114
|
|
|
$name, |
|
115
|
|
|
'<error>not executed</error>', |
|
116
|
|
|
'', |
|
117
|
|
|
$notes |
|
118
|
|
|
); |
|
119
|
|
|
|
|
120
|
|
|
} |
|
121
|
|
|
} else { |
|
122
|
|
|
$migration = $value['migration']; |
|
123
|
|
|
|
|
124
|
|
|
if (!isset($summary[$migration->status])) { |
|
125
|
|
|
$summary[$migration->status] = array($migration->status, 0); |
|
126
|
|
|
} |
|
127
|
|
|
$summary[$migration->status][1]++; |
|
128
|
|
|
if ($input->getOption('summary')) { |
|
129
|
|
|
continue; |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
if ($input->getOption('todo')) { |
|
133
|
|
|
if ($migration->status == Migration::STATUS_TODO) { |
|
134
|
|
|
$data[] = $migration->path; |
|
135
|
|
|
} |
|
136
|
|
|
|
|
137
|
|
|
continue; |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
View Code Duplication |
switch ($migration->status) { |
|
|
|
|
|
|
141
|
|
|
case Migration::STATUS_DONE: |
|
142
|
|
|
$status = '<info>executed</info>'; |
|
143
|
|
|
break; |
|
144
|
|
|
case Migration::STATUS_STARTED: |
|
145
|
|
|
$status = '<comment>execution started</comment>'; |
|
146
|
|
|
break; |
|
147
|
|
|
case Migration::STATUS_TODO: |
|
148
|
|
|
// bold to-migrate! |
|
149
|
|
|
$status = '<error>not executed</error>'; |
|
150
|
|
|
break; |
|
151
|
|
|
case Migration::STATUS_SKIPPED: |
|
152
|
|
|
$status = '<comment>skipped</comment>'; |
|
153
|
|
|
break; |
|
154
|
|
|
case Migration::STATUS_PARTIALLY_DONE: |
|
155
|
|
|
$status = '<comment>partially executed</comment>'; |
|
156
|
|
|
break; |
|
157
|
|
|
case Migration::STATUS_SUSPENDED: |
|
158
|
|
|
$status = '<comment>suspended</comment>'; |
|
159
|
|
|
break; |
|
160
|
|
|
case Migration::STATUS_FAILED: |
|
161
|
|
|
$status = '<error>failed</error>'; |
|
162
|
|
|
break; |
|
163
|
|
|
} |
|
164
|
|
|
$notes = array(); |
|
165
|
|
|
if ($migration->executionError != '') { |
|
166
|
|
|
$notes[] = "<error>{$migration->executionError}</error>"; |
|
167
|
|
|
} |
|
168
|
|
|
if (!isset($value['definition'])) { |
|
169
|
|
|
$notes[] = '<comment>The migration definition file can not be found any more</comment>'; |
|
170
|
|
|
} else { |
|
171
|
|
|
$migrationDefinition = $value['definition']; |
|
172
|
|
|
if (md5($migrationDefinition->rawDefinition) != $migration->md5) { |
|
173
|
|
|
$notes[] = '<comment>The migration definition file has now a different checksum</comment>'; |
|
174
|
|
|
} |
|
175
|
|
|
if ($migrationDefinition->path != $migrationDefinition->path) { |
|
176
|
|
|
$notes[] = '<comment>The migration definition file has now moved</comment>'; |
|
177
|
|
|
} |
|
178
|
|
|
} |
|
179
|
|
|
$notes = implode(' ', $notes); |
|
180
|
|
|
$data[] = array( |
|
181
|
|
|
$i++, |
|
182
|
|
|
$migration->name, |
|
183
|
|
|
$status, |
|
|
|
|
|
|
184
|
|
|
($migration->executionDate != null ? date("Y-m-d H:i:s", $migration->executionDate) : ''), |
|
185
|
|
|
$notes |
|
186
|
|
|
); |
|
187
|
|
|
} |
|
188
|
|
|
} |
|
189
|
|
|
|
|
190
|
|
|
if ($input->getOption('todo')) { |
|
191
|
|
|
foreach ($data as $migrationData) { |
|
192
|
|
|
echo "$migrationData\n"; |
|
193
|
|
|
} |
|
194
|
|
|
return; |
|
195
|
|
|
} |
|
196
|
|
|
|
|
197
|
|
|
if ($input->getOption('summary')) { |
|
198
|
|
|
$output->writeln("\n <info>==</info> Migrations Summary\n"); |
|
199
|
|
|
// do not print info about the not yet supported case |
|
200
|
|
|
unset($summary[Migration::STATUS_PARTIALLY_DONE]); |
|
201
|
|
|
$data = $summary; |
|
202
|
|
|
$headers = array('Status', 'Count'); |
|
203
|
|
|
} else { |
|
204
|
|
|
$headers = array('#', 'Migration', 'Status', 'Executed on', 'Notes'); |
|
205
|
|
|
} |
|
206
|
|
|
|
|
207
|
|
|
$table = new Table($output); |
|
208
|
|
|
$table |
|
209
|
|
|
->setHeaders($headers) |
|
210
|
|
|
->setRows($data); |
|
211
|
|
|
$table->render(); |
|
212
|
|
|
} |
|
213
|
|
|
} |
|
214
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.