5.2 Software Versioning#

What is Versioning?#

Versioning is the process of assigning unique version to distinct states of software projects, allowing developers and users to track progress, manage changes, and ensure compatibility between different components. It plays a critical role in software development and release management, offering a structured way to reflect the history, stability, and compatibility of software products over time. Through versioning, teams can effectively communicate the impact of changes, manage dependencies, and facilitate the adoption of new features while maintaining the integrity of existing systems.

What is Semantic Versioning?#

Semantic Versioning, often abbreviated as SemVer, is a versioning scheme that aims to convey meaning about the underlying changes in a release through the version number itself. Defined by Semantic Versioning 2.0.0, it adopts a structured format of MAJOR.MINOR.PATCH to differentiate between the types of changes made to a project:

  • MAJOR version when you make incompatible API changes,

  • MINOR version when you add functionality in a backward-compatible manner, and

  • PATCH version when you make backward-compatible bug fixes.

Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

The principles of Semantic Versioning help ensure a consistent, predictable approach to versioning that is directly tied to the significance of the changes made. It allows developers and consumers of software to make informed decisions about upgrading and integrating with other systems. By adhering to SemVer, projects can communicate the nature of changes efficiently, reduce the potential for conflicts, and facilitate easier dependency management in the complex ecosystem of software development.

Automate Semantic Versioning in Poetry Projects with Python Semantic Release#

We can use a python package called python-semantic-release to automate semantic versioning in projects managed with Poetry on Gitlab. This package automates the process of determining the next version number based on the changes made to the project, generating release notes, and publishing the release to a repository. By integrating python-semantic-release with Gitlab CI/CD, we can automate the entire release process, ensuring that version numbers are incremented correctly and that releases are published consistently.

Automating the semantic versioning of My Package#

Let’s see how to automate semantic versioning in a Python project managed with Poetry using python-semantic-release and Gitlab CI/CD. We will use the my-package project that we created in the previous section and configure it to use python-semantic-release for versioning and release management.

Creating a personal access token#

To allow python-semantic-release to interact with your Gitlab repository, you need to create a personal access token. This token will be used to authenticate the tool when it performs actions such as creating releases and tags.

To create a new personal access token, go to your GitLab account Preferences and click on “Access Tokens” in the “User Settings” section. Then, click on “Add new token” and fill in the required details. You can use:

  • Token name: semantic_release

  • Check the following scopes: api, read_user, read_repository, and write_repository. Once you’ve created the token, copy it to your clipboard, as you’ll need it later.

Adding the token to the GitLab CI/CD variables#

To add the token to the GitLab CI/CD variables, go to the settings of the repository and then to the CI/CD section. Browse until you find the “Variables” section and click on “Expand” to reveal the form for adding a new variable. Now click on “Add variable” and add a new variable with:

  • Type: Variable (default)

  • Environment: All (default)

  • Visibility: Masked

  • Flags: Protect Variable and Expand variable reference

  • Key: GITLAB_TOKEN

  • Value: <your token>

Setup Your Python Project with Poetry#

To ensure that our project is set up with Poetry and has a pyproject.toml file at its root, we need to initialize the project using Poetry.

If the pyproject.toml file is not there, we can create it by running the following command in the terminal:

poetry init

Change the Python Versions Supported by the Project#

Since python-semantic-release requires Python >=3.8, before installing python-semantic-release, it is important that you check and change, if needed, the Python version supported by the project. In the pyproject.toml file, look for requires-python and change the Python version to ^3.8 if a lower version is specified.

For example, if you find:

requires-python = ">=3.7"

Change it to:

requires-python = ">=3.8"

Add Python Semantic Release as a dependency of your project#

Add python-semantic-release to your development dependencies using Poetry.

poetry add --group dev python-semantic-release
The currently activated Python version 3.7.4 is not supported by the project (>=3.10).
Trying to find and use a compatible version. 
Using python3.12 (3.12.4)
Creating virtualenv my-package in /home/callaram/tds/home/ch5/my-package/.venv
Using version ^10.3.1 for python-semantic-release

