service?->id}", ), new PlannedStep( name: 'Run migrations', script: $this->service ? app(BuildMigrationScript::class)->execute($this->service) : 'true', ), new PlannedStep( name: 'Start Laravel replica', script: "docker compose -f /home/keystone/services/{$this->service?->id}/compose.yml up -d", ), ]); } public function serviceType(): ServiceType { return ServiceType::LARAVEL; } public function versionTrack(): string { return 'php-8.4'; } public function defaultImage(): string { return 'serversideup/php:8.4-frankenphp'; } public function defaultPorts(): array { return [80]; } public function firewallRules(): array { return []; } public function environmentSchema(): array { return [ 'APP_ENV' => 'string', 'SERVER_NAME' => 'string', ]; } public function resourceDefaults(): array { return []; } public function updateBehavior(): string { return 'stateless_gateway_cutover'; } public function composeService(): array { $image = $this->service?->available_image_digest ?: $this->service?->current_image_digest ?: ($this->service?->config['image'] ?? $this->defaultImage()); $service = [ 'image' => $image, 'restart' => 'unless-stopped', 'environment' => $this->environmentExports(), ]; if ($command = $this->service?->config['command'] ?? null) { $service['command'] = $command; } if (! in_array('worker', $this->service?->process_roles ?? [], true)) { $service['healthcheck'] = [ 'test' => ['CMD-SHELL', 'curl -fsS http://localhost'.($this->service?->config['health_path'] ?? '/up').' || exit 1'], 'interval' => '10s', 'timeout' => '5s', 'retries' => 5, ]; } if ($this->service?->default_cpu_limit) { $service['cpus'] = (string) $this->service->default_cpu_limit; } if ($this->service?->default_memory_limit_mb) { $service['mem_limit'] = "{$this->service->default_memory_limit_mb}m"; $service['memswap_limit'] = "{$this->service->default_memory_limit_mb}m"; } return $service; } public function composeVolumes(): array { return []; } public function environmentExports(): array { $environment = $this->service?->environment?->variables() ->pluck('value', 'key') ->all() ?? []; $environment = [ ...$environment, 'APP_ENV' => $this->service?->environment?->name ?? 'production', 'SERVER_NAME' => ':80', ]; if ($this->shouldAutorunScheduler()) { $environment['AUTORUN_LARAVEL_SCHEDULER'] = 'true'; } return $environment; } private function shouldAutorunScheduler(): bool { if (! in_array('scheduler', $this->service?->process_roles ?? [], true)) { return false; } $environment = $this->service?->environment; if (! $environment?->scheduler_enabled) { return false; } if ($environment->scheduler_target_service_id && $environment->scheduler_target_service_id !== $this->service?->id) { return false; } return $environment->scheduler_mode !== SchedulerMode::SINGLE || (int) $this->service?->desired_replicas === 1; } public function dockerfileTemplate(): string { $phpVersion = $this->service?->config['php_version'] ?? '8.4'; $documentRoot = $this->service?->config['document_root'] ?? 'public'; $jsBuildCommand = $this->service?->config['js_build_command'] ?? $this->service?->environment?->build_config['js_build_command'] ?? null; $jsPackageManager = $this->service?->config['js_package_manager'] ?? $this->service?->environment?->build_config['js_package_manager'] ?? 'bun'; $jsBuildSteps = $this->jsBuildSteps($jsPackageManager, $jsBuildCommand); return << "\nRUN npm ci && {$buildCommand}", default => "\nRUN curl -fsSL https://bun.sh/install | bash && export PATH=\"/root/.bun/bin:\$PATH\" && bun install --frozen-lockfile && {$buildCommand}", }; } }