码路 [ The road to be a better programmer. ]

Git Hooks 与测试部署自动化

理想状态下,我们期望在完成一次主干分支的代码合并后,能够由机器自动的完成所有的上线流程:测试、打包、发布、回归;

进一步的,团队、个人,存在规范约束、质量要求,最好有一个非人工的关卡来进行代码质量的检查,降低人工 review 的成本。

Git hooks 就是一个理想的工具,下面一起来看下如何实现我们期望的效果

什么是 Git Hooks

Git Hooks(钩子)是 Git 在特定的动作发生时,触发的自定义脚本事件 / 任务,若 hook 执行失败,将终止后续操作。根据 hook 执行的位置,可以分为两种类型:

  • 客户端钩子:控制客户端 Git 的提交工作流

    • pre-commit: 执行 git commit 时触发,可用于检查代码风格;
    • prepare-commit-msgcommit message 唤起前,默认 commit message 创建后触发;
    • commit-msg:完成并确认 commit message 后触发;
    • post-commit:完成 git commit 后触发
    • ...
  • 服务端钩子:在代码推送到服务器之前和之后的策略执行

    • pre-receive:服务端收到一个 push 操作时触发,可以检查提交内容;
    • update:与 pre-receive 相似,区别是,push 更新多个分支时,pre-receive 执行一次,update 每一个分支都会执行一次;
    • post-receive:完成 push 操作后触发
    • ...

如何使用 Git Hooks

在本地 Git 代码仓库中,存在一个隐藏目录:.git,用于保存所有的版本记录、钩子方法等,这里我们关注其中的子目录:hooks

➜ hooks git:(master) ls
applypatch-msg.sample     pre-applypatch.sample     pre-rebase.sample
commit-msg.sample         pre-commit.sample         pre-receive.sample
fsmonitor-watchman.sample pre-merge-commit.sample   prepare-commit-msg.sample
post-update.sample        pre-push.sample           update.sample

初始化 Git 的项目里,都会提供多种 Git hooks 的 shell 脚本模版,另外也可以在脚本首行声明、使用 py、node 等脚本语言编写 ,移除 文件 .sample 后缀后,即会生效;

下面举三个例子说明 Git hooks 的应用场景:

早间年的 DIY pre-commit

在使用原始 sass 本地编译的时候,非常容易出现语法错误导致 sass 编译失败、提交代码存在未解决冲突的情况,为了避免代码直接提交上线,导致线上 case,产生了提交前检查代码的想法:devTool

提供了 pre-commit 脚本的一键安装 sh,支持提交前检查

  • Merge conflicts, forbidden commit;
  • scss compile error, forbidden commit;
  • use elementUi, forbidden commit;

vue-cli 初始化的 pre-commit

这里的 yorkie 是尤大 fork 了 husky,进行了部分改写。husky 是用 node 实现的一个快速安装 Git hooks 的工具,可以在package.json中指定相关钩子执行的npm scripts。

➜ hooks git:(account) cat pre-commit
#!/bin/sh
#yorkie 2.0.0

command_exists () {
  command -v "$1" >/dev/null 2>&1
}

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

# OS X and Linux only
load_nvm () {
  # If nvm is not loaded, load it
  command_exists nvm || {
    export NVM_DIR="$1"
    [ -s "$1/nvm.sh" ] && . "$1/nvm.sh"
  }
}

# OS X and Linux only
run_nvm () {
  # If nvm has been loaded correctly, use project .nvmrc
  command_exists nvm && [ -f .nvmrc ] && nvm use
}

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"

# Try to load nvm using path of standard installation
load_nvm /Users/lideguang/.nvm
run_nvm

# 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
}

服务端自动发布 (未测试)

我们在进行静态博客更新时,往往需要 push 内容更新、登录服务器、pull 更新、重启服务 等一系列操作,才能完成一篇文章的更新,那能不能省掉这些重复操作呢?服务端钩子可以来简化操作:

进入服务器 repo .git/hooks 目录,创建一个文件,名为:post-update,写入执行脚本,并设置执行权限 chmod +x post-update

!/bin/sh
unset GIT_DIR

NowPath=`pwd`
DeployPath="../../www"

cd $DeployPath
git add . -A && git stash
git pull origin master
echo "your root password" | sudo -S nginx -s reload
cd $NowPath
echo "deploy done"
exit 0

以上是 三个例子来说明如何使用 Git hooks 进行自动化的检查和自动部署,事件和回调可以使用多种脚本语言来进行编写,具有很强的扩展性,将代码检查、消息通知、构建等需要重复性无脑操作的事情,变成自动化的模式。减少人的因素产生的不确定性、提高效率。

你觉着有哪些场景适合做成 钩子函数 呢 ?


参考:

作者:Deguang
创建时间:2020-05-06
修改时间:2020-05-07