Versioning

Versioning is an important aspect of software development. It helps users and developers keep track of changes, improvements, and bug fixes over time. A well-defined versioning strategy also helps with dependency management, compatibility checks, and release management.

Versioning Strategy

In general, this project will try to follow semantic versioning. We say “in general” because at the end of the day, all versioning strategies come down to human judgment.

Nonetheless, we will try to follow the following guidelines:

  1. Major version: incremented for incompatible API changes. This includes changes to the command line interface (CLI) that break backward compatibility.

  2. Minor version: incremented for new functionality that is backward compatible. This includes new features, new command line arguments, and new output formats.

  3. Patch version: incremented for backward-compatible bug fixes. This includes fixing incorrect behavior, improving performance, and improving documentation. It does not include new features or new command line arguments.

We looked at some tools built to help automate this process, but they were determined to not meet our needs as well as these. Some other options we considered were:

  • bump2version * This seemed focused on propagating changes across multiple checked-in files. Our goal was that we really only have the number written down in one place, so there shouldn’t be a need to propagate anything.

  • versioneer * This creates a custom _version.py, which to us just seems to introduce yet another possible source of truth (see below).

Single Source of Truth

One key challenge to keep in mind when managing version numbers is ensuring that the version number is consistent across all the places it is referenced. This is important because if the version number is not consistent, it can lead to confusion for users and developers, and worse, inconsistent dependency trees. This includes keeping at least all of the following consistent:

  1. The version shown by pip (e.g., when you run pip show memtab or pip freeze).

  • This one is likely the most important, in the end, because it is what is used for dependency management.

  1. The Python package version (e.g., in the pyproject.toml file).

  2. The version shown in the Python package itself (e.g., in the __init__.py file, if you choose to put it there).

  3. The version shown by importlib.metadata (e.g., when you run importlib.metadata.version("memtab")).

  4. The version displayed in the command line interface (CLI) when the user runs memtab --version.

  5. The version displayed in the documentation (e.g., in the header of the documentation pages).

  6. The version used in the release notes and changelog (if any).

  7. The version used in the Git tags for tracking and releases.

Some documentation from the python packaging guidelines on single-sourcing can be found here.

For this project, the single source of truth is the pyproject.toml file. Specifically, the version field in the [project] section. All other version references should be derived from this single source.

To make things easier and less error-prone, this derivation is enforced via a combination of the following:

  • The command line will use importlib.metadata to get the version number when the user runs memtab --version. This blends a few of the above together to reduce variables.

  • We won’t put anything in the __init__.py

  • When the main branch is updated, a github action will be used to apply the version number to the repository as a release + tag if it does not exist.

  • When a tag is created, new documentation will be generated containing the new version (and all previous versions). This way, developers only need to update the pyproject.toml version, they don’t also have to manually create a git tag.

Supporting Versioning in Documentation

One additional change that comes along with adding a versioning strategy is that we need to ensure that the documentation is also versioned. This is important because:

  1. Different versions of the software may have different features, command line arguments, and output formats.

  2. Users may want to refer to the documentation for a specific version of the software, especially if they are using an older version.

To that end, as we add a versioning notion to this repository, we need to update the documentation generation workflows to generate documentation for all previous versions of the package. That way, every time the documentation is re-deployed, it will include all historical versions as well as the latest. The basic process there is simply to repeat the documentation generation process for each version. This can be done by: #. Using a script to loop through all the versions we want to generate documentation for (checking out the repository to that version, via git tags). #. Running the documentation generation command (e.g., Sphinx) for each version. #. By leveraging sphinx-multiversion, we can generate a versioned documentation site that allows users to easily switch between all historical versions.