Please enable Javascript to view the contents

GitLab CI 之前端 Webpack 实践

 ·  ☕ 6 分钟

从 GitLab 8.0 开始,GitLab 开始集成 CI(持续集成) 功能。只需要在服务器上添加一个 Runner,同时在项目中添加一个 .gitlab-ci.yml 文件,就可以进行 CI。在 GitLab 搭建与配置 中笔者记录了从零开始搭建 GitLab 服务的整个流程。在 GitLab CI 持续集成 中笔者交代了 GitLab CI 的一些基本概念,并给出了一个简单的 Demo。
本文主要讨论的是使用 Git 作为代码仓库时,多人开发的工作模式;在前端开发过程中,如何利用 GitLab-CI 工具,自动化编译 Webpack 项目。

1. 问题与需求

笔者从事 SaaS 开发,简单描述下目前团队项目开发的现状。

SaaS 基于 PaaS 提供的框架和 CI/CD 功能进行开发、预发布、发布,使用 SVN 进行代码的管理。PaaS 提供了 SaaS 开发人员快速开发应用的能力。

但是,在多人合作开发的项目中,SVN 提供的分支功能不便于管理分支、合并代码。

相比较于 SVN,Git 拉取代码速度快、允许上千个并行开发的分支、完全分布式,受到大量开发人员的喜爱。多人合作开发项目时,倾向于使用 Git 来管理代码,而在准备部署时才提交到 SVN。为了缩短流程,一方面,可以推动 PaaS 提供 Git 代码托管服务;另一方面,可以通过一定的工具来自动化整个流程(Git -> SVN),节省开发部署的时间。

另外一个问题是,前端团队使用的是 Webpack 模块打包管理工具,前端工程需要打包编译。针对一些复杂的项目,可能会有多人参与前端的开发工作。有时,前端人员在打包 Webpack 项目之前,没有拉取最新代码,导致其他前端人员开发的功能没有及时更新到最新的版本文件中。

幸运的是,GitLab 提供 CI 的功能,能解决上面的问题。GitLab CI 提供了在服务器上执行脚本、自动化流程的能力。

2. Git 工作流

通常 Git 的工作流程,采用的是功能驱动式开发。先有需求,再有功能分支或补丁分支。完成开发后,该分支就合并到主分支,然后删除分支。

广泛使用的工作流程有三种:

  • Git flow
  • Github flow
  • Gitlab flow

Git flow 需要同时维护两个非常相似的分支 develop 和 master,比较适合具有较长版本发布周期的项目;Github flow 只需要维护一个分支,根据新需求从 master 拉取新分支,合并上线后,再删除新分支。Gitlab flow 在 master 分支以外,再建立不同的环境分支,通过不同环境的上下游关系合并新功能。

目前团队的SaaS 更新周期短,迭代频繁,新的功能和修复的 Bug 很快就能合并到 master ,个人认为使用 Github flow 工作流程会比较简单方便。

当有新需求或者 Bug 时,从最新的 master 拉出一个新的分支,在新的分支上进行开发。在新分支上开发完成之后,发起 Pull Request 合并到 master ,最后删掉新建的分支。

3. GitLab-CI 工作流

通过在项目中配置 CI 流程,GitLab 默认在代码提交之后触发 CI 过程。默认的 CI 配置文件路径是项目根目录下 .gitlab-ci.yml 文件,也可以在项目的【Settings】-【Pipelines】- 【Pipelines】中指定配置文件路径。

简单说,就是在项目下新增一个 .gitlab-ci.yml 文件,在文件中定义 CI 流程,提交代码之后,GitLab CI 就自动开始构建了。下面的一些讨论,主要是围绕 .gitlab-ci.yml 文件。

3.1 .gitlab-ci.yml 关键字及含义

  • before_script
    定义任何 Jobs 运行前都会执行的命令。
  • after_script
    定义任何 Jobs 运行完后都会执行的命令。(要求 GitLab 8.7+ 和 GitLab Runner 1.2+)
  • variables && Job.variables
    定义环境变量。如果定义了 Job 级别的环境变量的话,该 Job 会优先使用 Job 级别的环境变量。(要求 GitLab Runner 0.5.0+)
  • cache && Job.cache
    定义需要缓存的文件。每个 Job 开始的时候,Runner 都会删掉 .gitignore 里面的文件。如果有些文件 (如 node_modules/) 需要多个 Jobs 共用的话,我们只能让每个 Job 都先执行一遍 npm install。(要求 GitLab Runner 0.7.0+)
  • Job.script
    定义 Job 要运行的命令,必填项
  • Job.stage
    定义 Job 的 stage,默认为 test
  • Job.artifacts
    定义 Job 中生成的附件。当该 Job 运行成功后,生成的文件可以作为附件 (如生成的二进制文件) 保留下来,打包发送到 GitLab,之后我们可以在 GitLab 的项目页面下下载该附件。

3.2 工作流

本地开发功能、定义流程,提交代码后,GitLab 通过 Pipeline 完成 CI/CD 过程。

4. Webpack CI 实践

GitLab 服务器最低配置要求是 1 core CPU、1GB RAM、3GB swap,推荐配置是 2 cores CPU、4GB RAM。团队使用的 GitLab 服务器配置为:

  • Intel(R) Xeon(R) CPU E5-2420 六核,1.90GHz,线程数 12
  • 内存 64G
  • 硬盘 300G,10000RPM,SAS

