基于git hooks的前端代码质量控制解决方案

@kuitos 2015-10-28 12:24:07发表于 kuitos/kuitos.github.io 前端工程工具

基于git hooks的前端代码质量控制解决方案

国际惯例先说下故事背景

通常情况下,如果我们是一个对代码质量有要求或者存在code review这一流程的团队,我们必然会有一套团队内部达成共识的code style从而提高项目的可维护性及代码的可读性。而确保提交到代码仓库的代码是符合规范的手段通常是,代码提交前由工具帮忙指出,如早期的jslint、jshint以及现在的eslint。提交后code review阶段由其他同学确保代码没有其他规范及质量问题。

目前这种方式的症结点

  1. 整个流程全靠团队成员自觉遵守。也就是说,即使我们在coding之前已经有一份code style放在那,而且eslint(jslint、jshint)工具已经配置好,依然有可能存在漏看了(或者根本没看。。)工具的提示信息、忘记了部分规范要求而直接把代码提交的情况。
  2. code review阶段reviewer经常要指出一些纯代码风格上的问题价值太低。因为上一条中出现的情况,reviewer还得花一些时间去指出纯代码风格上的问题,这种事情价值太低而且往往会让reviewer感到有心无力如果ta刚好还是个强迫症患者的话😂
  3. 总之纯靠人肉去确保代码规范是一件价值很低而且很靠不住的事情。

解决思路

既然人肉靠不住那我们只能让整个流程自动化让工具替我们完成代码规范检查的事情。我能想到的大概有这几种方式。

  1. 配置代码质量工具每次代码提交之前跑一下,然后针对工具给出的信息调整。
  2. 配置代码质量工具reviewer每次review之前跑一下,检查有没有基本的规范问题。
  3. 依靠CI(持续集成工具)对每次提交的代码做code check,每次接收push之前跑一下code check,如果没有通过直接拒绝接收push。

1、2两种方式都是通过工具方式降低了人肉检查代码规范的工作量,不过本质还是人肉。。
3方式不再人肉了但是依赖外部系统去做会存在风险及成本,比如哪天CI工具由bamboo切到了jenkins 然后又切到travis。。

直到有一天在下在fork了github上的一个项目并对其做了一定改造然后自信满满地准备提交代码的时候(json-mock-server),git一直在报错代码始终push不上去,报错信息也看不出什么东西,不行只能请教了对git比较内行的同事@arzyu,猜测是git hooks上配置了什么钩子脚本(后来证实是单元测试没覆盖到位),尝试把项目路径下的.git文件夹删除之后,终于能正常提交了😄

这件事给了我一个新的思路,就是我们能不能基于git的hook功能来做这这种自动化的事情呢?刚好在下又会一点点shell😄

所以理想的方式是

我们在git hooks里配置各种预处理脚本,比如代码检查或者跑单元测试之类的事情,如果我们的代码没有通过代码检查或者测试用例覆盖率不够,我们的push甚至commit会直接被拒绝。

好东西啊这不就是我这种极端分子想要的么哈哈!

怎么做

首先介绍一下git hooks是什么吧

钩子(hooks)是一些在"$GIT-DIR/hooks"目录的脚本, 在被特定的事件(certain points)触发后被调用。当"git init"命令被调用后, 一些非常有用的示例钩子文件(hooks)被拷到新仓库的hooks目录中; 但是在默认情况下这些钩子(hooks)是不生效的。 把这些钩子文件(hooks)的".sample"文件名后缀去掉就可以使它们生效了。

也就是在我们的git仓库下会有一个.git配置文件夹,它里面包含git操作相关的一系列 预处理/后处理 脚本。如 pre-commit、post-push之类的。

在一番google研究之后发现这事情操作起来并没有想象中那么简单,比如我们在什么时机将自己的脚本插入到hooks里,让使用者手动调命令这种主动式的方式一直不是我推崇的(要把调用者想象成懒癌晚期),然后处理脚本怎么写写哪里也没想到合适方式,一度放弃。

后来突然想起,之前那个开源项目是怎么做的?它是怎么把预处理脚本偷偷摸摸插入到hooks里的??于是突发奇想去研究别人的项目。

结果发现作者自己写了一个插件husky,专门用来处理这种git提交时的自动化处理。

大致用法像这样:

// 根目录package.json
"scripts": {
    "codecheck": "NODE_ENV=test eslint src/**/*.js",
    "test": "BABEL_JEST_STAGE=0 jest --verbose --watch",
    "precommit": "npm run codecheck && npm test"
}

配置了precommit之后每次代码提交之前git都会自动去跑代码检查及单元测试任务,都跑通过之后才能提交成功。更多用法请看项目主页husky

插件实现的机制大致是,在你install该插件时,它会自动往git hooks里埋入很多相应的钩子脚本(git hook能识别的),这些钩子函数会去读取package.json中的配置信息,当用户做某些git操作触发相应钩子时会自然去调用配置好的任务,从而实现我们的自动化需求,包括代码检查跟单元测试验证以及更多的自动化任务。

拓展思考

基于npm对install动作的钩子接口,我们可以提供一个插件,当使用者install该插件时插件会帮助用户做一系列初始化构建工作。开发时各业务模块独立仓库开发,上线或测试时提供构建插件一行install命令把所有业务模块组合起来变成一个完整的项目。具体打包、发布的方式后面会写一篇关于前端工程化的blog来介绍。

另外,基于git hooks我们还能做这样一件事情,我们在生产环境的git仓库中配置一个hook,当一有push/merge操作之后,触发一个推送脚本告知ci(jenkins/travis)执行构建/打包操作,然后我们只需要告知运维去某个地址拉下最新的包发布就好了(这里千万不要使用ci自己去完成发布),实现整个流程的自动化