返回文章列表

GitLab CI/CD 執行器部署與自動化測試實戰指南

深入探討 GitLab CI/CD 執行器的完整部署流程與自動化測試整合實務,涵蓋 GitLab Runner 安裝配置、Docker Executor 選擇策略、單元測試框架整合、程式碼品質掃描機制、模糊測試實作,以及完整的測試報告生成與視覺化展示方法。

持續整合 DevOps 自動化測試

現代軟體開發實務中,持續整合與持續部署已成為確保產品品質與加速交付週期的核心策略。GitLab CI/CD 作為整合於版本控制系統的自動化平台,提供了從程式碼提交到生產部署的完整工作流程支援。然而,要充分發揮 CI/CD 的價值,首要任務是建立穩定且高效的執行器基礎架構。執行器作為實際執行測試與部署任務的運算節點,其配置品質直接影響整個開發團隊的工作效率與產品交付品質。

建構完善的執行器環境需要考量多個層面的技術決策。硬體資源的配置必須根據專案規模與並行任務需求進行評估,過少的資源導致任務排隊等待,過多則造成成本浪費。執行環境的選擇影響工具鏈的彈性與隔離性,Docker 容器化技術因其輕量級特性與環境一致性保證,成為主流的執行器實作方案。網路架構的規劃決定執行器與 GitLab 主機、外部服務的連接品質,對於需要存取私有套件儲存庫或內部 API 的測試任務尤其關鍵。

自動化測試的整合是 CI/CD 管線中最具價值的環節。單元測試作為最基礎的測試層級,透過驗證個別函式與模組的行為正確性,在開發早期發現邏輯錯誤。程式碼品質掃描工具透過靜態分析技術,識別潛在的程式設計缺陷、安全漏洞與風格問題,協助團隊維持程式碼庫的健康狀態。模糊測試透過生成大量隨機或變異輸入,探索程式在異常條件下的行為表現,發現傳統測試難以捕捉的邊界情況錯誤。這些測試技術的有機整合,構成多層次的品質保障體系。

本文將系統性地探討 GitLab CI/CD 執行器的完整建置流程,從作業系統層級的準備工作開始,逐步說明 GitLab Runner 軟體的安裝方法、註冊流程的技術細節、以及 Docker Executor 的配置策略。進一步深入探討自動化測試框架的整合實務,包括 Pytest 單元測試的撰寫方法、CI/CD 管線配置的最佳實踐、測試報告的生成與視覺化展示。最後討論程式碼品質工具與模糊測試的實作技巧,協助讀者建立完整且高效的持續整合環境。

GitLab Runner 執行器的安裝與配置

GitLab Runner 作為 GitLab CI/CD 生態系統的核心組件,負責接收來自 GitLab 主機的任務指派並在本地或遠端環境中執行。執行器的部署品質直接影響整個 CI/CD 管線的穩定性與效能表現。在開始安裝前,需要審慎評估硬體資源需求與作業系統環境,確保基礎設施能夠支撐預期的工作負載。

作業系統環境準備與套件來源配置

Linux 作業系統因其穩定性與豐富的套件生態,成為部署 GitLab Runner 的首選平台。然而,各 Linux 發行版本的官方套件儲存庫中的 GitLab Runner 版本通常滯後於上游發布,可能缺少最新的功能特性或安全修補程式。因此,建議直接使用 GitLab 官方維護的套件儲存庫,確保安裝的是最新穩定版本。

套件來源的配置流程因不同的 Linux 發行版本而異。以 Debian 與 Ubuntu 系統為例,需要先將 GitLab 官方的 GPG 金鑰加入系統信任清單,接著在 APT 套件管理系統的來源列表中新增 GitLab 儲存庫位址。對於 Red Hat Enterprise Linux 與 CentOS 系統,則需要透過 YUM 或 DNF 套件管理器配置儲存庫檔案。這些操作通常需要系統管理員權限,且配置檔案的路徑與語法因發行版本而異。