Updating dependencies
Resolving dependencies... (6.5s)

Package operations: 40 installs, 0 updates, 0 removals

  - Installing certifi (2025.8.3): Pending...
  - Installing charset-normalizer (3.4.3): Pending...
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Pending...
  - Installing charset-normalizer (3.4.3): Pending...
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Pending...
  - Installing certifi (2025.8.3): Installing...
  - Installing charset-normalizer (3.4.3): Pending...
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Pending...
  - Installing charset-normalizer (3.4.3): Pending...
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Pending...
  - Installing certifi (2025.8.3)
  - Installing charset-normalizer (3.4.3): Pending...
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Pending...
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Pending...
  - Installing charset-normalizer (3.4.3): Downloading... 0%
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Pending...
  - Installing urllib3 (2.5.0): Downloading... 0%
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Downloading... 0%
  - Installing charset-normalizer (3.4.3): Downloading... 100%
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Downloading... 0%
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Downloading... 0%
  - Installing charset-normalizer (3.4.3): Installing...
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Downloading... 0%
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Downloading... 0%
  - Installing charset-normalizer (3.4.3)
  - Installing idna (3.10): Pending...
  - Installing urllib3 (2.5.0): Downloading... 0%
  - Installing urllib3 (2.5.0): Downloading... 100%
  - Installing urllib3 (2.5.0): Installing...
  - Installing urllib3 (2.5.0): Installing...
  - Installing idna (3.10): Installing...
  - Installing urllib3 (2.5.0): Installing...
  - Installing urllib3 (2.5.0)
  - Installing urllib3 (2.5.0)
  - Installing idna (3.10)
  - Installing urllib3 (2.5.0)
  - Installing mdurl (0.1.2): Pending...
  - Installing requests (2.32.4): Pending...
  - Installing smmap (5.0.2): Pending...
  - Installing typing-extensions (4.14.1): Pending...
  - Installing typing-extensions (4.14.1): Pending...
  - Installing smmap (5.0.2): Installing...
  - Installing typing-extensions (4.14.1): Pending...
  - Installing smmap (5.0.2): Installing...
  - Installing typing-extensions (4.14.1): Pending...
  - Installing requests (2.32.4): Installing...
  - Installing smmap (5.0.2): Installing...
  - Installing typing-extensions (4.14.1): Pending...
  - Installing typing-extensions (4.14.1): Downloading... 0%
  - Installing typing-extensions (4.14.1): Downloading... 0%
  - Installing smmap (5.0.2)
  - Installing typing-extensions (4.14.1): Downloading... 0%
  - Installing requests (2.32.4): Installing...
  - Installing smmap (5.0.2)
  - Installing typing-extensions (4.14.1): Downloading... 0%
  - Installing mdurl (0.1.2): Installing...
  - Installing requests (2.32.4): Installing...
  - Installing smmap (5.0.2)
  - Installing typing-extensions (4.14.1): Downloading... 0%
  - Installing requests (2.32.4): Installing...
  - Installing smmap (5.0.2)
  - Installing typing-extensions (4.14.1): Downloading... 0%
  - Installing mdurl (0.1.2)
  - Installing requests (2.32.4): Installing...
  - Installing smmap (5.0.2)
  - Installing typing-extensions (4.14.1): Downloading... 0%
  - Installing smmap (5.0.2)
  - Installing typing-extensions (4.14.1): Downloading... 0%
  - Installing requests (2.32.4)
  - Installing smmap (5.0.2)
  - Installing typing-extensions (4.14.1): Downloading... 0%
  - Installing typing-extensions (4.14.1): Downloading... 100%
  - Installing typing-extensions (4.14.1): Installing...
  - Installing typing-extensions (4.14.1)
  - Installing annotated-types (0.7.0): Pending...
  - Installing click (8.1.8): Pending...
  - Installing gitdb (4.0.12): Pending...
  - Installing markdown-it-py (3.0.0): Pending...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Pending...
  - Installing pygments (2.19.2): Pending...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0): Pending...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing gitdb (4.0.12): Pending...
  - Installing markdown-it-py (3.0.0): Pending...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Pending...
  - Installing pygments (2.19.2): Pending...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0): Pending...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing click (8.1.8): Installing...
  - Installing gitdb (4.0.12): Pending...
  - Installing markdown-it-py (3.0.0): Pending...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Pending...
  - Installing pygments (2.19.2): Pending...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0): Pending...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing markdown-it-py (3.0.0): Pending...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Pending...
  - Installing pygments (2.19.2): Pending...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0): Pending...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing gitdb (4.0.12): Installing...
  - Installing markdown-it-py (3.0.0): Pending...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Pending...
  - Installing pygments (2.19.2): Pending...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0): Pending...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0): Pending...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0): Pending...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing six (1.17.0): Installing...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing gitdb (4.0.12): Installing...
  - Installing markdown-it-py (3.0.0): Pending...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing click (8.1.8)
  - Installing gitdb (4.0.12): Installing...
  - Installing markdown-it-py (3.0.0): Pending...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing markdown-it-py (3.0.0): Installing...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0): Pending...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Pending...
  - Installing wrapt (1.17.2): Installing...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Installing...
  - Installing requests-toolbelt (1.0.0): Installing...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Installing...
  - Installing markdown-it-py (3.0.0): Installing...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0): Installing...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Installing...
  - Installing gitdb (4.0.12)
  - Installing markdown-it-py (3.0.0): Installing...
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0): Installing...
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2): Installing...
  - Installing wrapt (1.17.2)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2)
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2)
  - Installing markdown-it-py (3.0.0)
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Pending...
  - Installing wrapt (1.17.2)
  - Installing wrapt (1.17.2)
  - Installing typing-inspection (0.4.1): Downloading... 0%
  - Installing wrapt (1.17.2)
  - Installing click (8.1.8)
  - Installing gitdb (4.0.12)
  - Installing markdown-it-py (3.0.0)
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Downloading... 0%
  - Installing wrapt (1.17.2)
  - Installing annotated-types (0.7.0): Installing...
  - Installing click (8.1.8)
  - Installing gitdb (4.0.12)
  - Installing markdown-it-py (3.0.0)
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Downloading... 0%
  - Installing wrapt (1.17.2)
  - Installing click (8.1.8)
  - Installing gitdb (4.0.12)
  - Installing markdown-it-py (3.0.0)
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Downloading... 0%
  - Installing wrapt (1.17.2)
  - Installing annotated-types (0.7.0)
  - Installing click (8.1.8)
  - Installing gitdb (4.0.12)
  - Installing markdown-it-py (3.0.0)
  - Installing markupsafe (3.0.2): Pending...
  - Installing pydantic-core (2.33.2): Downloading... 0%
  - Installing pygments (2.19.2): Installing...
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Downloading... 0%
  - Installing wrapt (1.17.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Downloading... 0%
  - Installing wrapt (1.17.2)
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Downloading... 0%
  - Installing wrapt (1.17.2)
  - Installing wrapt (1.17.2)
  - Installing typing-inspection (0.4.1): Downloading... 100%
  - Installing wrapt (1.17.2)
  - Installing wrapt (1.17.2)
  - Installing typing-inspection (0.4.1): Installing...
  - Installing wrapt (1.17.2)
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Installing...
  - Installing wrapt (1.17.2)
  - Installing pydantic-core (2.33.2): Downloading... 100%
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1): Installing...
  - Installing wrapt (1.17.2)
  - Installing wrapt (1.17.2)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing pydantic-core (2.33.2): Installing...
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing pydantic-core (2.33.2): Installing...
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing markupsafe (3.0.2): Downloading... 0%
  - Installing pydantic-core (2.33.2): Installing...
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing pydantic-core (2.33.2): Installing...
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing markupsafe (3.0.2): Downloading... 100%
  - Installing pydantic-core (2.33.2): Installing...
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing pydantic-core (2.33.2): Installing...
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing markupsafe (3.0.2): Installing...
  - Installing pydantic-core (2.33.2): Installing...
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing pydantic-core (2.33.2): Installing...
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing markupsafe (3.0.2)
  - Installing pydantic-core (2.33.2): Installing...
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing pydantic-core (2.33.2)
  - Installing pygments (2.19.2)
  - Installing requests-toolbelt (1.0.0)
  - Installing six (1.17.0)
  - Installing typing-inspection (0.4.1)
  - Installing wrapt (1.17.2)
  - Installing click-option-group (0.5.7): Pending...
  - Installing deprecated (1.2.18): Pending...
  - Installing dotty-dict (1.3.1): Pending...
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Pending...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0): Pending...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Pending...
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Pending...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Pending...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Pending...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Pending...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing packaging (25.0): Installing...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Pending...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Pending...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0): Installing...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Pending...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing dotty-dict (1.3.1): Installing...
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Pending...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0): Installing...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Pending...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing iniconfig (2.1.0): Installing...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0): Installing...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Installing...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0): Installing...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Installing...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0): Installing...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Installing...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing dotty-dict (1.3.1)
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0): Installing...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0): Installing...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing deprecated (1.2.18): Pending...
  - Installing dotty-dict (1.3.1)
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing click-option-group (0.5.7): Installing...
  - Installing deprecated (1.2.18): Pending...
  - Installing dotty-dict (1.3.1)
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing pluggy (1.6.0): Installing...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing dotty-dict (1.3.1)
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0): Installing...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing deprecated (1.2.18): Installing...
  - Installing dotty-dict (1.3.1)
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0): Installing...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Pending...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing deprecated (1.2.18): Installing...
  - Installing dotty-dict (1.3.1)
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0): Installing...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing click-option-group (0.5.7)
  - Installing deprecated (1.2.18): Installing...
  - Installing dotty-dict (1.3.1)
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0): Installing...
  - Installing pydantic (2.11.7): Downloading... 0%
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing pydantic (2.11.7): Downloading... 100%
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing dotty-dict (1.3.1)
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0): Installing...
  - Installing pydantic (2.11.7): Downloading... 100%
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing deprecated (1.2.18)
  - Installing dotty-dict (1.3.1)
  - Installing gitpython (3.1.45): Pending...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Pending...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0): Installing...
  - Installing pydantic (2.11.7): Downloading... 100%
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing pydantic (2.11.7): Downloading... 100%
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Downloading... 100%
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing jinja2 (3.1.6): Installing...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3): Pending...
  - Installing tzdata (2025.2): Installing...
  - Installing tzdata (2025.2): Installing...
  - Installing tomlkit (0.13.3): Installing...
  - Installing tzdata (2025.2): Installing...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Installing...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3): Installing...
  - Installing tzdata (2025.2): Installing...
  - Installing gitpython (3.1.45): Installing...
  - Installing importlib-resources (6.5.2): Pending...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Installing...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Pending...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3): Installing...
  - Installing tzdata (2025.2): Installing...
  - Installing tzdata (2025.2): Installing...
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Installing...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing importlib-resources (6.5.2): Downloading... 0%
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6): Installing...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0): Installing...
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing importlib-resources (6.5.2): Downloading... 0%
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing gitpython (3.1.45)
  - Installing importlib-resources (6.5.2): Downloading... 0%
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Pending...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing numpy (2.3.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7): Installing...
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing importlib-resources (6.5.2): Downloading... 100%
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing importlib-resources (6.5.2): Installing...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing importlib-resources (6.5.2)
  - Installing pytz (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0): Downloading... 0%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing python-gitlab (6.2.0): Downloading... 100%
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing python-gitlab (6.2.0): Installing...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing python-gitlab (6.2.0)
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0)
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing pytz (2025.2)
  - Installing jinja2 (3.1.6)
  - Installing numpy (2.3.2): Installing...
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0)
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2): Installing...
  - Installing tzdata (2025.2)
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0)
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2)
  - Installing numpy (2.3.2)
  - Installing packaging (25.0)
  - Installing pluggy (1.6.0)
  - Installing pydantic (2.11.7)
  - Installing python-dateutil (2.9.0.post0)
  - Installing python-gitlab (6.2.0)
  - Installing iniconfig (2.1.0)
  - Installing rich (14.1.0)
  - Installing shellingham (1.5.4)
  - Installing tomlkit (0.13.3)
  - Installing tzdata (2025.2)
  - Installing pandas (2.3.1): Pending...
  - Installing pytest (8.4.1): Pending...
  - Installing python-semantic-release (10.3.1): Pending...
  - Installing python-semantic-release (10.3.1): Pending...
  - Installing pytest (8.4.1): Installing...
  - Installing python-semantic-release (10.3.1): Pending...
  - Installing python-semantic-release (10.3.1): Pending...
  - Installing pytest (8.4.1)
  - Installing python-semantic-release (10.3.1): Pending...
  - Installing python-semantic-release (10.3.1): Installing...
  - Installing pytest (8.4.1)
  - Installing python-semantic-release (10.3.1): Installing...
  - Installing pandas (2.3.1): Installing...
  - Installing pytest (8.4.1)
  - Installing python-semantic-release (10.3.1): Installing...
  - Installing python-semantic-release (10.3.1)
  - Installing pytest (8.4.1)
  - Installing python-semantic-release (10.3.1)
  - Installing pandas (2.3.1)
  - Installing pytest (8.4.1)
  - Installing python-semantic-release (10.3.1)

