Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.deployer/
.cursor/.agent-tools/
.experiments/
.idea/
.vscode/
Expand Down
90 changes: 2 additions & 88 deletions app/Console/Server/ServerAddCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
/**
* Add and register a new server to the inventory.
*
* Prompts for server details and verifies SSH connectivity before saving.
* Prompts for server details and saves to inventory.
*/
#[AsCommand(name: 'server:add', description: 'Add a new server to the inventory')]
class ServerAddCommand extends BaseCommand
Expand All @@ -38,9 +38,7 @@ protected function configure(): void
->addOption('host', null, InputOption::VALUE_REQUIRED, 'Host/IP address')
->addOption('port', null, InputOption::VALUE_REQUIRED, 'SSH port (default: 22)')
->addOption('username', null, InputOption::VALUE_REQUIRED, 'SSH username (default: root)')
->addOption('private-key-path', null, InputOption::VALUE_REQUIRED, 'SSH private key path')
->addOption('skip', null, InputOption::VALUE_NONE, 'Skip SSH connection check')
->addOption('yes', 'y', InputOption::VALUE_NONE, 'Skip confirmation prompt');
->addOption('private-key-path', null, InputOption::VALUE_REQUIRED, 'SSH private key path');
}

//
Expand Down Expand Up @@ -146,46 +144,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$this->displayServerDeets($server);

//
// Verify connectivity

/** @var bool $skipCheck */
$skipCheck = $this->io->getOptionOrPrompt(
'skip',
fn (): bool => !$this->io->promptConfirm(
label: 'Test SSH connection before saving?',
default: true
)
);

if ($skipCheck) {
$this->io->warning('Skipping SSH connection check');
$this->io->writeln('');
} else {
if (!$this->testConnection($server)) {
return Command::FAILURE;
}
}

//
// Confirm creation

/** @var bool $confirmed */
$confirmed = $this->io->getOptionOrPrompt(
'yes',
fn (): bool => $this->io->promptConfirm(
label: 'Save this server to inventory?',
default: true
)
);

if (!$confirmed) {
$this->io->warning('Cancelled adding server');
$this->io->writeln('');

return Command::SUCCESS;
}

//
// Save to repository

Expand All @@ -209,53 +167,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
'port' => $port,
'username' => $username,
'private-key-path' => $privateKeyPath,
'skip' => $skipCheck,
'yes' => $confirmed,
]);

return Command::SUCCESS;
}

//
// Private Helpers
// -------------------------------------------------------------------------------

/**
* Test SSH connection to server with detailed output.
*/
private function testConnection(ServerDTO $server): bool
{
try {
$this->io->promptSpin(
callback: fn () => $this->ssh->assertCanConnect(
$server->host,
$server->port,
$server->username,
$server->privateKeyPath
),
message: 'Connecting to server...'
);

$this->io->success('SSH connection successful');

return true;
} catch (\RuntimeException $e) {
$this->io->error($e->getMessage());

$this->io->writeln([
'',
' <fg=yellow>Common issues:</>',
'',
' <fg=gray>• Check that the server is accessible from your network</>',
' <fg=gray>• Verify SSH is running on the server (port '.$server->port.')</>',
' <fg=gray>• Ensure your SSH key has correct permissions (chmod 600)</>',
' <fg=gray>• Confirm username "'.$server->username.'" exists on the server</>',
'',
' <fg=gray>Tip: Use</> <fg=cyan>--skip</> <fg=gray>to add server without testing connection.</>',
'',
]);

return false;
}
}
}
19 changes: 19 additions & 0 deletions app/Console/Server/ServerDeleteCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,28 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$server = $selection['server'];
$this->displayServerDeets($server);

// Get sites for this server
$serverSites = $this->sites->findByServer($server->name);

if (count($serverSites) > 0) {
$this->io->writeln([' Sites:']);
foreach ($serverSites as $site) {
$this->io->writeln([" • <fg=gray>{$site->domain}</>"]);
}

$this->io->writeln('');

$this->io->error("Cannot delete server '{$server->name}' because it has one or more sites.");

return Command::FAILURE;

}

//
// Confirm deletion

$this->io->writeln('');

/** @var bool $confirmed */
$confirmed = $this->io->getOptionOrPrompt(
'yes',
Expand Down
25 changes: 24 additions & 1 deletion app/Console/Server/ServerListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Bigpixelrocket\DeployerPHP\Contracts\BaseCommand;
use Bigpixelrocket\DeployerPHP\Traits\ServerHelpersTrait;
use Bigpixelrocket\DeployerPHP\Traits\SiteHelpersTrait;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -18,6 +19,7 @@
class ServerListCommand extends BaseCommand
{
use ServerHelpersTrait;
use SiteHelpersTrait;

//
// Execution
Expand All @@ -44,10 +46,31 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return Command::SUCCESS;
}

//
// Display servers with their sites

$this->io->h1('All Servers');

foreach ($allServers as $server) {
foreach ($allServers as $count => $server) {
$this->displayServerDeets($server);

// Get sites for this server
$serverSites = $this->sites->findByServer($server->name);

if (count($serverSites) > 0) {
$this->io->writeln([' Sites:']);
foreach ($serverSites as $site) {
$this->io->writeln([" • <fg=gray>{$site->domain}</>"]);
}
$this->io->writeln('');
}

if ($count < count($allServers) - 1) {
$this->io->writeln([
' ───',
'',
]);
}
}

return Command::SUCCESS;
Expand Down
Loading