内容简介:最近参加 leader 所举办的 CI 相关的 CoP(Communication of Practice),过程相当精彩且收获甚多,本文是听完了第二场分享“CI in action”之后的一次总结,分享本身是基于 Java 和 Gradle 进行的,但是由于对前端开发者而言,可能以前端所了解的技术栈进行总结更为熟悉。因此,本着吸收分享内容且结合前端实际开发的初衷,写下本文本次课进行了一次场景的模拟。首先,我们拥有一个共享仓库,它托管了实现一个简易计算器
最近参加 leader 所举办的 CI 相关的 CoP(Communication of Practice),过程相当精彩且收获甚多,本文是听完了第二场分享“CI in action”之后的一次总结,分享本身是基于 Java 和 Gradle 进行的,但是由于对前端开发者而言,可能以前端所了解的技术栈进行总结更为熟悉。因此,本着吸收分享内容且结合前端实际开发的初衷,写下本文
一、场景模拟
本次课进行了一次场景的模拟。首先,我们拥有一个共享仓库,它托管了实现一个简易计算器 APP
的代码(相关的代码在 Github ),其目录结构为:
其中:
./src ./test ./gulpfile.js
在本次场景模拟中,开发人员被分为两组,每组有三个人,他们将进行 Peering Programming (结对编程)
,因此,我们可以暂时省略 Code Review
这个过程:
A组 B组
场景中,涉及的机器有三台:
- A组的开发机
- B组的开发机
- 公共机(模拟的CI服务器)
二、开发功能与提交代码
1、A组开发
现在,A组开始实现乘法功能,一顿操作猛如虎:
git clone git@github.com:RuphiLau/ci-action-sample.git git checkout -b feat/multiply ./src/OperationType.ts
export enum OperationType { PLUS = 0, MINUS = 1, MULTIPLY = 2 // + }
- 打开项目文件
./src/Calculator.ts
,代码如下:
import { OperationType } from './OperationType' export default class Calculator { public calculate(action: OperationType, a: number, b: number): number { switch (action) { case OperationType.PLUS: return this._plus(a, b) case OperationType.MINUS: return this._minus(a, b) case OperationType.MULTIPLY: // + return this._multiply(a, b) // + } } private _plus(a: number, b: number): number { return a + b } private _minus(a: number, b: number): number { return a - b } private _multiply(a: number, b: number): number { // + return a * b // + } // + }
- 补充下自动化测试脚本,打开
./test/Calculator.spec.ts
,代码如下:
import { expect } from 'chai' import Calculator from '../src/Calculator' import { OperationType } from '../src/OperationType' describe('Calculator', () => { describe('Plus', () => { it('should return the plus result of two numbers', () => { const calc = new Calculator() const result = calc.calculate(OperationType.PLUS, 1, 2) expect(result).to.equal(3) }) }) describe('Minus', () => { it('should return the minus result of two numbers', () => { const calc = new Calculator() const result = calc.calculate(OperationType.MINUS, 2, 1) expect(result).to.equal(1) }) }) describe('Multiply', () => { // + it('should return the multiply result of two numbers', () => { // + const calc = new Calculator() // + const result = calc.calculate(OperationType.MULTIPLY, 1, 2) // + expect(result).to.equal(2) // + }) // + }) // + })
OK,接下来,A组需要先在本地执行测试和构建:
$ npm run build
输出:
> ci-action-sample@1.0.0 build /Users/ruphi.liu/Desktop/Lab/ci-action-sample > gulp [18:00:20] Using gulpfile ~/Desktop/Lab/ci-action-sample/gulpfile.js [18:00:20] Starting 'default'... [18:00:20] Starting 'test'... Calculator Plus ✓ should return the plus result of two numbers Minus ✓ should return the minus result of two numbers Multiply ✓ should return the multiply result of two numbers 3 passing (29ms) [18:00:22] Finished 'test' after 1.26 s [18:00:22] Starting 'build'... [18:00:22] Finished 'build' after 123 ms [18:00:22] Finished 'default' after 1.39 s
Nice~~,好了,A组已经完成了”乘法功能“,因此接下来需要提交代码,提PR,继续一顿操作猛如虎:
-
git add .
-
git commit -m 'feat: Implement the multiply operation'
-
git checkout master
-
git pull
-
git checkout feat/multiply
-
git rebase master
,没有冲突,完美~ -
git push
- 提
Pull Request
,而后Squash and merge
正常而言,在部署了CI自动化流程后,在PR通过且被 merge
到 master
后,便会触发相应钩子,从而触发 CI服务器
中的自动化测试和构建过程。但是为了直观地了解CI做了什么,因此我们本次采用手工模拟 CI服务器
所做的事情:
-
CI服务器
创建一个隔离的环境 -
CI服务器
克隆代码:git clone git@github.com:RuphiLau/ci-action-sample.git
-
CI服务器
执行测试和构建过程,由于本过程已经都在gulpfile.js
里写好了,拆解来看,做了如下事情:gulp.task('test') gulp.task('build')
-
CI服务器
完成构建过程,构建结果代码置于./dist
目录 -
CI服务器
执行自动化部署
,将构建后的代码部署到生产环境
2、B组开发
B组和A组同时开始开发,B组实现的是“除法功能”,在B组写完代码后,进行如下的流程:
-
npm run build
,得到:
> ci-action-sample@1.0.0 build /Users/ruphi.liu/Desktop/Lab/ci-action-sample > gulp [21:46:07] Using gulpfile ~/Desktop/Lab/ci-action-sample/gulpfile.js [21:46:07] Starting 'default'... [21:46:07] Starting 'test'... Calculator Plus ✓ should return the plus result of two numbers Minus ✓ should return the minus result of two numbers Divide ✓ should return the divide result of two numbers ✓ should throw error when the divisor is zero 4 passing (33ms) [21:46:08] Finished 'test' after 1.21 s [21:46:08] Starting 'build'... [21:46:08] Finished 'build' after 121 ms [21:46:08] Finished 'default' after 1.33 s
这说明构建是OK的,因此我们可以继续提交
git add . git commit -m 'feat: Implement the divide operation git checkout master git pull git rebase master
First, rewinding head to replay your work on top of it... Applying: feat: Implement the divide operation Using index info to reconstruct a base tree... M src/Calculator.ts M src/OperationType.ts M test/Calculator.spec.ts Falling back to patching base and 3-way merge... Auto-merging test/Calculator.spec.ts CONFLICT (content): Merge conflict in test/Calculator.spec.ts Auto-merging src/OperationType.ts CONFLICT (content): Merge conflict in src/OperationType.ts Auto-merging src/Calculator.ts CONFLICT (content): Merge conflict in src/Calculator.ts error: Failed to merge in the changes. Patch failed at 0001 feat: Implement the divide operation
因此我们首先需要解决冲突,在解决完冲突后,执行: npm run build
,OK,发现没问题,我们可以继续了:
git add . git rebase --continue git push --set-upstream origin feat/divide
接下来,我们可以再提一个 PR
,因为不需要 Code Review
,所以我们可以直接 Squash and merge
代码,之后 CI服务器
上仍然会执行自动化测试和构建工作,在最终构建完成后,我们就得到集成了 AB
两组开发的功能的代码,这也就是 CI
本质上所做的事情:
CI本质上就是合代码,持续地进行代码的合并。所谓持续,是经常性地提交代码并触发CI流程(业界通常认为这个频率是至少每天一次);而集成,就是将分支代码合并到主分支里。而我们所做的自动化测试等工作,是为了高质量地合并代码
三、冲突与构建失败问题
现在,我们假设一个情况:
B组开发完成后,提交的代码是有问题的,并且因为一些蜜汁操作,这些代码意外地上到 master
分支里了,那么会发生什么情况?
这将导致: 在merge后触发了CI流程,但由于代码有问题,跑CI的过程将出错 ,那么怎么办呢?
通常的做法是: 回滚
,将代码回滚到 merge
之前的状态,然后由 B组
解决 CI构建
过程中出现的问题,修复完成后重新提PR和合并代码
四、总结
经过本次的场景分析,我们可以把整个流程图示为:
而对这个流程的分析,我们可以总结出 CI
的最佳实践:
- 每次merge就触发CI 正因为CI是为了高质量地合代码,CI的本质也是合代码,所以每次
merge
都应该是执行CI
的时机。我们可以通过相应的hooks
,在监测到即将有代码要合到master
时,可以执行CI流程
,比如在这个时机之前检查是否有进行Code Review
,在此时机之后执行自动化测试和构建 - 隔离与独立的测试构建 CI的每次构建都应该是在一个独立的上下文中,在这个独立的上下文中安装依赖、构建和跑自动化测试等。可以通过
Docker
来实现 - CI流程开始后,开发人员保持关注,直到构建完成 在CI流程中,可能由于构建失败导致流程挂掉,如果开发人员未保持关注,则会在挂掉后很长一段时间内都无人知晓, 从而造成时间上的浪费 。比如可能开发人员通过过往经验得出这次构建需要一个小时,因此在CI跑起来后,开发人员就去做其他事情了,实际上CI在10分钟后就挂掉了,如果开发人员未保持关注,在一个小时后才来看CI的结果,那么中间这50分钟无疑是浪费的。假如开发人员保持关注,则能够在10分钟出现问题的时候及时回滚去修复问题,从而进入下一次的提交和
CI流程
。开发人员保持关注的手段有很多种,比如关注CI看板
(看板上会展示相关的构建信息,如失败后飘红),也可以是设立告警机制
(失败时开发者能够收到告警短信、邮件),更简单的则是开发者盯着CI日志
看,发现问题后立马进行处理
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 深入解析Redis中常见的应用场景
- 深入场景洞察用户 诸葛io决胜2017国际黑客松大赛
- 终于讲清楚了:深入理解 Java 应用程序中 final 关键字的各种使用场景
- HBase场景 | 对比MySQL,一文看透HBase的能力及使用场景
- 容器的应用场景
- Redis 应用场景汇总
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
编程人生(上卷)
[美] Peter Seibel / 图灵社区 / 人民邮电出版社 / 2014-12 / 39.00元
这是一本访谈笔录,记录了当今最具个人魅力的15 位软件先驱的编程生涯。包括Donald Knuth、Jamie Zawinski、Joshua Bloch、Ken Thompson等在内的业界传奇人物,为我们讲述了他们是怎么学习编程的,在编程过程中发现了什么以及他们对未来的看法,并对诸如应该如何设计软件等长久以来一直困扰很多程序员的问题谈了自己的观点。中文版分为上下卷,上卷介绍8位大师。一起来看看 《编程人生(上卷)》 这本书的介绍吧!
RGB CMYK 转换工具
RGB CMYK 互转工具
HSV CMYK 转换工具
HSV CMYK互换工具