husky+lint-staged规范代码提交

4,096 阅读4分钟

问题背景

之前在做代码合并的时候,部分同事的私仓代码合并到公仓分支时,被gitlabpre-merge-hooks拦住了,提示提交的commit信息不符合规范,同事的提交信息是这个样子:

commit fc442934c39fe700aa24d3f2f426d266e07a459e
Author: xxxxx
Date:   Fri Nov 5 10:08:50 2021 +0800

修改文案

公司的提交commit提交规范是这样一个标准:

  • 第一行是需求编号S开头,然后跟某个数值
  • 第二行是Message:开头,然后跟内容
commit fc442934c39fe700aa24d3f2f426d266e07a459e
Author: xxxxx
Date:   Fri Nov 5 10:08:50 2021 +0800

S200
Message: 修改文案

同事是知道这个规范的,提交的时候就忘了。 怎么避免这种情况,现在来说下一个常用的方案

解决方案

我们可以把这个拦截操作提前,在本地提交代码的时候,不符合commit信息规范的禁止提交。而不是在合并公仓的时候才发现。 现在流行的方案是husky + lint-staged

  • husky 可以在执行git-hooks处理一些额外配置任务,比如在commit-msg钩子检查提交信息是否规范
  • lint-staged 只会对暂存区的文件运行已经配置linter或其他任务,比如prettier格式化代码

husky

yarn add husky

安装完后可以在当前工程的.git/hooks/文件夹下检查是否安装成功

[~/Work_Space/demo/.git/hooks] []
-> % tree 
.
|____pre-rebase
|____pre-applypatch
|____husky.sh //新增了husky脚本,说明安装成功
|____husky.local.sh
|____commit-msg
|____pre-commit
|____pre-merge-commit

lint-staged:

yarn add lint-staged

安装后配置下package.json:

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "echo 'from husky'" // 输出一句话
    }
  },
  "lint-staged": {
    "src/**/*.{js,vue}": [
      "echo 'from lint-stage'"
    ]
  }
}

上面的配置意思是,在pre-commit钩子执行的时候,对暂存区中src目录下任意后缀为.js.vue输出一句话,commit-msg同理。

来测试下:

image.png 从结果来看配置是ok的。

项目配置

项目中的具体配置:

  • pre-commit的时候对暂存区文件执行lint操作
  • commit-msg的时候校验提交commit信息
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "node scripts/verify-commit.js"
    }
  },
  "lint-staged": {
    "src/**/*.{js,vue}": [
      "eslint""git add" // eslint结束后重新把文件加入暂存区
    ]
  }
}

verify-commit.js:

const chalk = require('chalk');
const path = require('path');
const msg = require('fs')
    .readFileSync(path.resolve('.git/COMMIT_EDITMSG'), 'utf-8')
    .trim();

const commitRE = /^(S|R\d+\r?\n(?:M|m)essage: .{1,50})/;

if (!commitRE.test(msg)) {
    console.log()
    console.error(
        `    ${chalk.bgRed.white(' ERROR ')} ${chalk.red(
            `Invalid commit message format.`
        )}\n\n` +
        chalk.red(
            `    Proper commit message format as below. Example:\n\n`
        ) +
        `    ${chalk.green(`S2222`)}\n` +
        `    ${chalk.green(
            `Message: feat(router): 修改文案字体大小`
        )}\n\n`
    );
    process.exit(1);
}

现在我们来测试下,不符合格式的提交信息是否能拦住: 我修改了下src/main.js,提交的时候pre-commit通过了,因为代码没有eslint错误, 然后也正确的拦截住了错误的提交信息。

image.png

其他问题

在vscode中不管是使用命令行还是插件gitLen是没问题的,但是不少同事习惯用source-tree提交代码,经过测试,mac电脑使用source-tree提交代码的时候,就算lint-stagedeslint错误或者提交信息格式不正确,source-tree吞掉了这些错误,硬是把代码提交上去了。查看了git日志后描述:

Can't find npx in PATH: /Applications/Sourcetree.app/Contents/Resources/git_local/libexec/git-core:/Applications/Sourcetree.app/Contents/Resources/bin:/Applications/Sourcetree.app/Contents/Resources/git_local/bin:/Applications/Sourcetree.app/Contents/Resources/git_local/gitflow:/Applications/Sourcetree.app/Contents/Resources/git_local/git-lfs:/usr/bin:/bin:/usr/sbin:/sbin

Skipping pre-commit hook!!!

原因是soucre-tree找不到npx命令,这种类型的客户端gui,访问的环境变量是需要提前配置的。这里需要给配置正确的node路径,使用node执行npx命令,需要在.git/hooks/pre-commit.git/hooks/commit-message加入:

#!/bin/sh
# husky
PATH=/Users/root/n/bin:$PATH # 加入这行
# Created by Husky v4.3.8 (https://github.com/typicode/husky#readme)
#   At: 2021/11/5 下午2:33:07
#  (https://github.com/typicode/husky#readme)

. "$(dirname "$0")/husky.sh"

PATH=/Users/root/n/bin:$PATH/Users/root/n/bin就是node所处的目录, 根据自己的电脑来配置node路径

which node
/Users/root/n/bin

设置完环境变量后,重新测试:

image.png

成功拦截!如果husky还配置其他钩子任务,使用source-tree提交也要加上上诉环境变量配置。

总结

github有标准的commit-msg的规范, 很多第开源项目都采用这个标准,比如vue-next的提交规范,可以作为一个标准的参考。