stouputils.continuous_delivery.cd_utils module#

This module contains utilities for continuous delivery, such as loading credentials from a file. It is mainly used by the stouputils.continuous_delivery.github module.

parse_commit_message(
message: str,
) tuple[str, str, str | None, bool][source]#

Parse a commit message following the conventional commits convention.

Convention format: <type>: <description> or <type>(<sub-category>): <description>

Parameters:

message (str) – The commit message to parse (first line only)

Returns:

str: The commit type (e.g., “Features”, “Bug Fixes”) str: The commit description str | None: The sub-category if present (e.g., “Project”) bool: True if it’s a breaking change (indicated by !)

Return type:

tuple[str, str, str | None, bool]

Source:

https://www.conventionalcommits.org/en/v1.0.0/

>>> parse_commit_message("feat: Add new feature")
('Features', 'Add new feature', None, False)
>>> parse_commit_message("fix(API): Fix bug in endpoint")
('Bug Fixes', 'Fix bug in endpoint', 'API', False)
>>> parse_commit_message("feat!: Breaking change")
('Features', 'Breaking change', None, True)
>>> parse_commit_message("docs(README): Update documentation")
('Documentation', 'Update documentation', 'README', False)
>>> parse_commit_message("chore: Cleanup code")
('Chores', 'Cleanup code', None, False)
>>> parse_commit_message("refactor(core): Refactor module")
('Code Refactoring', 'Refactor module', 'core', False)
>>> parse_commit_message("No conventional format")
('Other', 'No conventional format', None, False)
>>> parse_commit_message("build!: Major build change")
('Build System', 'Major build change', None, True)
>>> parse_commit_message("wip: Work in progress")
('Work in Progress', 'Work in progress', None, False)
>>> parse_commit_message("unknown_type: Some description")
('Unknowntype', 'Some description', None, False)
format_changelog(
commits: list[tuple[str, str]],
url_formatter: Callable[[str], str] | None = None,
latest_tag_version: str | None = None,
current_version: str | None = None,
compare_url_formatter: Callable[[str, str], str] | None = None,
) str[source]#

Generate a changelog from a list of commits.

Parameters:
  • commits (list[tuple[str, str]]) – List of (sha, message) tuples

  • url_formatter (Callable[[str], str] | None) – Function to format commit URLs. Takes a SHA and returns a URL string. If None, commits show only short SHA.

  • latest_tag_version (str | None) – Version of the previous tag for comparison link

  • current_version (str | None) – Current version being released

  • compare_url_formatter (Callable[[str, str], str] | None) – Function to format comparison URL. Takes (old_version, new_version) and returns a URL string.

Returns:

Generated changelog in Markdown format

Return type:

str

load_credentials(
credentials_path: str,
) dict[str, Any][source]#

Load credentials from a JSON or YAML file into a dictionary.

Loads credentials from either a JSON or YAML file and returns them as a dictionary. The file must contain the required credentials in the appropriate format.

Parameters:

credentials_path (str) – Path to the credentials file (.json or .yml)

Returns:

Dictionary containing the credentials

Return type:

dict[str, Any]

Example JSON format:

{
        "github": {
                "username": "Stoupy51",
                "api_key": "ghp_XXXXXXXXXXXXXXXXXXXXXXXXXX"
        }
}

Example YAML format:

github:
        username: "Stoupy51"
        api_key: "ghp_XXXXXXXXXXXXXXXXXXXXXXXXXX"
handle_response(
response: requests.Response,
error_message: str,
) None[source]#

Handle a response from the API by raising an error if the response is not successful (status code not in 200-299).

Parameters:
  • response (requests.Response) – The response from the API

  • error_message (str) – The error message to raise if the response is not successful

clean_version(version: str, keep: str = '') str[source]#

Clean a version string

Parameters:
  • version (str) – The version string to clean

  • keep (str) – The characters to keep in the version string

Returns:

The cleaned version string

Return type:

str

