Repository: relekang/python-semantic-release
Pull request #287 (2020-12-06 16:10:11)

New option: major_on_zero
The discussion is on #280 .

Set this option to `false`, it will avoid bump from 0.y.z to 1.0.0 automatically.  This is useful when develop in initial development stage.


Patch:
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 9de3f54..d0974d3 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -95,6 +95,23 @@ no tag in any commits since the last release.
 
 Default: `false`
 
+``major_on_zero``
+-----------------
+If this is set to `false`, semantic-release will create a new minor release
+instead of major release when current major version is zero.
+
+Quote from `Semantic Versioning Specification`_:
+
+  Major version zero (0.y.z) is for initial development. Anything MAY change at
+  any time. The public API SHOULD NOT be considered stable.
+
+.. _Semantic Versioning Specification: https://semver.org/spec/v2.0.0.html#spec-item-4
+
+If you do not want to bump version to 1.0.0 from 0.y.z automatically, you can
+set this option to `false`.
+
+Default: `true`.
+
 Commit Parsing
 ==============
 
diff --git a/semantic_release/cli.py b/semantic_release/cli.py
index 7b1d613..c570138 100644
--- a/semantic_release/cli.py
+++ b/semantic_release/cli.py
@@ -343,6 +343,7 @@ def main(**kwargs):
         "commit_message",
         "commit_parser",
         "patch_without_tag",
+        "major_on_zero",
         "upload_to_pypi",
         "version_source",
     ]:
diff --git a/semantic_release/defaults.cfg b/semantic_release/defaults.cfg
index 207ed9f..3a000b8 100644
--- a/semantic_release/defaults.cfg
+++ b/semantic_release/defaults.cfg
@@ -2,6 +2,7 @@
 minor_tag=:sparkles:
 fix_tag=:nut_and_bolt:
 patch_without_tag=false
+major_on_zero=true
 check_build_status=false
 hvcs=github
 commit_parser=semantic_release.history.angular_parser
diff --git a/semantic_release/history/logs.py b/semantic_release/history/logs.py
index 596d20a..575480c 100644
--- a/semantic_release/history/logs.py
+++ b/semantic_release/history/logs.py
@@ -71,6 +71,14 @@ def evaluate_version_bump(current_version: str, force: str = None) -> Optional[s
         bump = "patch"
         logger.debug(f"Changing bump level to patch based on config patch_without_tag")
 
+    if (
+        not config.get("major_on_zero")
+        and current_version.startswith("0.")
+        and bump == "major"
+    ):
+        bump = "minor"
+        logger.debug("Changing bump level to minor based on config major_on_zero")
+
     return bump
 
 
diff --git a/semantic_release/settings.py b/semantic_release/settings.py
index 85aea25..f052a7e 100644
--- a/semantic_release/settings.py
+++ b/semantic_release/settings.py
@@ -41,6 +41,7 @@ def _config_from_ini(paths):
         "check_build_status",
         "commit_version_number",
         "patch_without_tag",
+        "major_on_zero",
         "remove_dist",
         "upload_to_pypi",
         "upload_to_release",


Test patch:
diff --git a/tests/history/test_version_bump.py b/tests/history/test_version_bump.py
index 2bac38f..737643d 100644
--- a/tests/history/test_version_bump.py
+++ b/tests/history/test_version_bump.py
@@ -86,3 +86,11 @@ def test_should_return_none_without_commits():
 
     with mock.patch("semantic_release.history.config.get", lambda *x: False):
         assert evaluate_version_bump("1.1.0") is None
+
+
+@mock.patch(
+    "semantic_release.history.config.get", wrapped_config_get(major_on_zero=False)
+)
+@mock.patch("semantic_release.history.logs.get_commit_log", lambda *a, **kw: [MAJOR])
+def test_should_minor_without_major_on_zero():
+    assert evaluate_version_bump("0.1.0") == "minor"
diff --git a/tests/test_settings.py b/tests/test_settings.py
index f3c99ff..3d45c9a 100644
--- a/tests/test_settings.py
+++ b/tests/test_settings.py
@@ -36,6 +36,7 @@ class ConfigTests(TestCase):
         self.assertEqual(config.get("minor_tag"), ":sparkles:")
         self.assertEqual(config.get("fix_tag"), ":nut_and_bolt:")
         self.assertFalse(config.get("patch_without_tag"))
+        self.assertTrue(config.get("major_on_zero"))
         self.assertFalse(config.get("check_build_status"))
         self.assertEqual(config.get("hvcs"), "github")
         self.assertEqual(config.get("upload_to_pypi"), True)