diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 689d9282d1..3fbbfbddac 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,7 +1,7 @@
[bumpversion]
-current_version = 0.9.11
+current_version = 1.0.0b1
commit = True
tag = True
[bumpversion:file:commitizen/__version__.py]
-
+[bumpversion:file:pyproject.toml]
diff --git a/.codacy.yaml b/.codacy.yaml
new file mode 100644
index 0000000000..3a31bdb7be
--- /dev/null
+++ b/.codacy.yaml
@@ -0,0 +1,3 @@
+exclude_paths:
+ - 'tests/**'
+ - 'docs/**'
\ No newline at end of file
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000000..73998f1162
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,4 @@
+[flake8]
+exclude = .git,__pycache__,docs/source/conf.py,old,build,dist
+max-complexity = 10
+max-line-length = 100
diff --git a/.gitignore b/.gitignore
index 895048f74b..3ffcf22f07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,6 +45,7 @@ nosetests.xml
coverage.xml
*.cover
.hypothesis/
+.pytest_cache
# Translations
*.mo
@@ -104,3 +105,6 @@ venv.bak/
.mypy_cache/
.vscode/
+
+# build
+poetry.lock
\ No newline at end of file
diff --git a/Pipfile b/Pipfile
deleted file mode 100644
index 9292b4e7f5..0000000000
--- a/Pipfile
+++ /dev/null
@@ -1,17 +0,0 @@
-[[source]]
-url = "https://pypi.python.org/simple"
-verify_ssl = true
-name = "pypi"
-
-[dev-packages]
-ipython = "*"
-twine = "*"
-bumpversion = "*"
-black = "*"
-
-[packages]
-"delegator.py" = "*"
-whaaaaat = "*"
-
-[requires]
-python_version = "3.6"
diff --git a/Pipfile.lock b/Pipfile.lock
deleted file mode 100644
index 730ab08efb..0000000000
--- a/Pipfile.lock
+++ /dev/null
@@ -1,326 +0,0 @@
-{
- "_meta": {
- "hash": {
- "sha256": "15df7cc95b3b5001a688d346c6d53f5513563e14bcf53e5892757424dc35c27a"
- },
- "host-environment-markers": {
- "implementation_name": "cpython",
- "implementation_version": "3.6.3",
- "os_name": "posix",
- "platform_machine": "x86_64",
- "platform_python_implementation": "CPython",
- "platform_release": "4.13.0-1-amd64",
- "platform_system": "Linux",
- "platform_version": "#1 SMP Debian 4.13.4-2 (2017-10-15)",
- "python_full_version": "3.6.3",
- "python_version": "3.6",
- "sys_platform": "linux"
- },
- "pipfile-spec": 6,
- "requires": {
- "python_version": "3.6"
- },
- "sources": [
- {
- "name": "pypi",
- "url": "https://pypi.python.org/simple",
- "verify_ssl": true
- }
- ]
- },
- "default": {
- "delegator.py": {
- "hashes": [
- "sha256:2575c4adc923ad0b8fdaa433f862b2b7cf21982717fb23cc895fd8f249ea820c",
- "sha256:495e11ada66648650171a6c9a188df4eb050b235abff8771f41ee8a064eb9ded"
- ],
- "version": "==0.0.13"
- },
- "pexpect": {
- "hashes": [
- "sha256:f853b52afaf3b064d29854771e2db509ef80392509bde2dd7a6ecf2dfc3f0018",
- "sha256:3d132465a75b57aa818341c6521392a06cc660feb3988d7f1074f39bd23c9a92"
- ],
- "version": "==4.2.1"
- },
- "prompt-toolkit": {
- "hashes": [
- "sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4",
- "sha256:1df952620eccb399c53ebb359cc7d9a8d3a9538cb34c5a1344bdbeb29fbcc381",
- "sha256:858588f1983ca497f1cf4ffde01d978a3ea02b01c8a26a8bbc5cd2e66d816917"
- ],
- "version": "==1.0.15"
- },
- "ptyprocess": {
- "hashes": [
- "sha256:e8c43b5eee76b2083a9badde89fd1bbce6c8942d1045146e100b7b5e014f4f1a",
- "sha256:e64193f0047ad603b71f202332ab5527c5e52aa7c8b609704fc28c0dc20c4365"
- ],
- "version": "==0.5.2"
- },
- "pygments": {
- "hashes": [
- "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d",
- "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"
- ],
- "version": "==2.2.0"
- },
- "regex": {
- "hashes": [
- "sha256:78631e130c5cd5e1db2f9de6cc3383861ed1a6b511910244c35cd1d10a8bdcfe",
- "sha256:e20ceca05ca282d1b77740abc8fa162db4712a62844ff7b8de6444fa99863019",
- "sha256:9821defb1f4f0b3cef3d7dfd9366068df8463ecb994dfe95db1b0d2bc203cfd3",
- "sha256:43d5f80bee8c5cb98a95023f7f3769b5003040cebc361bc998e1edd65fb20dec",
- "sha256:f5be6cb2da3e1b549930401bd1d1559908f28bda3c6a0be54814e44edbe1a9c9",
- "sha256:7522b813a1efb93180bc12dde8d8886113cabed59e6023a3248ab3b1a52cdc3e",
- "sha256:184f7615881f55247d3b3bfd9bc157b3c037137b9658385278d3cf3d4fc31ca7",
- "sha256:dd03c7129eda818ca47719d75a0305bded34ac0087b25ceac959a515731fdd41",
- "sha256:015f34a520e34c6d592eab72891dc556b4a3de347f34f2434ac656b4cd9affe1",
- "sha256:4b93cf21ef93a9425760f0d03e65dbfd0796cab85b67b5fec822842bf290e19e",
- "sha256:1d34947bc23ab1710c2efad7d56b492fd812bc440fd9aca7a1436fb77742adab",
- "sha256:217257a2cbeb1599bd58acff2e4093cb38ed35ca28cc9b0185348ddf5bcee01b",
- "sha256:7080470e123a4ff138e4683f2c0bdfb44c86150748f730e39c5351eee21928e8",
- "sha256:ebba907aba65597226549bc576600b7257eb9b44a693e4a15a070ad1285112cf",
- "sha256:8cb9c053f53f2ad09e6400173ae065000e29320cab10897485b987114a106778",
- "sha256:4dd4125d13cd9b6457c17e24f3d44a28769b52308554402d701a973ab326dab8",
- "sha256:c25c8eae01b297881804296beed14cf20d42d41e1340fdb4ee90c3d565c10d9c",
- "sha256:bf4645f468e8e29f94e5b717e73adf3622c2eaaa3f2e502c5240b7f30549568f",
- "sha256:434ceb27bec894c03f383dbc3269e100d12c56c4f511ddbbc10f158c08c3c875"
- ],
- "version": "==2017.11.8"
- },
- "six": {
- "hashes": [
- "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
- "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
- ],
- "version": "==1.11.0"
- },
- "wcwidth": {
- "hashes": [
- "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c",
- "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e"
- ],
- "version": "==0.1.7"
- },
- "whaaaaat": {
- "hashes": [
- "sha256:cda3c149dc3e6dacdbc1f6d445ee8005c9fc540aa7d8b00c1553a6f7e37e917e"
- ],
- "version": "==0.5.2"
- }
- },
- "develop": {
- "bumpversion": {
- "hashes": [
- "sha256:6753d9ff3552013e2130f7bc03c1007e24473b4835952679653fb132367bdd57",
- "sha256:6744c873dd7aafc24453d8b6a1a0d6d109faf63cd0cd19cb78fd46e74932c77e"
- ],
- "version": "==0.5.3"
- },
- "certifi": {
- "hashes": [
- "sha256:244be0d93b71e93fc0a0a479862051414d0e00e16435707e5bf5000f92e04694",
- "sha256:5ec74291ca1136b40f0379e1128ff80e866597e4e2c1e755739a913bbc3613c0"
- ],
- "version": "==2017.11.5"
- },
- "chardet": {
- "hashes": [
- "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691",
- "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"
- ],
- "version": "==3.0.4"
- },
- "decorator": {
- "hashes": [
- "sha256:95a26b17806e284452bfd97fa20aa1e8cb4ee23542bda4dbac5e4562aa1642cd",
- "sha256:7cb64d38cb8002971710c8899fbdfb859a23a364b7c99dab19d1f719c2ba16b5"
- ],
- "version": "==4.1.2"
- },
- "enum34": {
- "hashes": [
- "sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79",
- "sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a",
- "sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1",
- "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850"
- ],
- "markers": "python_version == '2.7'",
- "version": "==1.1.6"
- },
- "idna": {
- "hashes": [
- "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4",
- "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f"
- ],
- "version": "==2.6"
- },
- "ipython": {
- "hashes": [
- "sha256:fcc6d46f08c3c4de7b15ae1c426e15be1b7932bcda9d83ce1a4304e8c1129df3",
- "sha256:51c158a6c8b899898d1c91c6b51a34110196815cc905f9be0fa5878e19355608"
- ],
- "version": "==6.2.1"
- },
- "ipython-genutils": {
- "hashes": [
- "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
- "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
- ],
- "version": "==0.2.0"
- },
- "jedi": {
- "hashes": [
- "sha256:3af518490ffcd00a3074c135b42511e081575e9abd115c216a34491411ceebb0",
- "sha256:f6d5973573e76b1fd2ea75f6dcd6445d02d41ff3af5fc61b275b4e323d1dd396"
- ],
- "version": "==0.11.0"
- },
- "parso": {
- "hashes": [
- "sha256:b573acb69f66a970197b5fdbbdfad3b8a417a520e383133b2b4e708f104bfc9a",
- "sha256:c5279916bb417aa2bf634648ff895cf35dce371d7319744884827bfad06f8d7b"
- ],
- "version": "==0.1.0"
- },
- "pathlib2": {
- "hashes": [
- "sha256:db3e43032d23787d3e9aec8c7ef1e0d2c3c589d5f303477661ebda2ca6d4bfba",
- "sha256:d32550b75a818b289bd4c1f96b60c89957811da205afcceab75bc8b4857ea5b3"
- ],
- "markers": "python_version in '2.6 2.7 3.2 3.3'",
- "version": "==2.3.0"
- },
- "pexpect": {
- "hashes": [
- "sha256:f853b52afaf3b064d29854771e2db509ef80392509bde2dd7a6ecf2dfc3f0018",
- "sha256:3d132465a75b57aa818341c6521392a06cc660feb3988d7f1074f39bd23c9a92"
- ],
- "version": "==4.2.1"
- },
- "pickleshare": {
- "hashes": [
- "sha256:c9a2541f25aeabc070f12f452e1f2a8eae2abd51e1cd19e8430402bdf4c1d8b5",
- "sha256:84a9257227dfdd6fe1b4be1319096c20eb85ff1e82c7932f36efccfe1b09737b"
- ],
- "version": "==0.7.4"
- },
- "pkginfo": {
- "hashes": [
- "sha256:31a49103180ae1518b65d3f4ce09c784e2bc54e338197668b4fb7dc539521024",
- "sha256:bb1a6aeabfc898f5df124e7e00303a5b3ec9a489535f346bfbddb081af93f89e"
- ],
- "version": "==1.4.1"
- },
- "prompt-toolkit": {
- "hashes": [
- "sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4",
- "sha256:1df952620eccb399c53ebb359cc7d9a8d3a9538cb34c5a1344bdbeb29fbcc381",
- "sha256:858588f1983ca497f1cf4ffde01d978a3ea02b01c8a26a8bbc5cd2e66d816917"
- ],
- "version": "==1.0.15"
- },
- "ptyprocess": {
- "hashes": [
- "sha256:e8c43b5eee76b2083a9badde89fd1bbce6c8942d1045146e100b7b5e014f4f1a",
- "sha256:e64193f0047ad603b71f202332ab5527c5e52aa7c8b609704fc28c0dc20c4365"
- ],
- "version": "==0.5.2"
- },
- "pygments": {
- "hashes": [
- "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d",
- "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"
- ],
- "version": "==2.2.0"
- },
- "requests": {
- "hashes": [
- "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b",
- "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
- ],
- "version": "==2.18.4"
- },
- "requests-toolbelt": {
- "hashes": [
- "sha256:42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237",
- "sha256:f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5"
- ],
- "version": "==0.8.0"
- },
- "scandir": {
- "hashes": [
- "sha256:913d0d04f3ea8f38a52a38e930a08deacd3643d71875a0751a5c01e006102998",
- "sha256:eb9d4a55bbeb0473a9c7d3ff81e12d44f0ad86daff48b02a95e2398c87ff1a00",
- "sha256:2b28d118b372de8950f85b65d8ddfd43643f139a5b721281dd6532bed6b8321c",
- "sha256:f14476800cfdd6809d5130840f78ca3c08aa25544113e2b33a0b2fe914583d69",
- "sha256:6db5aadb667bb709cc23921203e9c27f08225506a9b84b7ebe2b645dee47a4dd",
- "sha256:8129fe7b9211d080457e0ff87397d85bb9be6ebb482b6be6ad9700059ac2e516",
- "sha256:8fe782abf9314f2733c09d2191c1b3047475218ddbae90052b5c0f1a4215d5e2",
- "sha256:a93b6cc872eeccdc91b4c1c1e510820bee17f79c9455064fb8d3b73b51e52024",
- "sha256:9851e782da220073093da68b3451e3c33b10f84eca2aec17a24661c7c63357a2",
- "sha256:937d27e367af994afd3792904b794a82645ea9616dd336f5030e0b50e527eb57",
- "sha256:e0278a2d4bc6c0569aedbe66bf26c8ab5b2b08378b3289de49257f23ac624338"
- ],
- "markers": "python_version < '3.5'",
- "version": "==1.6"
- },
- "simplegeneric": {
- "hashes": [
- "sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173"
- ],
- "version": "==0.8.1"
- },
- "six": {
- "hashes": [
- "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
- "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
- ],
- "version": "==1.11.0"
- },
- "tqdm": {
- "hashes": [
- "sha256:733ce813ab83e17a03da34043c6265e29f6731e3cbbbe305b12694ced0af6770",
- "sha256:7ca803c2ea268c6bdb541e2dac74a3af23cf4bf7b4132a6a78926d255f8c8df1"
- ],
- "version": "==4.19.4"
- },
- "traitlets": {
- "hashes": [
- "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9",
- "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835"
- ],
- "version": "==4.3.2"
- },
- "twine": {
- "hashes": [
- "sha256:d3ce5c480c22ccfb761cd358526e862b32546d2fe4bc93d46b5cf04ea3cc46ca",
- "sha256:caa45b7987fc96321258cd7668e3be2ff34064f5c66d2d975b641adca659c1ab"
- ],
- "version": "==1.9.1"
- },
- "typing": {
- "hashes": [
- "sha256:349b1f9c109c84b53ac79ac1d822eaa68fc91d63b321bd9392df15098f746f53",
- "sha256:63a8255fe7c6269916baa440eb9b6a67139b0b97a01af632e7bd2842e1e02f15",
- "sha256:d514bd84b284dd3e844f0305ac07511f097e325171f6cc4a20878d11ad771849"
- ],
- "version": "==3.6.2"
- },
- "urllib3": {
- "hashes": [
- "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b",
- "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"
- ],
- "version": "==1.22"
- },
- "wcwidth": {
- "hashes": [
- "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c",
- "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e"
- ],
- "version": "==0.1.7"
- }
- }
-}
diff --git a/README.rst b/README.rst
index 99481f8149..a5fe93bba4 100644
--- a/README.rst
+++ b/README.rst
@@ -46,7 +46,7 @@ This client tool prompts the user with information about the commit.
Based on `conventional commits `_
-.. image:: docs/images/example.png
+.. image:: docs/images/demo.gif
:alt: Example running commitizen
This is an example of how the git messages history would look like:
@@ -245,8 +245,17 @@ things that won't work, like aliases of commands, so, to commit run:
cz commit
-Todo
-====
+Contributing
+============
-- [ ] auto changelog integration
-- [ ] tests
+
+1. Clone the repo.
+2. Add your modifications
+3. Run :code:`python setup.py test --pytest-args --cov=./commitizen`
+
+
+Testing
+========
+
+
+ pytest -s --cov-report term-missing --cov=commitizen tests/
\ No newline at end of file
diff --git a/commitizen/__init__.py b/commitizen/__init__.py
index 22d4f98578..4ff4b087b9 100644
--- a/commitizen/__init__.py
+++ b/commitizen/__init__.py
@@ -2,7 +2,6 @@
import logging
import logging.config
from commitizen.cz import registry
-from commitizen.cz.cz_base import BaseCommitizen # noqa
from commitizen.__version__ import __version__
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index 97f30d1228..a655b173e3 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = '0.9.11'
+__version__ = '1.0.0b1'
diff --git a/commitizen/cli.py b/commitizen/cli.py
index 0292ea20e5..a9be7e648c 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -3,6 +3,7 @@
import sys
import logging
import argparse
+from pathlib import Path
from configparser import RawConfigParser, NoSectionError
from commitizen import (
registered,
@@ -15,15 +16,8 @@
)
-req_version = (3,)
-cur_version = sys.version_info
logger = logging.getLogger(__name__)
-if cur_version > req_version:
- from pathlib import Path
-else:
- from pathlib2 import Path
-
def get_parser(config):
description = (
@@ -55,12 +49,7 @@ def get_parser(config):
lscz = subparser.add_parser("ls", help="show available commitizens")
lscz.set_defaults(func=registered)
- if cur_version > req_version:
- commit = subparser.add_parser("commit", aliases=["c"], help="create new commit")
- else:
- commit = subparser.add_parser("commit", help="create new commit")
- commit = subparser.add_parser("c", help="alias for commit")
-
+ commit = subparser.add_parser("commit", aliases=["c"], help="create new commit")
commit.set_defaults(func=run)
example = subparser.add_parser("example", help="show commit example")
@@ -78,10 +67,7 @@ def get_parser(config):
def load_cfg():
defaults = {"name": "cz_conventional_commits"}
config = RawConfigParser("")
- try:
- home = str(Path.home())
- except AttributeError:
- home = os.path.expanduser("~")
+ home = str(Path.home())
config_file = ".cz"
global_cfg = os.path.join(home, config_file)
diff --git a/commitizen/cz/__init__.py b/commitizen/cz/__init__.py
index 6d2e774b97..3c28dd7b71 100644
--- a/commitizen/cz/__init__.py
+++ b/commitizen/cz/__init__.py
@@ -1,17 +1,15 @@
import importlib
import pkgutil
-from commitizen.cz.cz_conventional_commits import ConventionalCommitsCz
-from commitizen.cz.cz_jira import JiraSmartCz
+from commitizen.cz.conventional_commits import ConventionalCommitsCz
+from commitizen.cz.jira import JiraSmartCz
registry = {
name: importlib.import_module(name).discover_this
- for finder, name, ispkg
- in pkgutil.iter_modules()
- if name.startswith('cz_')
+ for finder, name, ispkg in pkgutil.iter_modules()
+ if name.startswith("cz_")
}
-registry.update({
- 'cz_conventional_commits': ConventionalCommitsCz,
- 'cz_jira': JiraSmartCz
-})
+registry.update(
+ {"cz_conventional_commits": ConventionalCommitsCz, "cz_jira": JiraSmartCz}
+)
diff --git a/commitizen/cz/cz_base.py b/commitizen/cz/base.py
similarity index 80%
rename from commitizen/cz/cz_base.py
rename to commitizen/cz/base.py
index 2748ac5cda..0453fefa50 100644
--- a/commitizen/cz/cz_base.py
+++ b/commitizen/cz/base.py
@@ -3,25 +3,13 @@
import logging
import delegator
from abc import ABCMeta, abstractmethod
-from future.utils import with_metaclass
from tempfile import NamedTemporaryFile
-from whaaaaat import style_from_dict, Token, prompt
-
+from questionary import prompt
logger = logging.getLogger(__name__)
-class BaseCommitizen(with_metaclass(ABCMeta)):
-
- style = style_from_dict({
- Token.Separator: '#6C6C6C',
- Token.QuestionMark: '#FF9D00 bold',
- Token.Selected: '#5F819D',
- Token.Pointer: '#FF9D00 bold',
- Token.Instruction: '', # default
- Token.Answer: '#5F819D bold',
- Token.Question: '',
- })
+class BaseCommitizen(metaclass=ABCMeta):
@abstractmethod
def questions(self):
@@ -86,7 +74,7 @@ def show_info(self, *args, **kwargs):
def run(self, *args, **kwargs):
questions = self.questions()
- answers = prompt(questions, style=self.style)
+ answers = prompt(questions)
logger.debug('Answers:\n %s', answers)
m = self.message(answers)
logger.debug('Commit message generated:\n %s', m)
diff --git a/commitizen/cz/conventional_commits/__init__.py b/commitizen/cz/conventional_commits/__init__.py
new file mode 100644
index 0000000000..4ee406ca1a
--- /dev/null
+++ b/commitizen/cz/conventional_commits/__init__.py
@@ -0,0 +1 @@
+from .conventional_commits import ConventionalCommitsCz # noqa
diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
new file mode 100644
index 0000000000..6b79ffa061
--- /dev/null
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -0,0 +1,178 @@
+import os
+from commitizen.cz.base import BaseCommitizen
+
+__all__ = ["ConventionalCommitsCz"]
+
+
+class NoSubjectException(Exception):
+ ...
+
+
+def parse_scope(text):
+ if not text:
+ return ""
+
+ scope = text.strip().split()
+ if len(scope) == 1:
+ return scope[0]
+
+ return "-".join(scope)
+
+
+def parse_subject(text):
+ if isinstance(text, str):
+ text = text.strip(".").strip()
+
+ if not text:
+ raise NoSubjectException
+
+ return text
+
+
+class ConventionalCommitsCz(BaseCommitizen):
+ def questions(self):
+ questions = [
+ {
+ "type": "list",
+ "name": "prefix",
+ "message": "Select the type of change you are committing",
+ "choices": [
+ {
+ "value": "fix",
+ "name": "fix: A bug fix. Correlates with PATCH in SemVer",
+ },
+ {
+ "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",
+ "name": (
+ "style: Changes that do not affect the "
+ "meaning of the code (white-space, formatting,"
+ " missing semi-colons, etc)"
+ ),
+ },
+ {
+ "value": "refactor",
+ "name": (
+ "refactor: A code change that neither fixes "
+ "a bug nor adds a feature"
+ ),
+ },
+ {
+ "value": "perf",
+ "name": "perf: A code change that improves performance",
+ },
+ {
+ "value": "test",
+ "name": (
+ "test: Adding missing or correcting " "existing tests"
+ ),
+ },
+ {
+ "value": "build",
+ "name": (
+ "build: Changes that affect the build system or "
+ "external dependencies (example scopes: pip, docker, npm)"
+ ),
+ },
+ {
+ "value": "ci",
+ "name": (
+ "ci: Changes to our CI configuration files and "
+ "scripts (example scopes: GitLabCI)"
+ ),
+ },
+ ],
+ },
+ {
+ "type": "input",
+ "name": "scope",
+ "message": (
+ "Scope. Could be anything specifying place of the "
+ "commit change (users, db, poll):\n"
+ ),
+ "filter": parse_scope,
+ },
+ {
+ "type": "input",
+ "name": "subject",
+ "filter": parse_subject,
+ "message": (
+ "Subject. Concise description of the changes. "
+ "Imperative, lower case and no final dot:\n"
+ ),
+ },
+ {
+ "type": "input",
+ "name": "body",
+ "message": (
+ "Body. Motivation for the change and contrast this "
+ "with previous behavior:\n"
+ ),
+ },
+ {
+ "type": "input",
+ "name": "footer",
+ "message": (
+ "Footer. Information about Breaking Changes and "
+ "reference issues that this commit closes:\n"
+ ),
+ },
+ ]
+ return questions
+
+ def message(self, answers):
+ 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)
+ if body:
+ message += "\n\n{0}".format(body)
+ if footer:
+ message += "\n\n{0}".format(footer)
+ return message
+
+ def example(self):
+ 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"
+ )
+
+ def schema(self):
+ return (
+ "(): \n"
+ "\n"
+ "\n"
+ "\n"
+ "