Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT]: Add install commands #4

Merged
merged 30 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b6f2132
fix download helper
asciito Oct 11, 2023
93ff097
remove unnecessary files
asciito Oct 19, 2023
007971a
build: add dependencies for testing and debugging
asciito Oct 19, 2023
5be96c8
chore: ignore log files
asciito Oct 19, 2023
ea8c15b
feat: add 'install:browser' command
asciito Oct 19, 2023
38f8d41
refactor: remove trailing comma in composer.json
asciito Oct 19, 2023
7da59e5
build: add log dependency
asciito Oct 19, 2023
d87be4c
refactor: add unzip function
asciito Oct 19, 2023
788c0ad
refactor: change filename method for a generic one
asciito Oct 19, 2023
a5a2fa6
refactor: remove zip file at the end
asciito Oct 19, 2023
f081b3a
style: fix typo
asciito Oct 19, 2023
480027f
refactor: re-order arguments
asciito Oct 19, 2023
546ff8d
feat: add 'install:driver' command
asciito Oct 19, 2023
5b80fca
refactor: change base class
asciito Oct 19, 2023
33be908
refactor: change exception message
asciito Oct 19, 2023
f7e4606
refactor: remove unnecessary code
asciito Oct 20, 2023
e170818
test: change first two test
asciito Oct 20, 2023
f49ccef
refactor: add download method on GoogleDownloadable class
asciito Oct 20, 2023
de8e84f
test: add test to download browser in other path
asciito Oct 20, 2023
60f1a53
chore: fix styling
asciito Oct 20, 2023
820b2c1
refactor: download method
asciito Oct 20, 2023
8be42ba
fix: getBasePath to handle empty parameter
asciito Oct 20, 2023
dc139b4
refactor: change getFilename for getDownloadDirectory
asciito Oct 20, 2023
dfc1835
test: add final test for 'install:browser' command
asciito Oct 20, 2023
5f1c525
chore: fix styling
asciito Oct 20, 2023
aa00ff2
test: add chrome driver test
asciito Oct 20, 2023
02aff2c
refactor: remove unnecessary code
asciito Oct 20, 2023
15d1128
refactor: remove repeated code
asciito Oct 20, 2023
47d2f20
test: remove unnecessary code
asciito Oct 20, 2023
d2128f6
chore: fix styling
asciito Oct 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
/.vagrant
.phpunit.result.cache
.DS_Store
composer.lock
composer.lock
/storage/logs/*
!/storage/logs/.gitkeep
25 changes: 25 additions & 0 deletions app/Commands/InstallBrowserCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace App\Commands;

use App\GoogleDownloadable;

class InstallBrowserCommand extends InstallCommand
{
protected int $component = GoogleDownloadable::BROWSER;

protected $name = 'install:browser';

/**
* The description of the command.
*
* @var string
*/
protected $description = 'Install Google Browser';

/**
* Execute the console command.
*
* @return mixed
*/
}
166 changes: 166 additions & 0 deletions app/Commands/InstallCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

namespace App\Commands;

use App\Facades\GoogleForTesting;
use App\GoogleDownloadable;
use App\OperatingSystem;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Symfony\Component\Console\Input\InputOption;

use function Laravel\Prompts\error;
use function Laravel\Prompts\search;
use function Laravel\Prompts\spin;
use function Laravel\Prompts\warning;
use function Termwind\render;

abstract class InstallCommand extends Command
{
protected int $component;

protected array $platforms = [
'linux' => 'linux64',
'mac-arm' => 'mac-arm64',
'mac-intel' => 'mac-x64',
'win' => 'win64',
];

protected function configure(): void
{
$this->addOption(
'ver',
null,
InputOption::VALUE_OPTIONAL,
'Install a specific version',
'115.0.5763.0',
);

$this->addOption(
'latest',
null,
InputOption::VALUE_NONE,
'Install the latest version',
);

$this->addOption(
'path',
null,
InputOption::VALUE_OPTIONAL,
'Specify the path where to download it',
);
}

public function handle(): int
{
if (empty($downloadable = $this->version())) {
error("There' no versions available for [{$this->option('ver')}]");

return self::FAILURE;
}

$os = OperatingSystem::id();

$version = $downloadable->getVersion();

try {
spin(
callback: fn () => $downloadable->download(GoogleDownloadable::BROWSER, $this->getDownloadDirectory(), $this->platforms[$os], true),
message: "Downloading Google Chrome {$this->getComponentName()} [$version]"
);

$this->message("Google Chrome {$this->getComponentName()} unzip it on [{$this->getDownloadDirectory()}]", 'info');
} catch (\Throwable $e) {
Log::error($e->getMessage());

error("Unable to download/install Google Chrome {$this->getComponentName()} [$version]");

return self::FAILURE;
}

$this->message("Google Chrome {$this->getComponentName()} [$version] downloaded", 'success');

return self::SUCCESS;
}

protected function version(): ?GoogleDownloadable
{
if ($this->option('latest')) {
return GoogleForTesting::getLatestVersion();
}

$version = $this->option('ver');

$downloadable = spin(
callback: fn () => GoogleForTesting::getVersion($version),
message: "Searching for version [$version]"
);

if (filled($downloadable)) {
return $downloadable;
}

$versions = GoogleForTesting::getMilestone(Str::before($version, '.'));

if (empty($versions)) {
return null;
}

warning("There isn't an exact version [$version]");

$version = search(
label: 'We found similar versions, please choose one',
options: fn () => $versions->mapWithKeys(fn ($d) => [$d->getVersion() => $d->getVersion()])->all(),
placeholder: 'Choose your prefer version'
);

return GoogleForTesting::getVersion($version);
}

protected function getBasePath(string $path = null): string
{
$folder = join_paths(getenv('HOME'), '.google-for-testing');

File::ensureDirectoryExists($folder);

return join_paths($folder, $path ?? '');
}

public function message(string $text, string $type = 'line'): void
{
$color = match ($type) {
'success' => 'bg-green',
'warning' => 'bg-yellow',
'error' => 'bg-red',
'info' => 'bg-blue',
default => 'bg-gray-600',
};

$type = str($type)->upper();

render(<<<HTML
<p>
<span class="text-white $color px-2 mr-2">$type</span>

<span>$text</span>
</p>
HTML);
}

protected function getDownloadDirectory(): string
{
return $this->option('path') ?? $this->getBasePath();
}

protected function getComponent(): int
{
return $this->component;
}

protected function getComponentName(): string
{
return $this->getComponent() === GoogleDownloadable::BROWSER ? 'Browser' : 'Driver';
}
}
19 changes: 19 additions & 0 deletions app/Commands/InstallDriverCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace App\Commands;

