diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 12b0567..d1d1302 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -25,7 +25,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v4 + uses: actions/cache@v2 with: path: ${{ steps.composer-cache.outputs.dir }} key: composer-${{ hashFiles('**/composer.lock') }} diff --git a/app/Services/Forge/Actions/RecreateDatabase.php b/app/Services/Forge/Actions/RecreateDatabase.php deleted file mode 100644 index 288742b..0000000 --- a/app/Services/Forge/Actions/RecreateDatabase.php +++ /dev/null @@ -1,93 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace App\Services\Forge\Actions; - -use App\Services\Forge\ForgeService; -use App\Traits\Outputifier; - -class RecreateDatabase -{ - use Outputifier; - - /** - * Handle database recreation and creation process - * - * @param ForgeService $service The forge service - * @param string $dbName The database name - * @param string $dbPassword Password for database creation - * @return bool True if database needs to be updated in the env file - */ - public function handle(ForgeService $service, string $dbName, string $dbPassword): bool - { - $databaseId = null; - foreach ($service->forge->databases($service->server->id) as $database) { - if (isset($database->name) && $database->name === $dbName) { - $databaseId = $database->id; - } - } - - // If the setting is not enabled, we skip the deletion of existing databases. - if (isset($databaseId) && !$service->setting->forceDeleteOldDatabase) { - $this->warning('It seems there is an existing database with the same name. Skipping database creation. Ensure to update the database password in the .env file manually.'); - return true; // Still need to update env vars even if we don't create DB - } - - if (isset($databaseId)) { - $this->information('Force deleting existing matched database.'); - - $service->forge->deleteDatabase($service->server->id, $databaseId); - $this->information('---> Existing database deleted: ' . $dbName); - } - - foreach ($service->forge->databaseUsers($service->server->id) as $user) { - if (isset($user->name) && $user->name === $dbName) { - $service->forge->deleteDatabaseUser($service->server->id, $user->id); - $this->information('---> Existing database user found and deleted: ' . $dbName); - } - } - - if (isset($databaseId)) { - $this->information('---> Waiting for the database deletion to complete...'); - $this->waitForDatabaseDeletion(); - } - - // Create the database with the provided password - $this->information('Creating database.'); - $this->createDatabase($service, $dbName, $dbPassword); - - return true; - } - - /** - * Wait for database deletion to complete - */ - protected function waitForDatabaseDeletion(): void - { - sleep(6); - } - - /** - * Create a new database on the server - */ - public function createDatabase(ForgeService $service, string $dbName, string $dbPassword): void - { - $service->forge->createDatabase($service->server->id, [ - 'name' => $dbName, - 'user' => $dbName, - 'password' => $dbPassword, - ]); - - $this->information('Database created: ' . $dbName); - } -} diff --git a/app/Services/Forge/ForgeSetting.php b/app/Services/Forge/ForgeSetting.php index bb2e358..ab03b51 100644 --- a/app/Services/Forge/ForgeSetting.php +++ b/app/Services/Forge/ForgeSetting.php @@ -129,11 +129,6 @@ class ForgeSetting */ public bool $dbCreationRequired; - /** - * Flag indicating if Harbor should remove the existing old database. - */ - public bool $forceDeleteOldDatabase; - /** * The name of the database to be created */ @@ -263,7 +258,6 @@ protected function validate(array $configurations): \Illuminate\Validation\Valid 'site_isolation_username' => ['nullable', 'string'], 'job_scheduler_required' => ['boolean'], 'db_creation_required' => ['boolean'], - 'force_delete_old_database' => ['boolean'], 'db_name' => ['nullable', 'string'], 'auto_source_required' => ['boolean'], 'ssl_required' => ['boolean'], diff --git a/app/Services/Forge/Pipeline/CreateDatabase.php b/app/Services/Forge/Pipeline/CreateDatabase.php index e3bee7a..8ea8203 100644 --- a/app/Services/Forge/Pipeline/CreateDatabase.php +++ b/app/Services/Forge/Pipeline/CreateDatabase.php @@ -13,7 +13,6 @@ namespace App\Services\Forge\Pipeline; -use App\Services\Forge\Actions\RecreateDatabase; use App\Services\Forge\ForgeService; use App\Traits\Outputifier; use Closure; @@ -22,14 +21,7 @@ class CreateDatabase { use Outputifier; - - private RecreateDatabase $recreateDatabase; - public function __construct(RecreateDatabase $recreateDatabase = null) - { - $this->recreateDatabase = $recreateDatabase ?? new RecreateDatabase(); - } - public function __invoke(ForgeService $service, Closure $next) { if (! $service->setting->dbCreationRequired || ! $service->siteNewlyMade) { @@ -38,9 +30,12 @@ public function __invoke(ForgeService $service, Closure $next) $dbName = $service->getFormattedDatabaseName(); $dbPassword = Str::random(16); - - // Handle DB recreation and creation in one call - $this->recreateDatabase->handle($service, $dbName, $dbPassword); + + if (! $this->databaseExists($service, $dbName)) { + $this->information('Creating database.'); + + $this->createDatabase($service, $dbName, $dbPassword); + } $service->setDatabase([ 'DB_DATABASE' => $dbName, @@ -51,7 +46,23 @@ public function __invoke(ForgeService $service, Closure $next) return $next($service); } + protected function databaseExists(ForgeService $service, string $dbName): bool + { + foreach ($service->forge->databases($service->server->id) as $database) { + if ($database->name === $dbName) { + return true; + } + } + return false; + } - + protected function createDatabase(ForgeService $service, string $dbName, string $dbPassword): void + { + $service->forge->createDatabase($service->server->id, [ + 'name' => $dbName, + 'user' => $dbName, + 'password' => $dbPassword, + ]); + } } diff --git a/config/forge.php b/config/forge.php index c950789..4b25727 100644 --- a/config/forge.php +++ b/config/forge.php @@ -61,9 +61,6 @@ // Flag indicating if a database should be created (default: false). 'db_creation_required' => env('FORGE_DB_CREATION_REQUIRED', false), - // Flag indicating if Harbor should remove the existing old database (default: false). - 'force_delete_old_database' => env('FORCE_DELETE_OLD_DATABASE', false), - // Flag to enable Quick Deploy (default: true). 'quick_deploy' => env('FORGE_QUICK_DEPLOY', false), diff --git a/tests/Unit/Services/Forge/Actions/RecreateDatabaseTest.php b/tests/Unit/Services/Forge/Actions/RecreateDatabaseTest.php deleted file mode 100644 index 4afe480..0000000 --- a/tests/Unit/Services/Forge/Actions/RecreateDatabaseTest.php +++ /dev/null @@ -1,152 +0,0 @@ -action = new RecreateDatabase(); - $this->serverId = '12345'; - $this->dbName = 'test_db'; - $this->dbPassword = 'test_password'; - - // Create mocks that will be used by all tests - $this->forgeMock = Mockery::mock(Forge::class); - $this->settingMock = Mockery::mock(ForgeSetting::class); - $this->server = new Server(['id' => $this->serverId]); - - // Setup base service mock that can be customized per test - $this->service = Mockery::mock(ForgeService::class); - $this->service->forge = $this->forgeMock; - $this->service->server = $this->server; -}); - -afterEach(function () { - Mockery::close(); -}); - -it('skips recreation when database exists and force delete disabled', function () { - // Configure setting for this specific test - $this->settingMock->forceDeleteOldDatabase = false; - $this->service->setting = $this->settingMock; - - // Mock database exists - $this->forgeMock->shouldReceive('databases') - ->once() - ->with($this->serverId) - ->andReturn([ - (object) ['name' => $this->dbName, 'id' => 99], - ]); - - // Database should not be deleted - $this->forgeMock->shouldNotReceive('deleteDatabase'); - $this->forgeMock->shouldNotReceive('deleteDatabaseUser'); - - // Database should not be created when skipping recreation - $this->forgeMock->shouldNotReceive('createDatabase'); - - // Act - $result = $this->action->handle($this->service, $this->dbName, $this->dbPassword); - - // Assert - expect($result)->toBeTrue(); // Should return true to indicate env vars need updating -}); - -it('recreates database when exists and force delete enabled', function () { - // Configure setting for this specific test - $this->settingMock->forceDeleteOldDatabase = true; - $this->service->setting = $this->settingMock; - - // Mock existing database - $dbId = 99; - $userId = 88; - - $this->forgeMock->shouldReceive('databases') - ->once() - ->with($this->serverId) - ->andReturn([ - (object) ['name' => $this->dbName, 'id' => $dbId], - ]); - - // Should delete the database - $this->forgeMock->shouldReceive('deleteDatabase') - ->once() - ->with($this->serverId, $dbId); - - // Mock existing user - $this->forgeMock->shouldReceive('databaseUsers') - ->once() - ->with($this->serverId) - ->andReturn([ - (object) ['name' => $this->dbName, 'id' => $userId], - ]); - - // Should delete the database user - $this->forgeMock->shouldReceive('deleteDatabaseUser') - ->once() - ->with($this->serverId, $userId); - - // Should create a new database - $this->forgeMock->shouldReceive('createDatabase') - ->once() - ->with($this->serverId, [ - 'name' => $this->dbName, - 'user' => $this->dbName, - 'password' => $this->dbPassword, - ]); - - // Create a partial mock to skip actual waiting - $actionSpy = Mockery::mock(RecreateDatabase::class)->makePartial(); - $actionSpy->shouldAllowMockingProtectedMethods(); - $actionSpy->shouldReceive('waitForDatabaseDeletion')->once()->andReturn(null); - - // Act - $result = $actionSpy->handle($this->service, $this->dbName, $this->dbPassword); - - // Assert - expect($result)->toBeTrue(); -}); - -it('creates new database when none exists', function () { - // Set the service to use the mocked setting - $this->service->setting = $this->settingMock; - - // No existing database - $this->forgeMock->shouldReceive('databases') - ->once() - ->with($this->serverId) - ->andReturn([ - (object) ['name' => 'other_db', 'id' => 55], - ]); - - // Check for users (none match) - $this->forgeMock->shouldReceive('databaseUsers') - ->once() - ->with($this->serverId) - ->andReturn([ - (object) ['name' => 'other_user', 'id' => 66], - ]); - - // Should create a new database - $this->forgeMock->shouldReceive('createDatabase') - ->once() - ->with($this->serverId, [ - 'name' => $this->dbName, - 'user' => $this->dbName, - 'password' => $this->dbPassword, - ]); - - // We should not wait for deletion since nothing was deleted - $actionSpy = Mockery::mock(RecreateDatabase::class)->makePartial(); - $actionSpy->shouldAllowMockingProtectedMethods(); - $actionSpy->shouldNotReceive('waitForDatabaseDeletion'); - - // Act - $result = $actionSpy->handle($this->service, $this->dbName, $this->dbPassword); - - // Assert - expect($result)->toBeTrue(); -});