feat: add dedicated deploy logging to storage/logs/deploy.log
- Add 'deploy' channel in logging.php
- Instrument DeployTask with phpLog() for all state transitions
- Instrument DeploySiteAction with channel('deploy') for triggers, errors, clears
- Logs written to storage/logs/deploy.log (separate from laravel.log)
This commit is contained in:
@@ -2,72 +2,63 @@
|
|||||||
|
|
||||||
namespace App\Containers\Dashboard\Actions\Posts;
|
namespace App\Containers\Dashboard\Actions\Posts;
|
||||||
|
|
||||||
|
use App\Containers\Dashboard\Tasks\DeployTask;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class DeploySiteAction
|
class DeploySiteAction
|
||||||
{
|
{
|
||||||
private string $deployScript = '/var/www/_deploy/deploy.sh';
|
public function __construct(
|
||||||
private string $logFile = '/var/www/_deploy/deploy.log';
|
private readonly DeployTask $deployTask,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Запускает deploy.sh напрямую
|
|
||||||
*
|
|
||||||
* @return array ['success' => bool, 'message' => string]
|
|
||||||
*/
|
|
||||||
public function run(): array
|
public function run(): array
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (!file_exists($this->deployScript)) {
|
if (!$this->deployTask->scriptExists()) {
|
||||||
|
Log::channel('deploy')->warning('[DeployAction] Script not found');
|
||||||
return [
|
return [
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => 'Скрипт деплоя не найден на сервере',
|
'message' => 'Скрипт деплоя не найден на сервере',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, не запущен ли уже деплой
|
if ($this->deployTask->isDeployRunning()) {
|
||||||
if ($this->isDeployRunning()) {
|
Log::channel('deploy')->warning('[DeployAction] Deploy already running', [
|
||||||
|
'user_id' => auth()->id(),
|
||||||
|
]);
|
||||||
return [
|
return [
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => 'Деплой уже запущен! Подождите завершения текущего процесса.',
|
'message' => 'Деплой уже запущен! Подождите завершения текущего процесса.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Очищаем старый лог
|
$started = $this->deployTask->startDeploy();
|
||||||
if (file_exists($this->logFile)) {
|
|
||||||
unlink($this->logFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Запускаем deploy.sh в фоне (docker socket проброшен в контейнер)
|
|
||||||
$command = sprintf(
|
|
||||||
'bash %s > %s 2>&1 &',
|
|
||||||
escapeshellarg($this->deployScript),
|
|
||||||
escapeshellarg($this->logFile)
|
|
||||||
);
|
|
||||||
|
|
||||||
shell_exec($command);
|
|
||||||
|
|
||||||
// Даём процессу время на запуск
|
|
||||||
usleep(500000); // 0.5 сек
|
|
||||||
|
|
||||||
if (!$this->isDeployRunning()) {
|
|
||||||
// Процесс не запустился — читаем лог
|
|
||||||
$error = file_exists($this->logFile) ? file_get_contents($this->logFile) : 'Нет лога';
|
|
||||||
Log::error('Deploy script failed to start', ['log' => $error]);
|
|
||||||
|
|
||||||
|
if (!$started) {
|
||||||
|
Log::channel('deploy')->error('[DeployAction] Failed to start deploy script', [
|
||||||
|
'user_id' => auth()->id(),
|
||||||
|
]);
|
||||||
return [
|
return [
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => 'Скрипт деплоя не запустился. Проверьте лог.',
|
'message' => 'Скрипт деплоя не запустился. Проверьте лог.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info('Deploy triggered', ['user_id' => auth()->id()]);
|
Log::channel('deploy')->info('[DeployAction] Deploy triggered successfully', [
|
||||||
|
'user_id' => auth()->id(),
|
||||||
|
'user_email' => auth()->user()?->email,
|
||||||
|
]);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Деплой запущен! Процесс обновления сайта начнётся в течение нескольких секунд.',
|
'message' => 'Деплой запущен! Процесс обновления сайта начнётся в течение нескольких секунд.',
|
||||||
];
|
];
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::error('Deploy trigger failed', ['exception' => $e->getMessage()]);
|
Log::channel('deploy')->error('[DeployAction] Exception', [
|
||||||
|
'user_id' => auth()->id(),
|
||||||
|
'exception' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'success' => false,
|
'success' => false,
|
||||||
@@ -76,78 +67,31 @@ class DeploySiteAction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверяет статус деплоя
|
|
||||||
*/
|
|
||||||
public function getStatus(): array
|
public function getStatus(): array
|
||||||
{
|
{
|
||||||
if (!file_exists($this->logFile)) {
|
return $this->deployTask->getDeployStatus();
|
||||||
return ['status' => 'idle', 'message' => 'Деплой не запущен'];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$log = file_get_contents($this->logFile);
|
|
||||||
|
|
||||||
// Проверяем, запущен ли процесс
|
|
||||||
if ($this->isDeployRunning()) {
|
|
||||||
return [
|
|
||||||
'status' => 'running',
|
|
||||||
'message' => 'Деплой выполняется...',
|
|
||||||
'log' => $log,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Процесс завершил — проверяем результат
|
|
||||||
$lastLines = array_slice(explode("\n", trim($log)), -5);
|
|
||||||
$lastOutput = implode("\n", $lastLines);
|
|
||||||
|
|
||||||
$isSuccess = stripos($lastOutput, 'успешн') !== false
|
|
||||||
|| stripos($lastOutput, 'success') !== false
|
|
||||||
|| stripos($lastOutput, '✅') !== false;
|
|
||||||
|
|
||||||
$isFailed = stripos($lastOutput, 'ошибк') !== false
|
|
||||||
|| stripos($lastOutput, 'error') !== false
|
|
||||||
|| stripos($lastOutput, '❌') !== false;
|
|
||||||
|
|
||||||
if ($isSuccess) {
|
|
||||||
return [
|
|
||||||
'status' => 'completed',
|
|
||||||
'message' => 'Деплой завершён успешно!',
|
|
||||||
'log' => $log,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($isFailed) {
|
|
||||||
return [
|
|
||||||
'status' => 'failed',
|
|
||||||
'message' => 'Деплой завершён с ошибкой',
|
|
||||||
'log' => $log,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public function getLog(int $lines = 50): array
|
||||||
|
{
|
||||||
return [
|
return [
|
||||||
'status' => 'unknown',
|
'log' => $this->deployTask->getLogTail($lines),
|
||||||
'message' => 'Статус неизвестен',
|
'full_log' => $this->deployTask->getLog(),
|
||||||
'log' => $log,
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHistory(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'history' => $this->deployTask->getHistory(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Очищает статус деплоя
|
|
||||||
*/
|
|
||||||
public function clearStatus(): void
|
public function clearStatus(): void
|
||||||
{
|
{
|
||||||
if (file_exists($this->logFile)) {
|
Log::channel('deploy')->info('[DeployAction] Log cleared', [
|
||||||
unlink($this->logFile);
|
'user_id' => auth()->id(),
|
||||||
}
|
]);
|
||||||
}
|
$this->deployTask->clearLog();
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверяет, запущен ли процесс деплоя
|
|
||||||
*/
|
|
||||||
private function isDeployRunning(): bool
|
|
||||||
{
|
|
||||||
$output = shell_exec('pgrep -f "deploy\\.sh"');
|
|
||||||
|
|
||||||
return !empty(trim($output ?? ''));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Containers\Dashboard\Tasks;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class DeployTask
|
||||||
|
{
|
||||||
|
private string $deployScript = '/var/www/_deploy/deploy.sh';
|
||||||
|
private string $logFile = '/var/www/_deploy/deploy.log';
|
||||||
|
private string $historyDir = '/var/www/_deploy/history';
|
||||||
|
|
||||||
|
public function scriptExists(): bool
|
||||||
|
{
|
||||||
|
$exists = file_exists($this->deployScript);
|
||||||
|
$this->phpLog('check_script', $exists ? 'found' : 'not_found');
|
||||||
|
|
||||||
|
return $exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDeployRunning(): bool
|
||||||
|
{
|
||||||
|
$output = shell_exec('pgrep -f "deploy\\.sh"');
|
||||||
|
$running = !empty(trim($output ?? ''));
|
||||||
|
|
||||||
|
return $running;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function startDeploy(): bool
|
||||||
|
{
|
||||||
|
if ($this->isDeployRunning()) {
|
||||||
|
$this->phpLog('start', 'already_running');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists($this->logFile)) {
|
||||||
|
unlink($this->logFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->phpLog('start', 'launching deploy.sh');
|
||||||
|
|
||||||
|
$command = sprintf(
|
||||||
|
'bash %s > /dev/null 2>&1 &',
|
||||||
|
escapeshellarg($this->deployScript)
|
||||||
|
);
|
||||||
|
|
||||||
|
shell_exec($command);
|
||||||
|
usleep(500000);
|
||||||
|
|
||||||
|
$started = $this->isDeployRunning();
|
||||||
|
|
||||||
|
$this->phpLog('start', $started ? 'process_started' : 'process_failed_to_start');
|
||||||
|
|
||||||
|
return $started;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDeployStatus(): array
|
||||||
|
{
|
||||||
|
if (!$this->isDeployRunning()) {
|
||||||
|
if (!file_exists($this->logFile)) {
|
||||||
|
$this->phpLog('status', 'idle');
|
||||||
|
return ['status' => 'idle', 'message' => 'Деплой не запущен'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$log = $this->getLog();
|
||||||
|
$lastLines = array_slice(explode("\n", trim($log)), -5);
|
||||||
|
$lastOutput = implode("\n", $lastLines);
|
||||||
|
|
||||||
|
$isSuccess = stripos($lastOutput, '✅') !== false
|
||||||
|
|| stripos($lastOutput, 'completed successfully') !== false;
|
||||||
|
|
||||||
|
$isFailed = stripos($lastOutput, '❌') !== false
|
||||||
|
|| stripos($lastOutput, 'ERROR') !== false;
|
||||||
|
|
||||||
|
$status = $isSuccess ? 'completed' : ($isFailed ? 'failed' : 'unknown');
|
||||||
|
$this->phpLog('status', $status);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => $status,
|
||||||
|
'message' => $isSuccess ? 'Деплой завершён успешно!' : 'Деплой завершён',
|
||||||
|
'log' => $log,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->phpLog('status', 'running');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => 'running',
|
||||||
|
'message' => 'Деплой выполняется...',
|
||||||
|
'log' => $this->getLog(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLog(): string
|
||||||
|
{
|
||||||
|
if (!file_exists($this->logFile)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return file_get_contents($this->logFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogTail(int $lines = 50): string
|
||||||
|
{
|
||||||
|
$log = $this->getLog();
|
||||||
|
if (empty($log)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$allLines = explode("\n", trim($log));
|
||||||
|
return implode("\n", array_slice($allLines, -$lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clearLog(): void
|
||||||
|
{
|
||||||
|
if (file_exists($this->logFile)) {
|
||||||
|
unlink($this->logFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->phpLog('clear', 'deploy log cleared');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHistory(): array
|
||||||
|
{
|
||||||
|
if (!is_dir($this->historyDir)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = glob($this->historyDir . '/*.json');
|
||||||
|
usort($files, fn($a, $b) => strcmp($b, $a));
|
||||||
|
|
||||||
|
$history = [];
|
||||||
|
foreach (array_slice($files, 0, 50) as $file) {
|
||||||
|
$content = file_get_contents($file);
|
||||||
|
$data = json_decode($content, true);
|
||||||
|
if ($data) {
|
||||||
|
$history[] = $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $history;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function phpLog(string $event, string $message): void
|
||||||
|
{
|
||||||
|
Log::channel('deploy')->info('[Deploy] {event}: {message}', [
|
||||||
|
'event' => $event,
|
||||||
|
'message' => $message,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,6 +118,13 @@ return [
|
|||||||
'replace_placeholders' => true,
|
'replace_placeholders' => true,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'deploy' => [
|
||||||
|
'driver' => 'single',
|
||||||
|
'path' => storage_path('logs/deploy.log'),
|
||||||
|
'level' => 'info',
|
||||||
|
'replace_placeholders' => true,
|
||||||
|
],
|
||||||
|
|
||||||
'null' => [
|
'null' => [
|
||||||
'driver' => 'monolog',
|
'driver' => 'monolog',
|
||||||
'handler' => NullHandler::class,
|
'handler' => NullHandler::class,
|
||||||
|
|||||||
Reference in New Issue
Block a user