Skip to content

Commit 13b8e2c

Browse files
saygoxLee-W
authored andcommitted
feat(commands/commit): apply prepare-commit-msg hook
add --commit-msg-file argument
1 parent e6dcdbc commit 13b8e2c

File tree

5 files changed

+100
-2
lines changed

5 files changed

+100
-2
lines changed

.pre-commit-hooks.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,12 @@
2525
language: python
2626
language_version: python3
2727
minimum_pre_commit_version: "1.4.3"
28+
29+
- id: commitizen-prepare-commit-msg
30+
name: commitizen prepare commit msg
31+
description: "prepare commit message"
32+
entry: cz commit --commit-msg-file
33+
language: python
34+
language_version: python3
35+
require_serial: true
36+
minimum_pre_commit_version: "1.4.3"

commitizen/cli.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ def __call__(
174174
"dest": "double_dash",
175175
"help": "Positional arguments separator (recommended)",
176176
},
177+
{
178+
"name": "--commit-msg-file",
179+
"help": (
180+
"ask for the name of the temporal file that contains "
181+
"the commit message. "
182+
"Using it in a git hook script: MSG_FILE=$1"
183+
),
184+
},
177185
],
178186
},
179187
{

commitizen/commands/commit.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import shutil
66
import subprocess
7+
import sys
78
import tempfile
89

910
import questionary
@@ -26,6 +27,21 @@
2627
from commitizen.git import smart_open
2728

2829

30+
class WrapStdin:
31+
def __init__(self):
32+
fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY)
33+
tty = open(fd, "wb+", buffering=0)
34+
self.tty = tty
35+
36+
def __getattr__(self, key):
37+
if key == "encoding":
38+
return "UTF-8"
39+
return getattr(self.tty, key)
40+
41+
def __del__(self):
42+
self.tty.close()
43+
44+
2945
class Commit:
3046
"""Show prompt for the user to create a guided commit."""
3147

@@ -105,6 +121,15 @@ def __call__(self):
105121
if is_all:
106122
c = git.add("-u")
107123

124+
commit_msg_file: str = self.arguments.get("commit_msg_file")
125+
if commit_msg_file:
126+
old_stdin = sys.stdin
127+
old_stdout = sys.stdout
128+
old_stderr = sys.stderr
129+
sys.stdin = WrapStdin()
130+
sys.stdout = open("/dev/tty", "w")
131+
sys.stderr = open("/dev/tty", "w")
132+
108133
if git.is_staging_clean() and not (dry_run or allow_empty):
109134
raise NothingToCommitError("No files added to staging!")
110135

@@ -138,9 +163,24 @@ def __call__(self):
138163
if dry_run:
139164
raise DryRunExit()
140165

166+
if commit_msg_file:
167+
sys.stdin.close()
168+
sys.stdout.close()
169+
sys.stderr.close()
170+
sys.stdin = old_stdin
171+
sys.stdout = old_stdout
172+
sys.stderr = old_stderr
173+
defaultmesaage = ""
174+
with open(commit_msg_file) as f:
175+
defaultmesaage = f.read()
176+
with open(commit_msg_file, "w") as f:
177+
f.write(m)
178+
f.write(defaultmesaage)
179+
out.success("Commit message is successful!")
180+
return
181+
141182
always_signoff: bool = self.config.settings["always_signoff"]
142183
signoff: bool = self.arguments.get("signoff")
143-
144184
if signoff:
145185
out.warn(
146186
"signoff mechanic is deprecated, please use `cz commit -- -s` instead."

docs/getting_started.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,14 @@ repos:
9595
- id: commitizen
9696
- id: commitizen-branch
9797
stages: [push]
98+
- id: commitizen-prepare-commit-msg
99+
stages: [prepare-commit-msg]
98100
```
99101
100102
After the configuration is added, you'll need to run:
101103
102104
```sh
103-
pre-commit install --hook-type commit-msg --hook-type pre-push
105+
pre-commit install --hook-type commit-msg --hook-type pre-push --hook-type prepare-commit-msg
104106
```
105107

106108
If you aren't using both hooks, you needn't install both stages.
@@ -109,6 +111,7 @@ If you aren't using both hooks, you needn't install both stages.
109111
| ----------------- | ----------------- |
110112
| commitizen | commit-msg |
111113
| commitizen-branch | pre-push |
114+
| commitizen-prepare-commit-msg | prepare-commit-msg |
112115

113116
Note that pre-commit discourages using `master` as a revision, and the above command will print a warning. You should replace the `master` revision with the [latest tag](https://github.com/commitizen-tools/commitizen/tags). This can be done automatically with:
114117

tests/commands/test_commit_command.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,41 @@ def test_commit_command_shows_description_when_use_help_option(
525525

526526
out, _ = capsys.readouterr()
527527
file_regression.check(out, extension=".txt")
528+
529+
530+
def test_commit_from_pre_commit_msg_hook(config, mocker, capsys):
531+
testargs = ["cz", "commit", "--commit-msg-file", "some_file"]
532+
mocker.patch.object(sys, "argv", testargs)
533+
534+
prompt_mock = mocker.patch("questionary.prompt")
535+
prompt_mock.return_value = {
536+
"prefix": "feat",
537+
"subject": "user created",
538+
"scope": "",
539+
"is_breaking_change": False,
540+
"body": "",
541+
"footer": "",
542+
}
543+
544+
commit_mock = mocker.patch("commitizen.git.commit")
545+
mocker.patch("commitizen.commands.commit.WrapStdin")
546+
mocker.patch("os.open")
547+
reader_mock = mocker.mock_open(read_data="\n\n#test\n")
548+
mocker.patch("builtins.open", reader_mock, create=True)
549+
550+
cli.main()
551+
552+
out, _ = capsys.readouterr()
553+
assert "Commit message is successful!" in out
554+
commit_mock.assert_not_called()
555+
556+
557+
def test_WrapStdin(mocker):
558+
mocker.patch("os.open")
559+
reader_mock = mocker.mock_open(read_data="data")
560+
mocker.patch("builtins.open", reader_mock, create=True)
561+
562+
wrap_stdin = commands.commit.WrapStdin()
563+
564+
assert wrap_stdin.encoding == "UTF-8"
565+
assert wrap_stdin.read() == "data"

0 commit comments

Comments
 (0)