Skip to content
Krzysztof Czarnecki edited this page Oct 29, 2021 · 5 revisions

Suites

You can define and even propose new suites to be incorporated into mkcommit. We provide e.g. the semantic commits suite by default.

Using Semantic Commits

The most basic example creates a default short commit message which consists of {keywords}({scope}): {message}, e.g. feat: blah blah or fix(some scope): blah blah.

from mkcommit import CommitMessage, to_stdout
from mkcommit.suites import semantic

def commit():
    return CommitMessage(semantic.default_short(), semantic.default_long())

if __name__ == "__main__":
    to_stdout(commit())

You can mix and match with the building blocks, for example a message with longer length allowed:

from mkcommit import CommitMessage, to_stdout, CommaSeparatedList, ask
from mkcommit.validators import max_len
from mkcommit.suites import semantic

def commit():
    keywords = CommaSeparatedList(*semantic.ask_keywords())
    short = ask("Short commit message: ", check=max_len(70))

    return CommitMessage(
        f"{keywords}: {short}"
    )

if __name__ == "__main__":
    to_stdout(commit())

Using Conventional Commits

Conventional commits are the most widely used in the open-source community.

Conventional commits suite extends the definition of semantic suite and disallows having multiple descriptors in a commit message. For example feat(scope), fix: message is valid in semantic suite but not in conventional suite.

It also extends the definition of the possible keywords to choose from.

It is recommended as the first sensible default for new projects.

from mkcommit import CommitMessage, to_stdout
from mkcommit.suites import conventional

def commit():
    return CommitMessage(*conventional.default())

if __name__ == "__main__":
    to_stdout(commit())

Note *conventional.default() notation unpacks the short and the long commit message portions into the CommitMessage wrapper.

Using Technica Commits

At Technica Engineering we're using a modified semantic suite, where it will almost always be prepended by a Jira Ticket identifier and committer's initials. If your git config is set up properly your initials are automatically injected from git config. The typical config used at Technica looks like this:

from mkcommit import CommitMessage, to_stdout, Project
from mkcommit.suites import technica

def commit():
    project = Project("Some project", "SOMEPROJ")

    return CommitMessage(
        technica.default_short(project, ticket_first=True),
        technica.default_long()
    )

def on_commit(msg: CommitMessage):
    technica.is_technica(msg.first_line)

if __name__ == "__main__":
    to_stdout(commit())

The message looks typically like [SOMEPROJ-123/KrCz] feat: something. Since many teams invert the order of these two elements in the beginning we provide the ticket_first flag which is by default False. If you remove it from the example above, you'll end up with messages like: [KrCz/SOMEPROJ-123] feat: something.

The ticket number will be prefilled with SOMEPROJ- and the developer only needs to type in the integers of the Jira Ticket number for an associated project.

Creating and Proposing Suites

When you want to propose a new suite, open a GitHub issue. In general a suite should define two functions:

  • default_short - to generate a default short message with the guidelines fully compliant with the suite.
  • default_long - to generage a default long message.

Intermediate functions should follow the pattern of cross-calling ask at some point. For example from our own semantic module:

ask_keywords = lambda: ask(
    "Select one or more keywords applicable (use TAB): ",
    one_or_more=semantic_commit_keywords
)

ask_scope = lambda: ask(
    "(Optional) provide change scope: "
)

ask_short_commit_msg = lambda: ask(
    "Provide the short commit msg, max 55 characters long: ",
    check=max_len(55)
)
Clone this wiki locally