资讯专栏INFORMATION COLUMN

前端自动化工作流中的hooks

marek / 2433人阅读

摘要:例如提供的用于修改的钩子就需要在的同时从远程服务器下载到本地来替换,代码如下这当然是一种好方式。安装简单到看完配置就懂了吧,直接在中增加这一项,并直接把想执行的语句写在里面即可。

在前端的日常工作中,经常会出现“当执行一种操作之前(之后)需要同时执行另一种操作”的情况,比如我们希望在每次git commit之前都运行eslint代码检查、npm install之前检查项目依赖等。作为经典的情况,各类工具都可以让我们在特定的动作发生时触发自定义脚本,这个功能就叫钩子hooks

日常经常用到的工具有npmgitwebpack,其中的hooks用法我们分别介绍一下。

其中webpack的hooks是webpack为开发者提供的运行时事件钩子,我们能利用它来编写plugins,这个就不在这里说了,将来会多带带写一篇关于写plugin的文章(立个flag╮(╯▽╰)╭)。

npm hooks —— 监听npm操作 / 订阅npm源修改获取通知

目前提到npm hooks,有两个不同概念的操作。

通常意义下的监听npm各类操作的钩子是通过配置package.json文件中的scripts字段来实现的。

npm hooks则是npm提供的命令行操作,目的是为了订阅你需要的npm package发生的特定改动,比如可以订阅尤大(误)的新动态等。当然,这项功能需要你自己提供一个域名,并且需要有npm账号且购买服务,所以就不多讨论了╮(╯▽╰)╭,具体参见npm-hook官方文档。

npm script hooks

使用方法很简单,在项目的package.json中的scripts字段加入"hook": "script"键值对即可。hook名称由npm提供,script就是能够运行的shell语句。下面列几个常用的hook:

preinstall: 在npm install之前执行

install, postinstall: 在npm install之后执行

prestart: 在npm start之前执行

start, poststart: 在npm start之后执行

etc.

从以上的例子中可以看出,hooks的命名是pre[op]为操作之前的钩子,[op]post[op]为操作之后的钩子。还有很多其他的钩子,具体可以查阅npm script官方文档。

由于历史原因,publish相关的钩子会比较特殊,具体原因和修改后的样子都在文档里了,不多介绍。

e.g.

比如项目npm install之前依赖一个全局的npm包,用户需要先npm install -g package,这时就可以把该操作写到preinstall里:

package.json

...
"scripts": {
  "preinstall": "npm install -g package"
  ...
},

当然,shell的部分可以写所有的可以执行的shell语句。如果需要的操作比较多,也可以写shell脚本,然后执行该脚本。对很多前端来说,直接开搞shell脚本比较困难,也可以写node、python等脚本,然后用node script.js的方式执行也是ok的。

git hooks —— 监听git操作 介绍

git hooks基本跟上面介绍的npm script hooks差不多,也是配置相应的pre-[op]post-[op]之类的钩子。

git通过项目根目录下的.git目录中的内容来标记一个git库并记录相关信息,这点应该是众所周知了。

git hooks的配置就在.git/hooks目录下,以无后缀的脚本文件的形式存在,文件名称即是钩子名称,文件内容是shell脚本,你可以自行添加可执行内容。一般在刚npm init的hooks文件夹中全都是[hook].sample示例文件,需要复制并改名为该hook名称才可以正常使用

根据git版本不同,可用的hooks也不同,举下跟commit相关的hook例子:

pre-commit: 在commit之前运行

post-commit: 在commit之后运行

prepare-commit-msg: 在启动提交信息编辑器之前(git commit -s那个),默认信息被创建之后运行

commit-msg: 生成基本commit-msg时触发,可以用来在提交通过前验证项目状态或提交信息

还有很多基于其他操作的钩子,都因工作流程不同而有所不同,具体可以查阅Git 钩子官方文档。

**钩子脚本可以按照指责不同接收不同的参数并进行修改。**比如commit-msg钩子,钩子接收一个参数,是存有当前提交信息的临时文件的路径,参数可以在shell脚本中以$1的形式进行调用,有了这个我们就可以修改文件中的提交信息了。

举个例子,当你想在每次commit之前用检查代码规范与否,就可以直接在pre-commit脚本最后添加npm run lint(这里看自己相关配置,不一定是这句)。

但是,git hooks的设计思路是在每台终端以及服务器端提供不同的定制方案。说人话,就是因为git是一种分布式的系统,它保证了所有终端的版本都是相同的,但hooks在服务器端和私人终端的配置可能不一样,所以hooks的配置不能跟随git提交。

例如gerrit提供的用于修改commit message的commit-msg钩子就需要在git clone的同时从远程服务器下载到本地来替换,代码如下:

git clone ssh://kinice@gerrit.company.com:29418/All-Projects && scp -p -P 29418 kinice@gerrit.company.com:hooks/commit-msg All-Projects/.git/hooks/

这当然是一种好方式。但有一种情况,当我们没有其他的可以存储脚本的第三方服务器,又希望将hooks同步给所有终端,该怎么办呢?

用更简单的方式使用git hooks

为了解决上面的问题,有很多大神写了第三方工具来实现hooks同步。对于前端来说,可以在npm安装的第三方工具有很多,例如huskyyorkygit-hooks等。yorkie是Vue作者尤雨溪fork了husky并做了一些修改的工具,改善了一些使用体验,所这里我们介绍一下yorkie。

*注:git-hooks跟前两种工具的思路不同,感兴趣可以了解一下:git-hooks。

安装yorkie

$ npm install yorkie --save-dev
// package.json
{
    "gitHooks": {
      "pre-commit": "npm test",
      "commit-msg": "npm test",
      "...": "..."
    }
}

简单到看完配置就懂了吧,直接在package.json中增加gitHooks这一项,并直接把想执行的shell语句写在里面即可。

探究yorkie的实现原理

在安装过yorkie之后,比对一下安装之前的hook文件,会发现yorkie直接重写了所有的hooks。所以我们把/.git/hooks/pre-commit的核心代码贴出来看看yorkie做了什么:

has_hook_script () {
  [ -f package.json ] && cat package.json | grep -q ""$1"[[:space:]]*:"
}

cd "." 

# Check if pre-commit is defined, skip if not
has_hook_script pre-commit || exit 0

# Add common path where Node can be found
# Brew standard installation path /usr/local/bin
# Node standard installation path /usr/local
export PATH="$PATH:/usr/local/bin:/usr/local"

# Export Git hook params
export GIT_PARAMS="$*"

# Run hook
node "./node_modules/yorkie/src/runner.js" pre-commit || {
  echo
  echo "pre-commit hook failed (add --no-verify to bypass)"
  exit 1
}

忽略上面那些检查是否存在hook脚本的代码,最后执行了node ./node_modules/yorkie/src/runner.js

const fs = require("fs")                                                                                                                                                                 
const path = require("path")
const execa = require("execa")

const cwd = process.cwd()
const pkg = fs.readFileSync(path.join(cwd, "package.json"))
const hooks = JSON.parse(pkg).gitHooks // 将package.json重的hooks字段取出来
if (!hooks) { // 没有hook则退出
  process.exit(0)
}

const hook = process.argv[2] // 这里的process.argv[2]就是在hooks脚本里传过来的hook名称,如pre-commit
const command = hooks[hook]
if (!command) { // 不是当前hook则退出
  process.exit(0)
}

console.log(` > running ${hook} hook: ${command}`)
try {
  execa.shellSync(command, { stdio: "inherit" }) // 使用execa.shellSync运行命令
} catch (e) {
  process.exit(1)
}

关于对runner.js的解析,我写到了注释中,应该都能看得懂。即通过(npm install时改写hooks --> 将hooks改为运行自己的runner --> runner依赖package.json)的方式,实现了将hooks信息保存在package.json中并可以通过git共享给所有项目成员。

END

俗话说,懒惰是人类进步的动力,希望可以用这些东西,做到一键完成所有手工重复任务,提高我们的工作效率,把时间用在更有意义的事情上。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7206.html

相关文章

  • React教程:组件,Hooks和性能

    摘要:顾名思义,受控组件的值由控制,能为与用户交互的元素提供值,而不受控制的元素不获取值属性。另外我发现受控组件更容易理解和于使用。只是一种把组件作为参数的函数,并且与没有包装器的组件相比,能够返回具有扩展功能的新组件。其中三个基本的是,和。 翻译:疯狂的技术宅原文:https://www.toptal.com/react/... 本文首发微信公众号:jingchengyideng欢迎关...

    edagarli 评论0 收藏0
  • 不会写shell的程序员照样是好前端——用Node.JS实现git hooks

    摘要:当退出的错误码不为的时候,表示失败,操作终止,否则操作继续。执行命令进行测试,如果测试全部通过的话,退出,错误码为,否则错误码为,同样退出。这样虽然没有解决不会随着仓库移动的问题,但也提供了一种在项目组里通用一套的方案。 git hooks想必很多攻城狮都不陌生,官方对于hooks有详细的文档,也有站内网友的文章Git Hooks (1):介绍,GIt Hooks (2):脚本分类,说...

    BWrong 评论0 收藏0
  • PyCon China 深圳站精彩回顾(附PPT及视频)

    摘要:月日,第六届大会在深圳召开。这是这次大会的第二站活动,第一站已在上海成功举办。深圳站视频及,请在公众号后台回复,获取分享链接。据介绍,目前支持多种开发库,如内置和等。该协议的推出,是为了统一标准,提高效率。 本文为 PyChina 和「编程派」联合首发,作者为 EarlGrey。「编程派」是一个专注 Python 学习交流的微信公众号。 9 月 25 日,第六届 PyCon China...

    lykops 评论0 收藏0
  • Cobub无码埋点关键技术的实现

    摘要:目前埋点分为两种方式,有码与无码埋点。本文主要介绍无码埋点的技术实现。无码埋点的实现流程可视化视图圈选,在页面上会出现浮动的圆圈,拖动圆圈至想配置事件的控件上,将会弹出输入事件的弹框。 showImg(https://segmentfault.com/img/bVXMSS?w=900&h=500);随着大数据时代的到来,数据采集也已经变的越来越重要。前端埋点作为一个比较成熟的数据接入手...

    int64 评论0 收藏0

发表评论

0条评论

marek

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<