Skip to content

Commit

Permalink
Merge pull request #7 from deliciousbrains/ssh-commands
Browse files Browse the repository at this point in the history
SSH commands
  • Loading branch information
A5hleyRich authored Dec 7, 2021
2 parents 70f3bbf + 8c19375 commit 4411de6
Show file tree
Hide file tree
Showing 16 changed files with 670 additions and 220 deletions.
148 changes: 10 additions & 138 deletions app/Commands/BaseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

namespace App\Commands;

use App\Commands\Concerns\InteractsWithIO;
use App\Helpers\Configuration;
use DeliciousBrains\SpinupWp\SpinupWp;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Support\Collection;
use LaravelZero\Framework\Commands\Command;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Helper\Table;

abstract class BaseCommand extends Command
{
use InteractsWithIO;

protected Configuration $config;

protected SpinupWp $spinupwp;
Expand All @@ -35,7 +35,7 @@ public function handle(): int
{
if ($this->requiresToken && !$this->config->isConfigured()) {
$this->error("You must first run 'spinupwp configure' in order to set up your API token.");
return 1;
return self::FAILURE;
}

try {
Expand All @@ -52,21 +52,21 @@ public function handle(): int
]));
}

$this->format($this->action());

return 0;
return $this->action();
} catch (Exception $e) {
$this->error($e->getMessage());
return 1;
return self::FAILURE;
}
}

protected function apiToken(): string
{
$apiToken = $this->config->get('api_token', $this->profile());

if (!$apiToken) {
throw new Exception("The API token for the profile {$this->profile()} is not yet configured");
}

return $apiToken;
}

Expand All @@ -75,137 +75,9 @@ protected function profile(): string
if (is_string($this->option('profile'))) {
return $this->option('profile');
}
return 'default';
}

protected function format($resource): void
{
if (empty($resource) || ($resource instanceof Collection && $resource->isEmpty())) {
return;
}

$this->setStyles();

if ($this->displayFormat() === 'table' && $this->largeOutput) {
$this->largeOutput($resource);
return;
}

if ($this->displayFormat() === 'table') {
$this->toTable($resource);
return;
}

$this->toJson($resource);
}

protected function setStyles(): void
{
if (!$this->output->getFormatter()->hasStyle('enabled')) {
$this->output->getFormatter()->setStyle(
'enabled',
new OutputFormatterStyle('green'),
);
}

if (!$this->output->getFormatter()->hasStyle('disabled')) {
$this->output->getFormatter()->setStyle(
'disabled',
new OutputFormatterStyle('red'),
);
}
}

protected function displayFormat(): string
{
if (is_string($this->option('format'))) {
return $this->option('format');
}
return $this->config->get('format', $this->profile());
}

protected function toJson($resource): void
{
if (!is_array($resource)) {
$resource = $resource->toArray();
}
$this->line(json_encode($resource, JSON_PRETTY_PRINT));
}

protected function toTable($resource): void
{
$tableHeaders = [];

if ($resource instanceof Collection) {
$firstElement = $resource->first();

if (!is_array($firstElement)) {
$firstElement = $firstElement->toArray();
}

$tableHeaders = array_keys($firstElement);

$rows = [];

$resource->each(function ($item) use (&$rows) {
if (!is_array($item)) {
$item->toArray();
}

$row = array_map(function ($value) {
if (is_array($value)) {
$value = '';
}
if (is_bool($value)) {
$value = $value ? '<enabled>Y</enabled>' : '<disabled>N</disabled>';
}
return $value;
}, array_values($item));

$rows[] = $row;
});
}

$this->table(
$tableHeaders,
$rows,
);
}

protected function largeOutput(array $resource): void
{
$table = new Table($this->output);
$rows = [];

foreach ($resource as $key => $value) {
$rows[] = ['<info>' . $key . '</info>', $value];
}

$table->setRows($rows)->setStyle('default');

if (!empty($this->columnsMaxWidths)) {
foreach ($this->columnsMaxWidths as $column) {
$table->setColumnMaxWidth($column[0], $column[1]);
}
}

$table->render();
}

protected function formatBytes(int $bytes, int $precision = 1, bool $trueSize = false): string
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$block = ($trueSize) ? 1024 : 1000;

$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log($block));
$pow = min($pow, count($units) - 1);
$bytes /= pow($block, $pow);

$total = ($trueSize || $precision > 0) ? round($bytes, $precision) : floor($bytes);

return $total . ' ' . $units[$pow];
return 'default';
}