完成套件來源配置後,透過系統的套件管理器更新本地快取,即可從官方儲存庫下載並安裝 GitLab Runner。安裝過程會自動建立系統服務單元檔案,將 GitLab Runner 註冊為系統服務,確保開機自動啟動與故障後自動重啟。此外,安裝程式會建立專用的使用者帳號與工作目錄,遵循最小權限原則降低安全風險。

執行器與 GitLab 主機的版本相容性

GitLab 生態系統採用快速迭代的發展策略,每月發布新版本並提供重要功能更新。雖然 GitLab Runner 在設計上考慮了向後相容性,允許執行器版本與 GitLab 主機版本存在一定程度的差異,但為確保最佳穩定性與功能完整性,強烈建議保持兩者版本的同步。版本不匹配可能導致某些新功能無法正常運作,或是在任務執行過程中出現意外錯誤。

監控 GitLab 主機的版本升級計畫,並在主機升級後儘快更新執行器版本,是維持系統健康的重要實務。GitLab 官方提供詳細的版本相容性矩陣,明確列出不同版本組合的支援狀態。對於採用自架 GitLab 實例的組織,建立版本升級的標準作業程序,包括升級前的備份、測試環境驗證、以及逐步推展至生產環境,能夠有效降低升級風險。

執行器註冊流程與認證機制

安裝完成的 GitLab Runner 軟體尚未與任何 GitLab 專案建立關聯,需要透過註冊流程將執行器綁定至特定專案或群組。註冊過程涉及認證資訊的交換與執行器特性的宣告,這些資訊被儲存於執行器的配置檔案中,用於後續的任務分派與執行。

註冊流程需要準備的關鍵資訊包括 GitLab 實例的 URL 位址與專案的註冊權杖。前者指定執行器應連接的 GitLab 主機位址,支援 HTTPS 協定確保通訊安全。後者是由 GitLab 系統生成的唯一識別字串,用於驗證註冊請求的合法性。專案層級的註冊權杖可在專案設定介面的 CI/CD 區段中取得,而群組層級或實例層級的權杖則需要相應的管理權限才能存取。

註冊操作可透過互動式命令列介面或非互動式腳本執行。互動式模式會逐步提示使用者輸入必要資訊,適合手動配置少量執行器的情境。非互動式模式透過命令列參數一次性提供所有資訊,適合批次部署多個執行器或自動化配置流程。後者在大規模部署或基礎設施即程式碼實踐中更具優勢。

#!/usr/bin/env bash

# GitLab Runner 註冊配置腳本
# 提供非互動式註冊功能,支援批次部署多個執行器

set -euo pipefail

# 配置參數
readonly GITLAB_URL="https://gitlab.example.com/"
readonly REGISTRATION_TOKEN="your-project-registration-token"
readonly EXECUTOR_TYPE="docker"
readonly DEFAULT_DOCKER_IMAGE="alpine:latest"
readonly RUNNER_TAG_LIST="docker,linux,automated-testing"

# 函式:驗證必要工具是否已安裝
# 確保 gitlab-runner 與 docker 命令可用
validate_prerequisites() {
    echo "驗證系統先決條件"
    
    # 檢查 GitLab Runner 是否已安裝
    if ! command -v gitlab-runner &> /dev/null; then
        echo "錯誤:找不到 gitlab-runner 指令,請先安裝 GitLab Runner" >&2
        return 1
    fi
    
    # 顯示已安裝的 GitLab Runner 版本
    echo "已安裝的 GitLab Runner 版本:"
    gitlab-runner --version
    
    # 檢查 Docker 是否已安裝(使用 Docker executor 時必要)
    if [[ "$EXECUTOR_TYPE" == "docker" ]]; then
        if ! command -v docker &> /dev/null; then
            echo "錯誤:找不到 docker 指令,請先安裝 Docker" >&2
            return 1
        fi
        
        # 驗證 Docker 服務是否正在執行
        if ! docker info &> /dev/null; then
            echo "錯誤:Docker 服務未執行,請啟動 Docker" >&2
            return 1
        fi
        
        echo "Docker 服務狀態正常"
    fi
    
    echo "先決條件驗證完成"
    return 0
}

