fix(deploy): excluir CSRF en /webhook/* + script self-contained
Some checks are pending
Deploy DummyBot / Build & Deploy to ${{ env.DEPLOY_HOST }} (push) Waiting to run
Some checks are pending
Deploy DummyBot / Build & Deploy to ${{ env.DEPLOY_HOST }} (push) Waiting to run
- bootstrap/app.php: validateCsrfTokens except webhook/* - WebhookController: ejecuta deploy-runner.sh (vive en repo) - deploy-runner.sh: hace git pull + composer + npm + migrate + cache - docker-compose.yml: solo monta el repo (sin script externo)
This commit is contained in:
parent
65981b4bd6
commit
00fcbc9a7c
@ -5,15 +5,21 @@
|
|||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
class WebhookController extends Controller {
|
class WebhookController extends Controller {
|
||||||
/**
|
/**
|
||||||
* Webhook de Gitea para disparar el deploy.
|
* Webhook de Gitea → deploy self-contained.
|
||||||
* Gitea envía POST con X-Hub-Signature-256 (HMAC-SHA256).
|
*
|
||||||
* Si la firma coincide, lanza el script de deploy en background.
|
* Gitea envía POST al hacer push a main, con X-Hub-Signature-256.
|
||||||
|
* Si la firma coincide, lanza `git pull` + migrate + cache clear
|
||||||
|
* en background usando el binario git del container.
|
||||||
|
*
|
||||||
|
* IMPORTANTE: este código debe existir ANTES de que el webhook
|
||||||
|
* se active. El primer deploy se hace a mano vía SSH.
|
||||||
*/
|
*/
|
||||||
public function deploy(Request $r): JsonResponse {
|
public function deploy(Request $r): JsonResponse {
|
||||||
$secret = config('app.deploy_webhook_secret', env('DEPLOY_WEBHOOK_SECRET'));
|
$secret = env('DEPLOY_WEBHOOK_SECRET');
|
||||||
$body = $r->getContent();
|
$body = $r->getContent();
|
||||||
$sig = $r->header('X-Hub-Signature-256', '');
|
$sig = $r->header('X-Hub-Signature-256', '');
|
||||||
|
|
||||||
@ -26,27 +32,37 @@ public function deploy(Request $r): JsonResponse {
|
|||||||
$ref = $payload['ref'] ?? '';
|
$ref = $payload['ref'] ?? '';
|
||||||
$branch = str_starts_with($ref, 'refs/heads/') ? substr($ref, 10) : 'main';
|
$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)) {
|
if (! in_array($branch, ['main', 'master'], true)) {
|
||||||
Log::info("[webhook] push a '$branch' ignorado");
|
Log::info("[webhook] push a '$branch' ignorado");
|
||||||
return response()->json(['ok' => true, 'skipped' => 'branch != main'], 200);
|
return response()->json(['ok' => true, 'skipped' => 'branch != main'], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lanza el deploy en background, desacoplado del request
|
$logFile = storage_path('logs/deploy-' . date('Ymd-His') . '.log');
|
||||||
$script = '/usr/local/bin/dummybot-deploy.sh';
|
$this->dispatchDeployInBackground($branch, $logFile);
|
||||||
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");
|
Log::info("[webhook] deploy branch=$branch lanzado log=$logFile");
|
||||||
return response()->json(['ok' => true, 'status' => 'deploy started', 'log' => basename($logFile)], 202);
|
return response()->json([
|
||||||
|
'ok' => true,
|
||||||
|
'status' => 'deploy started',
|
||||||
|
'log' => basename($logFile),
|
||||||
|
], 202);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si el script no está (entorno local / sin permisos) — solo log
|
private function dispatchDeployInBackground(string $branch, string $logFile): void {
|
||||||
Log::info("[webhook] push branch=$branch (script no disponible)");
|
$script = base_path('deploy-runner.sh');
|
||||||
return response()->json(['ok' => true, 'status' => 'logged only'], 200);
|
if (! file_exists($script)) {
|
||||||
|
// Modo fallback: registrar en log y salir
|
||||||
|
Log::warning("[webhook] $script no existe, deploy no ejecutado");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$cmd = sprintf(
|
||||||
|
'BRANCH=%s BRANCH=%s nohup bash %s > %s 2>&1 &',
|
||||||
|
escapeshellarg($branch),
|
||||||
|
escapeshellarg($branch),
|
||||||
|
escapeshellarg($script),
|
||||||
|
escapeshellarg($logFile)
|
||||||
|
);
|
||||||
|
exec($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validSignature(string $body, string $sig, string $secret): bool {
|
private function validSignature(string $body, string $sig, string $secret): bool {
|
||||||
|
|||||||
@ -16,6 +16,11 @@
|
|||||||
$middleware->alias([
|
$middleware->alias([
|
||||||
'auth.device' => \App\Http\Middleware\AuthenticateDevice::class,
|
'auth.device' => \App\Http\Middleware\AuthenticateDevice::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Excluir CSRF para el webhook de Gitea (autenticado por HMAC)
|
||||||
|
$middleware->validateCsrfTokens(except: [
|
||||||
|
'webhook/*',
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions): void {
|
->withExceptions(function (Exceptions $exceptions): void {
|
||||||
$exceptions->shouldRenderJsonWhen(
|
$exceptions->shouldRenderJsonWhen(
|
||||||
|
|||||||
59
deploy-runner.sh
Executable file
59
deploy-runner.sh
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# DummyBot – deploy runner (vive en el repo)
|
||||||
|
# Llamado por el webhook de Gitea. Hace git pull + composer +
|
||||||
|
# npm + migrate + cache clear + restart.
|
||||||
|
#
|
||||||
|
# IMPORTANTE: este script se ejecuta DENTRO del container
|
||||||
|
# dummybot_app (PHP-FPM). Por tanto:
|
||||||
|
# - git, composer, npm están disponibles (Dockerfile los instala)
|
||||||
|
# - artisan funciona con `php artisan`
|
||||||
|
# - `docker compose` se puede invocar contra el socket del host
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
set -e
|
||||||
|
BRANCH="${BRANCH:-main}"
|
||||||
|
APP_DIR="${APP_DIR:-/var/www}"
|
||||||
|
cd "$APP_DIR"
|
||||||
|
|
||||||
|
echo "═══════════════════════════════════════════"
|
||||||
|
echo " DummyBot deploy — $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
echo " Branch: $BRANCH"
|
||||||
|
echo " App dir: $APP_DIR"
|
||||||
|
echo "═══════════════════════════════════════════"
|
||||||
|
|
||||||
|
echo "→ git fetch + reset"
|
||||||
|
git fetch origin "$BRANCH" 2>&1 | tail -3
|
||||||
|
git reset --hard "origin/$BRANCH" 2>&1 | tail -3
|
||||||
|
NEW_COMMIT=$(git rev-parse --short HEAD)
|
||||||
|
echo "HEAD: $NEW_COMMIT"
|
||||||
|
|
||||||
|
echo "→ composer install"
|
||||||
|
composer install --no-dev --no-interaction --no-progress --optimize-autoloader 2>&1 | tail -3
|
||||||
|
|
||||||
|
echo "→ npm install + build"
|
||||||
|
if [ -f package-lock.json ]; then
|
||||||
|
npm ci --no-audit --no-fund 2>&1 | tail -3
|
||||||
|
else
|
||||||
|
npm install --no-audit --no-fund 2>&1 | tail -3
|
||||||
|
fi
|
||||||
|
npm run build 2>&1 | tail -3 || echo " (build skipped)"
|
||||||
|
|
||||||
|
echo "→ artisan migrate"
|
||||||
|
php artisan migrate --force 2>&1 | tail -5
|
||||||
|
|
||||||
|
echo "→ artisan cache:clear"
|
||||||
|
php artisan config:clear 2>&1 | tail -1
|
||||||
|
php artisan route:clear 2>&1 | tail -1
|
||||||
|
php artisan view:clear 2>&1 | tail -1
|
||||||
|
php artisan event:clear 2>&1 | tail -1
|
||||||
|
php artisan optimize 2>&1 | tail -3
|
||||||
|
|
||||||
|
echo "→ restart php-fpm (graceful)"
|
||||||
|
# Recargar el FPM para que coja el nuevo código
|
||||||
|
if command -v kill &> /dev/null; then
|
||||||
|
kill -USR2 1 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "═══════════════════════════════════════════"
|
||||||
|
echo " Deploy OK: $NEW_COMMIT"
|
||||||
|
echo "═══════════════════════════════════════════"
|
||||||
@ -10,8 +10,6 @@ services:
|
|||||||
working_dir: /var/www
|
working_dir: /var/www
|
||||||
volumes:
|
volumes:
|
||||||
- .:/var/www
|
- .:/var/www
|
||||||
- /usr/local/bin/dummybot-deploy.sh:/usr/local/bin/dummybot-deploy.sh:ro
|
|
||||||
- /var/log/dummybot-deploy:/var/log/dummybot-deploy
|
|
||||||
environment:
|
environment:
|
||||||
- AUTO_MIGRATE=${AUTO_MIGRATE:-0}
|
- AUTO_MIGRATE=${AUTO_MIGRATE:-0}
|
||||||
- DEPLOY_WEBHOOK_SECRET=${DEPLOY_WEBHOOK_SECRET:-}
|
- DEPLOY_WEBHOOK_SECRET=${DEPLOY_WEBHOOK_SECRET:-}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user