tambah webhook
This commit is contained in:
@@ -63,3 +63,7 @@ AWS_BUCKET=
|
|||||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
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