# 函式:註冊單一 GitLab Runner
# 參數:
#   $1 - 執行器描述名稱
#   $2 - 執行器標籤(可選,以逗號分隔)
register_runner() {
    local runner_description="$1"
    local runner_tags="${2:-$RUNNER_TAG_LIST}"
    
    echo "開始註冊執行器:$runner_description"
    
    # 執行非互動式註冊命令
    # 使用 sudo 確保有足夠權限修改系統配置
    if sudo gitlab-runner register \
        --non-interactive \
        --url "$GITLAB_URL" \
        --registration-token "$REGISTRATION_TOKEN" \
        --executor "$EXECUTOR_TYPE" \
        --docker-image "$DEFAULT_DOCKER_IMAGE" \
        --description "$runner_description" \
        --tag-list "$runner_tags" \
        --run-untagged="false" \
        --locked="false" \
        --access-level="not_protected"; then
        
        echo "執行器註冊成功:$runner_description"
        return 0
    else
        echo "錯誤:執行器註冊失敗" >&2
        return 1
    fi
}

# 函式:驗證已註冊的執行器狀態
# 檢查執行器是否能夠正常連接 GitLab 主機
verify_registered_runners() {
    echo "驗證已註冊的執行器狀態"
    
    # 使用 verify 命令檢查所有已註冊執行器的連接狀態
    if sudo gitlab-runner verify; then
        echo "所有執行器驗證通過"
        return 0
    else
        echo "警告:部分執行器驗證失敗" >&2
        return 1
    fi
}

# 函式:顯示已註冊的執行器清單
list_registered_runners() {
    echo "=========================================="
    echo "已註冊的執行器清單"
    echo "=========================================="
    
    # 使用 list 命令顯示所有已註冊的執行器資訊
    sudo gitlab-runner list
    
    echo "=========================================="
}

# 函式:批次註冊多個執行器
# 參數:
#   $1 - 執行器基礎名稱
#   $2 - 註冊數量
batch_register_runners() {
    local base_name="$1"
    local count="${2:-1}"
    
    echo "批次註冊 $count 個執行器"
    echo "基礎名稱:$base_name"
    
    for ((i=1; i<=count; i++)); do
        local runner_name="${base_name} ${i}"
        echo "----------------------------------------"
        echo "註冊執行器 $i/$count"
        
        if ! register_runner "$runner_name"; then
            echo "警告:執行器 $i 註冊失敗,繼續處理下一個" >&2
        fi
        
        # 在註冊之間加入短暫延遲,避免過載 GitLab 伺服器
        sleep 2
    done
    
    echo "批次註冊完成"
}

# 函式:配置執行器並行數與檢查間隔
# 修改主配置檔案以最佳化執行器效能
configure_runner_settings() {
    local concurrent_jobs="${1:-2}"
    local check_interval="${2:-3}"
    local config_file="/etc/gitlab-runner/config.toml"
    
    echo "配置執行器全域設定"
    echo "並行任務數:$concurrent_jobs"
    echo "檢查間隔:$check_interval 秒"
    
    # 檢查配置檔案是否存在
    if [[ ! -f "$config_file" ]]; then
        echo "警告:找不到配置檔案 $config_file" >&2
        return 1
    fi
    
    # 備份原始配置檔案
    local backup_file="${config_file}.backup.$(date +%Y%m%d_%H%M%S)"
    sudo cp "$config_file" "$backup_file"
    echo "已建立配置備份:$backup_file"
    
    # 修改並行任務數設定
    # 使用 sed 替換 concurrent 行的數值
    sudo sed -i "s/^concurrent = .*/concurrent = $concurrent_jobs/" "$config_file"
    
    # 修改檢查間隔設定
    sudo sed -i "s/^check_interval = .*/check_interval = $check_interval/" "$config_file"
    
    echo "配置設定已更新"
    
    # 重新啟動 GitLab Runner 服務使設定生效
    echo "重新啟動 GitLab Runner 服務"
    sudo systemctl restart gitlab-runner
    
    # 等待服務完全啟動
    sleep 3
    
    # 驗證服務狀態
    if sudo systemctl is-active --quiet gitlab-runner; then
        echo "GitLab Runner 服務運作正常"
    else
        echo "警告:GitLab Runner 服務可能未正確啟動" >&2
    fi
    
    return 0
}

