File manager - Edit - /var/www/html/gop/api/public/hooks/deployapi.php
Back
<?php /** * Deploy Laravel via Webhook (cPanel) * - Auth: HMAC (X-Hub-Signature-256) usando segredo do .env (DEPLOY_WEBHOOK_SECRET) OU Authorization: Bearer (DEPLOY_BEARER_TOKEN) * - Eventos: ping (200 pong), push (dispara deploy) * - Passos: git fetch/reset, composer install --no-dev, caches, (migrate opcional), permissões * - Logs: /home/.../logs/deploy -> public/.hooks/deploy-logs -> /tmp/deploy-logs */ /* ================= Handshakes rápidos ================= */ // Auto-teste (bypass de tudo; útil para checar 403/ModSecurity/Rewrite) if (isset($_GET['selftest'])) { header('Content-Type: text/plain; charset=UTF-8'); echo "ok\n"; return; } // Resposta imediata ao ping do GitHub $__evt = $_SERVER['HTTP_X_GITHUB_EVENT'] ?? ''; if ($__evt === 'ping') { header('Content-Type: text/plain; charset=UTF-8'); http_response_code(200); echo "pong\n"; return; } /* ================= Util: carregar .env sem framework ================= */ function load_env_file_once(string $envPath): array { static $cache = null; if ($cache !== null) return $cache; $cache = []; if (!is_file($envPath) || !is_readable($envPath)) return $cache; $lines = file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: []; foreach ($lines as $line) { $line = trim($line); if ($line === '' || $line[0] === '#') continue; if (stripos($line, 'export ') === 0) $line = trim(substr($line, 7)); $pos = strpos($line, '='); if ($pos === false) continue; $key = trim(substr($line, 0, $pos)); $val = trim(substr($line, $pos + 1)); // remove aspas simples/duplas if ( (str_starts_with($val, '"') && str_ends_with($val, '"')) || (str_starts_with($val, "'") && str_ends_with($val, "'")) ) { $val = substr($val, 1, -1); } $cache[$key] = $val; } return $cache; } function env_get(string $key, ?string $default = null, ?array $envCache = null): ?string { foreach ([$_ENV, $_SERVER] as $src) { if (isset($src[$key]) && $src[$key] !== '') return $src[$key]; } $v = getenv($key); if ($v !== false && $v !== '') return $v; if ($envCache && array_key_exists($key, $envCache) && $envCache[$key] !== '') return $envCache[$key]; return $default; } /* ===================== Config básica ===================== */ set_time_limit(300); header('Content-Type: text/plain; charset=UTF-8'); umask(0002); $user = 'gopangratest'; $branch = 'develop'; $gitrepo = 'api-goptest-angra'; // Ajuste aqui o diretório do projeto $PROJECT_DIR = '/home/' . $user . '/public_html/'. $gitrepo; // Carrega .env do projeto (não o da public/) $ENV = load_env_file_once($PROJECT_DIR . '/.env'); // Segredos vindos do .env (preferencial) ou ambiente $WEBHOOK_SECRET = env_get('DEPLOY_WEBHOOK_SECRET', '', $ENV); // HMAC do GitHub Webhook $BEARER_TOKEN = env_get('DEPLOY_BEARER_TOKEN', '', $ENV); // opcional p/ Authorization: Bearer $RUN_MIGRATIONS = env_get('DEPLOY_RUN_MIGRATIONS', '0', $ENV); // "1" para rodar migrations // Caminhos fixos (podem ser sobrescritos por .env se desejar) $PHP_BIN = env_get('DEPLOY_PHP_BIN', '/usr/local/bin/php', $ENV); $COMPOSER_BIN = env_get('DEPLOY_COMPOSER_BIN', '/opt/cpanel/composer/bin/composer', $ENV); // PATH mais completo para este processo putenv('PATH=/usr/local/bin:/usr/bin:/bin:/opt/cpanel/composer/bin:' . getenv('PATH')); putenv('HOME=' . $PROJECT_DIR); // Alvos por branch (pode ajustar se usar múltiplos dirs) $targets = [ $branch => [ 'dir' => $PROJECT_DIR, 'php' => $PHP_BIN, 'composer' => $COMPOSER_BIN, 'build' => false, ], ]; // (Opcional) restringir repositório $allowRepos = ['CityConnectBr/api-gop-angra']; // Logs com fallback $PRIMARY_LOG_DIR = $PROJECT_DIR . '/logs/deploy'; $FALLBACK_LOG_DIR = __DIR__ . '/deployhml-logs'; $TMP_LOG_DIR = sys_get_temp_dir() . '/deployhml-logs'; $logDir = $PRIMARY_LOG_DIR; if (!is_dir($logDir) && !@mkdir($logDir, 0775, true)) { $logDir = $FALLBACK_LOG_DIR; if (!is_dir($logDir) && !@mkdir($logDir, 0775, true)) { $logDir = $TMP_LOG_DIR; @mkdir($logDir, 0775, true); } } $logFile = $logDir . '/deploy-' . date('Ymd-His') . '.log'; // PHP error log deste script ini_set('log_errors', '1'); ini_set('error_log', $logDir . '/php_error.log'); ini_set('display_errors', '1'); error_reporting(E_ALL); /* =================== Helpers =================== */ function line($s) { echo $s . "\n"; @file_put_contents($GLOBALS['logFile'], $s . "\n", FILE_APPEND); } function run_or_fail($title, $cmd) { line("$ " . $cmd); if (!function_exists('exec')) throw new RuntimeException("exec() disabled"); $out = []; $code = 0; exec($cmd . ' 2>&1', $out, $code); foreach ($out as $l) line($l); if ($code !== 0) throw new RuntimeException("$title failed (exit $code)"); } function run($title, $cmd) { line("$ " . $cmd); if (!function_exists('exec')) return [127, ["exec disabled"]]; $out = []; $code = 0; exec($cmd . ' 2>&1', $out, $code); foreach ($out as $l) line($l); return [$code, $out]; } function has_bin($bin) { $out = []; $code = 0; exec("command -v {$bin} 2>&1", $out, $code); return $code === 0 && !empty($out[0]); } /* ================= Payload + Auth ================= */ $payload = file_get_contents('php://input'); if ($payload === '' || $payload === false) { http_response_code(400); echo "empty payload\n"; return; } $data = json_decode($payload, true) ?: []; $ref = $data['ref'] ?? ''; // ex.: refs/heads/develop $repo = $data['repository']['full_name'] ?? ''; // Authorization: Bearer (opcional) $auth = $_SERVER['HTTP_AUTHORIZATION'] ?? ($_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''); if (!$auth && function_exists('getallheaders')) { $h = getallheaders(); $auth = $h['Authorization'] ?? ($h['authorization'] ?? ''); } $okBearer = false; if ($BEARER_TOKEN && preg_match('/Bearer\s+(.+)/i', $auth, $m)) { $tok = trim($m[1]); if ($tok !== '' && hash_equals($BEARER_TOKEN, $tok)) $okBearer = true; } // HMAC GitHub se Bearer não validou if (!$okBearer) { $sentSig = ''; if (isset($_SERVER['HTTP_X_HUB_SIGNATURE_256'])) { $sentSig = $_SERVER['HTTP_X_HUB_SIGNATURE_256']; } elseif (function_exists('getallheaders')) { $headers = getallheaders(); foreach ($headers as $key => $value) { if (strtolower($key) === 'x-hub-signature-256') { $sentSig = $value; break; } } } if (!$WEBHOOK_SECRET || !$sentSig) { http_response_code(401); echo "missing signature or secret\n"; return; } $calcSig = 'sha256=' . hash_hmac('sha256', $payload, $WEBHOOK_SECRET); if (!hash_equals($calcSig, $sentSig)) { http_response_code(403); echo "invalid signature\n"; return; } } // Valida repo/branch if ($allowRepos && !in_array($repo, $allowRepos, true)) { http_response_code(403); echo "repo not allowed: {$repo}\n"; return; } $branch = (strpos($ref, 'refs/heads/') === 0) ? substr($ref, 11) : basename($ref ?: ''); if (!$branch || !isset($targets[$branch])) { http_response_code(204); echo "ignored ref: {$ref}\n"; return; } /* ================= Deploy ================= */ $c = (object) $targets[$branch]; $DIR = $c->dir; $PHP = $c->php; $COMPOSERB = $c->composer; $BUILD = (bool) $c->build; $DIR_Q = escapeshellarg($DIR); $ARTISAN = escapeshellcmd($PHP) . ' ' . escapeshellarg($DIR . '/artisan'); // garantir composer cmd (phar vs bin) $COMPOSER = preg_match('/\.phar$/i', $COMPOSERB) ? (escapeshellcmd($PHP) . ' ' . escapeshellarg($COMPOSERB)) : escapeshellcmd($COMPOSERB); $__printed__ = false; register_shutdown_function(function () use (&$__printed__, $logFile) { if (!$__printed__ && is_file($logFile)) @readfile($logFile); }); try { line(str_repeat('=', 60)); line("Deploy start: repo={$repo} branch={$branch}"); line("DIR={$DIR}"); line("PHP={$PHP}"); line("COMPOSER_BIN={$COMPOSERB}"); line("COMPOSER={$COMPOSER}"); run('PATH', 'echo $PATH'); run('whoami', 'whoami'); if (!has_bin('git')) throw new RuntimeException('git not available'); // Git (sem -C para compatibilidade) run_or_fail('git work-tree?', 'cd ' . $DIR_Q . ' && git rev-parse --is-inside-work-tree'); run_or_fail('git fetch', 'cd ' . $DIR_Q . ' && git fetch --all --prune'); run_or_fail('git reset', 'cd ' . $DIR_Q . ' && git reset --hard ' . escapeshellarg('origin/' . $branch)); run('env', 'env'); run('whoami', 'whoami'); run('which php', 'which ' . $PHP); run('php -v', $PHP . ' -v'); run('php -m', $PHP . ' -m'); run('ls -la', 'ls -la ' . $DIR_Q); run('composer about', $COMPOSER . ' --working-dir=' . $DIR_Q . ' about'); run('composer diagnose', $COMPOSER . ' --working-dir=' . $DIR_Q . ' diagnose'); // Permissões — baseline + diretórios graváveis run('chown project', 'chown -R '. $user .':'. $user .' ' . $DIR_Q . ' || true'); run('chmod files 644', 'find ' . $DIR_Q . ' -type f -exec chmod 644 {} \;'); run('chmod dirs 755', 'find ' . $DIR_Q . ' -type d -exec chmod 755 {} \;'); run('chmod writable dirs', 'chmod -R 775 ' . $DIR_Q . '/storage ' . $DIR_Q . '/bootstrap/cache || true'); run('chmod storage files', 'find ' . $DIR_Q . '/storage -type f -exec chmod 664 {} \;'); run_or_fail( 'composer install', $COMPOSER . " --working-dir=$DIR_Q install --no-interaction --prefer-dist --optimize-autoloader" ); run_or_fail( 'composer dump-autoload', $COMPOSER . " --working-dir=$DIR_Q dump-autoload" ); // AGORA aplica as permissões novamente run('chown project', 'chown -R '. $user .':'. $user .' ' . $DIR_Q . ' || true'); run('chmod files 644', 'find ' . $DIR_Q . ' -type f -exec chmod 644 {} \;'); run('chmod dirs 755', 'find ' . $DIR_Q . ' -type d -exec chmod 755 {} \;'); run('chmod writable dirs', 'chmod -R 775 ' . $DIR_Q . '/storage ' . $DIR_Q . '/bootstrap/cache || true'); run('chmod storage files', 'find ' . $DIR_Q . '/storage -type f -exec chmod 664 {} \;'); // NPM run_or_fail( 'npm install', "npm --prefix $DIR_Q install --no-interaction --legacy-peer-deps" ); /* run_or_fail('rm swagger dirs', "rm -rf $DIR_Q/storage/api-docs $DIR_Q/public/docs"); run_or_fail('mkdir swagger dirs', "mkdir -p $DIR_Q/storage/api-docs $DIR_Q/public/docs"); run('whoami', 'whoami'); run('ls -l storage', "ls -ld $DIR_Q/storage"); run('ls -l api-docs', "ls -ld $DIR_Q/storage/api-docs"); run('ls api-docs', "ls -lh $DIR_Q/storage/api-docs/"); run('env', 'env'); run('cache clear', $ARTISAN.' cache:clear'); run('config clear', $ARTISAN.' config:clear'); run_or_fail('swagger generate', $ARTISAN . ' l5-swagger:generate --ansi'); run('ls api-docs after', "ls -lh $DIR_Q/storage/api-docs/"); run_or_fail('copy swagger JSON', "cp $DIR_Q/storage/api-docs/*.json $DIR_Q/public/docs/"); */ // Caches do Laravel run_or_fail('view:cache', $ARTISAN . ' optimize:clear'); // Migrations (opcional) if ($RUN_MIGRATIONS === '1') { run_or_fail('migrate', $ARTISAN . ' migrate --force'); } else { line('SKIP migrations (DEPLOY_RUN_MIGRATIONS != 1)'); } line("Deploy OK"); http_response_code(200); if (is_file($logFile)) { @readfile($logFile); $__printed__ = true; } } catch (Throwable $e) { line("FATAL: " . $e->getMessage()); http_response_code(500); if (is_file($logFile)) { @readfile($logFile); $__printed__ = true; } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.28 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings