Repository: appwrite/appwrite
File: src/Appwrite/Platform/Modules/Functions/Workers/Builds.php (PHP)

Code before:
            }

            if ($resource->getCollection() === 'sites') {
                // TODO: Refactor with structured command in future, using utopia library (CLI)
                $listFilesCommand = "cd /usr/local/build && cd " . \escapeshellarg($resource->getAttribute('outputDirectory', './')) . " && find . -name 'node_modules' -prune -o -type f -print";
                $command = $executor->createCommand(
                    deploymentId: $deployment->getId(),
                    projectId: $project->getId(),
                    command: $listFilesCommand,
                    timeout: 15
                );

                $files = \explode("\n", $command['output']); // Parse output
                $files = \array_filter($files); // Remove empty
                $files = \array_map(fn ($file) => \trim($file), $files); // Remove whitepsaces
                $files = \array_map(fn ($file) => \str_starts_with($file, './') ? \substr($file, 2) : $file, $files); // Remove beginning ./

                $detector = new Rendering($files, $resource->getAttribute('framework', ''));
                $detector
                    ->addOption(new SSR())
                    ->addOption(new XStatic());
                $detection = $detector->detect();

                $adapter = $resource->getAttribute('adapter', null);

                if ($adapter === null) {
                    $resource->setAttribute('adapter', $detection->getName());
                    $resource->setAttribute('fallbackFile', $detection->getFallbackFile() ?? '');
                    $resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource);
                } elseif ($adapter !== $detection->getName()) {
                    throw new \Exception('Adapter mismatch. Detected: ' . $detection->getName() . ' does not match with the set adapter: ' . $adapter);
                }
            }

            $executor->deleteRuntime($project->getId(), $deployment->getId(), '-build');

            /** Update the build document */
            $deployment->setAttribute('buildStartAt', DateTime::format((new \DateTime())->setTimestamp(floor($response['startTime']))));
            $deployment->setAttribute('buildEndAt', $endTime);
            $deployment->setAttribute('buildDuration', \intval(\ceil($durationEnd - $durationStart)));
            $deployment->setAttribute('status', 'ready');
            $deployment->setAttribute('buildPath', $response['path']);
            $deployment->setAttribute('buildSize', $response['size']);

            $logs = '';
            foreach ($response['output'] as $log) {
                $logs .= $log['content'];
            }
            $deployment->setAttribute('buildLogs', $logs);

            $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);

Review comment (suggestion):
Let's follow pattern as before, `empty($resource->getAttribute('adapter', ''))`
> empty() check, and empty string default

Code after:

            if ($resource->getCollection() === 'sites') {
                // TODO: Refactor with structured command in future, using utopia library (CLI)
                $listFilesCommand = "cd /usr/local/build && cd " . \escapeshellarg($resource->getAttribute('outputDirectory', './')) . " && find . -name 'node_modules' -prune -o -type f -print";
                $command = $executor->createCommand(
                    deploymentId: $deployment->getId(),
                    projectId: $project->getId(),
                    command: $listFilesCommand,
                    timeout: 15
                );

                $files = \explode("\n", $command['output']); // Parse output
                $files = \array_filter($files); // Remove empty
                $files = \array_map(fn ($file) => \trim($file), $files); // Remove whitepsaces
                $files = \array_map(fn ($file) => \str_starts_with($file, './') ? \substr($file, 2) : $file, $files); // Remove beginning ./

                $detector = new Rendering($files, $resource->getAttribute('framework', ''));
                $detector
                    ->addOption(new SSR())
                    ->addOption(new XStatic());
                $detection = $detector->detect();

                $adapter = $resource->getAttribute('adapter', '');

                if (empty($adapter)) {
                    $resource->setAttribute('adapter', $detection->getName());
                    $resource->setAttribute('fallbackFile', $detection->getFallbackFile() ?? '');
                    $resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource);
                } elseif ($adapter === 'ssr' && $detection->getName() === 'static') {
                    throw new \Exception('Adapter mismatch. Detected: ' . $detection->getName() . ' does not match with the set adapter: ' . $adapter);
                }
            }

            $executor->deleteRuntime($project->getId(), $deployment->getId(), '-build');

            /** Update the build document */
            $deployment->setAttribute('buildStartAt', DateTime::format((new \DateTime())->setTimestamp(floor($response['startTime']))));
            $deployment->setAttribute('buildEndAt', $endTime);
            $deployment->setAttribute('buildDuration', \intval(\ceil($durationEnd - $durationStart)));
            $deployment->setAttribute('status', 'ready');
            $deployment->setAttribute('buildPath', $response['path']);
            $deployment->setAttribute('buildSize', $response['size']);

            $logs = '';
            foreach ($response['output'] as $log) {
                $logs .= $log['content'];
            }
            $deployment->setAttribute('buildLogs', $logs);

            $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);