编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

为什么husky放弃了传统的Js配置

wxchong 2024-06-11 10:12:39 开源技术 19 ℃ 0 评论

原文地址:(https://blog.typicode.com/husky-git-hooks-javascript-config/)

一个关于我最喜欢 husky 的新功能的简单说明以及是什么促成了它。

背景

我已经为husky工作了七年,并且关闭了,在JavaScript项目中,600个和Git hooks相关的issues。在一月,随着新版本的发布,大约100个 issues 被关闭了。

从V0开始,husky使用JavaScript文件配置Git 钩子,它既可以是.huskyrc.js文件,也可以是package.json文件中的字段。

现在Git钩子是使用 .husky/ 目录下的单个钩子文件配置的。

为什么突然变化这么大?

万事万物都在不停地变化和进步。Js生态已经改变了(引进了yarn,monorepos变成了趋势,出现了新的工具和最佳实践,甚至npm都有些许不同了...),同时Git也引进了一个新的令人激动的特性。

在谈论新的配置方法和它的优点时,让我们先看看husky到目前为止所采取的方法。

husky是怎么样工作的

在Git上,配置一个钩子,使用一种非常linux的方式,你只需要简单地将一个可执行的文本文件放到.git/hooks目录下。

为了能够运行用户在.huskyrc.js文件中创建的任何Git钩子,husky必须在运行之前,在.git/hooks/目录下,安装所有可能的Git钩子。

例如,当一个commit被生成的时候,每一个Git钩子都会去检查,在.huskyrc.js文件中是否有与之相应的钩子被定义。

$ git commit

pre-commit (native) → husky/runner.js (node)
  → is a pre-commit defined in `.huskyrc.js`? → YES, run it

prepare-commit-msg (native) → husky/runner.js (node)
  → is a prepare-commit-msg defined in `.huskyrc.js`? → NO, do nothing

commit-msg (native) → husky/runner.js (node)
  → is a commit-msg defined in `.huskyrc.js`? → NO, do nothing

post-commit (native) → husky/runner.js (node)
  → is a post-commit defined in `.huskyrc.js`? → NO, do nothing

它的优点是:用户能够通过.huskyrc.js文件新增,更新和删除Git 钩子,并且相应的改变也会自动生效。

缺点是,即使没有任何东西需要运行,node也会被启动。

你可能会问,难道我们就不能只安装我们所需要的钩子吗?三年前我们就有这个想法,但是husky再也不会自动“工作”了。

例如,让我们考虑下面的配置:

// .huskyrc.js
module.exports = {
  hooks: {
    'pre-commit': 'cmd'
  }
}
.git/hooks/pre-commit    ← is somehow created

然后你再修改它:

// .huskyrc.js
module.exports = {
  hooks: {
    // 'pre-commit': 'cmd', ← 移除
    'commit-msg': 'cmd'     ← 新增
  }
}

但现在,你的 .git/hooks 目录是不一致的。

.git/hooks/pre-commit    ← 仍然存在
.git/hooks/commit-msg    ← 不存在

因为你的钩子不是在同一个地方定义的,而是两个地方(.huskyrc.js.git/hooks/),因此你需要一种机制来保持Js世界和Git世界的同步。

每一次.huskyrc.js改变时,你(和你的合作者)都需要去重新生成钩子文件。重新生成这件事,可以绑定在某些事件上,但是却没有一种可靠的方式,去覆盖所有可能的案例,同时这还可能会出现一些意想不到的事情。这就是为什么这种方式被驳回了。

husky的新方法

当你的抽象有问题的时候,它通常是需要退后一步的信号。

到目前为止,我们知道哪些?

  • 根据设计,Git钩子的配置是通过可执行脚本完成的。
  • Husky初始的化的方法是间接的,并且还有点神奇。
  • 从Js配置中生成Git钩子可能会不同步的。

我们可否利用Git做得更好?

在2016年,Git 2.9 引进了core.hooksPath配置。它让你告诉Git,不要使用默认的.git/hooks/,而是另外一个目录。

下面几点就是新版本husky构建的基础:

  • husky install命令,告诉Git使用.husky/目录作为Git的钩子目录。
  • husky add命令,创建一个轻量级包装的shell脚本,以支持某些额外的特性。

它既解决了第一个问题(额外的Git钩子),也解决了第二个(在同一个地方定义你的Git 钩子)。

就是说,当你用新版husky创建一个Git钩子的时候,它是一个纯碎的shell脚本并且能够直接运行。在Git和你直接已经不再有任何其他东西。我发现它很漂亮。

但是...

  • 有目录这种配置方式并不常见

做这个决定,是因为其他工具也使用目录去存储配置。例如,一个仓库可能会有下列目录.vscode/, .github/, .storybook/, …

技术上讲,它仅仅只是你目录树中另一个入口,一个 .husky/ 目录不会比.husky.js文件更加杂乱。

这也是Git钩子设计的方式,以及husky遵循这种方式的原因。

您也可以选择将.husky/目录放在任何地方。例如,它可以放在 config/ 目录下,和其他配置文件放到一起。

  • “Js工具像,Jest、ESlint、Prettier...都是用Js配置文件配置的,husky也是个Js工具”

你能用.jestrc.js, .eslintrc.js, .prettierrc.js配置,这是有意义的,因为它们完全是用Js写的。

但是husky是个特列,它不是纯碎的Js工具。它是“混合的”。它是用来和一个非Js,并且已经有一种配置方式的工具来交流的。

  • 设置起来肯定很困难

husky自带有一个 init 命令(推荐),你可以很好地开始。

$ npx @husky/init # done!

这就是说,手动设置husky只需完成一次。您必须在package.json中更改一行,并且运行一个命令去增加一个钩子(这就是全部)。

这和 jest, eslint 有着相同的步骤,添加命令到package.json文件中,然后创建一个配置文件.jestrc.js, .eslintrc.js, …

(不过有一个例外,如果您正在发布一个包,并且同时使用yarn v2,它需要改变三行)。

  • 已经有了core.hooksPath,为什么还需要husky

Husky基于上个版本的反馈,提供了一些安全保护。用户友好的错误信息和一些额外的特性。它在完全的native和一点点用户友好上取得平衡。

  • 迁移将会很复杂

有一个 husky-4-to-6 命令行工具会自动替你完成这件事。


但是我仍然想在package.json中定义钩子

好消息是,没人会阻止你这么做,实际上它非常简单,执行下面的命令:

$ npx husky add .husky/pre-commit "npm run pre-commit"

在你的package.json文件中创建一个pre-commit script:

// package.json
{
  "scripts": {
    "pre-commit": "npm test && eslint"
  }
}

你已经完成了定义。

结论

我希望它能让事情变得更加清晰。几个月来,整个版本都在仔细地考虑,怎么提供访问Git 钩子最好的方法,以及在保持husky4主要特性时,具有最好的扩展性。

总体来说,它更加符合Git哲学和包管理器的建议。

用Js格式配置纯Js工具是有意义的,毕竟是同一种语言。

然而,当有些东西是 native 的,使用Js仅仅作为一个中间工具去定义或者运行Git钩子,对我来说,感觉有点诡异。就像用钳子夹着锤子去钉钉子....,你只是需要一把锤子。

感谢您的阅读,我理解,他非常新颖。假如您还不是很确定,请考虑给它五分钟。


我已经离开了工作岗位,去从事开源工作。非常感谢那些赞助这个版本的人或公司,以及那些开始使用它的项目。

2021年3月26日。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表