# 主程式流程
main() {
    echo "=========================================="
    echo "GitLab Runner 註冊與配置工具"
    echo "=========================================="
    
    # 步驟一:驗證系統先決條件
    if ! validate_prerequisites; then
        echo "先決條件驗證失敗,程式終止" >&2
        exit 1
    fi
    
    # 步驟二:執行批次註冊
    # 根據實際需求調整執行器名稱與數量
    local project_name="Automated-Testing-Project"
    local runner_count=2
    
    batch_register_runners "$project_name Runner" "$runner_count"
    
    # 步驟三:驗證註冊結果
    echo "=========================================="
    verify_registered_runners
    
    # 步驟四:顯示已註冊的執行器
    list_registered_runners
    
    # 步驟五:最佳化執行器配置
    echo "=========================================="
    echo "最佳化執行器配置"
    
    # 設定並行任務數為 4,檢查間隔為 3 秒
    configure_runner_settings 4 3
    
    echo "=========================================="
    echo "GitLab Runner 配置完成"
    echo "=========================================="
    
    # 顯示後續操作提示
    cat << 'EOF'

後續操作建議:
  1. 前往 GitLab 專案設定頁面驗證執行器連接狀態
     路徑:專案設定 > CI/CD > Runners

  2. 根據實際需求調整執行器標籤
     編輯配置檔案:/etc/gitlab-runner/config.toml

  3. 監控執行器資源使用狀況
     指令:sudo gitlab-runner status

  4. 查看執行器日誌排查問題
     指令:sudo journalctl -u gitlab-runner -f
EOF
}

# 僅在直接執行時啟動主程式
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi
@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

start

:安裝 GitLab Runner 套件;
note right
  從官方儲存庫安裝
  確保版本最新
end note

:準備註冊資訊;
note right
  GitLab 實例 URL
  專案註冊權杖
  執行器類型選擇
end note

:執行註冊命令;

if (註冊成功?) then (是)
  :寫入配置檔案;
  
  :啟動執行器服務;
  
  :驗證連接狀態;
  
  if (連接正常?) then (是)
    :配置並行數與間隔;
    
    :重新啟動服務;
    
    :在 GitLab 介面確認;
    
    stop
  else (否)
    :檢查網路連接;
    :檢查防火牆規則;
    :驗證認證資訊;
    stop
  endif
  
else (否)
  :檢查錯誤訊息;
  :驗證權杖有效性;
  :確認 URL 正確性;
  stop
endif

@enduml

Docker Executor 的選擇理由與配置策略

GitLab Runner 支援多種執行器類型,包括 Shell、Docker、Kubernetes 等,各有其適用場景與特性。Docker Executor 因其提供的環境隔離性、工具鏈彈性與資源管理能力,成為最受歡迎的選擇。每個 CI/CD 任務在獨立的 Docker 容器中執行,確保任務間不會相互干擾,且任務結束後容器自動清理,避免環境污染問題。

Docker Executor 的最大優勢在於其工具鏈管理的彈性。不同的專案可能依賴不同版本的程式語言執行環境、編譯工具或測試框架,傳統的 Shell Executor 需要在主機系統上安裝所有可能用到的工具版本,導致環境配置複雜且容易衝突。Docker Executor 允許每個任務指定其所需的容器映像,映像中預先安裝了該任務所需的完整工具鏈。這種方式不僅簡化環境管理,更確保了任務執行環境的一致性與可重現性。

