tambah webhook
This commit is contained in:
@@ -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
|
||||
|
||||
132
public/webhook.php
Normal file
132
public/webhook.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* Gitea Webhook Handler
|
||||
*
|
||||
* URL: https://your-domain.com/webhook.php
|
||||
* Gitea Content-Type: application/json
|
||||
* Gitea Secret: nilai WEBHOOK_SECRET dalam .env
|
||||
* Gitea Events: Push Events
|
||||
*/
|
||||
|
||||
define('APP_ROOT', dirname(__DIR__));
|
||||
define('LOG_FILE', APP_ROOT . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . 'webhook.log');
|
||||
|
||||
function log_msg(string $message): void
|
||||
{
|
||||
$line = '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL;
|
||||
file_put_contents(LOG_FILE, $line, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
function respond(int $code, string $message): never
|
||||
{
|
||||
http_response_code($code);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['status' => $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");
|
||||
}
|
||||
77
scripts/deploy.ps1
Normal file
77
scripts/deploy.ps1
Normal file
@@ -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 ==="
|
||||
Reference in New Issue
Block a user