Please enable Javascript to view the contents

如何利用 CDN 进一步的前后端分离 - CI 脚本

 ·  ☕ 3 分钟

在团队中,开发流程相关的调整一定要相应的自动化工具配合。如果没有足够低的使用成本,这种调整将会是无意义的,因为根本就不会有人去使用。上一篇,我们提到 如何利用 CDN 进一步的前后端分离 , 这一篇主要讲,如何将这个流程结合到 CI 中。后端的配置,之前的 博客 中已经提及很多。后端 CI 主要是做代码检查,推送代码到 SVN 仓库,CI 部分本篇不会涉及。

1. 版本约定

前端版本由 package.json 文件决定。package.json 文件结构如下:

1
2
3
4
5
6
7
8
{
  "name": "my-project-webpack",
  "version": "1.1.3",
  "description": "",
  "main": "index.js",
  "dependencies": {
  }
}

前端打包输出的文件在当前目录的 static 目录下:

1
2
3
4
5
6
# 本地版本
./static/dev/
# 测试环境版本
./static/test/
# 正式环境版本
./static/dist/

前端静态链接形式约定:

1
https://cdn.domain.com/1.1.3/static/dev/js/app.js

版本号直接放在访问的 URL 中,这样可以方便做多版本管理。同时不需要每次发布之后需要强制刷新 CDN ,以更新缓存。

前端的版本由前端控制,正式发布的版本应该是偶数版本。发布之后,需要将版本号最后一位加一。同时,每次发布版本,需要在 GitLab 仓库的 wiki 中记录变更的内容。

2. 前端改造

由于对页面进行了 JS 拆分优化,页面默认引用的是本地的 JS。通过修改 publicPath 属性,可以将引用的路径指向 CDN。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var package = require("./package.json");

var version = package.version;

baseConfig.devtool = '(none)'
baseConfig.output = {
    path: path.resolve(__dirname, './static/test/'),
    publicPath: 'https://cdn.domain.com/'+version+'/static/test/',
    filename: 'js/[name].js',
    chunkFilename: '[name].js'
};

3. 后端改造

后端使用的是 Django。后端改造的目的有两个:

  • 适配不同环境,有三个环境:本地、测试、正式。
  • 前端版本控制。

适配不同环境主要是通过目录结构。前端版本控制需要从数据库中读取一条配置,前端发布时,修改配置即可。

settings.py,适配不同环境

1
2
3
4
5
6
7
APP_CDN_URL = 'https://cdn.domain.com/'
if RUN_MODE == 'DEVELOP':
    BUNDLE_NAME = '/static/dev/'
elif RUN_MODE == 'TEST':
    BUNDLE_NAME = '/static/test/'
elif RUN_MODE == 'PRODUCT':
    BUNDLE_NAME = '/static/dist/'

views.py,获取前端版本配置

1
2
3
4
def index(request,question_id):
    V = Config().get_content('V') or '1.1.3'
    REMOTE_BUNDLE_URL = '%s%s%s'%(settings.APP_STATIC_URL, V, settings.BUNDLE_NAME)
    return render(request, 'index.html', {'BUNDLE_URL': REMOTE_BUNDLE_URL,})

index.html,引用静态版本文件

1
2
3
4
...
<script type="text/javascript" src="{{BUNDLE_URL}}js/vendors.js"></script>
<script type="text/javascript" src="{{BUNDLE_URL}}js/app.js"></script>
...

4. 前端 GitLab 仓库

前端仓库新增了三个文件:

  • .gitlab-ci.yml,CI 配置
  • get_version.sh,获取前端版本脚本
  • upload.py,上传到腾讯云 COS 脚本

下面是,前端代码仓库的 .gitlab-ci.yml 文件配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
before_script:
  - source /data/runner/web/bin/activate
  - which node && node --version
  - which npm && npm --version
  - LANG="zh_CN.utf8"
  - export LC_ALL=zh_CN.UTF-8

stages:
  - build

build-webpack:
  stage: build
  cache:
    untracked: true
    paths:
      - ./node_modules
  script:
    - echo "start build test"
    - rm -rf ./static/*
    - npm install
    - if [[ $(git log -1 --pretty=%B) = *"["*"skipbuild"*"]"* && $CI_COMMIT_REF_NAME = 'master' ]]; then echo "skip build"; else npm run build; fi;
    - source get_version.sh
    - echo $PACKAGE_VERSION
    - touch ./static/$PACKAGE_VERSION
    - if [[ $(git log -1 --pretty=%B) = *"["*"deploy"*"]"* && $CI_COMMIT_REF_NAME = 'master' ]]; then python upload.py $DEPLOY_CMD ./static $PACKAGE_VERSION; else echo "not deploy"; fi;
  artifacts:
        name: "static"
        paths:
            - ./static
  only:
    - master

get_version.sh,获取 package.json 文件中的前端版本号

1
2
PACKAGE_VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[[:space:]]')
export PACKAGE_VERSION

DEPLOY_CMD 是在 GitLab Settings Pipelines 页面新增的环境变量,值为:

1
AKIXXXXXXXN   93nxxxxxxxxXHZE9  ap-shanghai app-120000000

前端文件上传腾讯云 COS 代码脚本,本地 ./static/ 目录,上传到云上 /:version/static/ 目录。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/usr/bin/python
# -*- coding: utf-8 -*-
############################################
# 使用 cos-python-sdk-v5 上传文件夹到腾讯云
#  安装方式
#  pip install cos-python-sdk-v5
#  使用方式
#  python upload.py AKIXXXXXXXN   93nxxxxxxxxXHZE9  ap-shanghai app-120000000  ./static 1.0.0
############################################
import os
import sys

from qcloud_cos import CosConfig, CosS3Client


def main(argv):
    # 校验参数
    if  len(argv) == 7 :
        secret_id  = sys.argv[1] # 用户的 secretId
        secret_key = sys.argv[2] # 用户的 secretKey
        region     = sys.argv[3] # 用户的 Region( 'ap-beijing-1')
        bucket     = sys.argv[4] # 用户的 Bucket
        filePath = sys.argv[5] # 上传文件夹路径
        version = sys.argv[6] # 上传文件 Version,可以理解为 Prefix
    else:
        print 'argv error'
        return
    # 0,获取本地目录文件列表
    # 获取文件列表
    file_list = []
    print 'upload dir: %s' % filePath
    for root, dirs, files in os.walk("./static", topdown=False):
        file_list.extend([os.path.join(root, name).replace('\\', '/') for name in files])
    print 'ready to upload num of files %s,list: %s' % (len(file_list), file_list)
    #1,用户配置
    token = None # 使用临时密钥需要传入 Token,默认为空,可不填
    scheme = 'http' # 指定使用 http/https 协议来访问 COS,默认为 https,可不填
    config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token, Scheme=scheme, Timeout=300)
    # 2. 获取客户端对象
    client = CosS3Client(config)
    print 'Get client Success'
    # 3. 开始上传
    result = []
    for file in file_list:
        response = client.put_object_from_local_file(
            Bucket=bucket,
            LocalFilePath=file,
            Key='/%s%s' % (version, file[1:]),
        )
        result.append((file, response['ETag']))
    print 'upload result: %s, Total: %s, Success:%s' % (result, len(file_list), len(result))


if __name__ == '__main__':
    main(sys.argv)

5. 最终效果

腾讯云 COS

访问应用首页

1
2
<script type="text/javascript" src="https://cdn.domain.com/1.1.3/static/test/js/vendors.js"></script>
<script type="text/javascript" src="https://cdn.domain.com/1.1.3/static/test/js/app.js"></script>

微信公众号
作者
微信公众号