選擇預設容器映像時需考慮多重因素。Alpine Linux 因其極小的映像體積與快速的下載速度,常被選為預設映像。然而,Alpine 使用 musl libc 而非傳統的 glibc,可能導致某些預編譯的二進位程式無法執行。對於需要廣泛相容性的場景,基於 Debian 或 Ubuntu 的映像是更保守的選擇。此外,許多語言生態系統提供官方維護的容器映像,例如 python:3.10 或 node:18-alpine,這些映像針對特定用途最佳化,通常是更好的選擇。

單元測試框架的整合與管線配置

單元測試作為軟體品質保證的基石,透過驗證個別函式與模組的行為正確性,在開發早期發現邏輯錯誤。將單元測試整合至 CI/CD 管線,確保每次程式碼變更都經過自動化驗證,防止缺陷程式碼被合併至主要分支。Pytest 作為 Python 生態系統中最受歡迎的測試框架,以其簡潔的語法與強大的功能特性,成為單元測試的首選工具。

測試專案的依賴管理與環境隔離

Python 專案的依賴管理透過 requirements.txt 或更現代的 pyproject.toml 檔案實現。前者採用簡單的行列格式,每行指定一個套件及其版本約束。版本約束可以是精確版本、版本範圍或最小版本要求,影響套件管理器的解析行為。精確版本約束確保環境的完全可重現性,但可能阻礙安全更新的套用。版本範圍提供彈性,允許接受補丁版本更新,但可能引入未預期的行為變更。

在 CI/CD 環境中,依賴安裝的速度直接影響管線的執行時間。利用套件快取機制能夠顯著加速後續任務的依賴安裝過程。GitLab CI/CD 提供內建的快取功能,允許指定特定目錄在任務間保留,典型的快取目標包括 pip 的下載快取目錄與虛擬環境目錄。然而,快取機制需要謹慎配置,過時的快取可能導致使用舊版本套件,引發難以追蹤的問題。

Pytest 測試案例的撰寫方法與組織結構

Pytest 採用基於函式的測試定義方式,測試函式名稱以 test_ 開頭,內部使用 assert 陳述式驗證預期行為。這種設計使得測試程式碼看起來像普通的 Python 程式碼,降低學習曲線。Pytest 的強大之處在於其豐富的插件生態系統與靈活的擴展機制,支援參數化測試、測試夾具、以及自訂斷言訊息等進階功能。

測試程式碼的組織結構影響可維護性與執行效率。慣例上,測試檔案與被測試的模組檔案保持對應關係,例如 login.py 的測試位於 test_login.py。測試函式的命名應清楚表達其測試意圖,例如 test_login_with_valid_credentials 比 test_login_case1 更具描述性。對於複雜的測試場景,可以使用測試類別組織相關的測試函式,利用類別層級的夾具共享設定邏輯。

#!/usr/bin/env python3
"""
使用者登入功能模組
提供基本的身分驗證功能
"""

def authenticate_user(username: str, password: str) -> bool:
    """
    驗證使用者登入憑證
    
    參數:
        username: 使用者名稱
        password: 使用者密碼
        
    返回:
        驗證成功返回 True,否則返回 False
        
    範例:
        >>> authenticate_user("admin", "secure_password")
        True
        >>> authenticate_user("admin", "wrong_password")
        False
    """
    # 實務中應查詢資料庫並驗證雜湊密碼
    # 此處僅為示範目的使用硬編碼憑證
    valid_credentials = {
        "admin": "secure_password",
        "dana": "p@ssw0rd",
        "test_user": "test123"
    }
    
    # 驗證使用者名稱是否存在
    if username not in valid_credentials:
        return False
    
    # 驗證密碼是否正確
    return valid_credentials[username] == password

