From 75adc53bd630839d102a659018072fa5ddef6f9f Mon Sep 17 00:00:00 2001 From: Tom Moore Date: Tue, 25 Feb 2025 14:09:50 +0100 Subject: [PATCH 01/10] Update dependency definitions --- composer.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 39b7d81..6b85c0e 100644 --- a/composer.json +++ b/composer.json @@ -21,13 +21,13 @@ ], "require": { "php": "^8.2", - "illuminate/contracts": "^10.0|^11.0", - "illuminate/console": "^10.0|^11.0", - "illuminate/database": "^10.0|^11.0", - "illuminate/support": "^10.0|^11.0" + "illuminate/contracts": "^10.0 | ^11.0", + "illuminate/console": "^10.0 | ^11.0", + "illuminate/database": "^10.0 | ^11.0", + "illuminate/support": "^10.0 | ^11.0" }, "require-dev": { - "orchestra/testbench": "^8.0|^9.0", + "orchestra/testbench": "^8.0 | ^9.0", "spatie/fork": "^1.1" }, "autoload": { From ad527a8be71cbd74885941a78a8f69b5be0b518b Mon Sep 17 00:00:00 2001 From: Tom Moore Date: Tue, 25 Feb 2025 14:25:47 +0100 Subject: [PATCH 02/10] Split provider code into separate classes --- composer.json | 3 +- ...ider.php => DependencyBindingProvider.php} | 58 +++++++------------ src/PackageProvider.php | 36 ++++++++++++ .../AbstractIntegrationTestCase.php | 6 +- 4 files changed, 64 insertions(+), 39 deletions(-) rename src/{ServiceProvider.php => DependencyBindingProvider.php} (60%) create mode 100644 src/PackageProvider.php diff --git a/composer.json b/composer.json index 6b85c0e..ddecbf2 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,8 @@ "extra": { "laravel": { "providers": [ - "Netsells\\LaravelMutexMigrations\\ServiceProvider" + "Netsells\\LaravelMutexMigrations\\DependencyBindingProvider", + "Netsells\\LaravelMutexMigrations\\PackageProvider" ] } }, diff --git a/src/ServiceProvider.php b/src/DependencyBindingProvider.php similarity index 60% rename from src/ServiceProvider.php rename to src/DependencyBindingProvider.php index 6b540e7..8ad3cc3 100644 --- a/src/ServiceProvider.php +++ b/src/DependencyBindingProvider.php @@ -1,5 +1,7 @@ */ - public function register() + public function provides(): array { - if (! $this->app->runningInConsole()) { - return; - } - - $this->mergeConfigFrom(__DIR__.'/../config/mutex-migrations.php', 'mutex-migrations'); + return [ + MigrateCommand::class, + MutexMigrateCommand::class, + MutexRelay::class, + ]; + } + /** + * Register any application services. + */ + public function register(): void + { $this->app->bind(MigrateCommand::class, MigrateCommandExtension::class); $this->app->when([MigrateCommandExtension::class, MutexMigrateCommand::class]) @@ -38,40 +46,18 @@ public function register() $store = Config::get('mutex-migrations.lock.store'); return new MutexRelay( - Cache::store($store), - Config::get('mutex-migrations.lock.ttl_seconds'), - Config::get("cache.stores.{$store}.lock_table", MutexRelay::DEFAULT_LOCK_TABLE), + cache: Cache::store($store), + lockDurationSeconds: Config::get('mutex-migrations.lock.ttl_seconds'), + lockTable: Config::get("cache.stores.{$store}.lock_table", MutexRelay::DEFAULT_LOCK_TABLE), ); }); } /** * Bootstrap any package services. - * - * @return void - */ - public function boot() - { - if (! $this->app->runningInConsole()) { - return; - } - - $this->publishes([ - __DIR__.'/../config/mutex-migrations.php' => config_path('mutex-migrations.php'), - ], 'mutex-migrations-config'); - } - - /** - * Get the services provided by the provider. - * - * @return array */ - public function provides() + public function boot(): void { - return [ - MigrateCommand::class, - MutexMigrateCommand::class, - MutexRelay::class, - ]; + // } } diff --git a/src/PackageProvider.php b/src/PackageProvider.php new file mode 100644 index 0000000..fac9df5 --- /dev/null +++ b/src/PackageProvider.php @@ -0,0 +1,36 @@ +app->runningInConsole()) { + return; + } + + $this->mergeConfigFrom(__DIR__.'/../config/mutex-migrations.php', 'mutex-migrations'); + } + + /** + * Bootstrap any package services. + */ + public function boot(): void + { + if (! $this->app->runningInConsole()) { + return; + } + + $this->publishes([ + __DIR__.'/../config/mutex-migrations.php' => config_path('mutex-migrations.php'), + ], 'mutex-migrations-config'); + } +} diff --git a/tests/Integration/AbstractIntegrationTestCase.php b/tests/Integration/AbstractIntegrationTestCase.php index ad2f97b..fca4e93 100644 --- a/tests/Integration/AbstractIntegrationTestCase.php +++ b/tests/Integration/AbstractIntegrationTestCase.php @@ -1,8 +1,10 @@ Date: Tue, 25 Feb 2025 14:30:15 +0100 Subject: [PATCH 03/10] Relocate command classes --- src/{ => Commands}/MigrateCommandExtension.php | 4 +++- src/{ => Commands}/MutexMigrateCommand.php | 4 +++- src/DependencyBindingProvider.php | 18 +++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) rename src/{ => Commands}/MigrateCommandExtension.php (94%) rename src/{ => Commands}/MutexMigrateCommand.php (95%) diff --git a/src/MigrateCommandExtension.php b/src/Commands/MigrateCommandExtension.php similarity index 94% rename from src/MigrateCommandExtension.php rename to src/Commands/MigrateCommandExtension.php index a8560a5..754c1af 100644 --- a/src/MigrateCommandExtension.php +++ b/src/Commands/MigrateCommandExtension.php @@ -1,6 +1,8 @@ app->bind(MigrateCommand::class, MigrateCommandExtension::class); + $this->app->bind(MigrateCommand::class, Commands\MigrateCommandExtension::class); - $this->app->when([MigrateCommandExtension::class, MutexMigrateCommand::class]) + $this->app->when([Commands\MigrateCommandExtension::class, Commands\MutexMigrateCommand::class]) ->needs(Migrator::class) ->give(function ($app) { return $app['migrator']; }); - $this->app->bind(MutexRelay::class, function ($app) { + $this->app->bind(Mutex\MutexRelay::class, function ($app) { $store = Config::get('mutex-migrations.lock.store'); - return new MutexRelay( + return new Mutex\MutexRelay( cache: Cache::store($store), lockDurationSeconds: Config::get('mutex-migrations.lock.ttl_seconds'), - lockTable: Config::get("cache.stores.{$store}.lock_table", MutexRelay::DEFAULT_LOCK_TABLE), + lockTable: Config::get("cache.stores.{$store}.lock_table", Mutex\MutexRelay::DEFAULT_LOCK_TABLE), ); }); } From 49864b1d7b55f2158bab1b91db4838875ea5133e Mon Sep 17 00:00:00 2001 From: Tom Moore Date: Tue, 25 Feb 2025 14:34:30 +0100 Subject: [PATCH 04/10] Fix issue where config defaults not set --- src/DependencyBindingProvider.php | 6 ++++-- src/Mutex/MutexRelay.php | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/DependencyBindingProvider.php b/src/DependencyBindingProvider.php index 3600329..eddf49a 100644 --- a/src/DependencyBindingProvider.php +++ b/src/DependencyBindingProvider.php @@ -47,8 +47,10 @@ public function register(): void return new Mutex\MutexRelay( cache: Cache::store($store), - lockDurationSeconds: Config::get('mutex-migrations.lock.ttl_seconds'), - lockTable: Config::get("cache.stores.{$store}.lock_table", Mutex\MutexRelay::DEFAULT_LOCK_TABLE), + lockDurationSeconds: Config::get('mutex-migrations.lock.ttl_seconds') + ?? Mutex\MutexRelay::DEFAULT_TTL_SECONDS, + lockTable: Config::get("cache.stores.{$store}.lock_table") + ?? Mutex\MutexRelay::DEFAULT_LOCK_TABLE, ); }); } diff --git a/src/Mutex/MutexRelay.php b/src/Mutex/MutexRelay.php index 68e2d98..dde3684 100644 --- a/src/Mutex/MutexRelay.php +++ b/src/Mutex/MutexRelay.php @@ -1,5 +1,7 @@ Date: Tue, 25 Feb 2025 14:34:40 +0100 Subject: [PATCH 05/10] Declare strict typing --- src/Mutex/DatabaseCacheTableNotFoundException.php | 2 ++ src/Mutex/MutexRelayInterface.php | 2 ++ src/Mutex/NullRelay.php | 2 ++ src/Processors/MigrationProcessorFactory.php | 2 ++ src/Processors/MigrationProcessorInterface.php | 2 ++ src/Processors/MutexMigrationProcessor.php | 2 ++ 6 files changed, 12 insertions(+) diff --git a/src/Mutex/DatabaseCacheTableNotFoundException.php b/src/Mutex/DatabaseCacheTableNotFoundException.php index ced1fd9..17251b5 100644 --- a/src/Mutex/DatabaseCacheTableNotFoundException.php +++ b/src/Mutex/DatabaseCacheTableNotFoundException.php @@ -1,5 +1,7 @@ Date: Tue, 25 Feb 2025 15:46:31 +0100 Subject: [PATCH 06/10] Add new --mutex-graceful option --- src/Commands/MigrateCommandExtension.php | 20 +++++++++++++++++--- src/Commands/MutexMigrateCommand.php | 7 +++++++ src/Mutex/MutexRelay.php | 5 ++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Commands/MigrateCommandExtension.php b/src/Commands/MigrateCommandExtension.php index 754c1af..ba9bb82 100644 --- a/src/Commands/MigrateCommandExtension.php +++ b/src/Commands/MigrateCommandExtension.php @@ -17,25 +17,39 @@ public function __construct(Migrator $migrator, Dispatcher $dispatcher) parent::__construct($migrator, $dispatcher); parent::addOption(...MutexMigrateCommand::getMutexOption()); + parent::addOption(...MutexMigrateCommand::getMutexGracefulOption()); } public function handle(): int { - if ($this->option(MutexMigrateCommand::OPTION_MUTEX)) { + if ($this->shouldUseMutex()) { try { return $this->call(MutexMigrateCommand::class, $this->getCommandOptions()); } catch (DatabaseCacheTableNotFoundException $e) { - $this->components->warn('Falling back to a standard migration'); + if ($this->option(MutexMigrateCommand::OPTION_MUTEX)) { + return self::FAILURE; + } elseif ($this->option(MutexMigrateCommand::OPTION_MUTEX_GRACEFUL)) { + $this->components->warn('Falling back to a standard migration'); + } } } return parent::handle(); } + private function shouldUseMutex(): bool + { + return $this->option(MutexMigrateCommand::OPTION_MUTEX) + || $this->option(MutexMigrateCommand::OPTION_MUTEX_GRACEFUL); + } + private function getCommandOptions(): array { return Collection::make($this->options()) - ->reject(fn ($value, $key) => $key === MutexMigrateCommand::OPTION_MUTEX) + ->reject(fn ($value, $key) => \in_array($key, [ + MutexMigrateCommand::OPTION_MUTEX, + MutexMigrateCommand::OPTION_MUTEX_GRACEFUL, + ])) ->mapWithKeys(fn ($value, $key) => ["--$key" => $value]) ->all(); } diff --git a/src/Commands/MutexMigrateCommand.php b/src/Commands/MutexMigrateCommand.php index 7b44a94..6096762 100644 --- a/src/Commands/MutexMigrateCommand.php +++ b/src/Commands/MutexMigrateCommand.php @@ -16,6 +16,8 @@ class MutexMigrateCommand extends MigrateCommand { public const OPTION_MUTEX = 'mutex'; + public const OPTION_MUTEX_GRACEFUL = 'mutex-graceful'; + private MigrationProcessorInterface $processor; public static function getMutexOption(): array @@ -23,6 +25,11 @@ public static function getMutexOption(): array return [self::OPTION_MUTEX, null, InputOption::VALUE_NONE, 'Run a mutually exclusive migration']; } + public static function getMutexGracefulOption(): array + { + return [self::OPTION_MUTEX_GRACEFUL, null, InputOption::VALUE_NONE, 'Run a mutually exclusive migration and gracefully failover to a standard migration if the application does not contain the required lock table']; + } + public function __construct( Migrator $migrator, Dispatcher $dispatcher, diff --git a/src/Mutex/MutexRelay.php b/src/Mutex/MutexRelay.php index dde3684..95c673d 100644 --- a/src/Mutex/MutexRelay.php +++ b/src/Mutex/MutexRelay.php @@ -63,6 +63,9 @@ private function isCacheTableNotFoundException(\Throwable $th): bool return false; } - return $th->getCode() === '42S02' && Str::contains($th->getMessage(), $this->lockTable); + return Str::contains($th->getMessage(), $this->lockTable) && \in_array($th->getCode(), [ + '42S02', // mysql + 'HY000', // sqlite + ], true); } } From d1e8640ba1285f12f26107be168305682e7a589b Mon Sep 17 00:00:00 2001 From: Tom Moore Date: Tue, 25 Feb 2025 23:14:44 +0100 Subject: [PATCH 07/10] Update dependencies --- .github/workflows/tests.yaml | 6 +++--- composer.json | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 67730d0..85e34cb 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -7,16 +7,16 @@ jobs: # not self-hosted, because it's a public repo runs-on: ubuntu-latest - # we want to run it on combination of PHP 8.1+ and Laravel 9.1+ + # we want to run it on combination of PHP and Laravel versions strategy: fail-fast: false matrix: php: ['8.2', '8.3'] - laravel: ['^10.0', '^11.0'] + laravel: ['^10.0', '^11.0', '^12.0'] steps: - name: Checkout the repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/composer.json b/composer.json index ddecbf2..45ad95d 100644 --- a/composer.json +++ b/composer.json @@ -21,14 +21,14 @@ ], "require": { "php": "^8.2", - "illuminate/contracts": "^10.0 | ^11.0", - "illuminate/console": "^10.0 | ^11.0", - "illuminate/database": "^10.0 | ^11.0", - "illuminate/support": "^10.0 | ^11.0" + "illuminate/contracts": "^10.0 | ^11.0 | ^12.0", + "illuminate/console": "^10.0 | ^11.0 | ^12.0", + "illuminate/database": "^10.0 | ^11.0 | ^12.0", + "illuminate/support": "^10.0 | ^11.0 | ^12.0" }, "require-dev": { - "orchestra/testbench": "^8.0 | ^9.0", - "spatie/fork": "^1.1" + "orchestra/testbench": "^8.0 | ^9.0 | ^10.0", + "spatie/fork": "^1.2" }, "autoload": { "psr-4": { From c59e0f3cd8f1a650e54ccfe3a98895a7d19e23ae Mon Sep 17 00:00:00 2001 From: Tom Moore Date: Wed, 26 Feb 2025 08:26:02 +0100 Subject: [PATCH 08/10] Add php 8.4 to workflow matrix --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 85e34cb..59d213d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.2', '8.3'] + php: ['8.2', '8.3', '8.4'] laravel: ['^10.0', '^11.0', '^12.0'] steps: From c69dacab649a0c79c1782507ae399101d6ead5cf Mon Sep 17 00:00:00 2001 From: Tom Moore Date: Wed, 26 Feb 2025 21:14:54 +0100 Subject: [PATCH 09/10] Add graceful handling for mutex errors --- src/Commands/MigrateCommandExtension.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Commands/MigrateCommandExtension.php b/src/Commands/MigrateCommandExtension.php index ba9bb82..abfcbab 100644 --- a/src/Commands/MigrateCommandExtension.php +++ b/src/Commands/MigrateCommandExtension.php @@ -27,6 +27,12 @@ public function handle(): int return $this->call(MutexMigrateCommand::class, $this->getCommandOptions()); } catch (DatabaseCacheTableNotFoundException $e) { if ($this->option(MutexMigrateCommand::OPTION_MUTEX)) { + if ($this->option('graceful')) { + $this->components->warn($e->getMessage()); + + return self::SUCCESS; + } + return self::FAILURE; } elseif ($this->option(MutexMigrateCommand::OPTION_MUTEX_GRACEFUL)) { $this->components->warn('Falling back to a standard migration'); From 76eb5ea24f79f18dab1ab74c8d39ccdb9cacebbf Mon Sep 17 00:00:00 2001 From: Tom Moore Date: Wed, 26 Feb 2025 21:21:47 +0100 Subject: [PATCH 10/10] Enable existing --graceful flag --- src/Commands/MigrateCommandExtension.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Commands/MigrateCommandExtension.php b/src/Commands/MigrateCommandExtension.php index abfcbab..579d27d 100644 --- a/src/Commands/MigrateCommandExtension.php +++ b/src/Commands/MigrateCommandExtension.php @@ -27,13 +27,7 @@ public function handle(): int return $this->call(MutexMigrateCommand::class, $this->getCommandOptions()); } catch (DatabaseCacheTableNotFoundException $e) { if ($this->option(MutexMigrateCommand::OPTION_MUTEX)) { - if ($this->option('graceful')) { - $this->components->warn($e->getMessage()); - - return self::SUCCESS; - } - - return self::FAILURE; + return $this->options('graceful') ? self::SUCCESS : self::FAILURE; } elseif ($this->option(MutexMigrateCommand::OPTION_MUTEX_GRACEFUL)) { $this->components->warn('Falling back to a standard migration'); }