Skip to content

Commit

Permalink
Added CPU architecture detection and option
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael committed Nov 20, 2024
1 parent 420326e commit bf3c48b
Show file tree
Hide file tree
Showing 15 changed files with 274 additions and 59 deletions.
2 changes: 1 addition & 1 deletion src/Command/BrowserCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ final protected function execute(InputInterface $input, OutputInterface $output)
$filePath = $driverDownloader->download($driver, $installPath);

$io->success(
sprintf('%s %s installed to %s', $driver->name->value, $driver->version->toBuildString(), $filePath),
sprintf('%s %s%s installed to %s', $driver->name->value, $driver->version->toBuildString(), $driver->cpuArchitecture->toCommandOutput(), $filePath),
);

return self::SUCCESS;
Expand Down
7 changes: 5 additions & 2 deletions src/Command/DriverCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ final protected function configure(): void
new Input\InstallPathArgument(),
new Input\VersionOption(),
new Input\OperatingSystemOption(),
new Input\CpuArchitectureOption(),
],
),
);
Expand All @@ -54,6 +55,7 @@ final protected function execute(InputInterface $input, OutputInterface $output)
$installPath = Input\InstallPathArgument::value($input);
$versionString = Input\VersionOption::value($input);
$operatingSystem = Input\OperatingSystemOption::value($input);
$cpuArchitecture = Input\CpuArchitectureOption::value($input);

// TODO: move this into VersionOption class
if ($versionString === Input\VersionOption::LATEST) {
Expand All @@ -68,7 +70,7 @@ final protected function execute(InputInterface $input, OutputInterface $output)
$version = Version::fromString($versionString);
}

$driver = new Driver($driverName, $version, $operatingSystem);
$driver = new Driver($driverName, $version, $operatingSystem, $cpuArchitecture);

if ($io->isVerbose()) {
$io->writeln(
Expand All @@ -81,9 +83,10 @@ final protected function execute(InputInterface $input, OutputInterface $output)

$io->success(
sprintf(
'%s %s installed to %s',
'%s %s%s installed to %s',
$driver->name->value,
$driver->version->toBuildString(),
$driver->cpuArchitecture->toCommandOutput(),
$filePath,
),
);
Expand Down
80 changes: 80 additions & 0 deletions src/Command/Input/CpuArchitectureOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace DBrekelmans\BrowserDriverInstaller\Command\Input;

use DBrekelmans\BrowserDriverInstaller\Cpu\CpuArchitecture;
use DBrekelmans\BrowserDriverInstaller\Exception\UnexpectedType;
use DBrekelmans\BrowserDriverInstaller\OperatingSystem\OperatingSystem;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use UnexpectedValueException;
use function array_map;
use function implode;
use function is_string;
use function sprintf;

/** @implements Option<OperatingSystem> */
final class CpuArchitectureOption extends InputOption implements Option
{
public function __construct()
{
parent::__construct(
self::name(),
$this->shortcut(),
$this->mode()->value,
$this->description(),
$this->default(),
);
}

public static function name(): string
{
return 'arch';
}

public function shortcut(): string|null
{
return null;
}

public function mode(): OptionMode
{
return OptionMode::REQUIRED;
}

public function description(): string
{
return sprintf(
'CPU architecture for which to install the driver (%s)',
implode('|', array_map(static fn ($case) => $case->value, CpuArchitecture::cases())),
);
}

public function default(): string|null
{
return CpuArchitecture::X86_64->value;
}

public static function value(InputInterface $input): CpuArchitecture

Check failure on line 60 in src/Command/Input/CpuArchitectureOption.php

View workflow job for this annotation

GitHub Actions / phpstan

Return type (DBrekelmans\BrowserDriverInstaller\Cpu\CpuArchitecture) of method DBrekelmans\BrowserDriverInstaller\Command\Input\CpuArchitectureOption::value() should be compatible with return type (DBrekelmans\BrowserDriverInstaller\OperatingSystem\OperatingSystem) of method DBrekelmans\BrowserDriverInstaller\Command\Input\Option<DBrekelmans\BrowserDriverInstaller\OperatingSystem\OperatingSystem>::value()
{
$value = $input->getOption(self::name());

if (! is_string($value)) {
throw UnexpectedType::expected('string', $value);
}

if (CpuArchitecture::tryFrom($value) === null) {
throw new UnexpectedValueException(
sprintf(
'Unexpected value %s. Expected one of: %s',
$value,
implode(', ', array_map(static fn ($case) => $case->value, CpuArchitecture::cases())),
),
);
}

return CpuArchitecture::from($value);
}
}
25 changes: 25 additions & 0 deletions src/Cpu/CpuArchitecture.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace DBrekelmans\BrowserDriverInstaller\Cpu;

enum CpuArchitecture: string
{
case X86_64 = 'x86_64';
case ARM64 = 'arm64';

public function toCommandOutput(): string
{
return match ($this) {
self::X86_64 => '',
self::ARM64 => ' arm64',
};
}

public static function detectFromPhp(): CpuArchitecture
{
return match (php_uname('m')) {
'arm64', 'aarch64' => CpuArchitecture::ARM64,
default => CpuArchitecture::X86_64,
};
}
}
12 changes: 11 additions & 1 deletion src/Driver/ChromeDriver/DownloadUrlResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace DBrekelmans\BrowserDriverInstaller\Driver\ChromeDriver;

use DBrekelmans\BrowserDriverInstaller\Cpu\CpuArchitecture;
use DBrekelmans\BrowserDriverInstaller\Driver\DownloadUrlResolver as DownloadUrlResolverInterface;
use DBrekelmans\BrowserDriverInstaller\Driver\Driver;
use DBrekelmans\BrowserDriverInstaller\OperatingSystem\OperatingSystem;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use UnexpectedValueException;

use function is_string;
use function sprintf;

Expand Down Expand Up @@ -40,6 +40,10 @@ public function byDriver(Driver $driver): string
throw new UnexpectedValueException(sprintf('Could not find the chromedriver downloads for version %s', $driver->version->toString()));
}

if ($driver->cpuArchitecture === CpuArchitecture::ARM64 && $driver->operatingSystem !== OperatingSystem::MACOS) {
throw new UnexpectedValueException('Chromedriver ARM64 is only available on macOS.');
}

$platformName = $this->getPlatformName($driver);
$downloads = $versions['builds'][$driver->version->toString()]['downloads']['chromedriver'];
foreach ($downloads as $download) {
Expand All @@ -59,6 +63,9 @@ public function byDriver(Driver $driver): string

private function getBinaryName(Driver $driver): string
{
if ($driver->operatingSystem === OperatingSystem::MACOS && $driver->cpuArchitecture === CpuArchitecture::ARM64) {
return 'chromedriver_mac_arm64';
}
return match ($driver->operatingSystem) {
OperatingSystem::LINUX => 'chromedriver_linux64',
OperatingSystem::MACOS => 'chromedriver_mac64',
Expand All @@ -68,6 +75,9 @@ private function getBinaryName(Driver $driver): string

private function getPlatformName(Driver $driver): string
{
if ($driver->cpuArchitecture === CpuArchitecture::ARM64) {
return 'mac-arm64';
}
return match ($driver->operatingSystem) {
OperatingSystem::LINUX => 'linux64',
OperatingSystem::MACOS => 'mac-x64',
Expand Down
16 changes: 11 additions & 5 deletions src/Driver/ChromeDriver/Downloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace DBrekelmans\BrowserDriverInstaller\Driver\ChromeDriver;

use DBrekelmans\BrowserDriverInstaller\Archive\Extractor;
use DBrekelmans\BrowserDriverInstaller\Cpu\CpuArchitecture;
use DBrekelmans\BrowserDriverInstaller\Driver\Downloader as DownloaderInterface;
use DBrekelmans\BrowserDriverInstaller\Driver\DownloadUrlResolver;
use DBrekelmans\BrowserDriverInstaller\Driver\Driver;
Expand All @@ -18,15 +19,13 @@
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use UnexpectedValueException;

use function in_array;
use function Safe\fclose;
use function Safe\fopen;
use function Safe\fwrite;
use function sprintf;
use function str_replace;
use function sys_get_temp_dir;

use const DIRECTORY_SEPARATOR;

final class Downloader implements DownloaderInterface
Expand All @@ -44,6 +43,9 @@ public function __construct(

public function supports(Driver $driver): bool
{
if ($driver->cpuArchitecture === CpuArchitecture::ARM64 && $driver->operatingSystem === OperatingSystem::LINUX) {
return false;
}
return $driver->name === DriverName::CHROME;
}

Expand Down Expand Up @@ -179,7 +181,7 @@ private function getFileName(OperatingSystem $operatingSystem): string
*/
public function cleanArchiveStructure(Driver $driver, string $unzipLocation, array $extractedFiles): array
{
$archiveDirectory = $this->getArchiveDirectory($driver->operatingSystem);
$archiveDirectory = $this->getArchiveDirectory($driver);
$filename = $this->getFileName($driver->operatingSystem);
$this->filesystem->rename(
$unzipLocation . DIRECTORY_SEPARATOR . $archiveDirectory . $filename,
Expand All @@ -190,9 +192,13 @@ public function cleanArchiveStructure(Driver $driver, string $unzipLocation, arr
return str_replace($archiveDirectory, '', $extractedFiles);
}

private function getArchiveDirectory(OperatingSystem $operatingSystem): string
private function getArchiveDirectory(Driver $driver): string
{
return match ($operatingSystem) {
if ($driver->operatingSystem === OperatingSystem::MACOS && $driver->cpuArchitecture === CpuArchitecture::ARM64) {
return 'chromedriver-mac-arm64/';
}

return match ($driver->operatingSystem) {
OperatingSystem::LINUX => 'chromedriver-linux64/',
OperatingSystem::WINDOWS => 'chromedriver-win32/', // This weirdly contains a forward slash on windows
OperatingSystem::MACOS => 'chromedriver-mac-x64/',
Expand Down
2 changes: 1 addition & 1 deletion src/Driver/DownloaderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function createFromDriver(Driver $driver): Downloader
}
}

throw NotImplemented::feature(sprintf('Downloader for %s', $driver->name->value));
throw NotImplemented::feature(sprintf('Downloader for %s %s', $driver->name->value, $driver->cpuArchitecture->value));
}

public function register(Downloader $downloader, string|null $identifier = null): void
Expand Down
2 changes: 2 additions & 0 deletions src/Driver/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace DBrekelmans\BrowserDriverInstaller\Driver;

use DBrekelmans\BrowserDriverInstaller\Cpu\CpuArchitecture;
use DBrekelmans\BrowserDriverInstaller\OperatingSystem\OperatingSystem;
use DBrekelmans\BrowserDriverInstaller\Version;

Expand All @@ -13,6 +14,7 @@ public function __construct(
public readonly DriverName $name,
public readonly Version $version,
public readonly OperatingSystem $operatingSystem,
public readonly CpuArchitecture $cpuArchitecture,
) {
}
}
6 changes: 5 additions & 1 deletion src/Driver/DriverFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use DBrekelmans\BrowserDriverInstaller\Browser\Browser;
use DBrekelmans\BrowserDriverInstaller\Browser\BrowserName;
use DBrekelmans\BrowserDriverInstaller\Cpu\CpuArchitecture;

final class DriverFactory
{
Expand All @@ -20,7 +21,10 @@ public function createFromBrowser(Browser $browser): Driver

$name = $this->getDriverNameForBrowser($browser);

return new Driver($name, $version, $browser->operatingSystem);
// Detect CPU arch
$cpuArchitecture = CpuArchitecture::detectFromPhp();

return new Driver($name, $version, $browser->operatingSystem, $cpuArchitecture);
}

private function getDriverNameForBrowser(Browser $browser): DriverName
Expand Down
Loading

0 comments on commit bf3c48b

Please sign in to comment.