Skip to content

Fix/16 move breaking change to body #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions commitizen/commands/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ def __call__(self):
current_version, increment, prerelease=prerelease
)
new_tag_version = bump.create_tag(new_version, tag_format=tag_format)
message = f"Bump version {current_version} → {new_version}"
message = f"bump: version {current_version} → {new_version}"

# Report found information
out.write(message)
out.write(f"Tag to create: {new_tag_version}")
out.write(f"Increment detected: {increment}")
out.write(f"tag to create: {new_tag_version}")
out.write(f"increment detected: {increment}")

# Do not perform operations over files or git.
if dry_run:
Expand Down
28 changes: 26 additions & 2 deletions commitizen/commands/commit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
from commitizen import factory
import questionary
from commitizen import factory, out, git


NO_ANSWERS = 5
COMMIT_ERROR = 6


class Commit:
Expand All @@ -9,4 +14,23 @@ def __init__(self, config: dict, *args):
self.cz = factory.commiter_factory(self.config)

def __call__(self):
self.cz.run()
cz = self.cz
questions = cz.questions()
answers = questionary.prompt(questions)
if not answers:
raise SystemExit(NO_ANSWERS)
m = cz.message(answers)

c = git.commit(m)

if c.err:
out.error(c.err)
raise SystemExit(COMMIT_ERROR)

if "nothing added" in c.out or "no changes added to commit" in c.out:
out.error(c.out)
elif c.err:
out.error(c.err)
else:
out.write(c.out)
out.success("Commit successful!")
4 changes: 2 additions & 2 deletions commitizen/commands/example.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from commitizen import factory
from commitizen import factory, out


class Example:
Expand All @@ -9,4 +9,4 @@ def __init__(self, config: dict, *args):
self.cz = factory.commiter_factory(self.config)

def __call__(self):
self.cz.show_example()
out.write(self.cz.example())
4 changes: 2 additions & 2 deletions commitizen/commands/info.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from commitizen import factory
from commitizen import factory, out


class Info:
Expand All @@ -9,4 +9,4 @@ def __init__(self, config: dict, *args):
self.cz = factory.commiter_factory(self.config)

def __call__(self):
self.cz.show_info()
out.write(self.cz.info())
4 changes: 2 additions & 2 deletions commitizen/commands/schema.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from commitizen import factory
from commitizen import factory, out


class Schema:
Expand All @@ -9,4 +9,4 @@ def __init__(self, config: dict, *args):
self.cz = factory.commiter_factory(self.config)

def __call__(self):
self.cz.show_schema()
out.write(self.cz.schema())
90 changes: 10 additions & 80 deletions commitizen/cz/base.py
Original file line number Diff line number Diff line change
@@ -1,96 +1,26 @@
import sys
import logging

from commitizen import out, git
from abc import ABCMeta, abstractmethod
from questionary import prompt

logger = logging.getLogger(__name__)


class BaseCommitizen(metaclass=ABCMeta):
def __init__(self, config: dict):
self.config = config

@abstractmethod
def questions(self):
"""Questions regarding the commit message.

Must have 'whaaaaat' format.
More info: https://github.com/finklabs/whaaaaat/

:rtype: list
"""
def questions(self) -> list:
"""Questions regarding the commit message."""

@abstractmethod
def message(self, answers):
"""Format your git message.

:param answers: Use answers
:type answers: dict
def message(self, answers: dict) -> str:
"""Format your git message."""

:rtype: string
"""

def commit(self, message: str):
c = git.commit(message)
# f = NamedTemporaryFile("wb", delete=False)
# f.write(message.encode("utf-8"))
# f.close()

# c = cmd.run(f"git commit -F {f.name}")
# os.unlink(f.name)
return c

def example(self):
"""Example of the commit message.

:rtype: string
"""
def example(self) -> str:
"""Example of the commit message."""
raise NotImplementedError("Not Implemented yet")

def schema(self):
"""Schema definition of the commit message.

:rtype: string
"""
def schema(self) -> str:
"""Schema definition of the commit message."""
raise NotImplementedError("Not Implemented yet")

def info(self):
"""Information about the standardized commit message.

:rtype: string
"""
def info(self) -> str:
"""Information about the standardized commit message."""
raise NotImplementedError("Not Implemented yet")

def show_example(self, *args, **kwargs):
out.write(self.example())

def show_schema(self, *args, **kwargs):
out.write(self.schema())

def show_info(self, *args, **kwargs):
out.write(self.info())

def run(self, *args, **kwargs):
questions = self.questions()
answers = prompt(questions)
logger.debug("Answers:\n %s", answers)
m = self.message(answers)
logger.debug("Commit message generated:\n %s", m)

c = self.commit(m)

if c.err:
logger.warning(c.err)
sys.exit(1)

if "nothing added" in c.out or "no changes added to commit" in c.out:
out.error(c.out)
elif c.err:
out.error(c.err)
else:
out.write(c.out)
out.success("Commit successful!")