Writing lock file

Configure Python Semantic Release#

To configure python-semantic-release, we will use the pyproject.toml. We will add two sections to the file: [tool.semantic_release] and [tool.semantic_release.remote]. The first section will define the version variable, whether to commit the version number, and whether to upload to PyPI. The second section will define the remote repository’s name and type.

[tool.semantic_release]
version_variable = "pyproject.toml:version"
commit_version_number = true
upload_to_pypi = "false"

[tool.semantic_release.remote]
name = "origin"
type = "gitlab"

Setup Gitlab for Continuous Deployment#

Now we will add a semantic-release step to our .gitlab-ci.yml file to automate the versioning and release process. This step will run in the deploy stage and will be triggered when a commit is made to the default branch that follows semantic versioning in its commit message.

Let’s replace the content of the .gitlab-ci.yml file with the following configuration:

image: python:latest

before_script:
  - git checkout "$CI_COMMIT_REF_NAME"
  - pip install poetry
  - poetry install --only dev --no-root
  
semantic-release:
  stage: deploy
  rules:
    - if: '$CI_COMMIT_MESSAGE =~ /^(\d+\.)?(\d+\.)?(\d+).*/ && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
      when: never
    - when: always
  script:
    - poetry run semantic-release -vvv version
    - poetry run semantic-release -vvv publish

