title: "Fabfile" date: 2024-01-04T14:21:55+07:00
apt install python3
apt install python3-pip
pip install fabric
touch 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>