def validate_password_strength(password: str) -> tuple[bool, str]:
    """
    驗證密碼強度是否符合安全要求
    
    參數:
        password: 待驗證的密碼字串
        
    返回:
        (驗證結果, 錯誤訊息) 的元組
        驗證通過時返回 (True, "")
        驗證失敗時返回 (False, "具體錯誤原因")
        
    密碼要求:
        - 最少 8 個字元
        - 至少包含一個大寫字母
        - 至少包含一個小寫字母
        - 至少包含一個數字
    """
    # 檢查密碼長度
    if len(password) < 8:
        return False, "密碼長度必須至少 8 個字元"
    
    # 檢查是否包含大寫字母
    if not any(char.isupper() for char in password):
        return False, "密碼必須包含至少一個大寫字母"
    
    # 檢查是否包含小寫字母
    if not any(char.islower() for char in password):
        return False, "密碼必須包含至少一個小寫字母"
    
    # 檢查是否包含數字
    if not any(char.isdigit() for char in password):
        return False, "密碼必須包含至少一個數字"
    
    return True, ""
#!/usr/bin/env python3
"""
登入功能單元測試套件
使用 Pytest 框架驗證身分驗證邏輯的正確性
"""

import pytest
from login import authenticate_user, validate_password_strength

class TestUserAuthentication:
    """使用者身分驗證功能測試類別"""
    
    def test_authenticate_with_valid_credentials(self):
        """
        測試使用有效憑證進行身分驗證
        預期結果:驗證成功,返回 True
        """
        assert authenticate_user("admin", "secure_password") is True
        assert authenticate_user("dana", "p@ssw0rd") is True
        assert authenticate_user("test_user", "test123") is True
    
    def test_authenticate_with_invalid_username(self):
        """
        測試使用不存在的使用者名稱進行驗證
        預期結果:驗證失敗,返回 False
        """
        assert authenticate_user("nonexistent_user", "any_password") is False
        assert authenticate_user("", "secure_password") is False
    
    def test_authenticate_with_invalid_password(self):
        """
        測試使用錯誤密碼進行驗證
        預期結果:驗證失敗,返回 False
        """
        assert authenticate_user("admin", "wrong_password") is False
        assert authenticate_user("dana", "incorrect") is False
    
    def test_authenticate_with_empty_credentials(self):
        """
        測試使用空白憑證進行驗證
        預期結果:驗證失敗,返回 False
        """
        assert authenticate_user("", "") is False
        assert authenticate_user("admin", "") is False
        assert authenticate_user("", "secure_password") is False
    
    @pytest.mark.parametrize("username,password,expected", [
        ("admin", "secure_password", True),
        ("dana", "p@ssw0rd", True),
        ("admin", "wrong", False),
        ("fake_user", "any_pass", False),
    ])
    def test_authenticate_parametrized(self, username, password, expected):
        """
        參數化測試:使用多組測試資料驗證身分驗證邏輯
        
        參數:
            username: 測試用使用者名稱
            password: 測試用密碼
            expected: 預期的驗證結果
        """
        assert authenticate_user(username, password) is expected

class TestPasswordStrength:
    """密碼強度驗證功能測試類別"""
    
    def test_validate_strong_password(self):
        """
        測試符合所有強度要求的密碼
        預期結果:驗證通過
        """
        is_valid, error_message = validate_password_strength("SecurePass123")
        assert is_valid is True
        assert error_message == ""
    
    def test_validate_password_too_short(self):
        """
        測試長度不足的密碼
        預期結果:驗證失敗,提示長度不足
        """
        is_valid, error_message = validate_password_strength("Short1")
        assert is_valid is False
        assert "長度" in error_message
    
    def test_validate_password_no_uppercase(self):
        """
        測試缺少大寫字母的密碼
        預期結果:驗證失敗,提示缺少大寫字母
        """
        is_valid, error_message = validate_password_strength("lowercase123")
        assert is_valid is False
        assert "大寫" in error_message
    
    def test_validate_password_no_lowercase(self):
        """
        測試缺少小寫字母的密碼
        預期結果:驗證失敗,提示缺少小寫字母
        """
        is_valid, error_message = validate_password_strength("UPPERCASE123")
        assert is_valid is False
        assert "小寫" in error_message
    
    def test_validate_password_no_digit(self):
        """
        測試缺少數字的密碼
        預期結果:驗證失敗,提示缺少數字
        """
        is_valid, error_message = validate_password_strength("NoDigitPass")
        assert is_valid is False
        assert "數字" in error_message
    
    @pytest.mark.parametrize("password,should_be_valid", [
        ("StrongPass123", True),
        ("AnotherGood1", True),
        ("weak", False),
        ("nouppercase1", False),
        ("NOLOWERCASE1", False),
        ("NoDigitsHere", False),
    ])
    def test_validate_password_parametrized(self, password, should_be_valid):
        """
        參數化測試:使用多組密碼驗證強度檢查邏輯
        
        參數:
            password: 測試用密碼字串
            should_be_valid: 預期的驗證結果
        """
        is_valid, _ = validate_password_strength(password)
        assert is_valid is should_be_valid

