|
# coding: utf-8 |
|
|
|
import os |
|
import datetime |
|
|
|
# from fabric.contrib.files import exists |
|
from fabric.api import run, env, local, task, cd, lcd, execute |
|
|
|
# 项目的仓库地址。 |
|
HUBNAME = 'map' |
|
# 项目上游 |
|
UPSTREAM = 'shenqihui' |
|
# 临时部署的目录,存储部署下载的仓库 |
|
TMP_CODE_DIR = '/var/deploy/' |
|
# 部署测试环境的位置 |
|
STAGING_DEST = '/opt/dev/' |
|
# 部署生产环境的位置 |
|
RELEASE_DEST = '/opt/www/' |
|
# 网站代码,未编译版本 |
|
BUILDING_CODE_DEST = '/map/src/' |
|
# 网站代码,已编译版本 |
|
BUILDED_CODE_DEST = '/map/dist/' |
|
# 主生产分支 |
|
MAIN_RELEASE_BRANCH = 'master' |
|
# 主开发分支 |
|
MAIN_DEV_BRANCH = 'dev' |
|
# 机器 ssh 端口,默认所有机器都是统一端口,不进行运行时设定。 |
|
SSH_PORT = '6666' |
|
# fab ssh 后缀 |
|
SSH_PORT_ENDFIX = ':%s' % SSH_PORT |
|
|
|
DEPLOY_BRANCH = '' |
|
DEPLOY_HUB = '' |
|
DEPLOY_DEST = '' |
|
|
|
|
|
# 产品发布部署目标机器。 |
|
RELEASE_HOST = ['ubuntu@10.10.10.10'] |
|
# 开发分支部署的目标机器 |
|
STAGING_HOST = ['ubuntu@10.10.10.11'] |
|
|
|
# add port for fab |
|
RELEASE_HOST = ['%s%s' % (host, SSH_PORT_ENDFIX) for host in RELEASE_HOST if host] |
|
STAGING_HOST = ['%s%s' % (host, SSH_PORT_ENDFIX) for host in STAGING_HOST if host] |
|
|
|
|
|
RSYNC = ('rsync -e "ssh -p %s" --rsync-path="sudo rsync" -avzP --delete {source}%s/ {server}:{dest}' % (SSH_PORT, BUILDED_CODE_DEST)) |
|
|
|
|
|
def get_repo_tmp_dir(repo_name): |
|
# 获取源码存放的目录。 |
|
return os.path.join(TMP_CODE_DIR, repo_name) |
|
|
|
|
|
def is_git_repo_exists(repo_name): |
|
# 判断源码是否已经存在了。 |
|
repo_path = get_repo_tmp_dir(repo_name) |
|
return os.path.exists(repo_path) |
|
|
|
|
|
def get_repo_url(repo_name): |
|
# 获取源码的仓库地址 |
|
return 'git@github.com:%s.git' % repo_name |
|
|
|
|
|
def git_clone(repo_name, branch): |
|
''' clone 版本库的代码到部署临时目录 |
|
''' |
|
repo_url = get_repo_url(repo_name) |
|
repo_path = get_repo_tmp_dir(repo_name) |
|
local('git clone %s %s' % (repo_url, repo_path)) |
|
with lcd(repo_path): |
|
local('git checkout %s' % branch) |
|
|
|
|
|
def git_pull(repo_name, branch): |
|
''' 更新本地代码至 MAIN_DEV_BRANCH 的版本 |
|
''' |
|
repo_path = get_repo_tmp_dir(repo_name) |
|
with lcd(repo_path): |
|
try: |
|
# 增加远程仓库,方便进行合并上下游代码 |
|
local('git remote add deploy_auto_upstream %s' % get_repo_url('%s/%s' % (UPSTREAM, HUBNAME))) |
|
except: |
|
pass |
|
local('git checkout .') |
|
local('git fetch origin') |
|
local('git fetch deploy_auto_upstream') |
|
local('git checkout -f deploy_auto_upstream/%s' % MAIN_DEV_BRANCH) |
|
local('git reset --hard deploy_auto_upstream/%s' % MAIN_DEV_BRANCH) |
|
if branch != MAIN_DEV_BRANCH: |
|
# 其他分支,进行部署前需要合并到主开发分支。 |
|
try: |
|
local('git branch -D deploy_auto_%s' % branch) |
|
except: |
|
pass |
|
local('git checkout -b deploy_auto_%s' % branch) |
|
# 合并上下游代码 |
|
local('git merge --no-commit --no-edit origin/%s -X theirs' % branch) |
|
|
|
|
|
def upload_code(repo_name, dest): |
|
''' 上传代码至机器 |
|
''' |
|
tmp_code_path = get_repo_tmp_dir(repo_name) |
|
|
|
# 源码同步 |
|
for server_host in env.all_hosts: |
|
local(RSYNC.format(source=tmp_code_path, server=server_host.replace(SSH_PORT_ENDFIX, ''), dest=dest)) |
|
|
|
|
|
def install_requirements(): |
|
# 安装依赖,包括 PIP 等等,python 网站需要安装 pip 包。等等 |
|
with cd(DEPLOY_DEST): |
|
# simple pip install, using env's pip |
|
run('sudo ./env/bin/pip install -r requirements.pip -i http://pypi.douban.com/simple --timeout 60 --trusted-host=pypi.douban.com') |
|
|
|
|
|
def front_action(repo_name): |
|
# 前端处理 |
|
repo_path = get_repo_tmp_dir(repo_name) |
|
with lcd(os.path.join(repo_path, BUILDING_CODE_DEST)): |
|
local('npm install') |
|
local('bower install') |
|
local('gulp build') |
|
|
|
|
|
def restart(): |
|
''' 重启 web 服务,以及相关服务 |
|
''' |
|
try: |
|
run('sudo /etc/init.d/nginx -s reload') |
|
except: |
|
try: |
|
run('sudo /etc/init.d/nginx start') |
|
except: |
|
pass |
|
|
|
|
|
def do_deploy(repo_name, dest): |
|
''' 部署代码到web服务器,默认部署至staging host |
|
''' |
|
if not is_git_repo_exists(repo_name): |
|
git_clone(repo_name, DEPLOY_BRANCH) |
|
git_pull(repo_name, DEPLOY_BRANCH) |
|
|
|
front_action(repo_name) |
|
upload_code(repo_name, dest) |
|
# install_requirements() # python 网站需要安装 pip 包。等等 |
|
restart() # 远程进行重启操作,或者其他 |
|
|
|
|
|
def execute_deploy(hosts): |
|
# 执行部署 deploy |
|
print '\nBegin deploy for %s at %s\n' % (hosts, datetime.datetime.now()) |
|
try: |
|
execute(do_deploy, DEPLOY_HUB, DEPLOY_DEST, hosts=hosts) |
|
except: |
|
pass |
|
print '\nFinished deploy for %s at %s\n' % (hosts, datetime.datetime.now()) |
|
|
|
|
|
@task |
|
def deploy(deploy_type, origin=UPSTREAM, branch=MAIN_DEV_BRANCH, target_host=''): |
|
''' |
|
部署: dev/master/test |
|
指令例子: |
|
* fab deploy:dev |
|
* fab deploy:master |
|
* fab deploy:test,hubid,funA,ubuntu@10.10.10.10 |
|
参数: |
|
origin,branch,target_host 这三个参数只对 deploy_type == 'test' 时候有效。而且必须填写这三个参数。 |
|
''' |
|
global HUBNAME |
|
global DEPLOY_HUB |
|
global DEPLOY_BRANCH |
|
global DEPLOY_DEST |
|
|
|
if deploy_type == 'test': |
|
# 部署特定仓库的测试分支。 |
|
if not origin or not branch or not target_host: |
|
print u'部署测试分支必须填写参数' |
|
deploy_type = '' |
|
else: |
|
HUBNAME = origin |
|
DEPLOY_BRANCH = branch |
|
DEPLOY_DEST = STAGING_DEST |
|
|
|
DEPLOY_HUB = '%s/%s' % (HUBNAME, HUBNAME) |
|
execute_deploy(target_host) |
|
elif deploy_type == 'dev': |
|
# 部署主开发分支 |
|
HUBNAME = UPSTREAM |
|
DEPLOY_BRANCH = MAIN_DEV_BRANCH |
|
DEPLOY_DEST = STAGING_DEST |
|
|
|
DEPLOY_HUB = '%s/%s' % (HUBNAME, HUBNAME) |
|
execute_deploy(STAGING_HOST) |
|
elif deploy_type == 'master': |
|
# 部署生产分支 |
|
HUBNAME = UPSTREAM |
|
DEPLOY_BRANCH = MAIN_RELEASE_BRANCH |
|
DEPLOY_DEST = RELEASE_DEST |
|
|
|
DEPLOY_HUB = '%s/%s' % (HUBNAME, HUBNAME) |
|
execute_deploy(RELEASE_HOST) |
|
|
|
if not deploy_type: |
|
print deploy.__doc__ |
|
exit() |