abstract protected function action();
abstract protected function action(): int;
}
178 changes: 178 additions & 0 deletions app/Commands/Concerns/InteractsWithIO.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php

namespace App\Commands\Concerns;

use Illuminate\Support\Collection;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Question\ChoiceQuestion;

trait InteractsWithIO
{
/**
* @param mixed $resource
*/
protected function format($resource): void
{
if (empty($resource) || ($resource instanceof Collection && $resource->isEmpty())) {
return;
}

$this->setStyles();

if ($this->largeOutput && $this->displayFormat() === 'table') {
$this->largeOutput($resource);
return;
}

if ($this->displayFormat() === 'table') {
$this->toTable($resource);
return;
}

$this->toJson($resource);
}

protected function setStyles(): void
{
if (!$this->output->getFormatter()->hasStyle('enabled')) {
$this->output->getFormatter()->setStyle(
'enabled',
new OutputFormatterStyle('green'),
);
}

if (!$this->output->getFormatter()->hasStyle('disabled')) {
$this->output->getFormatter()->setStyle(
'disabled',
new OutputFormatterStyle('red'),
);
}
}

protected function displayFormat(): string
{
if (is_string($this->option('format'))) {
return $this->option('format');
}

return (string) $this->config->get('format', $this->profile());
}

/**
* @param mixed $resource
*/
protected function toJson($resource): void
{
$this->line((string) json_encode($resource->toArray(), JSON_PRETTY_PRINT));
}

/**
* @param mixed $resource
*/
protected function toTable($resource): void
{
$rows = [];
$tableHeaders = [];

if ($resource instanceof Collection) {
$firstElement = $resource->first();

if (!is_array($firstElement)) {
$firstElement = $firstElement->toArray();
}

$tableHeaders = array_keys($firstElement);

$resource->each(function ($item) use (&$rows) {
if (!is_array($item)) {
$item->toArray();
}

$row = array_map(function ($value) {
if (is_array($value)) {
$value = '';
}
if (is_bool($value)) {
$value = $value ? '<enabled>Y</enabled>' : '<disabled>N</disabled>';
}
return $value;
}, array_values($item));

$rows[] = $row;
});
}

$this->table($tableHeaders, $rows);
}

public function askToSelectSite(string $question): int
{
$choices = collect($this->spinupwp->sites->list());

return $this->askToSelect(
$question,
$choices->keyBy('id')->map(fn ($site) => $site->domain)->toArray()
);
}

public function askToSelectServer(string $question): int
{
$choices = collect($this->spinupwp->servers->list());

return $this->askToSelect(
$question,
$choices->keyBy('id')->map(fn ($server) => $server->name)->toArray()
);
}

/**
* @param mixed $default
*/
protected function askToSelect(string $question, array $choices, $default = null): int
{
$question = new class($question, $choices, $default) extends ChoiceQuestion {
public function isAssoc(array $array): bool
{
return true;
}
};

return (int) $this->output->askQuestion($question);
}

protected function largeOutput(array $resource): void
{
$table = new Table($this->output);
$rows = [];

foreach ($resource as $key => $value) {
$rows[] = ['<info>' . $key . '</info>', $value];
}

$table->setRows($rows)->setStyle('default');

if (!empty($this->columnsMaxWidths)) {
foreach ($this->columnsMaxWidths as $column) {
$table->setColumnMaxWidth($column[0], $column[1]);
}
}

$table->render();
}

protected function formatBytes(int $bytes, int $precision = 1, bool $trueSize = false): string
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$block = ($trueSize) ? 1024 : 1000;

$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log($block));
$pow = min($pow, count($units) - 1);
$bytes /= pow($block, $pow);

$total = ($trueSize || $precision > 0) ? round($bytes, $precision) : floor($bytes);

return $total . ' ' . $units[$pow];
}
}
23 changes: 23 additions & 0 deletions app/Commands/Concerns/InteractsWithRemote.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Commands\Concerns;

trait InteractsWithRemote
{
protected function ssh(string $user, string $host, int $port = 22, string $command = ''): int
{
$options = collect([
'ConnectTimeout' => config('app.ssh_timeout'),
'ControlMaster' => 'auto',
'ControlPath' => $this->config->sshControlPath(),
'ControlPersist' => 100,
'LogLevel' => 'QUIET',
])->map(function ($value, $option) {
return "-o $option=$value";
})->implode(' ');

passthru("ssh {$options} -t {$user}@{$host} -p {$port} '{$command}'", $exitCode);

return (int) $exitCode;
}
}
Loading

0 comments on commit 4411de6

Please sign in to comment.