CI/CD 管線的配置與最佳化

GitLab CI/CD 管線透過專案根目錄的 .gitlab-ci.yml 檔案定義,採用 YAML 格式描述管線的階段劃分與任務配置。管線的基本結構包括階段定義與任務定義兩部分。階段定義透過 stages 關鍵字宣告管線的執行階段順序,任務會按照階段順序依次執行,同一階段內的任務則並行執行。任務定義透過任務名稱作為頂層鍵值,內部指定該任務所屬的階段、使用的容器映像、執行的腳本命令、以及其他配置選項。

測試任務的配置需要考慮多個面向。容器映像的選擇影響任務啟動速度與可用工具,應選擇包含必要工具且體積適中的映像。腳本段落定義任務的實際執行步驟,通常包括依賴安裝、測試執行、以及結果收集。測試報告的生成與保存透過 artifacts 關鍵字配置,允許指定特定檔案或目錄在任務結束後保留,供後續階段使用或供使用者下載。

# GitLab CI/CD 管線配置檔案
# 定義自動化測試與程式碼品質檢查流程

# 宣告管線的執行階段
# 階段會按照列出的順序依次執行
stages:
  - test          # 測試階段:執行單元測試與整合測試
  - quality       # 品質階段:執行程式碼品質掃描
  - security      # 安全階段:執行安全性測試

# 全域變數定義
# 可被所有任務存取的環境變數
variables:
  # Python 套件安裝快取目錄
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
  # Pytest 輸出設定
  PYTEST_OPTS: "--verbose --junit-xml=test_results.xml --cov=. --cov-report=xml --cov-report=term"

# 全域快取配置
# 定義在任務間保留的目錄,加速後續任務執行
cache:
  # 快取金鑰:基於分支名稱,確保不同分支使用獨立快取
  key: "${CI_COMMIT_REF_SLUG}"
  paths:
    - .cache/pip           # pip 下載快取
    - .venv/               # Python 虛擬環境(若使用)

# 任務:單元測試執行
unit-tests:
  # 指定任務所屬階段
  stage: test
  
  # 指定執行環境的 Docker 映像
  # 使用官方 Python 3.10 映像,基於 Debian
  image: python:3.10-slim
  
  # 任務執行前的準備步驟
  before_script:
    # 更新 pip 至最新版本,避免安裝問題
    - python -m pip install --upgrade pip
    # 安裝專案依賴套件
    - pip install -r requirements.txt
    # 安裝測試相關套件(若未包含在 requirements.txt)
    - pip install pytest pytest-cov pytest-html
  
  # 任務主要執行步驟
  script:
    # 執行 Pytest 測試框架
    # --verbose: 顯示詳細測試輸出
    # --junit-xml: 生成 JUnit 格式測試報告
    # --cov: 啟用覆蓋率統計
    # --cov-report: 指定覆蓋率報告格式
    - pytest $PYTEST_OPTS
  
  # 任務產出物配置
  # 定義需要保留的檔案,供後續使用或下載
  artifacts:
    # 即使任務失敗也保留產出物
    when: always
    # 產出物保留時間:30 天
    expire_in: 30 days
    # 測試報告配置
    reports:
      # JUnit 格式測試報告,會整合至 GitLab 介面
      junit: test_results.xml
      # Cobertura 格式覆蓋率報告
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
    # 額外保留的檔案路徑
    paths:
      - test_results.xml     # 測試結果
      - coverage.xml         # 覆蓋率報告
      - htmlcov/             # HTML 格式覆蓋率報告
  
  # 覆蓋率統計正規表達式
  # 從測試輸出中提取覆蓋率百分比
  coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
  
  # 任務執行規則
  # 定義在哪些情況下執行此任務
  rules:
    # 在合併請求中執行
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    # 在主要分支的推送中執行
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

