采用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/
4. then open git bash and run the following command:
    $ fab pre

- For linux
1. 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 = "user@www.domain.com"
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 = "dev@dev.esoaru.com"
    scp_to_server(folder, host=host)
    c = make_connection(host=host)
    deploy(c, folder)
    print("success to deploy the static files at production server.")

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值