Let’s break down this GitLab CI/CD configuration to understand how it automates semantic versioning:

Docker Image Configuration#

image: python:latest

We use the python:latest Docker image as our CI/CD environment. This provides a clean Python environment with pip and other Python tools pre-installed, ensuring consistent execution across different runners.

Global Setup (before_script)#

before_script:
  - git checkout "$CI_COMMIT_REF_NAME"
  - pip install poetry
  - poetry install --only dev --no-root

The before_script section runs before every job and performs essential setup:

  1. git checkout "$CI_COMMIT_REF_NAME" - Switches to the specific branch or tag that triggered the pipeline. The $CI_COMMIT_REF_NAME variable contains the branch/tag name, ensuring we’re working with the correct commit.

  2. pip install poetry - Installs Poetry package manager, which is required to manage our Python project dependencies.

  3. poetry install --only dev --no-root - Installs only development dependencies (including python-semantic-release) without installing the package itself. The --no-root flag prevents Poetry from installing the current project as a package, which is suitable for CI environments.

Semantic Release Job Configuration#

semantic-release:
  stage: deploy

This job runs in the deploy stage, typically after build and test stages have completed successfully.

Job Execution Rules#

rules:
  - if: '$CI_COMMIT_MESSAGE =~ /^(\d+\.)?(\d+\.)?(\d+).*/ && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
    when: never
  - when: always

