目录

    由于负责小组的 CI 公共事项,经常需要配置 CI 流程,或者帮助其他人解决一些问题,整理了一下常用的 CI 脚本,以方便查阅。

    1. .gitlab-ci.yml 结构

    下面是, GitLab CI 的配置文件结构。

    .gitlab-ci.yml 文件

    # 一些前置脚本,完成激活环境等操作
    before_script:
      - source /data/runner/node/bin/activate
      - which node && node --version
      - which npm && npm --version
      - LANG="zh_CN.utf8"
      - export LC_ALL=zh_CN.UTF-8
    
    # 编排需要执行的 stage
    stages:
      - build
      - deploy
    
    # 定义 job。job 属于某一个 stage,比如这里的 build 、deploy。GitLab CI 会按照 stages 配置的先后,顺序执行每一个 stage。
    

    2. 编译 Webpack 工程并提交到 GitLab 仓库

    这里约定:

    • 前端工程在根目录的 webpack 目录下
    • 前端工程编译之后的输出文件在根目录的 static/dist 目录下
    • 前端的编译命令是 npm run build

    GitLab CI 内置变量可以去 这里 查看。也可以,使用关键字 variables 定义自己的变量。比如,上面的目录路径,就可以配置为变量。但是每个项目有其独特性,这里就没有写成通用的形式,避免误导。

    通常,我会将一些账户信息配置在 settings/ci_cd 页面,并将这些变量勾选保护。勾选保护之后,只有受保护的分支才能够获取到这些变量,也就是下面的 GIT_USERNAMEGIT_PASSWORD 这部分。

    这里强烈建议,不要直接在当前代码目录直接提交 Git,应该将代码重新拉一份到本地,以保证环境不被污染。

    .gitlab-ci.yml 文件,

    stages:
      - build
    build-webpack:
      stage: build
      variables:
      CI_REPOSITORY_URL:
        http://$GIT_USERNAME:$GIT_PASSWORD@gitlab.yourdomain.com/$CI_PROJECT_PATH.git
      cache:
        untracked: true
        paths:
          - webpack/node_modules
      script:
        - echo "start build and commit"
        - cd webpack
        # 可通过关键字优化
        - npm install
        # 可通过关键字优化
        - npm run build
        - cd ..
        - rm -rf git-dir
        - git clone -b $CI_COMMIT_REF_NAME http://$GIT_USERNAME:$GIT_PASSWORD@gitlab.yourdomain.com/$CI_PROJECT_PATH.git git-dir
        - cd git-dir && rm -rf ./static/dist
        - mv ../static/dist static/
        - git config --global user.name $GITLAB_USER_NAME
        - git config --global user.email $GITLAB_USER_EMAIL
        - git add static/dist
        # 避免循环构建
        - git commit -m "auto commit [ci skip]`git log -1 --pretty=%B`" || exit 0
        - git push $CI_REPOSITORY_URL $CI_COMMIT_REF_NAME >/dev/null 2>&1 || exit 0
        - echo "end build and commit"
      tags:
        # 指定 runner
        - linux
        - shell
      only:
        # 指定分支
        - master
    

    3. 从 GitLab 推送代码到 SVN 仓库

    由于发布系统采用的是 SVN 仓库,但是 Git 更适合多人开发协助,在开发的过程中使用的是 GitLab 仓库。

    为了将 GitLab 仓库自动推送到 SVN 仓库,可以使用下面这个 job:

    push-to-svn:
      stage: deploy
      variables:
        CI_REPOSITORY_URL:
          http://$GIT_USERNAME:$GIT_PASSWORD@gitlab.yourdomain.com/$CI_PROJECT_PATH.git
      script:
        - echo "start push to svn"
        - echo "step 1/3: git clone"
        - git clone -b $CI_COMMIT_REF_NAME $CI_REPOSITORY_URL git-dir
        - echo "finished"
        - echo "setp 2/3: svn checkout"
        - echo 't' | svn checkout $SVN_PATH svn-dir --username $SVN_USERNAME --password $SVN_PASSWORD --no-auth-cache
        - echo "finshed"
        - echo "step 3/3: push git master to svn trunk"
        # 确保删除文件操作有效,可优化
        - cd svn-dir && svn delete *
        - rsync -avq git-dir/ svn-dir/
        - cd ./svn-dir
        - svn add * --force
        - echo 't' | svn commit -m "`git log -1 --pretty=%B`" --username $SVN_USERNAME --password $SVN_PASSWORD --no-auth-cache
        - echo "end push to svn"
    

    3. 通过关键字控制执行的脚本

    GitLab CI 提供了一些内置关键字用于控制 CI 的行为。比如,在提交信息中增加 [ci skip][skip ci] 就会跳过当次提交触发的 CI 构建。

    受到这种想法的启发,我们也可以在脚本中,从提交信息中匹配一些关键字,用于控制脚本执行逻辑。

    • 通过是否有关键字 [install] ,判断是否执行依赖包的安装
    if [[ $(git log -1 --pretty=%B) = *"["*"install"*"]"* ]]; then npm install; else echo "not npm install"; fi;
    
    • 通过是否有关键字 [build] ,判断是否进行前端的打包
    if [[ $(git log -1 --pretty=%B) = *"["*"build"*"]"* ]]; then npm run build; else echo "not npm install"; fi;
    
    • 通过是否有关键字 [delete] ,判断在执行 SVN 推送时,是否需要删除文件。这里主要是由于 svn add * 命令不能将删除的文件提交,删除文件必须使用 svn delete 处理。如果不使用 svn delete 命令,会导致 GitLab 中被删除的文件,在 SVN 中实际上没有被删除。
    if [[ $(git log -1 --pretty=%B) = *"["*"delete"*"]"*]]; then cd svn-dir && svn delete * && cd ..; else echo "not svn delete"; fi;
    

    4. 从 yml 获取变量

    yml 文件格式:

    code: test
    name: 测试
    author: admin
    version: 1.2.3
    

    以上面的 yml 格式为例,需要获取相关字段信息。

    parse_yaml() {
       local prefix=$2
       local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
       sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
            -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p"  $1 |
       awk -F$fs '{
          indent = length($1)/2;
          vname[indent] = $2;
          for (i in vname) {if (i > indent) {delete vname[i]}}
          if (length($3) > 0) {
             vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
             printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
          }
       }'
    }
    eval $(parse_yaml test.yml "config_")
    

    通过执行上面的脚本,可以获取到 test.yml 中的字段信息。

    echo ${config_code}
    echo ${config_name}
    ...
    

    5. 将环境变量值,写到 yml 文件

    yml 文件格式:

    code: test
    name: 测试
    author: admin
    version: 1.2.3
    

    以上面的 yml 格式为例,获取环境变量中的 VER 值,并将其写到 version: 后面中。

    sed -i "/version:/s/[0-9].*$/${VER}/g" test.yml