2020use Cake \Core \Plugin ;
2121use Migrations \Config \ConfigInterface ;
2222use Migrations \Db \Adapter \UnifiedMigrationsTableStorage ;
23+ use Migrations \Migration \Manager ;
2324use Migrations \Migration \ManagerFactory ;
2425
2526/**
@@ -78,6 +79,8 @@ protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOption
7879 'Add <info>-v</info> to also print the per-section migration tables. ' ,
7980 '<info>migrations status --cleanup</info> ' ,
8081 'Remove *MISSING* migrations from the migration tracking table ' ,
82+ '<info>migrations status --validate</info> ' ,
83+ 'Load every migration class and fail if any of them cannot be loaded. ' ,
8184 ])->addOption ('plugin ' , [
8285 'short ' => 'p ' ,
8386 'help ' => 'The plugin to run migrations for ' ,
@@ -104,6 +107,11 @@ protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOption
104107 'help ' => 'Remove MISSING migrations from the migration tracking table ' ,
105108 'boolean ' => true ,
106109 'default ' => false ,
110+ ])->addOption ('validate ' , [
111+ 'help ' => 'Load every migration class and fail if any of them cannot be loaded. '
112+ . 'Migration classes are otherwise only loaded when they are executed. ' ,
113+ 'boolean ' => true ,
114+ 'default ' => false ,
107115 ]);
108116
109117 return $ parser ;
@@ -146,6 +154,20 @@ public function execute(Arguments $args, ConsoleIo $io): ?int
146154 ]);
147155 $ manager = $ factory ->createManager ($ io );
148156
157+ if ($ args ->getOption ('validate ' )) {
158+ /** @var string|null $plugin */
159+ $ plugin = $ args ->getOption ('plugin ' );
160+ if (!$ this ->validateMigrations ($ manager , $ io , $ plugin ?? 'app ' )) {
161+ return Command::CODE_ERROR ;
162+ }
163+ if ($ format !== 'json ' ) {
164+ $ io ->out (sprintf (
165+ '<success>All %d migrations can be loaded.</success> ' ,
166+ count ($ manager ->getMigrationVersions ()),
167+ ));
168+ }
169+ }
170+
149171 if ($ clean ) {
150172 $ removed = $ manager ->cleanupMissingMigrations ();
151173 if ($ removed === 0 ) {
@@ -200,6 +222,8 @@ protected function executeAll(Arguments $args, ConsoleIo $io, ?string $format):
200222 }
201223
202224 $ verbose = (bool )$ args ->getOption ('verbose ' );
225+ $ validate = (bool )$ args ->getOption ('validate ' );
226+ $ validationFailed = false ;
203227 $ jsonResults = [];
204228 $ summary = [];
205229 $ exitCode = Command::CODE_SUCCESS ;
@@ -212,6 +236,11 @@ protected function executeAll(Arguments $args, ConsoleIo $io, ?string $format):
212236 'dry-run ' => $ args ->getOption ('dry-run ' ),
213237 ]);
214238 $ manager = $ factory ->createManager ($ io );
239+
240+ if ($ validate && !$ this ->validateMigrations ($ manager , $ io , $ label )) {
241+ $ validationFailed = true ;
242+ }
243+
215244 $ migrations = $ manager ->printStatus ($ format );
216245
217246 $ sectionExit = $ this ->statusExitCode ($ migrations );
@@ -241,6 +270,10 @@ protected function executeAll(Arguments $args, ConsoleIo $io, ?string $format):
241270 $ this ->display ($ migrations , $ io , $ manager ->getSchemaTableName ());
242271 }
243272
273+ if ($ validationFailed ) {
274+ return Command::CODE_ERROR ;
275+ }
276+
244277 if ($ format === 'json ' ) {
245278 $ flags = 0 ;
246279 if ($ verbose ) {
@@ -256,6 +289,33 @@ protected function executeAll(Arguments $args, ConsoleIo $io, ?string $format):
256289 return $ exitCode ;
257290 }
258291
292+ /**
293+ * Load every migration class and print the ones that could not be loaded.
294+ *
295+ * @param \Migrations\Migration\Manager $manager The manager to load migrations with.
296+ * @param \Cake\Console\ConsoleIo $io The console io.
297+ * @param string $label The section the migrations belong to.
298+ * @return bool True when every migration class could be loaded.
299+ */
300+ protected function validateMigrations (Manager $ manager , ConsoleIo $ io , string $ label ): bool
301+ {
302+ $ errors = $ manager ->validateMigrations ();
303+ if (!$ errors ) {
304+ return true ;
305+ }
306+
307+ $ io ->err (sprintf (
308+ '<error>%s: %d migration(s) could not be loaded:</error> ' ,
309+ $ label === 'app ' ? 'APP ' : $ label ,
310+ count ($ errors ),
311+ ));
312+ foreach ($ errors as $ version => $ message ) {
313+ $ io ->err (sprintf (' - %d: %s ' , $ version , $ message ));
314+ }
315+
316+ return false ;
317+ }
318+
259319 /**
260320 * Count actionable items (down + missing) in a section's migrations array.
261321 *
0 commit comments