>>> clean_version("v1.e0.zfezf0.1.2.3zefz")
'1.0.0.1.2.3'
>>> clean_version("v1.e0.zfezf0.1.2.3zefz", keep="v")
'v1.0.0.1.2.3'
>>> clean_version("v1.2.3b", keep="ab")
'1.2.3b'
version_to_float(
version: str,
error: bool = True,
) Any[source]#

Converts a version string into a float for comparison purposes. The version string is expected to follow the format of major.minor.patch.something_else…., where each part is separated by a dot and can be extended indefinitely. Supports pre-release suffixes with numbers: devN/dN (dev), aN (alpha), bN (beta), rcN/cN (release candidate). Ordering: 1.0.0 > 1.0.0rc2 > 1.0.0rc1 > 1.0.0b2 > 1.0.0b1 > 1.0.0a2 > 1.0.0a1 > 1.0.0dev1

Parameters:
  • version (str) – The version string to convert. (e.g. “v1.0.0.1.2.3”, “v2.0.0b2”, “v1.0.0rc1”)

  • error (bool) – Return None on error instead of raising an exception

Returns:

The float representation of the version. (e.g. 0)

Return type:

float

>>> version_to_float("v1.0.0")
1.0
>>> version_to_float("v1.0.0.1")
1.000000001
>>> version_to_float("v2.3.7")
2.003007
>>> version_to_float("v1.0.0.1.2.3")
1.0000000010020031
>>> version_to_float("v2.0") > version_to_float("v1.0.0.1")
True
>>> version_to_float("v2.0.0") > version_to_float("v2.0.0rc") > version_to_float("v2.0.0b") > version_to_float("v2.0.0a") > version_to_float("v2.0.0dev")
True
>>> version_to_float("v1.0.0b") > version_to_float("v1.0.0a")
True
>>> version_to_float("v1.0.0") > version_to_float("v1.0.0b")
True
>>> version_to_float("v3.0.0a") > version_to_float("v2.9.9")
True
>>> version_to_float("v1.2.3b") < version_to_float("v1.2.3")
True
>>> version_to_float("1.0.0") == version_to_float("v1.0.0")
True
>>> version_to_float("2.0.0.0.0.0.1b") > version_to_float("2.0.0.0.0.0.1a")
True
>>> version_to_float("2.0.0.0.0.0.1") > version_to_float("2.0.0.0.0.0.1b")
True
>>> version_to_float("v1.0.0rc") == version_to_float("v1.0.0c")
True
>>> version_to_float("v1.0.0c") > version_to_float("v1.0.0b")
True
>>> version_to_float("v1.0.0d") < version_to_float("v1.0.0a")
True
>>> version_to_float("v1.0.0dev") < version_to_float("v1.0.0a")
True
>>> version_to_float("v1.0.0dev") == version_to_float("v1.0.0d")
True
>>> version_to_float("v1.0.0rc2") > version_to_float("v1.0.0rc1")
True
>>> version_to_float("v1.0.0b2") > version_to_float("v1.0.0b1")
True
>>> version_to_float("v1.0.0a2") > version_to_float("v1.0.0a1")
True
>>> version_to_float("v1.0.0dev2") > version_to_float("v1.0.0dev1")
True
>>> version_to_float("v1.0.0") > version_to_float("v1.0.0rc2") > version_to_float("v1.0.0rc1")
True
>>> version_to_float("v1.0.0rc1") > version_to_float("v1.0.0b2")
True
>>> version_to_float("v1.0.0b1") > version_to_float("v1.0.0a2")
True
>>> version_to_float("v1.0.0a1") > version_to_float("v1.0.0dev2")
True
>>> versions = ["v1.0.0", "v1.0.0rc2", "v1.0.0rc1", "v1.0.0b2", "v1.0.0b1", "v1.0.0a2", "v1.0.0a1", "v1.0.0dev2", "v1.0.0dev1"]
>>> sorted_versions = sorted(versions, key=version_to_float, reverse=True)
>>> sorted_versions == versions
True