diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aa7acc..8518113 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - New add `--release-num` to increment the `alphaN`/`betaN`/`a0`/`b0`/etc. release number - Fix gitlab #8: Push tags only pushed tags, not actual commit. - Fix gitlab #9: Make commit message configurable. + - Switch main repo from gitlab to github. ## v201907.0036 diff --git a/README.md b/README.md index d02232c..3fa315a 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,7 @@ # [PyCalVer: Automatic Calendar Versioning][repo_ref] -PyCalVer is a cli tool to search and replace version strings in the files of -your project. - -By default PyCalVer uses a format that looks like this: -`v201812.0123-beta`, but it can be configured to generate version strings -in many formats, including SemVer and other CalVer variants. - +PyCalVer is a CLI-tool to search and replace version strings in your project files. This project follows the pattern conventions from [calver.org][calver_org_ref]. Project/Repo: @@ -73,18 +67,15 @@ Code Quality/CI: The fastest way to setup a project is to use `pycalver init`. - ```shell $ pip install pycalver ... Installing collected packages: click pathlib2 typing toml pycalver -Successfully installed pycalver-202007.36 - -$ pycalver --version -pycalver, version v202007.0036 +Successfully installed pycalver-202009.1037 $ cd myproject -~/myproject$ pycalver init --dry +~/myproject/ +$ pycalver init --dry WARNING - File not found: pycalver.toml Exiting because of '--dry'. Would have written to pycalver.toml: @@ -106,14 +97,16 @@ Exiting because of '--dry'. Would have written to pycalver.toml: ] ``` -If you already have a `setup.cfg` file, the `init` sub-command will write to that -instead. +If you already have a `setup.cfg` file, the `init` sub-command will +write to that instead. ``` -~/myproject$ ls +~/myproject +$ ls README.md setup.cfg setup.py -~/myproject$ pycalver init +~/myproject +$ pycalver init WARNING - Couldn't parse setup.cfg: Missing [pycalver] section. Updated setup.cfg ``` @@ -150,14 +143,14 @@ make. ```ini [pycalver:file_patterns] setup.cfg = - current_version = {pycalver} + current_version = {version} setup.py = - version="{pep440_pycalver}" + version="{pep440_version}" src/mymodule_v*/__init__.py = - __version__ = "{pycalver}" + __version__ = "{version}" README.md = - [PyCalVer {calver}{build}{release}] - img.shields.io/static/v1.svg?label=PyCalVer&message={pycalver}&color=blue + [CalVer {version}] + img.shields.io/static/v1.svg?label=CalVer&message={version}&color=blue ``` To see if a pattern is found, you can use `pycalver bump --dry`, which will @@ -202,8 +195,8 @@ If there is no match for a pattern, bump will report an error. ```shell $ pycalver bump --dry --no-fetch -INFO - Old Version: v201901.0001-beta -INFO - New Version: v201902.0002-beta +INFO - Old Version: v201901.1001-beta +INFO - New Version: v201902.1002-beta ERROR - No match for pattern 'img.shields.io/static/v1.svg?label=PyCalVer&message={pycalver}&color=blue' ERROR - Pattern compiles to regex 'img\.shields\.io/static/v1\.svg\?label=PyCalVer&message=(?Pv(?P\d{4})(?P(?:0[0-9]|1[0-2]))\.(?P\d{4,})(?:-(?P (?:alpha|beta|dev|rc|post|final)))?)&color=blue' @@ -368,7 +361,7 @@ that shields.io parses the two "-" dashes before `beta` as one literal "-"): ``` -https://img.shields.io/badge/myproject-v201812.0116--beta-blue.svg +https://img.shields.io/badge/myproject-v202010.1116--beta-blue.svg ``` While you could use the following pattern, which will work fine for a @@ -376,20 +369,20 @@ while: ```ini README.md = - /badge/myproject-v{year}{month}.{build_no}--{release_tag}-blue.svg + /badge/myproject-{vYYYY0M.BUILD[--RELEASE]}-blue.svg ``` Eventually this will break, when you do a `final` release, at which point the following will be put in your README.md: ``` -https://img.shields.io/badge/myproject-v201812.0117--final-blue.svg +https://img.shields.io/badge/myproject-v202010.1117--final-blue.svg ``` When what you probably wanted was this (with the `--final` tag omitted): ``` -https://img.shields.io/badge/myproject-v201812.0117-blue.svg +https://img.shields.io/badge/myproject-v202010.1117-blue.svg ``` ### Examples @@ -397,18 +390,21 @@ https://img.shields.io/badge/myproject-v201812.0117-blue.svg The easiest way to test a pattern is with the `pycalver test` sub-command. ```shell -$ pycalver test 'v18w01' 'v{yy}w{iso_week}' +$ pycalver test 'v18w01' 'vYYw0W' New Version: v19w06 PEP440 : v19w06 -$ pycalver test 'v18.01' 'v{yy}w{iso_week}' +# TODO (mb 2020-09-24): Update regexp pattern + +$ pycalver test 'v18.01' 'vYYw0W' ERROR - Invalid version string 'v18.01' for pattern - 'v{yy}w{iso_week}'/'v(?P\d{2})w(?P(?:[0-4]\d|5[0-3]))' -ERROR - Invalid version 'v18.01' and/or pattern 'v{yy}w{iso_week}'. + 'vYYw0W'/'v(?P\d{2})w(?P<0W>(?:[0-4]\d|5[0-2]))' +ERROR - Invalid version 'v18.01' and/or pattern 'vYYw0W'. ``` -As you can see, each pattern is internally translated to a regular -expression. +As you can see, each pattern is internally translated to a regular expression. +All version strings in your project must match either this regular expression or +the corresponding regular expression for the PEP440 version string. The `pycalver test` sub-command accepts the same cli flags as `pycalver bump` to update the components that are not updated automatically (eg. @@ -474,15 +470,8 @@ The current version that will be bumped is defined either as supported VCS), the value of `pycalver.current_version` in `setup.cfg` / `pyproject.toml` / `pycalver.toml`. -As part of doing `pycalver bump`, your local VCS index is updated using -`git fetch --tags`/`hg pull`. This reduces the risk that some tags are -unknown locally and makes it less likely that the same version string is -generated for different commits, which would result in an ambiguous version -tag. This can happen if multiple maintainers produce a release at the same -time or if a build system is triggered multiple times and multiple builds -run concurrently to each other. For a small project (with only one -maintainer and no build system) this is a non-issue and you can always use -`-n/--no-fetch` to skip updating the tags. +As part of doing `pycalver bump` and `pycalver show`, your local VCS +index is updated using `git fetch --tags`/`hg pull`. ```shell $ time pycalver show --verbose @@ -499,6 +488,20 @@ $ time pycalver show --verbose --no-fetch real 0m0,840s ``` +Here we see that: + +- The VCS had a newer version than we had locally. +- It took 4 seconds to fetch the tags from the remote repository. + +This approach reduces the risk that new tags are unknown locally and makes it +less likely that the same version string is generated for different commits, +which would result in an ambiguous version tag. This can happen if multiple +maintainers produce a release at the same time or if a build system is triggered +multiple times and multiple builds run concurrently to each other. + +For a small project (with only one maintainer and no build system) this is a +non-issue and you can always use `-n/--no-fetch` to skip updating the tags. + ### Bump It Up @@ -745,115 +748,12 @@ with extra zeros (see [Lexical Ids](#lexical-ids) ). ### Lexical Ids -The build number padding may eventually be exhausted. In order to preserve -lexical ordering, build numbers for the `{build_no}` pattern are -incremented in a special way. Examples will perhaps illustrate more -clearly. - -```python -"0001" -"0002" -"0003" -... -"0999" -"11000" -"11001" -... -"19998" -"19999" -"220000" -"220001" -``` - -What is happening here is that the left-most digit is incremented -early/preemptively. Whenever the left-most digit would change, the padding -of the id is expanded through a multiplication by 11. - -```python ->>> prev_id = "0999" ->>> num_digits = len(prev_id) ->>> num_digits -4 ->>> prev_int = int(prev_id, 10) ->>> prev_int -999 ->>> maybe_next_int = prev_int + 1 ->>> maybe_next_int -1000 ->>> maybe_next_id = f"{maybe_next_int:0{num_digits}}" ->>> maybe_next_id -"1000" ->>> is_padding_ok = prev_id[0] == maybe_next_id[0] ->>> is_padding_ok -False ->>> if is_padding_ok: -... # normal case -... next_id = maybe_next_id -... else: -... # extra padding needed -... next_int = maybe_next_int * 11 -... next_id = str(next_int) ->>> next_id -"11000" -``` - -This behaviour ensures that the following semantic is always preserved: -`new_version > old_version`. This will be true, regardless of padding -expansion. To illustrate the issue this solves, consider what would happen -if we did not expand the padding and instead just incremented numerically. - -```python -"0001" -"0002" -"0003" -... -"0999" -"1000" -... -"9999" -"10000" -``` - -Here we eventually run into a build number where the lexical ordering is -not preserved, since `"10000" > "9999" == False` (because the string `"1"` -is lexically smaller than `"9"`). With large enough padding this may be a -non issue, but it's better to not have to think about it. - -Just as an example of why lexical ordering is a nice property to have, -there are lots of software which read git tags, but which have no logic to -parse version strings. This software can nonetheless order the version tags -correctly using commonly available lexical ordering. At the most basic -level it can allow you to use the UNIX `sort` command, for example to parse VCS tags. -```shell -$ printf "v0.9.0\nv0.10.0\nv0.11.0\n" | sort -v0.10.0 -v0.11.0 -v0.9.0 - -$ printf "v0.9.0\nv0.10.0\nv0.11.0\n" | sort -n -v0.10.0 -v0.11.0 -v0.9.0 - -$ printf "0998\n0999\n11000\n11001\n11002\n" | sort -0998 -0999 -11000 -11001 -11002 -``` This sorting even works correctly in JavaScript! -``` -> var versions = ["11002", "11001", "11000", "0999", "0998"]; -> versions.sort(); -["0998", "0999", "11000", "11001", "11002"] -``` - ## Semantics of PyCalVer @@ -1001,7 +901,7 @@ release *only* if 1. no `final` release is available 2. the `--pre` flag is explicitly used, or 3. if the requirement specifier _explicitly_ includes the version number of a - pre release, eg. `pip install mypkg==v201812.0007-alpha`. + pre release, eg. `pip install mypkg==v202009.1007-alpha`. Should a release include a bug (heaven forbid and despite all precautions), then the maintainer should publish a new release which either fixes the bug @@ -1012,20 +912,20 @@ package which included the bug, they only have to do `pip install --upgrade Perhaps a timeline will illustrate more clearly: ``` -v201812.0665 # last stable release -v201812.0666-beta # pre release for testers -v201901.0667 # final release after testing +v202008.1665 # last stable release +v202008.1666-beta # pre release for testers +v201901.1667 # final release after testing -# bug is discovered which effects v201812.0666-beta and v201901.0667 +# bug is discovered which effects v202008.1666-beta and v201901.1667 -v201901.0668-beta # fix is issued for testers -v201901.0669 # fix is issued everybody +v201901.1668-beta # fix is issued for testers +v201901.1669 # fix is issued everybody # Alternatively, revert before fixing -v201901.0668 # same as v201812.0665 -v201901.0669-beta # reintroduce change from v201812.0666-beta + fix -v201901.0670 # final release after testing +v201901.1668 # same as v202008.1665 +v201901.1669-beta # reintroduce change from v202008.1666-beta + fix +v201901.1670 # final release after testing ``` In the absolute worst case, a change is discovered to break backward @@ -1048,18 +948,18 @@ package will perhaps have 99% overlap to the previous one and the old one may eventually be abandoned. ``` -mypkg v201812.0665 # last stable release -mypkg v201812.0666-rc # pre release for testers -mypkg v201901.0667 # final release after testing period +mypkg v202008.1665 # last stable release +mypkg v202008.1666-rc # pre release for testers +mypkg v201901.1667 # final release after testing period -# bug is discovered in v201812.0666-beta and v201901.0667 +# bug is discovered in v202008.1666-beta and v201901.1667 -mypkg v201901.0668 # same as v201812.0665 +mypkg v201901.1668 # same as v202008.1665 # new package is created with compatibility breaking code -mypkg2 v201901.0669 # same as v201901.0667 -mypkg v201901.0669 # updated readme, declaring support +mypkg2 v201901.1669 # same as v201901.1667 +mypkg v201901.1669 # updated readme, declaring support # level for mypkg, pointing to mypgk2 # and documenting how to upgrade. ``` @@ -1116,6 +1016,8 @@ of the software as a whole, it is metadata about a particular release artifact of a package, eg. a `.whl` file. +[calver_org_ref]: https://calver.org/ + [repo_ref]: https://gitlab.com/mbarkhau/pycalver [setuptools_ref]: https://setuptools.readthedocs.io/en/latest/setuptools.html#specifying-your-project-s-version @@ -1124,6 +1026,8 @@ artifact of a package, eg. a `.whl` file. [pep_440_ref]: https://www.python.org/dev/peps/pep-0440/ +[pep_440_normalzation_ref]: https://www.python.org/dev/peps/pep-0440/#id31 + [zeno_1_dot_0_ref]: http://sedimental.org/designing_a_version.html#semver-and-release-blockage [pep_101_ref]: https://www.python.org/dev/peps/pep-0101/ diff --git a/setup.py b/setup.py index 5ad112f..18477ea 100644 --- a/setup.py +++ b/setup.py @@ -80,8 +80,8 @@ setuptools.setup( license="MIT", author="Manuel Barkhau", author_email="mbarkhau@gmail.com", - url="https://gitlab.com/mbarkhau/pycalver", - version="202007.36", + url="https://github.com/mbarkhau/pycalver", + version="202007.1036", keywords="version versioning bumpversion calver", description="CalVer for python libraries.", long_description=long_description, diff --git a/src/pycalver/__main__.py b/src/pycalver/__main__.py index 0c28381..04ae1f6 100755 --- a/src/pycalver/__main__.py +++ b/src/pycalver/__main__.py @@ -241,7 +241,7 @@ def _bump( try: vcs_api = vcs.get_vcs_api() except OSError: - logger.warning("Version Control System not found, aborting commit.") + logger.warning("Version Control System not found, skipping commit.") filepaths = set(cfg.file_patterns.keys()) diff --git a/src/pycalver/v1patterns.py b/src/pycalver/v1patterns.py index a3ace0c..8569674 100644 --- a/src/pycalver/v1patterns.py +++ b/src/pycalver/v1patterns.py @@ -160,7 +160,7 @@ FULL_PART_FORMATS = { 'release_tag' : "{tag}", 'build' : ".{bid}", # NOTE (mb 2019-01-04): since release is optional, it - # is treates specially in version.format + # is treated specially in v1version.format_version # 'release' : "-{tag}", 'month' : "{month:02}", 'month_short': "{month}", diff --git a/test/test_cli.py b/test/test_cli.py index 1983c89..308af00 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -175,13 +175,12 @@ def test_incr_invalid(runner): def _add_project_files(*files): if "README.md" in files: + README_TEXT = """ + Hello World v201701.1002-alpha ! + aka. 201701.1002a0 ! + """ with pl.Path("README.md").open(mode="wt", encoding="utf-8") as fobj: - fobj.write( - """ - Hello World v201701.1002-alpha ! - aka. 201701.1002a0 ! - """ - ) + fobj.write(README_TEXT) if "setup.cfg" in files: with pl.Path("setup.cfg").open(mode="wt", encoding="utf-8") as fobj: