diff --git a/CHANGELOG.md b/CHANGELOG.md index 55c0bafb..d1157f48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ **unreleased** **v1.0.2-dev** +- Added: expanded set of special character escapes and added documentation - Housekeeping: migrated from travis+appveyor to GitHub Actions for CI, thanks @clbarnes **v1.0.1** diff --git a/README.md b/README.md index 0e2fa552..f40437c1 100644 --- a/README.md +++ b/README.md @@ -472,6 +472,47 @@ search = MY_VERSION="{current_version}" replace = MY_VERSION="{new_version}" ``` +### Configuration file -- Special characters + +Some characters have a special meaning in the config file and may need to be +escaped if you want to use them as part of values. Examples are comment +characters `#` and `;`, as well as whitespace characters. Leading whitespace +characters are dropped, and comment characters at the beginning of a line make +the whole line a comment. + +When you do need to express these characters at the beginning of a line, you can +use the special variables `{#}`, `{;}`, `{space}` and `{tab}` respectively. + +For example, given the following `version.json`: +``` +{ + "name": "mypackage", + "version": "1.0.0", + "dependencies": [ + { + "name": "mydependency", + "version": "1.0.0" + } + ] +} +``` + +Suppose we want to match the version for "mypackage", but not the version of any +dependency. This could be specified as follows: +```ini +[bumpversion] +current_version = 1.0.0 +# leading spaces are dropped: use {space} instead for at least the first space +# we want to match on each line +search = "name": "mypackage", + {space}{space}"version": "{current_version}" +replace = "name": "mypackage", + {space}{space}"version": "{new_version}" + +[bumpversion:file:version.json] +``` + + ## Command-line Options Most of the configuration values above can also be given as an option on the command-line. diff --git a/bumpversion/cli.py b/bumpversion/cli.py index d627d702..5935da75 100644 --- a/bumpversion/cli.py +++ b/bumpversion/cli.py @@ -57,7 +57,11 @@ logger_list = logging.getLogger("bumpversion.list") logger = logging.getLogger(__name__) time_context = {"now": datetime.now(), "utcnow": datetime.utcnow()} -special_char_context = {c: c for c in ("#", ";")} +special_char_context = { + **{c: c for c in ("#", ";")}, + "space": " ", + "tab": "\t", +} OPTIONAL_ARGUMENTS_THAT_TAKE_VALUES = [ diff --git a/tests/test_cli.py b/tests/test_cli.py index fbbd38d8..a7b9dafb 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2392,3 +2392,90 @@ def test_independent_falsy_value_in_config_does_not_bump_independently(tmpdir): main(['major']) assert '3.0.0-0' == tmpdir.join("VERSION").read() + + +@pytest.mark.parametrize( + "char, var", + [ + ("#", "#"), + (";", ";"), + (" ", "space"), + ("\t", "tab"), + ], +) +def test_special_char_escapes(tmpdir, char, var): + """ + Verify behavior of escapes (through string format variables) for characters that have a special meaning in the .cfg file + syntax when used in a value. + """ + tmpdir.join("VERSION").write( + dedent( + """ + 1.0.0 + %s1.0.0 + """.lstrip("\n") % char + ) + ) + tmpdir.join(".bumpversion.cfg").write( + dedent( + """ + [bumpversion] + current_version: 1.0.0 + search = {%s}{current_version} + replace = {%s}{new_version} + + [bumpversion:file:VERSION] + """ % (var, var) + ) + ) + tmpdir.chdir() + main(["major"]) + assert tmpdir.join("VERSION").read() == dedent( + # assert that first line did not match while second did + """ + 1.0.0 + %s2.0.0 + """.lstrip("\n") % char + ) + + +def test_special_characters_docs(tmpdir): + """ + Verify that special characters snippets in the documentation works as advertized. + """ + def json_for_version(version): + return dedent( + """ + { + "name": "mypackage", + "version": "%s", + "dependencies": [ + { + "name": "mydependency", + "version": "1.0.0" + } + ] + } + """.lstrip("\n") % version + ) + + tmpdir.join("version.json").write(json_for_version("1.0.0")) # same version as mydependency + tmpdir.join(".bumpversion.cfg").write( + dedent( + """ + [bumpversion] + current_version = 1.0.0 + # leading spaces are dropped: use {space} instead for at least the first space + # we want to match on each line + search = "name": "mypackage", + {space}{space}"version": "{current_version}" + replace = "name": "mypackage", + {space}{space}"version": "{new_version}" + + [bumpversion:file:version.json] + """ + ) + ) + tmpdir.chdir() + main(["major"]) + assert tmpdir.join("version.json").read() == json_for_version("2.0.0")