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

Use Prompts #328

Merged
merged 57 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
d305c80
feat: prompts working state
gcavanunez Oct 27, 2023
b2f4f0c
Merge branch 'tm/upgrade-laravel-zero' into feat/prompts-enable-list
gcavanunez May 3, 2024
81c13ef
update prompts
gcavanunez May 3, 2024
fa611a4
updates tests
gcavanunez Aug 15, 2024
6b18b53
clean up
gcavanunez Aug 15, 2024
3657d11
Merge branch 'main' into feat/prompts-enable-list
tonysm Dec 16, 2024
13f2c5a
cleanup enable cmd and drops windows specifics
gcavanunez Dec 16, 2024
a673b99
refactors disable cmd to use prompts and drops windows tests
gcavanunez Dec 16, 2024
cba21cf
refactored stop cmd
gcavanunez Dec 16, 2024
9d1a7e4
Lint
tonysm Dec 16, 2024
564427d
refactor the disable command to remove the menu dependency
tonysm Dec 17, 2024
64dd734
refactors disable, enable, start n stop cmds
gcavanunez Dec 17, 2024
80d5486
Use Prompts for asking the additional questions
tonysm Dec 17, 2024
69b4bf5
Fix tests
tonysm Dec 17, 2024
8ef351e
Simplify the exceptions using prompts
tonysm Dec 17, 2024
55d9328
Merge the default prompts with the extra ones and use Laravel Prompts…
tonysm Dec 17, 2024
5c2e6e5
Use prompts for the list command
tonysm Dec 17, 2024
4a6c85d
Replace netstat with fsockopen to check if a port is available
tonysm Dec 17, 2024
698a42a
wip
tonysm Dec 17, 2024
4dab6b2
Use an if instead of a ternary
tonysm Dec 17, 2024
9fc28e5
Use prompts on the shell errors
tonysm Dec 17, 2024
d374058
Bump prompts dependency version
tonysm Dec 17, 2024
6b0c808
Use prompts in the invalid service shortname exception
tonysm Dec 17, 2024
9effc50
Remove unused method
tonysm Dec 17, 2024
a4f52de
Turn the formatMessage function private
tonysm Dec 17, 2024
98d8f97
Lint
tonysm Dec 17, 2024
3dafccd
Fix BaseServiceTest
tonysm Dec 18, 2024
31a8b64
Fix the environment port availability detection
tonysm Dec 18, 2024
b314256
Lint
tonysm Dec 18, 2024
f82efcc
Wrap the socket creation in a function
tonysm Dec 18, 2024
0dcc165
Ensure the sockets extensions is loaded
tonysm Dec 18, 2024
87cc23c
Tweaks failed test message
tonysm Dec 18, 2024
30cd08a
Rename method
tonysm Dec 18, 2024
b3de93c
Ensure the sockets extension is on the list of extensions on CI
tonysm Dec 18, 2024
4efc521
Remove unused mock
tonysm Dec 18, 2024
09a8ac8
Tweaks the fake process
tonysm Dec 18, 2024
83f2e0d
Tweaks the fake process creation
tonysm Dec 18, 2024
7c85a0f
Rewrite the test to use socket_create_listen
tonysm Dec 18, 2024
7dbe819
Adds the sockets extension as required
tonysm Dec 18, 2024
c012df3
Remove pcntl and posix dependencies (the posix one was required by th…
tonysm Dec 18, 2024
6670bdc
Remove pcntl and posix from CI so we can test it
tonysm Dec 18, 2024
1328dc3
Tweaks the output format
tonysm Dec 18, 2024
74f8d26
Detect if Takeout is running in a container and use host.docker.local…
tonysm Dec 18, 2024
245c6bc
allows container id to be passed to start and stop
gcavanunez Dec 18, 2024
adc8b6b
refactors to active containers and adds dropdown to logs cmd
gcavanunez Dec 18, 2024
aaee0e3
Allow printing the logs output as plain notes (without the OUT|ERR pr…
tonysm Dec 18, 2024
f38d211
adds tests for id or name on stop n start cmds
gcavanunez Dec 18, 2024
ae0a5e0
adds tess for logs cmds
gcavanunez Dec 18, 2024
c79869b
formatting
tonysm Dec 18, 2024
b10b59e
Port must be a valid number
tonysm Dec 18, 2024
57db66b
Must return the port for the test to work
tonysm Dec 18, 2024
0582928
formatting
tonysm Dec 18, 2024
a02d0b7
No need to wrap a collection as a collection
tonysm Dec 19, 2024
d0157c0
formatting
tonysm Dec 19, 2024
05913d0
wording
tonysm Dec 19, 2024
3bbe9de
Rename method
tonysm Dec 19, 2024
c98d2e4
Fix typo
tonysm Jan 3, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: posix, dom, curl, libxml, fileinfo, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
extensions: dom, curl, libxml, fileinfo, mbstring, zip, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, sockets
coverage: none

- name: Install dependencies
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@ The recommended way to install Takeout is the dockerized version via an alias (a
On Linux or macOS, use:

```bash
alias takeout="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -it tighten/takeout:latest"
alias takeout="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock --add-host=host.docker.internal:host-gateway -it tighten/takeout:latest"
```

On Windows 10, if you're using Bash, use:
On Windows 10|11, if you're using Bash, use:

```bash
alias takeout="docker run --rm -v //var/run/docker.sock:/var/run/docker.sock -it tighten/takeout:latest"
alias takeout="docker run --rm -v //var/run/docker.sock:/var/run/docker.sock --add-host=host.docker.internal:host-gateway -it tighten/takeout:latest"
```

On Windows 10, if you're using PowerShell, use:
On Windows 10|11, if you're using PowerShell, use:

```bash
function takeout { docker run --rm -v //var/run/docker.sock:/var/run/docker.sock -it tighten/takeout:latest $args }
function takeout { docker run --rm -v //var/run/docker.sock:/var/run/docker.sock --add-host=host.docker.internal:host-gateway -it tighten/takeout:latest $args }
```

That's it. You may now use Takeout on your terminal. The first time you use this alias, it will pull the Takeout image from Docker Hub.
Expand Down
98 changes: 35 additions & 63 deletions app/Commands/DisableCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
use App\InitializesCommands;
use App\Shell\Docker;
use App\Shell\Environment;
use Illuminate\Support\Collection;
use LaravelZero\Framework\Commands\Command;
use Throwable;
use Illuminate\Support\Str;

use function Laravel\Prompts\confirm;
use function Laravel\Prompts\select;

class DisableCommand extends Command
{
Expand All @@ -25,100 +30,76 @@ public function handle(Docker $docker, Environment $environment)
$this->docker = $docker;
$this->environment = $environment;
$this->initializeCommand();
$this->disableableServices = $this->disableableServices();

$disableableServices = $this->disableableServices();

if ($this->option('all')) {
foreach ($this->disableableServices as $containerId => $name) {
$disableableServices->keys()->each(function ($containerId) {
$this->disableByContainerId($containerId);
}
});

return;
}

if (empty($this->disableableServices)) {
if ($disableableServices->isEmpty()) {
$this->info("There are no containers to disable.\n");

return;
}

if (filled($services = $this->argument('serviceNames'))) {
foreach ($services as $service) {
$this->disableByServiceName($service);
$this->disableByServiceName($service, $disableableServices);
}

return;
}

$this->showDisableServiceMenu();
$this->disableByContainerId(
$this->selectOptions($disableableServices),
);
}

public function disableableServices(): array
private function disableableServices(): Collection
{
return $this->docker->takeoutContainers()->mapWithKeys(function ($container) {
return [$container['container_id'] => str_replace('TO--', '', $container['names'])];
})->toArray();
});
}

public function disableByServiceName(string $service): void
private function disableByServiceName(string $service, Collection $disableableServices): void
{
$serviceMatches = collect($this->disableableServices)
$serviceMatches = collect($disableableServices)
tonysm marked this conversation as resolved.
Show resolved Hide resolved
->filter(function ($containerName) use ($service) {
return substr($containerName, 0, strlen($service)) === $service;
return Str::startsWith($containerName, $service);
});

switch ($serviceMatches->count()) {
case 0:
$this->error("\nCannot find a Takeout-managed instance of {$service}.");

return;
case 1:
$serviceContainerId = $serviceMatches->flip()->first();
break;
default: // > 1
$serviceContainerId = $this->selectMenu($this->disableableServices);
if ($serviceMatches->isEmpty()) {
$this->error("\nCannot find a Takeout-managed instance of {$service}.");

if (! $serviceContainerId) {
return;
}
return;
}

$this->disableByContainerId($serviceContainerId);
}
if ($serviceMatches->count() === 1) {
$this->disableByContainerId($serviceMatches->flip()->first());

public function showDisableServiceMenu($disableableServices = null): void
{
if ($serviceContainerId = $this->selectMenu($disableableServices ?? $this->disableableServices)) {
$this->disableByContainerId($serviceContainerId);
}
}

private function selectMenu($disableableServices): ?string
{
if ($this->environment->isWindowsOs()) {
return $this->windowsMenu($disableableServices);
return;
}

return $this->defaultMenu($disableableServices);
}

private function defaultMenu($disableableServices): ?string
{
return $this->menu(self::MENU_TITLE, $disableableServices)
->addLineBreak('', 1)
->setPadding(2, 5)
->open();
$this->disableByContainerId(
$this->selectOptions($disableableServices),
);
}

private function windowsMenu($disableableServices): ?string
private function selectOptions(Collection $disableableServices)
{
array_push($disableableServices, '<info>Exit</>');

$choice = $this->choice(self::MENU_TITLE, array_values($disableableServices));

return array_search($choice, $disableableServices);
return select(
label: self::MENU_TITLE,
options: $disableableServices
);
}

public function disableByContainerId(string $containerId): void
private function disableByContainerId(string $containerId): void
{
try {
$volumeName = $this->docker->attachedVolumeName($containerId);
Expand All @@ -134,16 +115,7 @@ public function disableByContainerId(string $containerId): void
if (count($this->docker->allContainers()) === 0) {
$question = 'No containers are running. Turn off Docker?';

if ($this->environment->isWindowsOs()) {
$option = $this->confirm($question);
} else {
$option = $this->menu($question, [
'Yes',
'No',
])->disableDefaultItems()->open();
}

if ($option === 0 || $option === true) {
if (confirm($question)) {
$this->task('Stopping Docker service ', $this->docker->stopDockerService());
}
}
Expand Down
115 changes: 28 additions & 87 deletions app/Commands/EnableCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
use App\InitializesCommands;
use App\Services;
use App\Shell\Environment;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
use LaravelZero\Framework\Commands\Command;

use function Laravel\Prompts\search;

class EnableCommand extends Command
{
use InitializesCommands;
Expand Down Expand Up @@ -46,21 +47,17 @@ public function handle(Environment $environment, Services $services): void
return;
}

$option = $this->selectService();
$service = $this->selectService($this->availableServices());

if (! $option) {
return;
}

$this->enable($option, $useDefaults, $passthroughOptions);
$this->enable($service, $useDefaults, $passthroughOptions);
}

/**
* Since we're pulling the *full* list of server arguments, not just relying on
* $this->argument, we have to do our own manual overriding for testing scenarios,
* because pulling $_SERVER['argv'] won't give the right results in testing.
*/
public function serverArguments(): array
private function serverArguments(): array
{
if (App::environment() === 'testing') {
$string = array_merge(['takeout', 'enable'], $this->argument('serviceNames'));
Expand Down Expand Up @@ -104,7 +101,7 @@ public function extractPassthroughOptions(array $arguments): array
public function removeOptions(array $arguments): array
{
$arguments = collect($arguments)
->reject(fn ($argument) => str_starts_with($argument, '--') && strlen($argument) > 2)
->reject(fn($argument) => str_starts_with($argument, '--') && strlen($argument) > 2)
->values()
->toArray();

Expand All @@ -119,91 +116,36 @@ public function removeOptions(array $arguments): array
return array_slice($arguments, $start);
}

private function selectService(): ?string
private function availableServices(): Collection
{
if ($this->environment->isWindowsOs()) {
return $this->windowsMenu();
}

return $this->defaultMenu();
return $this->enableableServicesByCategory()->flatMap(function ($services, $category) {
return $this->menuItemsForServices($services)->mapWithKeys(function ($row, $key) use ($category) {
return [$key => "{$category}: {$row}"];
})->toArray();
});
}

private function defaultMenu(): ?string
private function selectService(Collection $servicesList): ?string
{
$option = $this->menu(self::MENU_TITLE)->setTitleSeparator('=');

foreach ($this->enableableServicesByCategory() as $category => $services) {
$separator = str_repeat('-', 1 + Str::length($category));

$option->addStaticItem("{$category}:")
->addStaticItem($separator)
->addOptions($this->menuItemsForServices($services))
->addLineBreak('', 1);
}

return $option->open();
return search(
label: self::MENU_TITLE,
options: fn(string $value) => strlen($value) > 0
? $servicesList->filter(function ($row) use ($value) {
return str($row)->lower()->contains(str($value)->lower());
})->toArray()
: $servicesList->toArray(),
scroll: 10
);
}

private function windowsMenu($category = null): ?string
{
$choices = [];
$groupedServices = $this->enableableServicesByCategory();

if ($category) {
$groupedServices = Arr::where($groupedServices, function ($value, $key) use ($category) {
return Str::contains($category, strtoupper($key));
});
}

foreach ($groupedServices as $serviceCategory => $services) {
$serviceCategoryMenuItem = '<fg=white;bg=blue;options=bold> ' . (Str::upper($serviceCategory)) . ' </>';
array_push($choices, $serviceCategoryMenuItem);

foreach ($this->menuItemsForServices($services) as $menuItemKey => $menuItemName) {
array_push($choices, $menuItemName);
}
}

if ($category) {
array_push($choices, '<info>Back</>');
}

array_push($choices, '<info>Exit</>');

$choice = $this->choice(self::MENU_TITLE, $choices);

if (Str::contains($choice, 'Back')) {
return $this->windowsMenu();
}

if (Str::contains($choice, 'Exit')) {
return null;
}

foreach ($this->enableableServices() as $shortName => $fqcn) {
if ($choice === $fqcn) {
return $shortName;
}
}

return $this->windowsMenu($choice);
}

private function menuItemsForServices($services): array
private function menuItemsForServices($services): Collection
{
return collect($services)->mapWithKeys(function ($service) {
return [$service['shortName'] => $service['name']];
})->toArray();
});
}

public function enableableServices(): array
{
return collect($this->services->all())->mapWithKeys(function ($fqcn, $shortName) {
return [$shortName => $fqcn::name()];
})->toArray();
}

public function enableableServicesByCategory(): array
private function enableableServicesByCategory(): Collection
{
return collect($this->services->all())
->mapToGroups(function ($fqcn, $shortName) {
Expand All @@ -214,11 +156,10 @@ public function enableableServicesByCategory(): array
],
];
})
->sortKeys()
->toArray();
->sortKeys();
}

public function enable(
private function enable(
string $service,
bool $useDefaults = false,
array $passthroughOptions = [],
Expand Down
8 changes: 5 additions & 3 deletions app/Commands/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
use App\Shell\Docker;
use LaravelZero\Framework\Commands\Command;

use function Laravel\Prompts\info;
use function Laravel\Prompts\table;

class ListCommand extends Command
{
use InitializesCommands;
Expand All @@ -31,15 +34,14 @@ public function handle(Docker $docker): void
}

if ($containersCollection->isEmpty()) {
$this->info("No Takeout containers are enabled.\n");
info('No Takeout containers are enabled.');

return;
}

$containers = $containersCollection->toArray();
$columns = array_map('App\title_from_slug', array_keys(reset($containers)));

$this->line("\n");
$this->table($columns, $containers);
table($columns, $containers);
}
}
Loading
Loading