diff --git a/app/Models/Server.php b/app/Models/Server.php index 340d4d2bcc..086b9897f0 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -1728,25 +1728,34 @@ $siteAddress { public function validateOS(): bool|Stringable { $os_release = instant_remote_process(['cat /etc/os-release'], $this); - $releaseLines = collect(explode("\n", $os_release)); - $collectedData = collect([]); - foreach ($releaseLines as $line) { - $item = str($line)->trim(); - $collectedData->put($item->before('=')->value(), $item->after('=')->lower()->replace('"', '')->value()); - } - $ID = data_get($collectedData, 'ID'); - // $ID_LIKE = data_get($collectedData, 'ID_LIKE'); - // $VERSION_ID = data_get($collectedData, 'VERSION_ID'); - $supported = collect(SUPPORTED_OS)->filter(function ($supportedOs) use ($ID) { - if (str($supportedOs)->contains($ID)) { - return str($ID); + + return self::supportedOsFromOsRelease($os_release); + } + + /** + * Match /etc/os-release against SUPPORTED_OS: ID first, then each ID_LIKE token (e.g. Manjaro: ID=manjaro, ID_LIKE=arch). + */ + public static function supportedOsFromOsRelease(?string $osRelease): bool|Stringable + { + $collectedData = collect(explode("\n", (string) $osRelease)) + ->mapWithKeys(function ($line) { + $item = str($line)->trim(); + + return [$item->before('=')->value() => $item->after('=')->lower()->replace(['"', "'"], '')->value()]; + }); + + $candidates = collect([data_get($collectedData, 'ID')]) + ->merge(explode(' ', (string) data_get($collectedData, 'ID_LIKE'))) + ->filter(); + + foreach ($candidates as $candidate) { + $supported = collect(SUPPORTED_OS)->filter(fn ($supportedOs) => str($supportedOs)->contains($candidate)); + if ($supported->count() === 1) { + return str($supported->first()); } - }); - if ($supported->count() === 1) { - return str($supported->first()); - } else { - return false; } + + return false; } public function gatherServerMetadata(): ?array diff --git a/tests/Unit/ServerSupportedOsTest.php b/tests/Unit/ServerSupportedOsTest.php new file mode 100644 index 0000000000..25b3fa0fb1 --- /dev/null +++ b/tests/Unit/ServerSupportedOsTest.php @@ -0,0 +1,24 @@ +value())->toBe($expected); +})->with([ + 'ubuntu by ID' => ["ID=ubuntu\nID_LIKE=debian", 'ubuntu debian raspbian pop'], + 'arch by ID' => ['ID=arch', 'arch'], + 'rocky by ID' => ["ID=\"rocky\"\nID_LIKE=\"rhel centos fedora\"", 'centos fedora rhel ol rocky amzn almalinux'], + 'omarchy by ID_LIKE' => ["NAME=\"Omarchy\"\nID=omarchy\nID_LIKE=arch", 'arch'], + 'manjaro by ID_LIKE' => ["ID=manjaro\nID_LIKE=arch", 'arch'], + 'linux mint by ID_LIKE' => ["ID=linuxmint\nID_LIKE=\"ubuntu debian\"", 'ubuntu debian raspbian pop'], + 'single-quoted ID_LIKE' => ["ID=zorin\nID_LIKE='ubuntu debian'", 'ubuntu debian raspbian pop'], + 'unsupported without ID_LIKE' => ['ID=gentoo', false], + 'unsupported ID_LIKE' => ["ID=foo\nID_LIKE=bar", false], + 'empty output' => ['', false], +]); + +it('returns false for null os-release output', function () { + expect(Server::supportedOsFromOsRelease(null))->toBeFalse(); +});