# 任務:整合測試執行(範例)
integration-tests:
  stage: test
  image: python:3.10-slim
  
  # 服務容器:用於提供測試所需的外部依賴
  services:
    # PostgreSQL 資料庫服務
    - name: postgres:14-alpine
      alias: postgres
  
  # 環境變數:配置服務連接資訊
  variables:
    POSTGRES_DB: test_db
    POSTGRES_USER: test_user
    POSTGRES_PASSWORD: test_password
    DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/test_db"
  
  before_script:
    - pip install --upgrade pip
    - pip install -r requirements.txt
    # 等待資料庫服務就緒
    - apt-get update && apt-get install -y postgresql-client
    - until pg_isready -h postgres -U test_user; do sleep 1; done
  
  script:
    # 執行需要資料庫的整合測試
    - pytest tests/integration/ --verbose
  
  # 僅在合併請求時執行整合測試
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

# 任務:程式碼品質掃描
code-quality:
  stage: quality
  
  # 使用 GitLab 官方的程式碼品質分析映像
  image: registry.gitlab.com/gitlab-org/ci-cd/codequality:latest
  
  # 允許任務失敗而不影響管線狀態
  # 適用於尚未完全符合品質標準的專案
  allow_failure: true
  
  script:
    # 執行程式碼品質分析工具
    # 輸出 GitLab 格式的品質報告
    - /analyzer run --format gitlab > gl-code-quality-report.json
  
  artifacts:
    when: always
    expire_in: 30 days
    reports:
      # 程式碼品質報告會整合至合併請求介面
      codequality: gl-code-quality-report.json
    paths:
      - gl-code-quality-report.json
  
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

# 任務:靜態安全掃描
security-scan:
  stage: security
  image: python:3.10-slim
  
  before_script:
    - pip install --upgrade pip
    # 安裝安全掃描工具
    - pip install bandit safety
  
  script:
    # Bandit:Python 程式碼安全漏洞掃描
    - bandit -r . -f json -o bandit-report.json || true
    # Safety:檢查依賴套件的已知安全漏洞
    - safety check --json > safety-report.json || true
  
  artifacts:
    when: always
    expire_in: 30 days
    paths:
      - bandit-report.json
      - safety-report.json
  
  allow_failure: true
  
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

本文深入探討了 GitLab CI/CD 執行器的完整建置流程與自動化測試整合實務,從基礎設施準備到測試框架整合,提供了系統性的技術指南。透過詳細的程式碼範例與配置說明,展示了如何在實際專案中建立穩定且高效的持續整合環境。GitLab Runner 的註冊與配置提供了任務執行的基礎平台,Docker Executor 的採用確保了環境的一致性與隔離性。Pytest 測試框架的整合實現了自動化的程式碼驗證,程式碼品質掃描與安全檢查則從多個維度保障產品品質。這些技術的有機整合構成了現代軟體開發的標準實踐,協助開發團隊在快速迭代的同時維持高水準的品質標準。持續最佳化 CI/CD 管線配置,根據專案特性調整測試策略與資源配置,是確保開發效率與產品品質的關鍵。