採用fabric自動部署前端專案
本文已參與「新人創作禮」活動,一起開啟掘金創作之路。
背景:
前端專案採用Nginx部署在雲上
``` server { server_name www.domain.com; client_max_body_size 10m;
location /chairong/dist {
root /home/user/frontend;
try_files $uri $uri/ /project-name/dist/index.html;
}
location = / {
rewrite (.*) project-name/dist permanent;
}
...
} ```
本地安裝了python3.8+和fabric(如果是windows系統需配置一下,讓fab命令可用)
$cat fabfile.py # 該檔案放在前端程式碼倉庫裡,與package.json同級目錄
``
__author__ = "WJ"
"""
Auto update server deploy
~~~~~~~~~~~~~~~~~~~~~~~~~
前端
output_dir包自動部署指令碼
自動將yarn pre構建出來的
output_dir`資料夾,替換掉測試伺服器上的舊資料夾
Usage:
- For windows
1. Install python3.8
2. pip install fabric
3. add folder of fab.exe to environment path
Example: /c/Users/Administrator/AppData/Roaming/Python/Python38/Scripts/
- then open git bash and run the following command: $ fab pre
- For linux
- just run: $ cd /path/to/project && (which fab||pip install fabric --user) && fab dist """ import os from datetime import datetime from os.path import getmtime from pathlib import Path from typing import Union
from fabric import Connection, task
PROJECT = Path().resolve().name # 假設當前資料夾名就是專案名 HOST = "[email protected]" PATH = f"~/frontend/{PROJECT}" # 伺服器上dist包放置的位置 DELTA = 60 * 1 # 這個時間內(1 min),如果output_dir已存在,不再重新yarn build DEPLOY_DIR = "dist" # 伺服器上部署的資料夾名 OUTPUT_DIR = "bundle" # yarn pre生成的資料夾名
def parse_args(domain=None, host=None, port: int = 22): if host is None: if domain is None: host = HOST else: user = os.getenv(f"{domain}user".replace(".", "")) host = f"{user}@{domain}" if domain is None: domain = host.split("@")[-1] if (p := os.getenv(f"{domain}_port")) : # noqa: E231, E203 port = int(p) # type: ignore return domain, host, port
def make_connection(domain=None, host=None, port: int = 22): domain, host, port = parse_args(domain, host, port) passwd = os.getenv(f"{domain}_passwd") c = Connection(host, port=port, connect_kwargs={"password": passwd}) print(f"Success to make a connection with {host}") return c
def seconds_from_last_updated(fpath: Union[str, Path]) -> int:
"""return the seconds passed after fpath
last motified"""
mtime = datetime.fromtimestamp(getmtime(fpath))
passed = datetime.now() - mtime
return passed.seconds
def run_and_echo(cmd: str, run_func=None) -> int: """執行cmd,並列印執行的語句""" print("-->", cmd) if run_func is None: run_func = os.system return run_func(cmd)
def build_compiled_dir(action: str = "pre", verbose: str = "") -> None: """自動給yarn管理的專案編譯出output_dir資料夾""" actions = [i for i in (action, verbose) if i] cmds = [ f"{tool} {action}" for tool in ("yarn", "npm run") for action in actions ] for cmd in cmds: if run_and_echo(cmd) == 0: break
def zip_output_dir(folder: str) -> None: """如果output_dir資料夾不存在,則編譯後用zip壓縮,否則直接壓縮""" actions = ("pre",) if folder == "bundle" else ("b", "build") if not (p := Path(folder)).exists(): # noqa: E231, E203 build_compiled_dir(actions) elif seconds_from_last_updated(p) > DELTA: run_and_echo(f"rm -rf {p}") build_compiled_dir(actions) run_and_echo(f"zip -qr {folder}.zip {folder}/")
def create_or_fresh_zip(folder: str = OUTPUT_DIR) -> str: p = Path(f"{folder}.zip") if not p.exists(): zip_output_dir(folder) elif seconds_from_last_updated(p) > DELTA: run_and_echo(f"mv {folder}.zip {folder}.zip.bak") zip_output_dir(folder) return folder
def scp_to_server( folder: str, domain: Union[str, None] = None, port: int = 22, host: Union[str, None] = None, path: str = PATH, ) -> None: _, host, port = parse_args(domain, host, port) run_and_echo(f"scp -P {port} {folder}.zip {host}:{path}")
def deploy(c, folder: str): deploy_dir = DEPLOY_DIR with c.cd(PATH): run_and_echo(f"rm -rf {deploy_dir}", c.run) # c.run("rm -rf dist.zip") # c.put("./dist.zip", ".") run_and_echo(f"unzip -q {folder}.zip", c.run) if deploy_dir != folder: run_and_echo(f"mv {folder} {deploy_dir}", c.run)
@task def pre(c): """自動部署前端yarn pre出來的資料夾到測試伺服器""" folder = create_or_fresh_zip() scp_to_server(folder)
c = make_connection()
deploy(c, folder)
print("success to deploy the static files")
@task def es(c): """自動部署前端yarn build出來的資料夾到生產伺服器""" folder = create_or_fresh_zip("dist") domain = "www.domain-of-production-server.com" scp_to_server(folder, domain) c = make_connection(domain) deploy(c, folder) print("success to deploy the static files at production server.")
@task def dev(c): """自動部署前端yarn f出來的資料夾到內部伺服器""" folder = create_or_fresh_zip("dist") host = "[email protected]" scp_to_server(folder, host=host) c = make_connection(host=host) deploy(c, folder) print("success to deploy the static files at production server.") ```