Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
169 changes: 169 additions & 0 deletions app/Console/Server/ServerOptimizeCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php

declare(strict_types=1);

namespace Bigpixelrocket\DeployerPHP\Console\Server;

use Bigpixelrocket\DeployerPHP\Contracts\BaseCommand;
use Bigpixelrocket\DeployerPHP\Traits\PlaybooksTrait;
use Bigpixelrocket\DeployerPHP\Traits\ServersTrait;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'server:optimize',
description: 'Optimize PHP-FPM and OPcache settings based on server hardware'
)]
class ServerOptimizeCommand extends BaseCommand
{
use PlaybooksTrait;
use ServersTrait;

// ----
// Configuration
// ----

protected function configure(): void
{
parent::configure();

$this->addOption('server', null, InputOption::VALUE_REQUIRED, 'Server name');
}

// ----
// Execution
// ----

protected function execute(InputInterface $input, OutputInterface $output): int
{
parent::execute($input, $output);

$this->heading('Optimize Server');

//
// Select server & display details
// ----

$server = $this->selectServer();

if (is_int($server)) {
return $server;
}

$this->displayServerDeets($server);

//
// Get server info (includes hardware detection)
// ----

$info = $this->getServerInfo($server);

if (is_int($info)) {
return $info;
}

// Extract hardware info
if (!isset($info['hardware']) || !is_array($info['hardware'])) {
$this->io->error('Server hardware information not available. Run server:install first.');

return Command::FAILURE;
}

$hardware = $info['hardware'];
$cpuCores = $hardware['cpu_cores'] ?? '1';
$ramMb = $hardware['ram_mb'] ?? '512';
$diskType = $hardware['disk_type'] ?? 'hdd';

/** @var string $cpuCores */
/** @var string $ramMb */
/** @var string $diskType */

$permissions = $info['permissions'] ?? 'none';
/** @var string $permissions */

//
// Display hardware summary
// ----

$this->io->writeln([
'',
'<fg=cyan>Hardware Configuration:</>',
" CPU Cores: <fg=yellow>{$cpuCores}</>",
" RAM: <fg=yellow>{$ramMb}MB</>",
" Disk: <fg=yellow>{$diskType}</>",
'',
]);
Comment on lines +62 to +98

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove duplicate hardware display.

The hardware information is already displayed by getServerInfo() at line 62, which calls displayServerInfo() in ServersTrait. The second display at lines 91-98 shows the same information in a different format, creating redundant console output.

Apply this diff to remove the duplicate display:

     }
 
-    //
-    // Display hardware summary
-    // ----
-
-    $this->io->writeln([
-        '',
-        '<fg=cyan>Hardware Configuration:</>',
-        "  CPU Cores: <fg=yellow>{$cpuCores}</>",
-        "  RAM: <fg=yellow>{$ramMb}MB</>",
-        "  Disk: <fg=yellow>{$diskType}</>",
-        '',
-    ]);
-
     //
     // Confirm optimization
     // ----
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$info = $this->getServerInfo($server);
if (is_int($info)) {
return $info;
}
// Extract hardware info
if (!isset($info['hardware']) || !is_array($info['hardware'])) {
$this->io->error('Server hardware information not available. Run server:install first.');
return Command::FAILURE;
}
$hardware = $info['hardware'];
$cpuCores = $hardware['cpu_cores'] ?? '1';
$ramMb = $hardware['ram_mb'] ?? '512';
$diskType = $hardware['disk_type'] ?? 'hdd';
/** @var string $cpuCores */
/** @var string $ramMb */
/** @var string $diskType */
$permissions = $info['permissions'] ?? 'none';
/** @var string $permissions */
//
// Display hardware summary
// ----
$this->io->writeln([
'',
'<fg=cyan>Hardware Configuration:</>',
" CPU Cores: <fg=yellow>{$cpuCores}</>",
" RAM: <fg=yellow>{$ramMb}MB</>",
" Disk: <fg=yellow>{$diskType}</>",
'',
]);
$info = $this->getServerInfo($server);
if (is_int($info)) {
return $info;
}
// Extract hardware info
if (!isset($info['hardware']) || !is_array($info['hardware'])) {
$this->io->error('Server hardware information not available. Run server:install first.');
return Command::FAILURE;
}
$hardware = $info['hardware'];
$cpuCores = $hardware['cpu_cores'] ?? '1';
$ramMb = $hardware['ram_mb'] ?? '512';
$diskType = $hardware['disk_type'] ?? 'hdd';
/** @var string $cpuCores */
/** @var string $ramMb */
/** @var string $diskType */
$permissions = $info['permissions'] ?? 'none';
/** @var string $permissions */
//
// Confirm optimization
// ----
🤖 Prompt for AI Agents
In app/Console/Server/ServerOptimizeCommand.php around lines 62 to 98, the
hardware summary is being printed a second time (duplicate of displayServerInfo
invoked by getServerInfo()). Remove the redundant console output block that
writes the hardware summary (the $this->io->writeln([...]) section) so only the
original display from getServerInfo() remains; ensure any related empty lines or
comments are cleaned up to preserve formatting and return behavior.


//
// Confirm optimization
// ----

$confirmed = $this->io->promptConfirm(
'Apply hardware-optimized settings to PHP-FPM and OPcache?',
default: true
);

if (!$confirmed) {
$this->io->info('Optimization cancelled');

return Command::SUCCESS;
}

//
// Execute optimization playbook
// ----

$result = $this->executePlaybook(
$server,
'server-optimize',
'Optimizing server...',
[
'DEPLOYER_PERMS' => $permissions,
'DEPLOYER_CPU_CORES' => $cpuCores,
'DEPLOYER_RAM_MB' => $ramMb,
'DEPLOYER_DISK_TYPE' => $diskType,
],
true
);

if (is_int($result)) {
$this->io->error('Server optimization failed');

return $result;
}

$this->yay('Server optimization completed successfully');

//
// Display applied settings
// ----

if (isset($result['php_fpm_settings']) && is_string($result['php_fpm_settings'])) {
$this->io->writeln([
'',
'<fg=cyan>Applied Settings:</>',
" PHP-FPM: <fg=yellow>{$result['php_fpm_settings']}</>",
]);
}

if (isset($result['opcache_settings']) && is_string($result['opcache_settings'])) {
$this->io->writeln([
" OPcache: <fg=yellow>{$result['opcache_settings']}</>",
'',
]);
}

//
// Show command replay
// ----

$this->showCommandReplay('server:optimize', [
'server' => $server->name,
]);

return Command::SUCCESS;
}
}
32 changes: 32 additions & 0 deletions app/Traits/ServersTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,38 @@ protected function displayServerInfo(array $info): void
$this->io->displayDeets($deets);
$this->io->writeln('');

