fabfile.md 6.5 KB


title: "Fabfile" date: 2024-01-04T14:21:55+07:00

draft: false

工程部署一般腳本

Install

apt install python3
apt install python3-pip
pip install fabric
touch fabfile.py

fabfile.py

# BsGRaUtSYaX5
# 206.238.113.33
import os
import re
import abc
import shutil
import platform
import requests
import subprocess

from datetime import datetime

from fabric import task, Connection


'''
部署工具
部署时,受限于PHP本身。自动生成 version 文件,记录版本信息。
'''


class Tar:
    @classmethod
    def compress(cls, args):
        pass

    @classmethod
    def uncompress(cls, args):
        pass


class Zip:
    @classmethod
    def compress(cls, args):
        pass

    @classmethod
    def uncompress(cls, args):
        pass


class MessengerNull:
    '''
    No need to do more complicated implements.
    I simply use Duck-Type.
    '''
    @classmethod
    def send(cls, msg=''):
        pass


class MessengerTg:
    @classmethod
    def send(cls, msg=''):
        pass


class Remote:
    """
    Remote

    Server where my project deployed. An instance refers a remote server.

    NOTE: I leave it's defination here for simplicity, so no need to maintain multi-files.
    """
    def __init__(self, host, *, name='服务器', branch='', port=22, user='root', password='', 
        user_group='www:www', pem='', deploy_path='/home', domain='', backup=False, tgmsg=False) -> None:
        '''
        Most people cares nothing, so do i.
        '''
        self.host = host
        self.name = name
        self.branch = branch    # MUST run in specified git branch
        self.port = port
        self.user = user
        self.password = password
        self.user_group = user_group
        self.pem = pem
        self.deploy_path = deploy_path # install path
        self.domain = domain    #
        self.backup = backup    # if backup before substitution
        self.tgmsg = tgmsg      # deliver message to telegram


''' <System Configures> '''
# 要部署的文件/目录列表
ITEMS = ('app', )

# 部署是压缩上传。这个是压缩上传时压缩包文件
FILE_TAR = './admin_api.tar.gz'

# 上传位置 (注意不是部署目录,远程服务器必须首先接收文件)
UPLOAD_PATH = '~'

# 服务器列表
mapModeRemote = {
    'dev': Remote(
        '192.168.1.15', 
        name='dev',
        password='123456',
        deploy_path='/www',
    ),
    'prod': Remote(
        '192.168.3.2',
        name='2server',
        backup=True,
        password='23423423',
        deploy_path='/home/ddd',
    ),
}
''' </System Confirgures> '''


def check_quit_mode(mode):
    '''
    Exit process if mode is invalid
    '''
    if mode not in mapModeRemote.keys():
        print(f"Invalid mode: {mode}")
        exit(-1)


def get_server(mode):
    return mapModeRemote.get(mode)


def git_branch_name():
    '''
    Get active(current) git branch

    @return str
    '''
    cmd = ['git', 'rev-parse', '--abbrev-ref', 'HEAD']
    return subprocess.check_output(cmd).decode('ascii').strip()


def git_commit_hash(short=True):
    '''
    Get git latest commit hash

    @short: is short hash or long(full)
    @return str
    '''
    cmd = ['git', 'rev-parse', '--short', 'HEAD']
    if not short:
        cmd = ['git', 'rev-parse', 'HEAD']
    return subprocess.check_output(cmd).decode('ascii').strip()


def git_last_n_log(n=3):
    cmd = ['git', 'log', '--oneline', f"-{n}"]
    return subprocess.check_output(cmd).decode('utf8').strip()


def run(c, cmd):
    ''' Fabric Connection.run() wrapper'''
    return c.run(cmd, hide=True)


def generate_version_file():
    '''
    '''
    commit = git_commit_hash()
    with open('./app/version', 'wt', encoding='utf8') as fh:
        content = f"{commit}"
        fh.write(f"export const version='{content}'\n")


def backup(r, server):
    '''
    backup to homedir before substitution

    @r: remote connection
    '''
    now = datetime.now().strftime('%Y_%m_%d_%H_%M_%S')
    filename = os.path.basename(server.deploy_path)
    backup_filepath = os.path.join('~', f"{filename}{now}.tar.gz")
    run(r, f"tar czf {backup_filepath} {server.deploy_path}")


@task
def test(c):
    backup(None)


def tar_posix(c):
    run(c, f"rm -f {FILE_TAR}")
    run(c, f"tar czf {FILE_TAR} {' '.join(ITEMS)}")


def tar_win(c):
    print('Unsupport function')
    exit(-2)
    # try:
    #     os.unlink(FILE_TAR)
    # except Exception as e:
    #     print('Remove file warn: {}'.format(e))
    # name, _ = os.path.splitext(FILE_TAR)
    # shutil.make_archive(name, 'zip', ITEMS)
    # print('zip file generated: {}'.format(FILE_TAR))


@task
def tar(c):
    plt = platform.platform()
    pattern_win = re.compile(r'windows', re.IGNORECASE)
    match = pattern_win.search(plt)
    _ = tar_win(c) if match else tar_posix(c)


@task(help={'mode': 'Upload destination: "prod" or "dev"'})
def upload(c, mode='dev'):
    '''
    Upload tarball(generated by tar command) to a remote server

    @mode: specify server by mode. @see mapModeRemote
    '''
    check_quit_mode(mode)

    if not os.path.isfile(FILE_TAR):
        print("Please run tar command first.")
        return

    server = get_server(mode)

    with Connection(host=server.host, port=server.port, 
        user=server.user, connect_kwargs={'password': server.password}) as r:
        with r.cd(UPLOAD_PATH):
            r.put(FILE_TAR)
            if server.backup:
                backup(r, server)

            run(r, f"tar xzf {FILE_TAR} -C {server.deploy_path}")
            run(r, f"rm -f {FILE_TAR}")
            if server.tgmsg:
                send(c, f"新版本 {VersionString} 已发布 {server.name}: *.*.*.{server.host.split('.')[3]}")


@task
def send(c, msg='hello'):
    '''
    Send a telegram message

    @msg: what to be sent
    '''
    chat_id = '1234567'
    token = 'x454565465:Adskjfskldfjdsklfjskldfjskld-ppdfVdfd3f'
    url = f"https://api.telegram.org/bot{token}/sendMessage?chat_id={chat_id}&text={msg}"
    ret = requests.get(url).json()
    if not ret['ok']:
        print('send Telegram msg failed:', ret)


@task(help={'mode': 'Deploy mode: "prod" or "dev"'})
def deploy(c, mode='dev'):
    check_quit_mode(mode)
    server = get_server(mode)
    branch = git_branch_name()
    generate_version_file()
    
    if server.branch and branch != branch:
        print(f"Wrong branch. mode={mode}, branch={branch}. branch should be {server.branch}")
        exit(-3)

    # dependencies
    tar(c)
    upload(c, mode)
    print('Done.')

更改配置

修改爲與當前工程適應的配置

運行

fab deploy -m=<remoteName>