项目的文件结构:

1
2
3
4
- webpack/src
- webpack/package.json
- webpack/...
- .gitlab-ci.yml

4.1 .gitlab-ci.yaml 配置

需要注意的是 GitLab CI 提供了一些内置的环境变量,比如,$CI_PROJECT_PATH 表示项目的路径。使用这些内置变量,能够显著提高 yaml 配置的可移植性,减少拼写错误。下面是配置的 yaml 文件,具体每个步骤的功能,写在注释中。

 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
before_script:
  # 激活 nodeenv 虚拟环境
  - source /data/gitlab-runner/paas-webfe/bin/activate
  # 查看 node 版本
  - which node && node --version
  # 为了能方便使用 npm ,给它取一个别名
  - alias npm="/data/gitlab-runner/node/bin/npm"
  - which npm && npm --version

stages:
  - build

build-test-webpack:
  variables:
    # 需要修改为项目的 GitLab 地址格式 
    CI_REPOSITORY_URL:
      http://$GIT_USERNAME:[email protected]/$CI_PROJECT_PATH.git
    # 打包生成的文件存放目录
    OUT_PUT_DIR:
      test
  stage: build
  # 允许在 GitLab 页面上,直接下载 $OUT_PUT_DIR 内容
  artifacts:
    paths:
      - $OUT_PUT_DIR
  # 没有 Git 版本的文件,设置缓存,可以避免每次 npm install 重复安装
  cache:
    untracked: true
  script:
    # 开始执行打包编译命令,并提交到当前的 Git 仓库,具体的命令,需要根据项目编写,也可以放在一个 shell 文件,执行
    - echo "start build test"
    - rm -rf  $CI_PROJECT_NAME $OUT_PUT_DIR
    - cd ./webpack && tnpm install && tnpm run build-test && cd ..
    - git clone $CI_REPOSITORY_URL
    - rm -rf $CI_PROJECT_NAME/$OUT_PUT_DIR && cp -r $OUT_PUT_DIR $CI_PROJECT_NAME/
    - cd $CI_PROJECT_NAME
    - git add $OUT_PUT_DIR
    - git status >> build.log
    - date >> build.log
    - git add build.log
    - git commit -m "auto build-test[ci skip]"
    - git push $CI_REPOSITORY_URL master
    - echo "end build test"

上面的 yaml 配置实现了,提交代码之后,自动打包编译 Webpack 项目,并将编译生成的文件更新到 GitLab 仓库。如果需要更新到 SVN ,只需要执行 SVN 相应的提交命令即可,这个就当做是作业布置给看本篇文档的你了。

4.2 通过环境变量隐藏敏感信息

在项目页面依次选择 - 【Settings】- 【Pipelines】- 【Secret variables】

4.3 查看 CI 结果

向 GitLab 提交代码之后,CI 过程将被自动触发。等待执行的完成。

需要说明的另外一个关键点是:循环构建。在 script 里面,在每次构建时,会执行一次 git 的提交,这样会触发一次新的构建,如此反复,不停地执行构建。为了解决这个问题,在 script 里面提交代码时,需要在 commit 的 message 中加上 “[ci skip] 或者 [skip ci] 关键字”,跳过当次构建。在 Web 上查看 【Pipeline】 的执行状态时,可以看到一次提交会新增两条记录,其中一条为 【skipped】。

执行 job 时,除了 console 会输出构建信息。在 【Jobs】选项中还可以查看 job 的执行流和执行结果,为了更好的页面效果,下面是另外一个 Example 的执行结果(特意写了很多个 stage 和 job)。

4.4 一些小技巧

  • job 中使用 when 关键字,可以控制 job 在满足一定条件时,才被执行
1
when: [on_success | on_failure | always | manual]
  • 跳过 CI,在提交代码时,增加 ci skip或skip ci,可以跳过当次提交触发的 CI 过程
1
git commit -m "[ci skip]"
  • 在 job 中可以设置 only ,限制仅指定分支才执行
1
2
  only:
    - master

5. 参考

6. 附录

另外一份将 Git 仓库代码推送到 SVN 的配置

 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
before_script:
  - svn --version
  - git --version
  - 'echo "clear svn & git dir"'
  - rm -rf svn-dit git-dir
  - LANG="zh_CN.utf8"
  - export LC_ALL=zh_CN.UTF-8

  
stages:
  - deploy

push-to-svn:
  stage: deploy
  variables:
    CI_REPOSITORY_URL:
      http://$GIT_USERNAME:[email protected]/$CI_PROJECT_PATH.git
  artifacts:
    paths:
      - svn-dir
      - git-dir
  script:
    - 'echo "start push to svn"'
    - 'echo "step 1/3: git clone"'
    - git clone $CI_REPOSITORY_URL git-dir
    - 'echo "finished"'
    - 'echo "setp 2/3: svn checkout"'
    - 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 *
    - cd ..
    - cp -rf git-dir/* svn-dir/
    - cd ./svn-dir
    - svn add * --force
    - svn commit -m "`git log -1 --pretty=%B`" --username $SVN_USERNAME --password $SVN_PASSWORD  --no-auth-cache
    - 'echo "end push to svn"'

  only:
    - master

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