-
Notifications
You must be signed in to change notification settings - Fork 1
Hooks
A slightly more complex situation is when you want to:
- provide the ability to autogenerate commit messages
- but also check the ones that were not generated by
mkcommit.
For this purpose standard Git Hooks could be used but for convenience we've created a way for you to run mkcommit as a hook.
Because this is a completely different paradigm of operation, apart from commit function you will have to define a separate on_commit function to define how you want to validate textual commit messages.
Existing suites like mkcommit.suites.semantic have a built-in set of on_commit validators.
from mkcommit import CommitMessage, to_stdout
from mkcommit.suites import semantic
def commit():
return CommitMessage(semantic.default_short(), semantic.default_long())
def on_commit(msg: CommitMessage):
semantic.is_semantic(msg.first_line)
semantic.has_short_commit_msg_proper_length(msg.first_line)
if __name__ == "__main__":
to_stdout(commit())-
semantic.is_semantic()simply checks whether the provided message is in the form of{keyword}({scope}): {description}. -
semantic.has_short_commit_msg_proper_lengthextracts the{description}portion from the original commit message and checks if it doesn't exceed 55 characters in length.
As always, you can use existing building blocks (validators in particular) to define your own validation steps for externally-fed commit messages in the on_commit definition. The pattern you should be using is raising a ValidationFailedException each time the commit message fails to comply with some particular rule:
from mkcommit import CommitMessage, to_stdout, ask, ValidationFailedException
from mkcommit.validators import validate_initials, matches
def initials_are_two_letter(s: str) -> bool:
if not validate_initials(2, 2)(s):
raise ValidationFailedException("initials")
else:
return True
def something_matches_blah(s: str) -> bool:
if not matches(r"blah")(s):
raise ValidationFailedException("something")
else:
return True
def commit() -> CommitMessage:
initials = ask("Your initials: ", check=initials_are_two_letter)
something = ask("Your something: ", check=something_matches_blah)
return CommitMessage(
f"{initials} | {something}"
)
def on_commit(commit_message: CommitMessage):
initials, something = commit_message.first_line.split(" | ")
initials_are_two_letter(initials)
something_matches_blah(something)
if __name__ == "__main__":
to_stdout(commit())In the example above, on_commit provides a validation procedure for a commit message that will be provided through the command line as input. Note how the two functions: initials_are_two_letter and something_matches_blah reuse the validation logic between the commit and on_commit definition. This pattern is strongly encouraged.
When mkcommit is called with -x "some commit message" argument, the provided commit message will be fed into on_commit function and validated according to your own definition of on_commit. Hence you can simply add the following call to the commit-msg Git Hook script in .git/hooks.
-
Create or open the exisitng
./.git/hooks/commit-msghook in your repo. -
Append the
mkcommithook call to the end of the file:#!/usr/bin/env bash mkcommit -x "$(cat $1)" --autoselect
Note: shebang (
#!/usr/bin/env bash) should be placed only at the very top of the file.-
--autoselectflag will automatically select the first found*.mkcommit.pyfile as the associated config. - You can use an explicit file path using the
-fflag, e.g.
#!/usr/bin/env bash mkcommit -x "$(cat $1)" -f ".mkcommit.py"
-
-
You might also need to run
chmod +x ./.git/hooks/commit-msgto mark the hook script as executable. If you're setting this up on Windows, make sure you run this command from within Git Bash at the root of your repository.
mkcommit -x "$(cat $1)" command will load the contents of the COMMIT_EDITMSG file (the path is under $1 in the commit-msg hook) where the commit message is stored, create a CommitMessage instance from raw input and feed this instance to the on_commit function.