The rules section implements smart logic to prevent infinite CI/CD loops:

  1. First rule (when: never) - Prevents the job from running when:

    • The commit message starts with a version number pattern (regex ^(\d+\.)?(\d+\.)?(\d+).* matches patterns like 1.2.3, 0.1.0, etc.)

    • AND the commit is on the default branch ($CI_DEFAULT_BRANCH is typically main or master)

    This prevents semantic-release from triggering another pipeline when it creates its own version bump commits.

  2. Second rule (when: always) - Runs the job in all other cases, ensuring semantic-release processes regular feature and bug fix commits.

Release Automation Scripts#

script:
  - poetry run semantic-release -vvv version
  - poetry run semantic-release -vvv publish

The script section executes two critical commands:

  1. poetry run semantic-release -vvv version - This command:

    • Analyzes commit messages since the last release using conventional commit standards

    • Determines the next version number based on semantic commit types (feat: = minor, fix: = patch, BREAKING CHANGE: = major)

    • Updates version numbers in pyproject.toml and other configured files

    • The -vvv flag provides verbose logging for debugging

  2. poetry run semantic-release -vvv publish - This command:

    • Creates a Git tag for the new version

    • Generates or updates the CHANGELOG.md file based on commit history

    • Creates a GitLab release with release notes

    • Pushes the changes and tags back to the repository

