This commit is contained in:
F4ilji
2026-04-09 17:41:22 +05:00
parent 4aa4b9cadf
commit c0babe8277
@@ -6,44 +6,70 @@ use Illuminate\Support\Facades\Log;
class DeploySiteAction class DeploySiteAction
{ {
private string $lockFile = '/var/www/_deploy/deploy.lock'; private string $deployScript = '/var/www/_deploy/deploy.sh';
private string $logFile = '/var/www/_deploy/deploy.log'; private string $logFile = '/var/www/_deploy/deploy.log';
private string $pidFile = '/var/www/_deploy/deploy.pid';
/** /**
* Создаёт файл-триггер для запуска деплоя * Запускает deploy.sh напрямую
* *
* @return array ['success' => bool, 'message' => string] * @return array ['success' => bool, 'message' => string]
*/ */
public function run(): array public function run(): array
{ {
try { try {
// Проверяем, не запущен ли уже деплой if (!file_exists($this->deployScript)) {
if (file_exists($this->lockFile)) { return [
$lockContent = file_get_contents($this->lockFile); 'success' => false,
$lockData = json_decode($lockContent, true); 'message' => 'Скрипт деплоя не найден на сервере',
];
if ($lockData && isset($lockData['status']) && $lockData['status'] === 'running') {
return [
'success' => false,
'message' => 'Деплой уже запущен! Подождите завершения текущего процесса.',
];
}
} }
// Создаём файл-триггер // Проверяем, не запущен ли уже деплой
$lockData = [ if ($this->isDeployRunning()) {
'status' => 'pending', return [
'started_at' => now()->toDateTimeString(), 'success' => false,
'started_by' => auth()->id(), 'message' => 'Деплой уже запущен! Подождите завершения текущего процесса.',
];
}
// Запускаем deploy.sh в фоне через proc_open
$command = sprintf(
'nohup bash %s > %s 2>&1 & echo $!',
escapeshellarg($this->deployScript),
escapeshellarg($this->logFile)
);
$descriptors = [
1 => ['pipe', 'w'], // stdout
2 => ['pipe', 'w'], // stderr
]; ];
file_put_contents($this->lockFile, json_encode($lockData, JSON_PRETTY_PRINT)); $process = proc_open($command, $descriptors, $pipes);
Log::info('Deploy triggered', ['user_id' => auth()->id()]); if (!is_resource($process)) {
Log::error('Deploy script failed to start — proc_open unavailable');
return [
'success' => false,
'message' => 'Ошибка при запуске скрипта деплоя (proc_open недоступен)',
];
}
// Читаем PID процесса
$pid = trim(stream_get_contents($pipes[1]));
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
if (!empty($pid) && is_numeric($pid)) {
file_put_contents($this->pidFile, $pid);
}
Log::info('Deploy triggered', ['user_id' => auth()->id(), 'pid' => $pid]);
return [ return [
'success' => true, 'success' => true,
'message' => 'Деплой запущен! Процесс обновления сайта начнётся в течение 1 минуты.', 'message' => 'Деплой запущен! Процесс обновления сайта начнётся в течение нескольких секунд.',
]; ];
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error('Deploy trigger failed', ['exception' => $e->getMessage()]); Log::error('Deploy trigger failed', ['exception' => $e->getMessage()]);
@@ -60,28 +86,52 @@ class DeploySiteAction
*/ */
public function getStatus(): array public function getStatus(): array
{ {
if (!file_exists($this->lockFile)) { if (!file_exists($this->logFile)) {
return ['status' => 'idle', 'message' => 'Деплой не запущен']; return ['status' => 'idle', 'message' => 'Деплой не запущен'];
} }
$content = file_get_contents($this->lockFile); $log = file_get_contents($this->logFile);
$data = json_decode($content, true);
if (!$data) { // Проверяем, запущен ли процесс
return ['status' => 'error', 'message' => 'Ошибка чтения статуса']; if ($this->isDeployRunning()) {
return [
'status' => 'running',
'message' => 'Деплой выполняется...',
'log' => $log,
];
} }
// Читаем лог если есть // Процесс завершил — проверяем результат
$log = ''; $lastLines = array_slice(explode("\n", trim($log)), -5);
if (file_exists($this->logFile)) { $lastOutput = implode("\n", $lastLines);
$log = file_get_contents($this->logFile);
$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,
];
} }
return [ return [
'status' => $data['status'] ?? 'unknown', 'status' => 'unknown',
'started_at' => $data['started_at'] ?? null, 'message' => 'Статус неизвестен',
'completed_at' => $data['completed_at'] ?? null,
'message' => $data['message'] ?? '',
'log' => $log, 'log' => $log,
]; ];
} }
@@ -91,8 +141,31 @@ class DeploySiteAction
*/ */
public function clearStatus(): void public function clearStatus(): void
{ {
if (file_exists($this->lockFile)) { if (file_exists($this->logFile)) {
unlink($this->lockFile); unlink($this->logFile);
}
if (file_exists($this->pidFile)) {
unlink($this->pidFile);
} }
} }
/**
* Проверяет, запущен ли процесс деплоя
*/
private function isDeployRunning(): bool
{
if (!file_exists($this->pidFile)) {
return false;
}
$pid = trim(file_get_contents($this->pidFile));
if (empty($pid)) {
return false;
}
// Проверяем существование процесса
exec(sprintf('kill -0 %s 2>/dev/null', escapeshellarg($pid)), $output, $exitCode);
return $exitCode === 0;
}
} }