sys.exit(0)
57 changes: 28 additions & 29 deletions commitizen/cz/conventional_commits/conventional_commits.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def parse_subject(text):


class ConventionalCommitsCz(BaseCommitizen):
def questions(self):
def questions(self) -> list:
questions = [
{
"type": "list",
Expand All @@ -45,13 +45,6 @@ def questions(self):
"value": "feat",
"name": "feat: A new feature. Correlates with MINOR in SemVer",
},
{
"value": "BREAKING CHANGE",
"name": (
"BREAKING CHANGE: introduces a breaking API change. "
"Correlates with MAJOR in SemVer"
),
},
{"value": "docs", "name": "docs: Documentation only changes"},
{
"value": "style",
Expand Down Expand Up @@ -112,6 +105,12 @@ def questions(self):
"Imperative, lower case and no final dot:\n"
),
},
{
"type": "confirm",
"message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer",
"name": "is_breaking_change",
"default": False,
},
{
"type": "input",
"name": "body",
Expand All @@ -131,46 +130,46 @@ def questions(self):
]
return questions

def message(self, answers):
def message(self, answers: dict) -> str:
prefix = answers["prefix"]
scope = answers["scope"]
subject = answers["subject"]
body = answers["body"]
footer = answers["footer"]
message = ""

if prefix:
message += "{0}".format(prefix)
if scope:
message += "({0})".format(scope)
message += ": "
if subject:
message += "{0}".format(subject)
is_breaking_change = answers["is_breaking_change"]

if scope:
scope = f"({scope})"
if is_breaking_change:
body = f"BREAKING CHANGE: {body}"
if body:
message += "\n\n{0}".format(body)
body = f"\n\n{body}"
if footer:
message += "\n\n{0}".format(footer)
footer = f"\n\n{footer}"

message = f"{prefix}{scope}: {subject}{body}{footer}"

return message

def example(self):
def example(self) -> str:
return (
"feat($injector): ability to load new modules after bootstrapping\n"
"\nThe new method `$injector.loadNewModules(modules)` will add "
"each of the\ninjectables to the injector and execute all of the "
"config and run blocks\nfor each module passed to the method.\n"
"\nCloses #324"
"fix: correct minor typos in code\n"
"\n"
"see the issue for details on the typos fixed\n"
"\n"
"closes issue #12"
)

def schema(self):
def schema(self) -> str:
return (
"<type>(<scope>): <subject>\n"
"<BLANK LINE>\n"
"<body>\n"
"(BREAKING CHANGE: )<body>\n"
"<BLANK LINE>\n"
"<footer>"
)

def info(self):
def info(self) -> str:
dir_path = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(dir_path, "conventional_commits_info.txt")
with open(filepath, "r") as f:
Expand Down
39 changes: 25 additions & 14 deletions commitizen/cz/conventional_commits/conventional_commits_info.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
Commit Message Format
The commit contains the following structural elements, to communicate
intent to the consumers of your library:

Each commit message consists of a header, a body and a footer. The header has a special format that
includes a type, a scope and a subject:
fix: a commit of the type fix patches a bug in your codebase
(this correlates with PATCH in semantic versioning).

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
feat: a commit of the type feat introduces a new feature to the codebase
(this correlates with MINOR in semantic versioning).

The header is mandatory and the scope of the header is optional.
BREAKING CHANGE: a commit that has the text BREAKING CHANGE: at the beginning of
its optional body or footer section introduces a breaking API change
(correlating with MAJOR in semantic versioning).
A BREAKING CHANGE can be part of commits of any type.

Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on GitHub as well as in various git tools.
Others: commit types other than fix: and feat: are allowed,
like chore:, docs:, style:, refactor:, perf:, test:, and others.

Footer should contain a closing reference to an issue if any.
We also recommend improvement for commits that improve a current
implementation without adding a new feature or fixing a bug.

This leads to more readable messages that are easy to follow when looking through the project history.
But also, the git commit messages can be used to generate the changelog.
Notice these types are not mandated by the conventional commits specification,
and have no implicit effect in semantic versioning (unless they include a BREAKING CHANGE).

A scope may be provided to a commit’s type, to provide additional contextual
information and is contained within parenthesis, e.g., feat(parser): add ability to parse arrays.

<type>[optional scope]: <description>

[optional body]

[optional footer]
6 changes: 3 additions & 3 deletions commitizen/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ def commit(message: str, args=""):

def get_commits(start: str, end: str = "HEAD", from_beginning: bool = False) -> list:

c = cmd.run(f"git log --pretty=format:%s {start}...{end}")
c = cmd.run(f"git log --pretty=format:%s%n%b {start}...{end}")

if from_beginning:
c = cmd.run(f"git log --pretty=format:%s {end}")
c = cmd.run(f"git log --pretty=format:%s%n%b {end}")

if not c.out:
return []

print(c.out)
return c.out.split("\n")


Expand Down
Loading