'', 'repo'=>'', 'hash'=>'']; } function setLastDeployData($repoDir, $data) { $jsonFile = $repoDir . '/deploy_last.json'; file_put_contents($jsonFile, json_encode($data)); } function runShellSteps($steps, $cwd = null) { // Runs an array of ['desc'=>..., 'cmd'=>...] steps, prints output and returns overall success $allOk = true; echo "
Shell Output:\n";
    foreach ($steps as $step) {
        $desc = $step['desc'] ?? '';
        $cmd  = $step['cmd'];
        echo "\n# $desc\n$ $cmd\n";
        passthru(($cwd ? "cd ".escapeshellarg($cwd)." && " : "") . "$cmd 2>&1", $ret);
        if ($ret !== 0 && stripos($desc, 'commit') === false) {
            echo "\n❌ Error in step: $desc\n";
            $allOk = false;
            break;
        }
    }
    echo "
"; return $allOk; } function sanitizeRepoInput($input, $type = 'alphanum') { // Usage: sanitizeRepoInput($_POST['repo']), etc if ($type === 'hash') return preg_replace('/[^a-fA-F0-9]/', '', $input); if ($type === 'repo') return preg_replace('/[^a-zA-Z0-9_-]/', '', $input); return trim($input); } ?>