use App\GoogleDownloadable;

class InstallDriverCommand extends InstallCommand
{
protected int $component = GoogleDownloadable::DRIVER;

protected $name = 'install:driver';

/**
* The description of the command.
*
* @var string
*/
protected $description = 'Install Google Driver';
}
20 changes: 20 additions & 0 deletions app/Facades/GoogleForTesting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Facades;

use App\GoogleDownloadable;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;

/**
* @method static null|GoogleDownloadable getLatestVersion() Get the latest version of Google Chrome Browser and Google Chrome Driver
* @method static null|GoogleDownloadable getVersion(string $version) Get a specific version
* @method static null|Collection<GoogleDownloadable> getMilestone(string $version) Get a collection with all the versions available for a Milestone
*/
class GoogleForTesting extends Facade
{
public static function getFacadeAccessor(): string
{
return 'gft';
}
}
97 changes: 97 additions & 0 deletions app/GoogleDownloadable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

namespace App;

use Illuminate\Support\Str;

class GoogleDownloadable
{
const BROWSER = 1;

const DRIVER = 2;

protected function __construct(
protected string $version,
protected string $revision,
protected array $browserDownloads,
protected array $driverDownloads
) {
//
}

public function getVersion(): string
{
return $this->version;
}

public function getMilestone(): string
{
return Str::of($this->version)->before('.');
}

/**
* @throws \RuntimeException if the required platform doesn't exist
*/
public function getChromeBrowserURL(string $platform): string
{
$item = collect($this->browserDownloads)->first(fn (array $item) => $item['platform'] === $platform);

if (empty($item)) {
throw new \RuntimeException("The URL for Google Chrome Browser for platform [$platform], it's not available");
}

return $item['url'];
}

/**
* @throws \RuntimeException if the required platform doesn't exist
*/
public function getChromeDriverURL(string $platform): string
{
$item = collect($this->driverDownloads)->first(fn (array $item) => $item['platform'] === $platform);

if (empty($item)) {
throw new \RuntimeException("The URL for Google Chrome Driver for platform [$platform], it's not available");
}

return $item['url'];
}

public function download(int $component, string $to, string $platform, bool $unzip = false): void
{
if ($component & static::BROWSER) {
$url = $this->getChromeBrowserURL($platform);
$filename = join_paths($to, Str::afterLast($url, '/'));

download($url, $filename);

$unzip && unzip($filename);
}

if ($component & static::DRIVER) {
$url = $this->getChromeDriverURL($platform);
$filename = join_paths($to, Str::afterLast($url, '/'));

download($url, $filename);

$unzip && unzip($filename);
}
}

public static function make(string $version, string $revision, array $browserDownloads, array $driverDownloads): static
{
return new static($version, $revision, $browserDownloads, $driverDownloads);
}

public static function makeFromArray(array $data): static
{
$downloads = $data['downloads'];

$version = $data['version'];
$revision = $data['revision'];
$browserDownloads = $downloads['chrome'];
$driverDownloads = $downloads['chromedriver'] ?? [];

return static::make($version, $revision, $browserDownloads, $driverDownloads);
}
}
58 changes: 58 additions & 0 deletions app/GoogleForTesting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace App;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

class GoogleForTesting
{
protected static string $started = '115.0.5763.0';

protected static string $latest = 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions.json';

protected static string $versions = 'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions.json';

protected static string $downloads = 'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json';

public function getLatestVersion(): ?GoogleDownloadable
{
$response = Http::get(static::$latest);

$channel = $response->json('channels')['Stable'];

$version = $channel['version'];

return static::getVersion($version);
}

public function getVersion(string $version): ?GoogleDownloadable
{
$response = Http::get(static::$downloads);

$exact = collect($response->json('versions'))
->first(fn (array $item) => $item['version'] == $version);

if (empty($exact)) {
return null;
}

return GoogleDownloadable::makeFromArray($exact);
}

public function getMilestone(string $milestone): ?Collection
{
$response = Http::get(static::$downloads);

$versions = collect($response->json('versions'))
->filter(fn (array $item) => Str::before($item['version'], '.') == $milestone)
->map(fn (array $version) => GoogleDownloadable::makeFromArray($version));

if ($versions->isEmpty()) {
return null;
}

return $versions;
}
}
Loading