feat(deploy): webhook de Gitea → /webhook/deploy
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
Gitea 1.22.6 no soporta Actions completo. Solución: - WebhookController valida HMAC-SHA256, dispara script en background - /usr/local/bin/dummybot-deploy.sh hace git pull + composer + npm + migrate + up - Webhook en Gitea → POST http://157.180.77.232:18080/webhook/deploy - Secreto DEPLOY_WEBHOOK_SECRET en .env, validado por X-Hub-Signature-256 - Mount del script y log dir al container dummybot_app
This commit is contained in:
parent
5bd82937c0
commit
65981b4bd6
57
app/Http/Controllers/WebhookController.php
Normal file
57
app/Http/Controllers/WebhookController.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class WebhookController extends Controller {
|
||||||
|
/**
|
||||||
|
* Webhook de Gitea para disparar el deploy.
|
||||||
|
* Gitea envía POST con X-Hub-Signature-256 (HMAC-SHA256).
|
||||||
|
* Si la firma coincide, lanza el script de deploy en background.
|
||||||
|
*/
|
||||||
|
public function deploy(Request $r): JsonResponse {
|
||||||
|
$secret = config('app.deploy_webhook_secret', env('DEPLOY_WEBHOOK_SECRET'));
|
||||||
|
$body = $r->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,8 +10,11 @@ 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:-}
|
||||||
networks:
|
networks:
|
||||||
- laravel
|
- laravel
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use App\Http\Controllers\PageController;
|
use App\Http\Controllers\PageController;
|
||||||
use App\Http\Controllers\TaskController;
|
use App\Http\Controllers\TaskController;
|
||||||
|
use App\Http\Controllers\WebhookController;
|
||||||
use App\Http\Controllers\Auth\LoginController;
|
use App\Http\Controllers\Auth\LoginController;
|
||||||
use App\Http\Controllers\Api\DeviceController;
|
use App\Http\Controllers\Api\DeviceController;
|
||||||
use App\Http\Controllers\Api\ProjectController;
|
use App\Http\Controllers\Api\ProjectController;
|
||||||
@ -16,6 +17,9 @@
|
|||||||
Route::post('/register',[LoginController::class, 'register']);
|
Route::post('/register',[LoginController::class, 'register']);
|
||||||
Route::post('/logout', [LoginController::class, 'logout'])->middleware('auth:sanctum')->name('logout');
|
Route::post('/logout', [LoginController::class, 'logout'])->middleware('auth:sanctum')->name('logout');
|
||||||
|
|
||||||
|
// ─── Webhook de Gitea (dispara deploy) ───
|
||||||
|
Route::post('/webhook/deploy', [WebhookController::class, 'deploy']);
|
||||||
|
|
||||||
// ─── Páginas protegidas ───
|
// ─── Páginas protegidas ───
|
||||||
Route::middleware('auth:sanctum')->group(function () {
|
Route::middleware('auth:sanctum')->group(function () {
|
||||||
Route::get('/app', [PageController::class, 'app'])->name('app');
|
Route::get('/app', [PageController::class, 'app'])->name('app');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user