Python定時任務前端專案本地自動打包遠端部署指令碼實現

槑孒發表於2024-06-14
import os
import time
import subprocess
import schedule
from datetime import datetime
import paramiko
from dateutil import parser


def read_last_build_time():
    """
    讀取上次打包的時間
    :return: 上次打包的時間(datetime物件),如果檔案不存在則返回None
    """
    try:
        with open(LAST_BUILD_TIME_FILE, "r") as f:
            return datetime.fromisoformat(f.read().strip())
    except FileNotFoundError:
        return None


def write_last_build_time(time):
    """
    寫入本次打包的時間
    :param time: 當前時間(datetime物件)
    """
    with open(LAST_BUILD_TIME_FILE, "w") as f:
        f.write(time.isoformat())


def git_pull():
    """
    執行git pull命令,拉取最新程式碼
    """
    subprocess.run(["git", "pull"], cwd=REPO_DIR, check=True, stdout=subprocess.DEVNULL)


def get_last_commit_time():
    """
    獲取倉庫中最新一次提交的時間
    :return: 最新一次提交的時間(datetime物件)
    """
    result = subprocess.run(["git", "log", "-1", "--format=%cd", "--date=iso"], cwd=REPO_DIR, capture_output=True,
                            text=True, check=True)
    return parser.parse(result.stdout.strip())


def build_project():
    """
    執行打包命令,構建專案
    """
    subprocess.run(["pnpm", "i"], cwd=REPO_DIR, shell=True, check=True, stdout=subprocess.DEVNULL)
    subprocess.run(["pnpm", "run", "build"], cwd=REPO_DIR, shell=True, check=True, stdout=subprocess.DEVNULL)


def rename_remote_dirs(ssh):
    """
    判斷遠端目錄並重新命名
    """
    try:
        new_dir = os.path.join(os.path.dirname(REMOTE_DIR), datetime.now().strftime("%Y%m%d%H%M"))
        new_dir = new_dir.replace("\\", "/").replace(r"\\", "/")
        ssh.exec_command(f"mv {REMOTE_DIR} {new_dir}")
    except IOError:
        pass  # 如果目錄不存在,繼續
    ssh.exec_command(f"mv {REMOTE_DIR_TEMP} {REMOTE_DIR}")


def upload_build():
    """
    上傳打包生成的資料夾到遠端伺服器
    """
    # 連線遠端伺服器
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(REMOTE_SERVER, port=REMOTE_PORT, username=REMOTE_USER, password=REMOTE_PASSWORD)
    sftp = ssh.open_sftp()
    # 遍歷本地build目錄,逐個檔案上傳到遠端伺服器
    for root, dirs, files in os.walk(BUILD_DIR):
        for file in files:
            local_path = os.path.join(root, file)
            relative_path = os.path.relpath(local_path, BUILD_DIR)
            remote_file = os.path.join(REMOTE_DIR_TEMP, relative_path).replace("\\", "/").replace(r"\\", "/")
            remote_dir = os.path.dirname(remote_file)
            # 建立目標目錄
            try:
                sftp.put(local_path, remote_file)
            except FileNotFoundError:
                ssh.exec_command(f"mkdir -p {remote_dir}")
            except IOError:
                pass  # 如果目錄已存在,繼續
            finally:
                sftp.put(local_path, remote_file)
    rename_remote_dirs(ssh)
    # 關閉連線
    sftp.close()
    ssh.close()


def deploy():
    """
    執行部署任務,包括:
    1. 拉取最新程式碼
    2. 檢查是否有新提交
    3. 如果有新提交,則構建專案並上傳到遠端伺服器
    """
    print(f"檢查更新時間 {datetime.now()}")
    git_pull()
    last_build_time = read_last_build_time()
    last_commit_time = get_last_commit_time().replace(tzinfo=None)
    # 如果上次打包時間不存在或有新提交,則構建並上傳專案
    if last_build_time is None or last_commit_time > last_build_time:
        print("檢測到新提交,正在打包...")
        build_project()
        print("上傳打包檔案...")
        upload_build()
        write_last_build_time(datetime.now())
        print("部署完成")
    else:
        print("沒有檢測到提交")


if __name__ == "__main__":
    # 配置引數
    REPO_DIR = "D:/vue_test"  # 倉庫目錄
    BUILD_DIR = os.path.join(REPO_DIR, "build")  # 打包輸出目錄
    REMOTE_SERVER = "your.server.ip"  # 遠端伺服器地址
    REMOTE_PORT = 22  # SSH埠
    REMOTE_USER = "your_user"  # SSH使用者名稱
    REMOTE_PASSWORD = "your_password"  # SSH密碼
    REMOTE_DIR = "/web/vue_test"  # 遠端伺服器目錄
    LAST_BUILD_TIME_FILE = "last_build_time.txt"  # 記錄上次打包時間的檔案

    # 遠端臨時目錄
    REMOTE_DIR_TEMP = os.path.join(os.path.dirname(REMOTE_DIR), 'temp').replace("\\", "/").replace(r"\\", "/")
    # 啟動時先執行一次
    deploy()
    # 每小時執行一次部署任務
    schedule.every(1).hours.do(deploy)
    while True:
        schedule.run_pending()
        time.sleep(10)

相關文章