diff --git a/config/coold.php b/config/coold.php index 86a28a91a..2468eac40 100644 --- a/config/coold.php +++ b/config/coold.php @@ -6,4 +6,5 @@ return [ 'corrosion_version' => env('COOLIFY_CORROSION_VERSION', 'v1.0.0'), 'dev_builder_capacity' => (int) env('COOLIFY_COOLD_VM_BUILDER_CAPACITY', 2), 'dev_builder_enabled' => (int) env('COOLIFY_COOLD_VM_BUILDER_CAPACITY', 2) > 0, + 'dev_ssh_user' => env('COOLIFY_CLI_SSH_USER', get_current_user()), ]; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 4f5c4431a..3ec75c4f5 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -35,6 +35,7 @@ class DatabaseSeeder extends Seeder if (in_array(config('app.env'), ['local', 'development', 'dev'], true)) { $this->call([ DevelopmentRailpackExamplesSeeder::class, + V5DevLimaSeeder::class, ]); } } diff --git a/database/seeders/V5DevLimaSeeder.php b/database/seeders/V5DevLimaSeeder.php new file mode 100644 index 000000000..1151f8fb3 --- /dev/null +++ b/database/seeders/V5DevLimaSeeder.php @@ -0,0 +1,85 @@ +find(0) ?? Team::query()->orderBy('id')->first(); + $user = User::query()->find(0) ?? User::query()->orderBy('id')->first(); + + if (! $team instanceof Team || ! $user instanceof User) { + return; + } + + $cluster = Cluster::query()->updateOrCreate([ + 'team_id' => $team->id, + 'name' => self::CLUSTER_NAME, + ], [ + 'created_by_user_id' => $user->id, + 'description' => 'Local Lima development cluster managed by scripts/dev.sh.', + ]); + + $privateKey = PrivateKey::query() + ->where('team_id', $team->id) + ->where('is_git_related', false) + ->orderBy('id') + ->first(); + + $builderCapacity = max(0, (int) config('coold.dev_builder_capacity', 2)); + $builderEnabled = $builderCapacity > 0; + $capabilities = $builderEnabled ? ['coold', 'builder'] : ['coold']; + $sshUser = (string) config('coold.dev_ssh_user', get_current_user()); + + foreach ($this->servers() as $server) { + Server::query()->updateOrCreate([ + 'team_id' => $team->id, + 'host' => $server['host'], + 'ssh_port' => $server['ssh_port'], + ], [ + 'cluster_id' => $cluster->id, + 'created_by_user_id' => $user->id, + 'private_key_id' => $privateKey?->id, + 'name' => $server['name'], + 'ssh_user' => $sshUser, + 'status' => 'installed', + 'capabilities' => $capabilities, + 'builder_enabled' => $builderEnabled, + 'builder_capacity' => $builderCapacity, + 'last_bootstrapped_at' => now(), + ]); + } + } + + /** + * @return array + */ + private function servers(): array + { + return [ + [ + 'name' => 'coold-dev', + 'host' => 'lima-coold-dev', + 'ssh_port' => 22, + ], + [ + 'name' => 'coold-dev-2', + 'host' => 'lima-coold-dev-2', + 'ssh_port' => 22, + ], + ]; + } +} diff --git a/scripts/dev.sh b/scripts/dev.sh index e0f6014a4..6b8a600d0 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -417,29 +417,17 @@ follow_logs() { } sync_v5_dev_lima_servers() { - local count - local builder_capacity - local args=() - local instance - local node + local ssh_user - count="$(coold_vm_count)" - builder_capacity="$(read_coolify_env COOLIFY_COOLD_VM_BUILDER_CAPACITY 2)" - - for index in $(seq 1 "$count"); do - instance="$(coold_vm_instance "$index")" - node="$(lima_ssh_target "$index")" - args+=(--server "${instance}|${node}|$(coolify_ssh_user)|22") - done + ssh_user="$(coolify_ssh_user)" echo "==> Running pending migrations before syncing v5 dev Lima state..." spin exec -T coolify php artisan migrate --force - echo "==> Syncing dev Lima VM(s) into v5 clusters/servers..." - spin exec -T coolify php artisan v5:sync-dev-lima-servers \ - --cluster="Development-Lima" \ - --builder-capacity="$builder_capacity" \ - "${args[@]}" + echo "==> Seeding dev Lima VM(s) into v5 clusters/servers..." + spin exec -T \ + -e COOLIFY_CLI_SSH_USER="$ssh_user" \ + coolify php artisan db:seed --class=V5DevLimaSeeder --force } configure_flux_dev_for_vm() { diff --git a/tests/Feature/DevEnvironmentExampleTest.php b/tests/Feature/DevEnvironmentExampleTest.php index d5f2320cd..2462a113e 100644 --- a/tests/Feature/DevEnvironmentExampleTest.php +++ b/tests/Feature/DevEnvironmentExampleTest.php @@ -29,3 +29,11 @@ it('defaults coold dev VM settings in Laravel config', function () { ->and(config('coold.dev_builder_capacity'))->toBe(2) ->and(config('coold.dev_builder_enabled'))->toBeTrue(); }); + +it('runs the v5 dev Lima seeder with the normal development database seeder', function () { + $databaseSeeder = file_get_contents(database_path('seeders/DatabaseSeeder.php')); + $developmentSeederBlock = str($databaseSeeder)->after("if (in_array(config('app.env'), ['local', 'development', 'dev'], true)) {")->before(' }')->toString(); + + expect($developmentSeederBlock)->toContain('DevelopmentRailpackExamplesSeeder::class') + ->and($developmentSeederBlock)->toContain('V5DevLimaSeeder::class'); +}); diff --git a/tests/Feature/DevScriptFirewallDelegationTest.php b/tests/Feature/DevScriptFirewallDelegationTest.php index f243adc55..b776ef38f 100644 --- a/tests/Feature/DevScriptFirewallDelegationTest.php +++ b/tests/Feature/DevScriptFirewallDelegationTest.php @@ -59,13 +59,14 @@ it('retries dev coolify bootstrap because fresh Lima setup can complete across p ->and($script)->toContain('fresh Lima hosts can finish setup after partial bootstrap phases'); }); -it('syncs bootstrapped Lima VMs into v5 development server state', function () { +it('seeds bootstrapped Lima VMs into v5 development server state', function () { $script = file_get_contents(base_path('scripts/dev.sh')); expect($script)->toContain('sync_v5_dev_lima_servers()') - ->and($script)->toContain('v5:sync-dev-lima-servers') - ->and($script)->toContain('--cluster="Development-Lima"') - ->and($script)->toContain('--server "${instance}|${node}|$(coolify_ssh_user)|22"'); + ->and($script)->toContain('COOLIFY_CLI_SSH_USER="$ssh_user"') + ->and($script)->toContain('db:seed --class=V5DevLimaSeeder --force') + ->and($script)->not->toContain('v5:sync-dev-lima-servers') + ->and($script)->not->toContain('--server "${instance}|${node}|$(coolify_ssh_user)|22"'); }); it('supports down cleanup as the preferred VM cleanup command', function () { diff --git a/tests/Feature/V5/HomeTest.php b/tests/Feature/V5/HomeTest.php index e914f6cef..363efb56e 100644 --- a/tests/Feature/V5/HomeTest.php +++ b/tests/Feature/V5/HomeTest.php @@ -11,6 +11,7 @@ use App\Models\User; use App\Models\V5\Cluster; use App\Models\V5\Server as V5Server; use App\Services\Flux\FluxHealth; +use Database\Seeders\V5DevLimaSeeder; use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Process; @@ -465,6 +466,28 @@ it('syncs dev Lima VMs into v5 clusters and servers', function () { ->and(V5Server::query()->where('name', 'coold-dev-2')->where('host', 'lima-coold-dev-2')->exists())->toBeTrue(); }); +it('seeds dev Lima VMs into v5 clusters and servers idempotently', function () { + createSharedUserAndTeamTables(); + [$user, $team] = createV5UserWithTeam(); + createV5PrivateKey($team, 'Dev Lima Key'); + config()->set('coold.dev_builder_capacity', 2); + + (new V5DevLimaSeeder)->run(); + (new V5DevLimaSeeder)->run(); + + $cluster = Cluster::query()->where('name', 'Development-Lima')->sole(); + + expect($cluster->team_id)->toBe($team->id) + ->and($cluster->created_by_user_id)->toBe($user->id) + ->and($cluster->description)->toBe('Local Lima development cluster managed by scripts/dev.sh.') + ->and(V5Server::query()->count())->toBe(2) + ->and(V5Server::query()->where('name', 'coold-dev')->where('host', 'lima-coold-dev')->where('ssh_user', get_current_user())->where('ssh_port', 22)->exists())->toBeTrue() + ->and(V5Server::query()->where('name', 'coold-dev-2')->where('host', 'lima-coold-dev-2')->where('ssh_user', get_current_user())->where('ssh_port', 22)->exists())->toBeTrue() + ->and(V5Server::query()->where('status', 'installed')->count())->toBe(2) + ->and(V5Server::query()->where('builder_enabled', true)->where('builder_capacity', 2)->count())->toBe(2) + ->and(V5Server::query()->where('cluster_id', $cluster->id)->count())->toBe(2); +}); + function fakeFluxHealth(bool $available = true, string $message = 'Flux is running.'): void { app()->instance(FluxHealth::class, Mockery::mock(FluxHealth::class, function (MockInterface $mock) use ($available, $message) {