使用 Git Hook 自動化部署工作流程 文章首圖

使用 Git Hook 自動化部署工作流程

前言

在現代軟體開發流程中,自動化部署是提升效率與減少人為錯誤的關鍵一環。許多開發者習慣在本地測試無誤後,手動透過 FTP 或 SCP 將檔案上傳至伺服器,這種方式不僅效率低落,在多人協作時更容易產生版本衝突。

Git Hook 提供了一個強大的機制,讓我們能在 Git 事件觸發時(例如程式碼推送完成後),自動執行腳本。透過配置 post-receive hook,我們可以实现「推送即部署」的無縫體驗。這篇文章將帶領你逐步設定一個基於 Git Hook 的自動化部署環境,適合具備基礎 Linux 操作能力的中階使用者。

環境準備與目錄結構

首先,我們需要明確區分「Git 倉庫」與「Web 根目錄」。為了安全起見,我們不建議直接在 Git 倉庫內進行部署,而是使用一個「裸倉庫」(Bare Repository)作為接收端,並將內容部署到另一個獨立的目錄。

假設我們使用 www-data 使用者來運行 Web 服務,並建立以下目錄結構:

  1. /var/git/project.git:Git 裸倉庫,用於接收推送。
  2. /var/www/html:實際的 Web 根目錄,供瀏覽器存取。

請先建立這些目錄並設定正確的權限:

# 建立 Git 倉庫目錄
sudo mkdir -p /var/git/project.git
cd /var/git/project.git
# 初始化裸倉庫
sudo git init --bare

# 建立 Web 根目錄並設定權限,讓 www-data 有權寫入
sudo mkdir -p /var/www/html
sudo chown -R www-data:www-data /var/www/html

配置 Post-Receive Hook

接下來,我們要在裸倉庫中創建 post-receive 腳本。這個腳本會在每次成功接收推送後執行。

/var/git/project.git/hooks/ 目錄下建立名為 post-receive 的檔案:

sudo nano /var/git/project.git/hooks/post-receive

輸入以下內容。這段腳本的邏輯是:從裸倉庫中提取最新程式碼,並覆蓋到 /var/www/html 目錄。

#!/bin/bash

# 定義部署目標路徑
DEPLOY_PATH="/var/www/html"

# 從標準輸入讀取推送資訊,並獲取當前分支名稱
while read oldrev newrev refname
do
    branch=$(git rev-parse --symbolic --abbrev-ref "$refname")

    # 僅在推送到 main 分支時執行部署
    if [ "$branch" == "main" ]; then
        echo "Deploying to $DEPLOY_PATH..."

        # 清除舊檔案並提取最新內容
        # --force 確保覆蓋現有檔案
        git --work-tree=$DEPLOY_PATH --git-dir=/var/git/project.git checkout -f $branch

        echo "Deployment successful!"
    else
        echo "Ignoring push to non-main branch: $branch"
    fi
done

寫入檔案後,務必賦予該腳本執行權限,否則 Git 將忽略它:

sudo chmod +x /var/git/project.git/hooks/post-receive

設定 Git 權限與防火牆

為了讓自動化部署順利運作,我們需要確保 Git 倉庫的擁有者是 www-data,這樣腳本執行時才有權限寫入 Web 目錄。

sudo chown -R www-data:www-data /var/git/project.git

此外,如果你的伺服器啟用了防火牆(如 UFW),請確保 SSH 端口(預設 22)是開放的,以便從本地電腦推送程式碼:

sudo ufw allow ssh

從本地推送程式碼

現在,回到你的本地開發環境。首先,將你的專案初始化為 Git 倉庫(如果尚未初始化),然後將遠端地址指向伺服器的裸倉庫。

假設你的伺服器 IP 為 192.168.1.100,使用者為 root 或具有 sudo 權限的使用者:

# 在本地專案目錄執行
git init
git add .
git commit -m "Initial commit"

# 添加遠端倉庫
git remote add production ssh://root@192.168.1.100/var/git/project.git

# 推送程式碼到 main 分支
git push production main

推送過程中,系統會詢問伺服器密碼。輸入正確密碼後,Git 會將程式碼傳送至伺服器。由於我們配置了 post-receive hook,伺服器會自動將內容提取到 /var/www/html。你可以登入伺服器檢查該目錄,確認檔案是否已同步。

常見問題

1. 推送後網頁內容未更新

如果推送成功但網頁內容沒有改變,請檢查以下幾點:

  • 權限問題:確保 /var/www/html 目錄的所有者是 www-data,且 post-receive 腳本有執行權限。
  • 緩存問題:瀏覽器或伺服器(如 Nginx/Apache)可能有快取,嘗試清除快取或強制重新整理。
  • 分支名稱:確認 post-receive 腳本中的分支名稱(如 main)與你推送的分支名稱完全一致。

2. 安全性風險

將部署權限交給 Git Hook 可能帶來安全隱患。建議採取以下措施:

  • 限制推送權限:不要使用 root 帳號進行日常推送。建立一個專門的部署使用者(如 deployer),並透過 SSH 金鑰對驗證,僅允許該使用者推送程式碼。
  • 隔離目錄:確保 Git 倉庫與 Web 根目錄分離,避免直接暴露 .git 目錄到網路。
  • 腳本驗證:在 post-receive 中加入更嚴格的驗證邏輯,例如檢查提交者身份或簽章,防止惡意推送。

小結

透過配置 Git Hook,我們成功建立了一個簡潔的自動化部署流程。這不僅減少了手動上傳的繁瑣步驟,也確保了開發環境與生產環境的一致性。雖然此範例展示了基本的部署邏輯,但在實際生產環境中,建議進一步結合 CI/CD 工具(如 Jenkins 或 GitLab CI)來處理更複雜的構建與測試步驟。希望這篇文章能幫助你邁向更高效能的 DevOps 工作流程。