Husky commit-msg hook执行完毕后触发Enquirer脚本的方案问询
看起来你遇到的核心问题是两个Husky钩子(commit-msg和post-commit)的执行时机没按预期串行,且post-commit里的Enquirer脚本会跟着commit-msg的进程退出而终止。这通常是因为commit-msg中的HTTP请求是异步执行的,导致钩子提前结束,而Git的进程组会在commit完成后终止所有关联子进程,进而影响到post-commit的脚本。下面给你几个可行的解决方案:
方案一:确保commit-msg脚本同步完成HTTP请求
最直接的方法是让commit-msg钩子等待HTTP请求完全完成后再退出,这样post-commit会在commit-msg彻底结束后才启动,自然不会出现并行执行的问题。
如果你的commit-msg脚本用Node.js编写,把异步HTTP请求改成阻塞式等待:
const fs = require('fs'); const axios = require('axios'); // 用axios更方便处理异步请求 async function sendCommitData() { // 读取提交信息 const commitMsgPath = process.env.HUSKY_GIT_PARAMS; const commitMsg = fs.readFileSync(commitMsgPath, 'utf8'); try { // 等待HTTP请求完成 await axios.post('https://your-api-endpoint.com/submit', { message: commitMsg }); console.log('HTTP请求已完成'); } catch (err) { console.error('HTTP请求失败,终止提交:', err); process.exit(1); // 请求失败时终止commit流程 } } // 执行并等待异步任务完成 sendCommitData().catch(() => process.exit(1));
如果是Bash脚本,用curl时加上--fail确保请求完成后再继续:
#!/bin/bash COMMIT_MSG=$(cat "$1") # 发送HTTP请求并等待响应 curl --fail --silent -X POST \ -H "Content-Type: application/json" \ -d "{\"message\": \"$COMMIT_MSG\"}" \ https://your-api-endpoint.com/submit # 如果请求失败,终止commit if [ $? -ne 0 ]; then echo "HTTP请求失败" exit 1 fi
方案二:用标记文件实现钩子间的同步
如果因为某些原因无法让commit-msg同步执行(比如HTTP请求耗时极长,不想阻塞commit流程),可以用临时标记文件让post-commit等待commit-msg的异步任务完成。
步骤1:修改commit-msg脚本
在脚本开头创建标记文件,HTTP请求完成后删除它:
#!/bin/bash # 创建临时标记文件 touch /tmp/commit_msg_pending # 异步执行HTTP请求,完成后删除标记 curl --fail --silent -X POST \ -H "Content-Type: application/json" \ -d "{\"message\": \"$(cat "$1")\"}" \ https://your-api-endpoint.com/submit && rm /tmp/commit_msg_pending &
步骤2:修改post-commit脚本
循环检查标记文件是否存在,直到文件被删除再执行Enquirer脚本:
#!/bin/bash # 等待标记文件被删除 while [ -f /tmp/commit_msg_pending ]; do sleep 0.3 done # 运行Enquirer脚本 node path/to/your-enquirer-script.js
方案三:让Enquirer脚本脱离进程组
如果post-commit脚本被终止是因为它属于Git的进程组,当commit-msg完成后Git终止整个进程组,你可以让Enquirer脚本脱离父进程,独立运行。
Bash版本
用nohup让脚本后台运行并脱离会话:
#!/bin/bash nohup node path/to/your-enquirer-script.js > /dev/null 2>&1 &
Node.js版本
用child_process.spawn创建独立进程:
const { spawn } = require('child_process'); // 启动Enquirer脚本并脱离父进程 const enquirerProcess = spawn('node', ['path/to/your-enquirer-script.js'], { detached: true, stdio: 'ignore' // 忽略标准输入输出,避免依赖父进程终端 }); // 解除与父进程的关联 enquirerProcess.unref();
总结
优先尝试方案一,它从根源上解决了钩子并行的问题,也是最符合Husky钩子设计逻辑的方式。如果必须让HTTP请求异步执行,再考虑方案二或三。
内容的提问来源于stack exchange,提问作者Thabo

