一个服务器部署脚本

对公司部署用的脚本不满已经很久了,加上最近比较有时间,所以重写了公司服务端部署的脚本。之前的脚本所做的仅仅是从 git 上pull下代码,然后编译打包部署到服务器。这里有一个问题就是:我们的静态文件是放到 cdn 上的,公司之前一贯的做法是在本地修改的时候,引用本地的静态文件,在修改完毕后,手动再改回来,然后再静态文件后加上一个版本号,然后手动将改动的文件放到 cdn 上。这样经常遇到的问题是,在本地改了 js 或者 css ,刷新页面发现不生效,整个人都蒙了,又或者是修改完后忘了将引用本地的静态文件改为引用 cdn 上的文件,导致浪费不必要的带宽。其实这些都还是小问题,一个隐藏的问题是,如果部署多台机器的时候,部分后部署的机器会使用新的静态文件,引起页面错误。

所以在此基础上,修改了部署用的脚本,此脚本满足一下功能:

  1. 自动将静态文件压缩,加上版本号,上传到 cdn
  2. 将引用本地的静态文件自动替换为cdn上的文件
  3. 记录每次替换的文件和版本号

这里对以上功能做下解释,
1、静态文件加上版本号再上传到 cdn 而不是在静态文件后加上一个请求参数,这一点上面也有提到,加上请求参数虽然也能使客户端刷新静态文件缓存,但是 cdn 上的文件是被替换掉的,如果有多台机器的情况下,先部署的机器为了能正常运行,需要将 cdn 的文件替换为新版本的文件,此时,旧版本的机器尚未部署,却引用 cdn 上新版本的文件,这样将会导致页面错误。而在静态文件上加上版本号却不会导致此问题,因为 cdn 上会保留新旧两个版本的文件。
2、不必解释。
3、因为每次部署所修改的文件不一样,假设上次修改了 a.js ,在 a.html 在部署的时候将文件中引用修改为 cdn 上的加上版本号的 a_2.js 文件,但是本次部署的时候并未修改此文件,所以在本次部署的时候无从知道 a.html 引用的 a.js 具体版本号,此时为了保证 a.html 引用正确的文件,只能重新上传一个a_3.jscdna.html 引用此文件,这样的问题是所有的静态文件都要加上新的版本号,而客户端需要刷新所有的静态文件,用户体验差。

下面是修改后的脚本

#!/usr/bin/env python
import upyun
import os
import re
import copy


CODE_HOME = 'code_home'
PROJECT_NAME = 'project'
DIR_NAME = 'project'
TOMCAT_HOME = 'tomcao_home'


def main():
    # pull 代码,并把改动信息保存到数组中
    str_arr = pull_code()
    (new_file_list, hash) = get_static_file(str_arr)
    ope_replace(new_file_list, hash)
    deploy()


def pull_code():
    os.chdir(CODE_HOME + '/' + DIR_NAME)
    os.system('git clean -fd')
    os.system('git checkout .')
    str_arr = os.popen('git pull').read().split('\n')
    return str_arr


def get_static_file(str_arr):
    # 我们用的是又拍云
    up = upyun.UpYun("", username="", password="")

    new_file_list = []
    hash = ''
    # compress static file
    for str in str_arr:
        if 'Already' in str: # 已经是最新的代码
            break
        if 'Updating' in str: # 这里我把commit id 拿下来当做版本号
            matchObj = re.match(r'Updating.*\.\.(\w+)', str, re.M | re.I)
            hash = matchObj.group(1)
            print(hash)
        elif '|' in str and 'public' in str and ('js' in str or 'css' in str): # js 和 css
            str = str.split("|")[0].strip()
            # 可能有一些文件被删除了,所以判断下文件是否存在
            if os.path.isfile(str):
                compress_str = str.replace('.', '_' + hash + '.')
                os.system('java -jar ~/yuicompressor.jar ' + str + ' -o ' + str) # 用yuicompressor压缩文件
                os.system('cp ' + str + ' ' + compress_str)
                up.put('/' + compress_str, open(compress_str, 'rb')) # 上传
                print(compress_str)

                # 这里因为我们的静态文件都在public目录下,这个目录已经被映射为静态文件,
                # 所以我们引用的时候并不需要带上public,而且用require.js,并不需要后缀
                # 所以在具体引用的时候可能是'/js/common/a',具体文件为public/js/common/a.js
                replace_base_str = str.split('.')[0].strip().replace('public', '')
                new_file_list.append(replace_base_str)

    return (new_file_list, hash) # 返回本次修改的静态文件数组和版本号


def ope_replace(new_file_list, new_hash):
    # 在 CODE_HOME 目录,用 static_version.db 记录每次修改的文件和版本号,以,号隔开
    os.chdir(CODE_HOME)
    try:
        fo = open("static_version.db", "r+")
    except IOError:
        fo = open("static_version.db", "w+")

    unhash_file_list = copy.deepcopy(new_file_list)
    while True:
        line = fo.readline().strip()

        if len(line) == 0:
            break

        print line
        _arr = line.split(',')
        file_name = _arr[0]
        file_hash = _arr[1]

        for new_file in new_file_list:
            if file_name == new_file:
                if new_file in unhash_file_list:
                    unhash_file_list.remove(new_file)

                seek_len = len(file_hash) + 1
                fo.seek(-seek_len, 1)
                fo.write(new_hash)
                fo.flush()
                fo.seek(1, 1)
                file_hash = new_hash
                break

        # replace file_name, file_hash
        replace_static(file_name, file_hash)

    if len(unhash_file_list) > 0:
        for new_file in unhash_file_list:
            fo.write(new_file + ',' + new_hash + '\n')
            fo.flush()
            # replace new_file, new_hash
            replace_static(new_file, new_hash)

    fo.close()


# 将引用本地文件改为引用 `cdn` 上的文件
def replace_static(str, hash):
    os.chdir(CODE_HOME + '/' + DIR_NAME)
    cdn_url = 'http://xxx.b0.upaiyun.com' # cdn 上文件前缀
    # 替换用sed替换,具体请搜索sed语法
    old_str = str.replace('/', '\/')
    new_str = (cdn_url + '/public' + str + '_' + hash).replace('/', '\/')
    os.system('sed -i "s/' + old_str + '/' + new_str + '/g" `grep [\\"\\\']' + old_str + ' -rl ./app/views`')


def deploy():
    # 编译,部署


if __name__ == '__main__':
    main()

python就没用过几次,依靠文档,折腾一下午。总算能用,有这个部署的脚本,开发起来也方便多了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值