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)