采用fabric自动部署前端项目

语言: CN / TW / HK

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

背景:

前端项目采用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/

  1. then open git bash and run the following command: $ fab pre
  2. For linux
  3. 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.") ```