You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:37:38