Mac系统如何在pre-commit hook中交互式运行Commitizen脚本
解决Mac下Husky+Commitizen预提交钩子崩溃问题
我在Linux系统中通过Husky的pre-commit钩子启动Commitizen完全正常,但切换到Mac系统后出现异常。使用的钩子脚本如下:
#!/bin/bash exec < /dev/tty && cz commit || true
问题根源是Mac系统的轮询机制故障,导致Commitizen崩溃,错误涉及键盘输入轮询、无法设置事件委托,具体堆栈跟踪信息如下:
Traceback (most recent call last): File "/opt/homebrew/bin/cz", line 8, in <module> sys.exit(main()) ~~~~^^ File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/commitizen/cli.py", line 656, in main args.func(conf, arguments)() ~~~~~~~~~~~~~~~~~~~~~~~~~~^^ File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/commitizen/commands/commit.py", line 127, in __call__ m = self.prompt_commit_questions() File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/commitizen/commands/commit.py", line 58, in prompt_commit_questions answers = questionary.prompt(questions, style=cz.style) File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/questionary/prompt.py", line 78, in prompt return unsafe_prompt(questions, answers, patch_stdout, true_color, **kwargs) File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/questionary/prompt.py", line 221, in unsafe_prompt answer = question.unsafe_ask(patch_stdout) File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/questionary/question.py", line 89, in unsafe_ask return self.application.run() ~~~~~~~~~~~~~~~~~~~~^^ File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/prompt_toolkit/application/application.py", line 1002, in run return asyncio.run(coro) ~~~~~~~~~~~^^^^^^ File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 195, in run return runner.run(main) ~~~~~~~~~~^^^^^^ File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^ File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py", line 725, in run_until_complete return future.result() ~~~~~~~~~~~~~^^ File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/prompt_toolkit/application/application.py", line 886, in run_async return await _run_async(f) ^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/prompt_toolkit/application/application.py", line 734, in _run_async with self.input.raw_mode(), self.input.attach( ~~~~~~~~~~~~~~~~~^ read_from_input_in_context ^^^^^^^^^^^^^^^^^^^^^^^^^^ ), attach_winch_signal_handler(self._on_resize): ^ File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 141, in __enter__ return next(self.gen) File "/opt/homebrew/Cellar/commitizen/4.5.1/libexec/lib/python3.13/site-packages/prompt_toolkit/input/vt100.py", line 165, in _attached_input loop.add_reader(fd, callback_wrapper) ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/selector_events.py", line 347, in add_reader self._add_reader(fd, callback, *args) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/selector_events.py", line 279, in _add_reader self._selector.register(fd, selectors.EVENT_READ, ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^ (handle, None)) ^^^^^^^^^^^^^^^ File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/selectors.py", line 505, in register self._selector.control([kev], 0, 0) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ OSError: [Errno 22] Invalid argument
可行解决方案
1. 替换钩子脚本,使用script命令适配Mac的TTY
Mac上exec < /dev/tty的终端处理逻辑和Linux不同,改用script命令创建伪终端,让Commitizen能正确获取输入:
#!/bin/bash script -q /dev/null cz commit || true
如果需要兼容Linux和Mac,可以添加系统判断:
#!/bin/bash if [ "$(uname)" = "Darwin" ]; then script -q /dev/null cz commit || true else exec < /dev/tty && cz commit || true fi
2. 降级Python版本到3.12及以下
报错中使用的Python 3.13在Mac上的asyncio和select模块存在兼容性bug,降级到3.12版本即可规避该问题。
3. 更新Commitizen到最新版本
新版本Commitizen可能已经修复了Mac平台的TTY适配问题,执行以下命令更新:
pip install --upgrade commitizen
内容的提问来源于stack exchange,提问作者Charlie Benger-Stevenson
相关产品推荐
相关产品推荐

