Package: dynamic-versioning
Dynamic Versioning Utility for SetuptoolsThis utility provides the capability to dynamically determine the version of a python distribution package at build-time based on the current version (as determined by the most recent git tag) and a "version bumping" policy. It extends functionality in thesetuptoolslibrary and as such requires it as the configured build-backend.FunctionalityIn order to properly hooksetuptools, Dynamic Versioning needs both asetup.pyfile and apyproject.tomlfile. Advocates of the use ofsetup.pytend to not want to usepyproject.toml, and vice versa. And most would prefer to not use both. However,setuptoolsdoes not provide a way inpyproject.tomlto dictate "overrides" ofCommandclasses, thus the override ofegg_infomust be defined insetup.py. A minimalsetup.pycould be:fromsetuptoolsimportsetupimportdynamic_versioningsetup(cmdclass={"egg_info":dynamic_versioning.DynamicVersioningEggInfo})... and all other configuration valuescouldbe defined inpyproject.toml.Meanwhile, PEP 517 front-ends, such aspipexpect the build-backend to be defined inpyproject.toml, so to force the use ofsetuptoolsas the build-backend, apyproject.tomlfile is also necessary. A minimalpyproject.tomlfile could be:[build-system]requires=["setuptools", "dynamic-versioning"]build-backend="setuptools.build_meta"... and all other configuration valuecouldbe defined insetup.py.Note: A Future version of Dynamic Versioning will provide a command line utility for generating these minimal configuration files.This may be helpful to initialize a new project.ConfigurationThe software providesfourconfiguration values that define how it will dynamically determine the version at build-time. No change is needed to the command to build - bothpython setup.py bdist_wheel|install|...(soon to be deprecated bysetuptools) and the more modernpip wheel .will trigger the version determination.New VersionThe first configuration value is "new version" and can be set in one of three ways:In a configuration file nameddynamic_versioning.ini:[dynamic_versioning]new-version=1.2.3Ifdynamic_versioning.iniis not found, inpyproject.toml:[tool.dynamic_versioning]new-version="1.2.3"Or, in the environment variableDV_NEW_VERSIONIf this configuration value is set, the version will be set to the value - simple as that. This overrides any version set insetup.pyand/orpyproject.toml. If this configuration value is set, all other configuration values will be ignored.Version BumpThe second configuration value is "version bump" and is the heart of Dynamic Versioning. This configuration value can be set usingversion-bumpin eitherdynamic_versioning.iniorpyproject.toml, or the environment variableDV_VERSION_BUMP. The value provided must be one of:PATCH(UPDATEis also accepted),MINOR, orMAJOR(using any case). When provided, Dynamic Versioning will grab the most recentannotatedgit tag and parse it as thecurrentversion. It is assumed that tags will followsemantic versioningformat, with an optional prepended "V" (ex.[v|V]4.2.7). The portion of the current version designated by the value of "version bump" is then incremented by 1 ("bumped") and the lesser portions are zeroed out.For example:1.2.3 bumped by PATCH (or UPDATE) -> 1.2.41.2.3 bumped by MINOR -> 1.3.01.2.3 bumped by MAJOR -> 2.0.0If an annotated git tag is not available, the version will be set to0.0.1by default. Since the system depends on annotated git tags, it is expected that a tag will be created after an official version has been built and published. This default allows a "bootstrap" to that process. Of course,new-versioncan always be used to bootstrap the process at a different version.Current VersionThe third configuration value is "current version" and can be set in the same three ways:current-versionin eitherdynamic_versioning.iniorpyproject.toml, or the environment variableDV_CURRENT_VERSION. This value is used as a "fall-back" for when the version cannot be found by parsing the most recent annotated git tag. This is useful in circumstances when gitis not available(or cannot be counted onto be available) on the system building the distribution package. This value is required when generating a source distribution, as the.gitdirectory will almost certainlynotbe packed inside the source distribution.Development VersionThe last configuration value provided is "dev version" and can be set in the same three ways:dev-versionin eitherdynamic_versioning.iniorpyproject.toml, or the environment variableDV_DEV_VERSION. This allows the developers to denote the build as a development version. This will result in a version that adheres to thePEP 440section on development versioning. While determining thecurrent versionthrough the most recent git tag, the number of commits since that tag is also captured. The current version is then "bumped" using the value set by "version bump," defaulting toMAJORwhen absent. The version is then assembled using the format{major}.{minor}.{patch}.dev{commit_count}. For instance, if there have been 12 commits since4.2.7, the development version could become:5.0.0.dev12(Major),4.3.0.dev12(Minor), or4.2.8.dev12(Patch).Assumptions Madegitis being usedtags areannotated, or at least the ones that provide version numbers arethe annoted tags follow (simple) semantic versioning ([v|V]{major}.{minor}.{patch})LoggingA note about logging. If you usepython setup.py ...(usingsetuptoolsasbotha build-frontend and build-backend), and define a logger in yoursetup.pyfile, this library will log a few things along the way. However, if you usepip wheel .,pipsilences all logging from the build-backend. That is, unless there is an error.pipis then quick to tell you it's notpip's fault - check for the error in the build-backend or your build configuration. And when it is doing that, it will allow the logs fromsetuptoolsand Dynamic Versioning to be seen.Development and TestingIn case you want to develop against this code base, or just run the unit tests, here's how you do it:Optionally (but suggested), create a virtual environment (I likevirtualenvwrapper)Run the tests:tests/run_tests.sh. This process will install the software, along with the testing dependencies, run several linters, and then the unit tests.The Story BehindIn most places I have developed software, developers forget to update the version field in thesetup.pyfile. And then one is faced with making a commit to SCM just to change it, and that commit doesn't represent a change in theactualcode. In an environment which adopts the CI/CD attitude of: "every commit has the potential to be a release," that doesn't fly so well. Further, when the build server automatically publishes packages, the build process needs to determine the version (dynamically or manually) in order to publish.Some project managers like to have a human-in-the-loop (usually themselves) and manually kick off the build that actually publishes the package. In this case, a build server configuration that allows providing parameters can be helpful. This software adds a configuration ("new version") so a version can easily be provided, for instance from the build config parameters.But in other cases, "versions" are cheap - especially when the distribution packages are published to aninternalartifact server so "plugin" developers, possibly from a different department, the customer, or even a partnered company, have access. But, because of the distinct API lines drawn between these players, "development" versions might be discouraged. The versions published areofficial, but are often-updated and a prime use of the "continuous" part of CI/CD. Either of "version bump" or "dev version" can help accomplish this in a dynamic way.