// Display hardware information if available
if (isset($info['hardware']) && is_array($info['hardware'])) {
$hardwareItems = [];

if (isset($info['hardware']['cpu_cores'])) {
/** @var int|string $cpuCores */
$cpuCores = $info['hardware']['cpu_cores'];
$coresText = $cpuCores === '1' || $cpuCores === 1 ? '1 core' : "{$cpuCores} cores";
$hardwareItems[] = "CPU: {$coresText}";
}

if (isset($info['hardware']['ram_mb'])) {
/** @var int|string $ramMb */
$ramMb = $info['hardware']['ram_mb'];
$ramGb = round((int) $ramMb / 1024, 1);
$ramText = $ramGb >= 1 ? "{$ramGb} GB" : "{$ramMb} MB";
$hardwareItems[] = "RAM: {$ramText}";
}

if (isset($info['hardware']['disk_type'])) {
/** @var string $diskType */
$diskType = $info['hardware']['disk_type'];
$diskText = strtoupper($diskType);
$hardwareItems[] = "Disk: {$diskText}";
}

if (count($hardwareItems) > 0) {
$this->io->displayDeets(['Hardware' => $hardwareItems]);
$this->io->writeln('');
}
}

$services = [];

// Add listening ports if any
Expand Down
64 changes: 58 additions & 6 deletions playbooks/server-info.sh
Original file line number Diff line number Diff line change
Expand Up @@ -143,22 +143,64 @@ ensure_tools() {
local family=$1 perms=$2
export DEPLOYER_PERMS=$perms

# If the command is already installed, return
command -v ss > /dev/null 2>&1 && return 0
command -v netstat > /dev/null 2>&1 && return 0
# Check if required tools exist
command -v ss > /dev/null 2>&1 && command -v lsblk > /dev/null 2>&1 && return 0

case $family in
debian)
run_cmd apt-get update -q 2> /dev/null
run_cmd apt-get install -y -q iproute2 2> /dev/null
run_cmd apt-get install -y -q iproute2 util-linux 2> /dev/null
;;
fedora | redhat | amazon)
run_cmd yum install -y -q iproute 2> /dev/null \
|| run_cmd dnf install -y -q iproute 2> /dev/null
run_cmd yum install -y -q iproute util-linux 2> /dev/null \
|| run_cmd dnf install -y -q iproute util-linux 2> /dev/null
;;
esac
}

#
# Hardware Detection
# ----

#
# Detect CPU core count

detect_cpu_cores() {
nproc 2> /dev/null || echo "1"
}

#
# Detect total system RAM in MB

detect_ram_mb() {
free -m 2> /dev/null | awk 'NR==2 {print $2}' || echo "512"
}

#
# Detect disk type (ssd or hdd)

detect_disk_type() {
local disc_gran rotation

# Primary detection: Check discard granularity (TRIM support = SSD)
# Works reliably in virtualized environments (cloud VMs)
disc_gran=$(lsblk -d -o name,disc-gran 2> /dev/null | grep -E "^[sv]da" | head -n1 | awk '{print $2}')

# If disc-gran is non-zero (e.g., "512B"), it's an SSD
if [[ -n $disc_gran && $disc_gran != "0B" ]]; then
echo "ssd"
return
fi

# Fallback: Check rotation flag (works for physical disks)
rotation=$(lsblk -d -o name,rota 2> /dev/null | grep -E "^[sv]da" | head -n1 | awk '{print $2}')
if [[ $rotation == "0" ]]; then
echo "ssd"
else
echo "hdd"
fi
}

# ----
# Service Metrics
# ----
Expand Down Expand Up @@ -332,6 +374,7 @@ get_php_fpm_metrics() {

main() {
local distro family permissions
local cpu_cores ram_mb disk_type

#
# Gather basic info
Expand All @@ -343,6 +386,11 @@ main() {
echo "✓ Checking permissions..."
permissions=$(check_permissions)

echo "✓ Detecting hardware..."
cpu_cores=$(detect_cpu_cores)
ram_mb=$(detect_ram_mb)
disk_type=$(detect_disk_type)

echo "✓ Cataloging services..."
ensure_tools "$family" "$permissions"

Expand Down Expand Up @@ -374,6 +422,10 @@ main() {
distro: $distro
family: $family
permissions: $permissions
hardware:
cpu_cores: $cpu_cores
ram_mb: $ram_mb
disk_type: $disk_type
caddy:
available: $caddy_available
version: ${caddy_version:-unknown}
Expand Down
Loading