How the Automation Works#

  1. Developer commits with semantic messages (e.g., feat: add user authentication, fix: resolve login bug)

  2. Pipeline triggers and the semantic-release job analyzes the commit history

  3. Version calculation happens automatically based on commit types since the last release

  4. Release artifacts are generated (tags, changelog, GitLab releases) without manual intervention

  5. Loop prevention ensures semantic-release’s own version commits don’t trigger new releases

This automation ensures consistent versioning following semantic versioning principles, eliminates manual release management tasks, and provides clear release documentation through automatically generated changelogs.

Commit all changes#

Commit all the changes to the repository, using the message “feat(semantic-release): add semantic release to the repository”.

Make Semantic Commits#

To ensure python-semantic-release correctly increments version numbers, use semantic commit messages (e.g., fix:, feat:, BREAKING CHANGE:) for your commits. This practice helps the tool to automatically determine the next version number based on the changes made. You can find more information about semantic commits messages supported by python-semantic-release here.

What to notice

  • After pushing our first commit and successfully running python-semantic-release, the tool will automatically generate both a release and a tag for your project, as well as a CHANGELOG.md file. You can find the generated release in the Releases section of the my-package repository. This section provides detailed information about what’s new, improved, or fixed in each release, along with any associated assets.

Similarly, the automatically created tag, marking the specific point in the repository’s history for the release, can be found in the Tags section. Tags serve as important reference points, indicating version releases and facilitating easy navigation through the project’s version history.

See it in action#

You can create a new commit with a semantic commit message and push it to the repository to see python-semantic-release in action. For example, you can create a new file in the repository and push it to the default branch.

cd tests
touch test.py
git add test.py
git commit -m "feat(test): add new test module"
git push --set-upstream origin master

References#

Semantic Versioning