getContent(); $sig = $r->header('X-Hub-Signature-256', ''); if ($secret && ! $this->validSignature($body, $sig, $secret)) { Log::warning('[webhook] firma inválida', ['sig' => substr((string) $sig, 0, 20)]); return response()->json(['ok' => false, 'error' => 'invalid signature'], 401); } $payload = json_decode($body, true) ?: []; $ref = $payload['ref'] ?? ''; $branch = str_starts_with($ref, 'refs/heads/') ? substr($ref, 10) : 'main'; // Solo main (o el branch que se quiera) if (! in_array($branch, ['main', 'master'], true)) { Log::info("[webhook] push a '$branch' ignorado"); return response()->json(['ok' => true, 'skipped' => 'branch != main'], 200); } // Lanza el deploy en background, desacoplado del request $script = '/usr/local/bin/dummybot-deploy.sh'; if (is_executable($script)) { $logDir = '/var/log/dummybot-deploy'; @mkdir($logDir, 0755, true); $logFile = $logDir . '/deploy-' . date('Ymd-His') . '.log'; $cmd = sprintf('BRANCH=%s nohup %s >> %s 2>&1 &', escapeshellarg($branch), $script, escapeshellarg($logFile)); exec($cmd); Log::info("[webhook] deploy branch=$branch lanzado log=$logFile"); return response()->json(['ok' => true, 'status' => 'deploy started', 'log' => basename($logFile)], 202); } // Si el script no está (entorno local / sin permisos) — solo log Log::info("[webhook] push branch=$branch (script no disponible)"); return response()->json(['ok' => true, 'status' => 'logged only'], 200); } private function validSignature(string $body, string $sig, string $secret): bool { if (! str_starts_with($sig, 'sha256=')) return false; $expected = 'sha256=' . hash_hmac('sha256', $body, $secret); return hash_equals($expected, $sig); } }