Skip to main content

Process Firefox versions numbers. Tells whether they are valid or not, whether they are nightlies or regular releases, whether this version precedes that other.

Project description

mozilla-version

Task Status Documentation Status

Process Mozilla's version numbers. Support main products like Firefox desktop, Fennec, Fenix, and Thunderbird. Tell whether version numbers are valid or not, whether they are nightlies or regular releases, whether this version precedes that other.

Rationale

Mozilla's build and release pipelines deal with version numbers. This has naturally grown into many scripts living at various places. Most of them reimplemented the same logic that determined whether a version was of which type. In theory, it's a simple problem that could (and was) solved with a regular expression. Although, as a human group, Mozilla hasn't been 100% consistent in giving version numbers to its products. Thus, the regular expression had to grow and we had to update each place that dealt with such exceptions.

Hence, mozilla-version is an attempt to become the source of truth when a piece of software has to deal with version numbers. It was build to take existing exceptions into account while enforcing new versions to comply with known schemes.

Documentation

Want to use mozilla-version? Here's how: https://mozilla-version.readthedocs.io/en/latest/

Get the code

Just install it from pip:

pip install mozilla-version

Hack on the code

virtualenv venv         # create the virtualenv in ./venv
. venv/bin/activate    # activate it
git clone https://github.com/mozilla-releng/mozilla-version
cd mozilla-version
pip install --require-hashes -r requirements/local.txt

Design choices

Object-oriented programming

mozilla-version uses classes to represent version numbers. For readers wondering: the original author is not a big fan of OOP and usually tries to not use this paradigm. Although, version handling turns out to be simpler when consumers of mozilla-version get objects. For instance:

# Functional programming
version_string = "84.0b3"
if is_version_beta(version_string) and get_beta_number(version_string) >= 2:
    do_something()

# OOP
version = FirefoxVersion.parse("84.0b3")
if version.is_beta and version.beta_number >= 2:
    do_something()

In the latter case, data gets parsed once and the if statement is closer to a regular English sentence. Another example:

# Functional programming
from functools import cmp_to_key

version_strings = ["84.0b3", "100.0.1", "84.0a1", "84.0"]
sorted_versions = sorted(version_strings, key=cmp_to_key(compare_versions))
# compare_version() would be a function provided by the mozilla-version library.
# Signature would look like this: compare_versions(version_a, version_b)
#
# cmp_to_key() comes from https://docs.python.org/3/howto/sorting.html#the-old-way-using-the-cmp-parameter

# OOP
version_strings = ["84.0b3", "100.0.1", "84.0a1", "84.0"]
versions = [FirefoxVersion.parse(string) for string in version_strings]
sorted_version = sorted(versions)

Once again, compare_versions() would have to parse both versions every time it's called. We could memoize intermediary results but it doesn't help in writing a straightforward sorted() call.

One major drawback of OOP is the complexity of the class hierarchy. Remembering side effects of a parent's function call can be hard to remember and debug. Luckily, the problem around version numbers is restricted enough to likely not be a problem in the future.

About sanity-checks

Like mentioned in the rationale, this problem cannot be solved with a regular expression anymore. mozilla-version checks whether a version number is valid thanks to 2 passes.

The first one is a regular expression. Its purpose to take out strings that are obviously neither version numbers, nor roughly matching the expected scheme. It's not meant to be strict.

The second pass ensures data is 100% valid. This is done at the end of the __init__() call. Therefore, a consumer of say FirefoxVersion() is guaranteed to deal with a valid version number the constructor succeeds (which is the implicit rule of a constructor, by the way). Speaking of __init__(), mozilla-version relies on attrs which implicitly defines constructors for us. Hence, the second pass is processed in __attrs_post_init__().

About edge case handling

Edge cases are handled in the second pass. This means: the first pass needs to be broad enough to let edge cases pass through.

They are explicitly called out in each class as _RELEASED_EDGE_CASES. They were found on:

About testing

Version numbers are a very simple problem to unit test. This library has 100% coverage and unit tests are small enough to be extended. Cases are usually written in a simple way where version under tests are plain strings. Then, the test itself parses each string. It's really meant to favor case addition. Feel free to add any edge cases you have in mind.

Creating a release

Bump dependencies

Run maintenance/pin.sh

Versioning

mozilla-version follows semver. Essentially, increment the

  1. MAJOR version when you make incompatible API changes,
  2. MINOR version when you add API functionality in a backwards-compatible manner, and
  3. PATCH version when you make backwards-compatible bug fixes.

Release files

Update the changelog and pyproject.toml to the new version before making a new release, and run uv lock.

Tagging

To enable gpg signing in git,

  1. you need a gpg keypair
  2. you need to set your user.signingkey in your ~/.gitconfig or scriptworker/.git/config
  3. If you want to specify a specific gpg executable, specify your gpg.program in your ~/.gitconfig or scriptworker/.git/config

Checkout the current main branch, tag and sign!

    # make sure you've committed your changes first!
    VERSION=<version>
    git tag -s $VERSION -m"$VERSION"

Push!

    # By default this will push the new tag to origin; make sure the tag gets pushed to
    # mozilla-releng/mozilla-version
    git push --tags

Finally, create a release on github. Source tarball and wheel will be automatically uploaded to pypi.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mozilla_version-5.1.0.tar.gz (99.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mozilla_version-5.1.0-py3-none-any.whl (44.4 kB view details)

Uploaded Python 3

File details

Details for the file mozilla_version-5.1.0.tar.gz.

File metadata

  • Download URL: mozilla_version-5.1.0.tar.gz
  • Upload date:
  • Size: 99.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for mozilla_version-5.1.0.tar.gz
Algorithm Hash digest
SHA256 b09d56f0d7e66fa2ff3df5ad1299df456afd8a63e6c2b9521a94b4616a6cb1e8
MD5 b0f88376421351bc2a4de18cb1611d5b
BLAKE2b-256 367cc23a10f59db273ed3f0dc078a1b1263e9f9126c4e36af86021e5bfaf9e24

See more details on using hashes here.

Provenance

The following attestation bundles were made for mozilla_version-5.1.0.tar.gz:

Publisher: pypi-publish.yml on mozilla-releng/mozilla-version

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mozilla_version-5.1.0-py3-none-any.whl.

File metadata

  • Download URL: mozilla_version-5.1.0-py3-none-any.whl
  • Upload date:
  • Size: 44.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for mozilla_version-5.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2e8de52712c3fbefac7f42f69319a664c6ba01d6e97bf07d5353a65ed34de94b
MD5 4592ed11687388be260968c5762ab62a
BLAKE2b-256 e9aacdf061be6b68f245c9405c17b899802b1ccc40eb77200579608936c14b33

See more details on using hashes here.

Provenance

The following attestation bundles were made for mozilla_version-5.1.0-py3-none-any.whl:

Publisher: pypi-publish.yml on mozilla-releng/mozilla-version

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page