使用fabric进行后端自动化部署的一个脚本实例

168 阅读2分钟

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

fabfile.py文件内容如下:

import functools
import os
import sys
from pathlib import Path
from typing import Callable, Optional

from dotenv import load_dotenv
from fabric import Connection, task

load_dotenv()

"""
to update code of server, run the following command:
    $ fab pull
"""

USER = "my-username"
DOMAIN = "www.my-domain.com"
PROJECT = Path(__file__).parent.resolve().name
REPO = "~/coding/" + PROJECT
HOST = f"{USER}@{DOMAIN}"


def run_and_echo(cmd: str, func: Optional[Callable] = None) -> int:
    print("-->", cmd)
    if func is None:
        func = os.system
    return func(cmd)


def seconds_after_lock_updated() -> str:
    """print the seconds passed after `poetry.lock` last motified"""
    s = """
    from datetime import datetime
    from os.path import getmtime, exists
    fn = 'poetry.lock'
    fname = fn if exists(fn) else 'pyproject.toml'
    mtime = datetime.fromtimestamp(getmtime(fname))
    passed = datetime.now() - mtime
    print(passed.seconds)
    """
    cmd = ";".join(i.strip() for i in s.strip().split("\n"))
    return f"python3 -c {cmd!r}"


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")
            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 or port)
    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 git_push(func):
    @functools.wraps(func)
    def wrap(*args, **kwargs):
        if run_and_echo("git push") != 0:
            sys.exit()
        return func(*args, **kwargs)

    return wrap


@git_push
def remote_exec(repo: str, cmd: str) -> None:
    c = make_connection()
    with c.cd(repo):
        run_and_echo(cmd, c.run)
    print("Done!")


@task
def pull(c, repo=REPO):
    _pull(c, repo)


def _pull(c, repo: str) -> None:
    remote_exec(repo, "git pull")


@task
def gitpull(c, repo=REPO):
    _pull(c, repo)


@task
def migrate(c, repo=REPO):
    cmd = "git pull && poetry run python manage.py migrate"
    remote_exec(repo, cmd)


@task
def restart(c, repo=REPO):
    _restart(c, repo)


@task
def rs(c, repo=REPO):
    _restart(c, repo)


def _restart(c, repo: str) -> None:
    cmd = f"git pull && sudo supervisorctl restart {PROJECT}"
    remote_exec(repo, cmd)


@task
def roll(c, repo=REPO):
    cmd = "git reset --hard HEAD"
    remote_exec(repo, cmd)


@task
def lock(c, repo=REPO):
    cmd = "git pull && poetry lock && git commit -am 'poetry lock' && git push"
    remote_exec(repo, cmd)

注:

  1. 需要Python3.8+
  2. pip install fabric --user
  3. pip install python-dotenv
  4. 要求项目部署在~/coding/目录下,且配置了--reload

如何使用

直接在虚拟环境中执行:

fab pull

如果报错说没有fab命令,可以通过pip install fabric安装 如果是Django项目,而且migrations有更新,可以执行fab migrate 如果是使用supervisor部署的,而且没有配置--reload,可以执行fab restart