首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 自動化部署:透過 SVN 更新網站程式碼的完整實作

     編輯:PHP 相關知識

場景:一鍵網頁部署的需求與挑戰

開發者常希望在後台按鈕即可觸發 svn update,但直接在 PHP 執行 shell 指令會遇到權限、編碼、衝突處理等坑。本文整理從伺服器端佈署到 PHP 呼叫的完整 SOP。

伺服器端前置作業 (一次性)

  1. 以 Web 使用者身份 checkout,確保 Apache/PHP-FPM 進程擁有工作目錄讀寫權:
    # Ubuntu 範例
    sudo -u www-data -H bash -c "
      cd /var/www
      svn checkout https://svn.example.com/repo/project/trunk my_site
    "
    
  2. 確認 SVN 認證已快取:首次 checkout 時輸入帳密,會存入 ~www-data/.subversion/auth/,後續無需互動。
  3. 目錄權限:chown -R www-data:www-data /var/www/my_site,必要時加上 setgid 確保新檔案群組一致。

PHP 呼叫 SVN 的關鍵參數解析

參數/環境作用必要性
putenv('LANG=C.UTF-8')修正 locale,避免中文檔名導致 svn: E000022: Can't convert string from 'UTF-8' to native encoding★★★
--accept theirs-full衝突時自動採用版本庫版本 (覆蓋本地修改),適合部署流程★★★
2>&1將 stderr 合併到 stdout,讓 shell_exec() 捕捉完整輸出 (含錯誤訊息)★★★
--non-interactive禁止互動提示,避免卡住★★
--trust-server-cert自信任自簽憑證 (HTTPS)視情況

生產級 PHP 部署腳本

<?php
/**
 * SVN 部署端點
 * POST /deploy.php?project=my_site
 * 回傳 JSON: {success: bool, output: string, time: float}
 */
declare(strict_types=1);

header('Content-Type: application/json; charset=utf-8');

$project = $_GET['project'] ?? 'my_site';
$allowedProjects = ['my_site', 'admin_portal', 'api_gateway'];
if (!in_array($project, $allowedProjects, true)) {
    http_response_code(400);
    echo json_encode(['success' => false, 'error' => 'Invalid project']);
    exit;
}

$workDir = "/var/www/{$project}";
$logFile = "/var/log/deploy_{$project}.log";

function logDeploy(string $msg, string $logFile): void {
    $line = "[" . date('Y-m-d H:i:s') . "] {$msg}\n";
    file_put_contents($logFile, $line, FILE_APPEND | LOCK_EX);
}

logDeploy("=== Deploy started for {$project} ===", $logFile);

$start = microtime(true);

// 關鍵環境變數
putenv('LANG=C.UTF-8');
putenv('LC_ALL=C.UTF-8');

$cmd = "svn update --accept theirs-full --non-interactive {$workDir} 2>&1";
$output = shell_exec($cmd);
$exitCode = $output === null ? -1 : 0; // shell_exec 失敗回傳 null

$elapsed = round(microtime(true) - $start, 3);
$success = ($exitCode === 0);

$logMsg = $success ? "SUCCESS ({$elapsed}s)" : "FAILED ({$elapsed}s)";
logDeploy($logMsg, $logFile);
logDeploy($output ?? 'No output', $logFile);

http_response_code($success ? 200 : 500);
echo json_encode([
    'success' => $success,
    'project' => $project,
    'output' => $output ?? '',
    'time' => $elapsed,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

前端整合範例 (Vue 3 + Axios)

<template>
  <button @click="deploy" :disabled="loading" class="btn btn-primary">
    <span v-if="loading" class="spinner"></span> 一鍵部署
  </button>
  <pre v-if="result" class="mt-3 log-box">{{ result }}</pre>
</template>

<script setup>
import { ref } from 'vue';
import axios from 'axios';

const loading = ref(false);
const result = ref('');

async function deploy() {
  loading.value = true;
  result.value = '部署中...';
  try {
    const res = await axios.post('/deploy.php?project=my_site', null, { timeout: 120000 });
    result.value = `✅ 成功 (${res.data.time}s)\n${res.data.output}`;
  } catch (e) {
    result.value = `❌ 失敗\n${e.response?.data?.output || e.message}`;
  } finally {
    loading.value = false;
  }
}
</script>

進階:無縫部署與回滾策略

  1. 符號連結切換:維護 /var/www/current -> /var/www/releases/20240115_1030,部署新版本至新目錄,原子性切換連結,失敗秒級回滾。
  2. 佇列非同步:將部署請求推入 Redis/RabbitMQ,Worker 處理,避免 HTTP 超時並支援並發控制。
  3. 鎖機制:flock 或 Redis SETNX 防止重複部署。
  4. 通知整合:完成後發送 Slack/Discord/Telegram Webhook,附上 commit log 與耗時。

常見錯誤排查表

錯誤訊息原因解決
svn: E170013: Unable to connect to a repository at URL網路不通、憑證不信任、URL 錯誤加 --trust-server-cert、檢查防火牆、驗證 URL
svn: E155037: Previous operation has not finished上次中斷留下 locksvn cleanup 或刪除 .svn/wc.db 重新 checkout
Permission denied目錄擁有者非 www-datachown -R www-data:www-data /var/www/project
中文檔名顯示亂碼locale 未設定 UTF-8加 putenv('LANG=C.UTF-8')

完整專案含 Docker、systemd timer 定時部署、Slack 通知已開源於 GitHub。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved