Fork me on GitHub

pre-commit

我们都知道接手一个项目是有多么的痛苦,没有文档,代码分格等等问题都不说了。今天说的‘pre-commit’就是来帮助大家来检测代码的。

为什么要检测代码

codereview是一个很费事,费时的事情,尤其在项目紧张期根本没有时间去做,而且说别人代码咋样咋样,但是被别人说自己的代码的时候,都不是太好!除非让老大来做codereview。但是老大一般都没时间的!所以写个脚本来检测代码,符合规范,开(zhuang)心(bi)敲代码。

pre-commit

顾名思义’pre-commit‘,就是在代码提交之前做些东西,比如代码打包,代码检测,称之为钩子(hook)。可以理解为回调好了,在commit之前执行一个函数(callback)。这个函数成功执行完之后,再继续commit,但是失败之后就阻止commit了。

在.git->hooks->下面有个pre-commit.sample*,这个里面就是默认的函数(脚本)样本。

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
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".

if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --bool hooks.allownonascii)

# Redirect output to stderr.
exec 1>&2

# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
Error: Attempt to add a non-ASCII file name.

This can cause problems if you want to work with people on other platforms.

To be portable it is advisable to rename the file.

If you know what you are doing you can disable this check using:

git config hooks.allownonascii true
EOF
exit 1
fi

# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

这里面应该是一段shell脚本,博主不懂shell脚本。这可怎么办呢?

npm script

之前在掘金里面看到一篇Run npm scripts in a git pre-commit Hook, 可以利用npm script来做脚本。

安装pre-commit

1
npm install pre-commit --save-dev

修改package.json

1
2
3
4
5
6
7
8
"scripts": {
"test": "npm -v && node -v",
"start": "node index.js",
"eslint": "node check.js" // 检查的脚本
},
"pre-commit": [
"eslint" // 与scripts中的脚本名称一一对应
],

问题

按照之前的那篇文章,接下来更改某个文件,应该是可以执行check脚本了,但是博主window并没有pre-commit。

github上找到了原因, 因为在window下pre-commit npm,由于权限问题,导致无法在hooks文件下生成文件。

需要以管理员打开cmd,执行node ./node_modules/pre-commit/install.js就可以了。o(╯□╰)o

检测脚本check

隐患检测

博主一开始的思路就是使用eslint检测潜在的错误,由于eslint比较严格,一下子会有很多的error,可以在项目里面新建.eslintrc文件, 用来覆盖默认的严格的eslint rules。

但是博主又有个问题了!就是检测代码的时候,由于引用是第3方的库,比如框架、组件、ui库等等。这些应该不需要检测的,不要影响项目本身的代码。这里就需要在项目里新建个.eslintignore文件,用来忽略检测的文件夹。

这个时候执行eslint ./ --cache,会有很多warnings,巴拉巴拉一大推,o(╯□╰)o。参考ESLint配置,执行eslint ./ --cache --quiet,就可以只报出error的信息了。到项目后期,可以慢慢将eslint越来越严格,甚至warn也不允许。

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
var exec = require('child_process').exec;
var fs = require('fs');
var errTip = ['还存在很多错误的地方哦!,避免隐患,还是现在改了吧!', '哎呀呀!还有错误哦!'];
var successTip = ['不错哦!加油!', '赞!', '棒棒哒!'];
var lint = function(cb) {
exec('eslint ./ --cache --quiet', function(error, stdout, stderr) {// 通过node子进程执行命令
if(stdout) {
console.log('\x1B[31m%s',errTip[Math.floor(errTip.length*Math.random())]);
console.log('\x1B[37m', stdout);//输出eslint错误信息
cb(1);
return;
}
cb(0);
});
}

var taskList = [lint];
// 执行检查
var task = function() {
if(!taskList.length) {
console.log('\x1B[32m%s', successTip[Math.floor(successTip.length*Math.random())]);
process.exit(0);
return;
}
var func = taskList.shift();
func(function(pass) {
if(pass === 1) {
process.exit(1);
return;
}
task();
});
}

var startTask = function() {
console.log('开始检查代码咯!O(∩_∩)O~\n');
task();
}

// 执行检查
startTask();

规范检测

除了一些隐患要检测,还可能要检测一些代码规范,tab键和空格键乱用等等,这个适合各个团队不同的情况。

但是现在一下子接手个项目,一下子全部去改造有点不现实。试想能不能只对修改的文件进行检查?其实是可以的。

通过git diff HEAD --name-only --diff-filter=ACMR命令能够拿到修改过的代码的文件列表,同时我们新增了一个第三方的库,也可以再添加参数,过滤不需要的文件夹。

核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//name为检测的文件夹,如‘modules component static’
exec('git diff HEAD --name-only --diff-filter=ACMR -- '+name+'', function(error, stdout, stderr) {// 通过node子进程执行命令,
if(stdout) {
array = stdout.split('\n');//通过切割换行,拿到文件列表
array.pop();// 去掉最后一个换行符号
array.forEach(function(value) {
text = fs.readFileSync(value, 'utf-8');// 拿到文件内容
if(检测函数) {
cb(1);
return;
}
});
cb(0);
}else {
cb(0);
}
});

实例检测函数

博主有些点点洁癖,由于每个人的代码编辑器不一样!最简单的分格就是tab键和空格键混用。所以就写个很简单的检测tab键和空格的函数。

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
var extraTab = function(cb) {
var conf = JSON.parse(fs.readFileSync('./.check', 'utf8'));
var name = conf.dir.join(' ');
var bTabAndSpace = conf.bTabAndSpace;
var array;
var text;
var checkTab = function(text, name) {//检测函数
if(/\t\s/.test(text)) {
console.log('\x1B[31m%s', name);
console.log('\x1B[37m', '存在tab键和空格键乱用哦!');
return false;
}
return true;
};
exec('git diff HEAD --name-only --diff-filter=ACMR -- '+name+'', function(error, stdout, stderr) {// 通过node子进程执行命令
if(stdout) {
array = stdout.split('\n');//通过切割换行,拿到文件列表
array.pop();// 去掉最后一个换行符号
array.forEach(function(value) {
text = fs.readFileSync(value, 'utf-8');// 拿到文件内容
if(bTabAndSpace && !checkTab(text, value)) {//检测函数
cb(1);
return;
}
});
cb(0);
}else {
cb(0);
}
});
};

将extraTab加入taskList任务队列里面就可以了!

总结

如果项目实在没时间去改的话,可以git commit -m 'XXX' --no-verify强制提交。也欢迎大家在https://github.com/xiaoqiang730730/pre-commit-check 提交一些规范类的代码测试!↖(^ω^)↗

本文地址 http://xiaoqiang730730.github.io/2016/11/04/pre-commit/

觉得有点意思,打个赏鼓励博主继续写哈!がんばって
前端-小强 WeChat Pay

微信打赏

前端-小强 Alipay

支付宝打赏