From c5b5e6069de625ffdfed2cfddd3b3a0565c5cda0 Mon Sep 17 00:00:00 2001 From: Saufi Date: Fri, 22 May 2026 21:57:33 +0800 Subject: [PATCH] tambah webhook --- .env.example | 4 ++ public/webhook.php | 132 +++++++++++++++++++++++++++++++++++++++++++++ scripts/deploy.ps1 | 77 ++++++++++++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 public/webhook.php create mode 100644 scripts/deploy.ps1 diff --git a/.env.example b/.env.example index c0660ea..0f07525 100644 --- a/.env.example +++ b/.env.example @@ -63,3 +63,7 @@ AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false VITE_APP_NAME="${APP_NAME}" + +# Gitea Webhook +WEBHOOK_SECRET=tukar_kepada_secret_yang_kuat +WEBHOOK_BRANCH=master diff --git a/public/webhook.php b/public/webhook.php new file mode 100644 index 0000000..79d21cb --- /dev/null +++ b/public/webhook.php @@ -0,0 +1,132 @@ + $code, 'message' => $message]); + exit; +} + +function read_env(string $key, string $default = ''): string +{ + static $env = null; + if ($env === null) { + $env = []; + $file = APP_ROOT . DIRECTORY_SEPARATOR . '.env'; + if (file_exists($file)) { + foreach (file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) { + $line = trim($line); + if ($line === '' || str_starts_with($line, '#')) { + continue; + } + if (str_contains($line, '=')) { + [$k, $v] = explode('=', $line, 2); + $env[trim($k)] = trim($v, " \t\"'"); + } + } + } + } + + return $env[$key] ?? (getenv($key) ?: $default); +} + +// ── Hanya terima POST ──────────────────────────────────────────────────────── +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + respond(405, 'Method Not Allowed'); +} + +$payload = file_get_contents('php://input'); +if (empty($payload)) { + respond(400, 'Empty payload'); +} + +// ── Sahkan signature Gitea ─────────────────────────────────────────────────── +$secret = read_env('WEBHOOK_SECRET'); +if (!empty($secret)) { + $signature = $_SERVER['HTTP_X_GITEA_SIGNATURE'] ?? ''; + $expected = hash_hmac('sha256', $payload, $secret); + + if (!hash_equals($expected, $signature)) { + log_msg('WARN: Signature tidak sah dari IP ' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown')); + respond(403, 'Invalid signature'); + } +} + +// ── Parse payload ──────────────────────────────────────────────────────────── +$data = json_decode($payload, true); +if (json_last_error() !== JSON_ERROR_NONE) { + respond(400, 'Invalid JSON payload'); +} + +$event = $_SERVER['HTTP_X_GITEA_EVENT'] ?? 'unknown'; +$ref = $data['ref'] ?? ''; +$branch = basename($ref); +$pusher = $data['pusher']['login'] ?? 'unknown'; +$commitId = substr($data['after'] ?? '', 0, 8); + +log_msg("Event: $event | Branch: $branch | Pusher: $pusher | Commit: $commitId"); + +// ── Hanya deploy pada push ke master/main ─────────────────────────────────── +$targetBranch = read_env('WEBHOOK_BRANCH', 'master'); +if ($event !== 'push' || $branch !== $targetBranch) { + respond(200, "Skipped — event=$event branch=$branch (target=$targetBranch)"); +} + +// ── Jalankan deployment script ─────────────────────────────────────────────── +$deployScript = APP_ROOT . '\\scripts\\deploy.ps1'; +$deployLog = APP_ROOT . '\\storage\\logs\\deploy-' . date('Ymd-His') . '.log'; + +if (!file_exists($deployScript)) { + log_msg('ERROR: Deploy script tidak dijumpai: ' . $deployScript); + respond(500, 'Deploy script not found'); +} + +$command = sprintf( + 'powershell.exe -NonInteractive -ExecutionPolicy Bypass -File "%s" > "%s" 2>&1', + $deployScript, + $deployLog +); + +log_msg("Menjalankan: $command"); + +$descriptors = [ + 0 => ['pipe', 'r'], + 1 => ['file', $deployLog, 'a'], + 2 => ['file', $deployLog, 'a'], +]; + +$process = proc_open($command, $descriptors, $pipes, APP_ROOT); + +if (!is_resource($process)) { + log_msg('ERROR: Gagal memulakan proses deployment'); + respond(500, 'Failed to start deployment process'); +} + +fclose($pipes[0]); +$exitCode = proc_close($process); + +log_msg("Deployment selesai — exit code: $exitCode | log: $deployLog"); + +if ($exitCode === 0) { + respond(200, "Deployment berjaya — commit $commitId"); +} else { + respond(500, "Deployment gagal — exit code $exitCode, semak $deployLog"); +} diff --git a/scripts/deploy.ps1 b/scripts/deploy.ps1 new file mode 100644 index 0000000..0c5f6e4 --- /dev/null +++ b/scripts/deploy.ps1 @@ -0,0 +1,77 @@ +# ============================================================================= +# Deploy Script — myEventPostMortem (Laravel) +# Windows Server 2019 +# +# Dipanggil oleh public/webhook.php apabila Gitea push event diterima. +# Pastikan user yang menjalankan IIS/PHP ada akses ke git, composer, npm, php. +# ============================================================================= + +$AppRoot = Split-Path -Parent $PSScriptRoot +$ErrorActionPreference = "Stop" + +function Write-Log { + param([string]$Message) + $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Write-Output "[$ts] $Message" +} + +function Invoke-Step { + param([string]$Label, [scriptblock]$Action) + Write-Log ">>> $Label" + & $Action + if ($LASTEXITCODE -ne 0) { + throw "$Label gagal dengan exit code $LASTEXITCODE" + } +} + +# ── Masuk ke direktori aplikasi ────────────────────────────────────────────── +Set-Location $AppRoot +Write-Log "=== Deployment Bermula ===" +Write-Log "App Root : $AppRoot" +Write-Log "User : $env:USERNAME" +Write-Log "Masa : $(Get-Date)" + +# ── Git pull ───────────────────────────────────────────────────────────────── +Invoke-Step "Git pull" { + git pull origin master +} + +# ── Composer install ───────────────────────────────────────────────────────── +Invoke-Step "Composer install" { + composer install --no-dev --optimize-autoloader --no-interaction --no-progress +} + +# ── NPM install & build ────────────────────────────────────────────────────── +Invoke-Step "NPM install" { + npm ci --prefer-offline +} + +Invoke-Step "NPM build" { + npm run build +} + +# ── Laravel: cache config & migrate ───────────────────────────────────────── +Invoke-Step "Artisan down (maintenance mode)" { + php artisan down --retry=10 +} + +try { + Invoke-Step "Database migrate" { + php artisan migrate --force + } + + Invoke-Step "Optimize Laravel" { + php artisan optimize + } + + Invoke-Step "Queue restart" { + php artisan queue:restart + } +} +finally { + # Pastikan aplikasi dibuka semula walaupun ada ralat + Write-Log ">>> Artisan up" + php artisan up +} + +Write-Log "=== Deployment Berjaya ==="