为了给同事讲githooks,我用30行代码实现了简易的husky

447 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

前言

大家好,我是小阵 🔥,一路奔波不停的码字业务员
身为一个前端小菜鸟,总是有一个飞高飞远的梦想,因此,每点小成长,我都想要让它变得更有意义,为了自己,也为了更多值得的人
如果喜欢我的文章,可以关注 ➕ 点赞,与我一同成长吧~😋
加我微信:zzz886885,邀你进群,一起学习交流,摸鱼学习两不误🌟

开开心心学技术大法~~

开心

来了来了,他真的来了~

正文

为了证明不是标题党,先贴上实现简易husky的关键代码

image-20220615233127664

husky做了什么

  • husky安装

  • husky注册钩子

    • 创建.husky文件
    • 在.husky文件下添加钩子
    • 更改本地git的hooksPath
  • 执行git命令触发钩子

这里都用到了git hooks的相关知识,想要详细了解的小伙伴可以来这里查看

husky关键步骤

husky注册钩子时用到了以下命令

npx husky add .husky/pre-commit "npm test"
git add .husky/pre-commit

意思是,通过husky在项目.git的同级目录生成一个.husky/pre-commit的文件,并且文件中的内容是npm test

下面是git的暂存命令,暂时不用考虑。

我们需要做什么

安装不需要考虑,因为我们这里只是实现一个git hooks的钩子拦截,不需要全局cli。

所以我们只需要考虑注册钩子和命令触发即可。

注册钩子

定义入口

因为我们忽略了安装的步骤,所以不会有可运行的cli命令,但是可以通过node来进入到我们的入口。

package.json中添加如下scripts

 "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "test2":"echo "我是test2"",
    "test3":"echo "我是test3"",
    "zzzgithooks":"node node.js"
  },
  "zzzgithooks":{
    "pre-commit":"npm run test3"
  },

可以看到我们通过zzzgithooks定义了node的入口,node node.js中的node.js文件是我们的关键文件,我们将在这个文件中生成pre-commit钩子。

而下面的zzzgithooks是我们通过package.json中传入一个想要实现的钩子内容。

这块对应husky中写死的"npm test"

生成pre-commit文件

假设我们要生成的目录是.test2/pre-commit

const fsPromise = require('fs/promises')
const path = require('path')
const packageJSON = require('./package.json')
​
const nodePreCommitContent = `#! /usr/bin/env node
​
const child_process = require('child_process')
​
console.log('我还是pre-commit-新增的')
​
child_process.exec('${packageJSON.zzzgithooks['pre-commit']}',(err,res)=>{
  if(err){
    return console.log('err',err)
  }
  console.log(res)
})
`
​
​
fsPromise.mkdir(path.resolve(__dirname, '.test2')).then(res => {
  fsPromise.writeFile(path.resolve(__dirname, '.test2', 'pre-commit'), nodePreCommitContent,{
    flag:'w+'
  }).then(res => {
    console.log('pre-commit-res', res);
  }).catch(err => {
    console.error('pre-commit-err', err);
  });
}).catch(err => {
  console.error('mkdirerr', err);
});

我们先接收到package.json中定义的hook内容,然后塞入到一个模板字符串,这个字符串就是将来我们生成的pre-commit中的内容。

可以看到开头的#! /usr/bin/env node交代了要用node执行这个文件。

然后通过child_process.exec启用一个node子容器从而可以在node的子容器中运行我们拿到的pre-commit的命令,之后将执行后的结果console.log出来。

之后我们通过fsPromise.mkdir生成了.test2的目录。

又通过fsPromise.writeFile生成了pre-commit文件。

之后我们的期望是更改git本地的hooksPath,让他指向我们新生成的pre-commit文件。

更改hooksPath

我们在生成pre-commit文件的fs.writeFilethen中执行git config core.hooksPath xxxx

child_process.exec(`git config core.hooksPath ${path.resolve(__dirname, '.test2')} && chmod +x ${path.resolve(__dirname, '.test2', 'pre-commit')}`, (err) => {
      if (err) {
        return console.log('err', err)
      }
      console.log('hhhh');
    })

可以看到我们不只是更改了git的hooksPath,还将之前生成的pre-commit文件更改了权限,通过chmod +x 目标文件将我们的目标文件更改成可执行文件。

触发钩子

注意,我们在触发钩子之前要通过npm run zzzgithooks先生成我们的pre-commit文件,然后在进行常规的git add . && git commit -am 'xxx'来触发钩子。

可以看到我们刚才的一系列操作已经完成了husky的功能。

image-20220615232805636

当然,代码中还要一些问题,我们生成目录是没有检验是否存在该目录的逻辑的,这会导致重复执行npm run zzzgithooks的时候会提示mkdirerr [Error: EEXIST: file already exists, mkdir

我们代码中也写死了指定的pre-commit命令,这个当然也可以通过遍历还动态生成。

还有一些路径问题,我因为书写方便,全部用了绝对路径,这里也可以优化一下,另外就是各种测试log,当然,这都不影响我们的功能。

好啦,这样就完成了我们初版simpleHusky啦,有精力的小伙伴可以再后期润色下,或者更积极的小伙伴可以上github上看下完整源码。

完整代码github

结语

感兴趣的小伙伴可以自己实现下哟,重点不是代码,而是思路哟!希望大家每天都在进步,一步一步成为自己心目中的大神!!

往期好文推荐「我不推荐下,大家可能就错过了史上最牛逼vscode插件集合啦!!!(嘎嘎~)😄」