Skip to content

config

Config

Bases: BaseModel

Object to store the m project configuration.

Attributes:

Name Type Description
m_dir str

...

owner str

...

repo str

...

version str

...

workflow Workflow

...

require_pr_changelog bool

...

changelog_bypassers list[str]

...

git_flow GitFlowConfig

...

m_flow MFlowConfig

...

build_tag_with_version bool

...

docker_config DockerConfig | None

...

Source code in m/ci/config.py
class Config(BaseModel):
    """Object to store the m project configuration."""

    # pylint: disable=too-many-instance-attributes
    m_dir: str
    owner: str
    repo: str
    version: str = '0.0.0'
    workflow: Workflow = Workflow.free_flow

    # If true it will require an update to the changelog for every PR.
    require_pr_changelog: bool = True

    # List of github logins allowed to bypass the changelog requirement.
    changelog_bypassers: list[str] = ['dependabot']

    git_flow: GitFlowConfig = GitFlowConfig(
        master_branch=Branches.master,
        develop_branch=Branches.develop,
        release_prefix=Branches.release,
        hotfix_prefix=Branches.hotfix,
    )
    m_flow: MFlowConfig = MFlowConfig(
        master_branch=Branches.master,
        release_prefix=Branches.release,
        hotfix_prefix=Branches.hotfix,
    )
    build_tag_with_version: bool = False
    docker_config: DockerConfig | None = None

    model_config = ConfigDict(use_enum_values=True)

    def uses_git_flow(self) -> bool:
        """Check if configuration is using the git flow.

        Returns:
            True if workflow is the git flow.
        """
        return self.workflow == Workflow.git_flow

    def uses_m_flow(self) -> bool:
        """Check if configuration is using the m flow.

        Returns:
            True if workflow is the m flow.
        """
        return self.workflow == Workflow.m_flow

    def uses_free_flow(self) -> bool:
        """Check if configuration is using the free flow.

        Returns:
            True if workflow is the free flow.
        """
        return self.workflow == Workflow.free_flow

    def get_master_branch(self) -> str:
        """Obtain the name of the branch that aliases the "master" branch.

        Returns:
            The name/alias assigned to the `master` branch.
        """
        if self.uses_m_flow():
            return self.m_flow.master_branch
        if self.uses_git_flow():
            return self.git_flow.master_branch
        return 'master'

    def get_develop_branch(self) -> str:
        """Obtain the name of the branch that aliases the "develop" branch.

        Returns:
            The name/alias assigned to the `develop` branch.
        """
        if self.uses_git_flow():
            return self.git_flow.develop_branch
        return 'develop'

    def get_default_branch(self) -> str:
        """Obtain the name of the branch that aliases the "default" branch.

        This is dependent on the flow. For instance, in the git flow we
        use the `develop` branch as default.

        Returns:
            The name/alias assigned to the default branch.
        """
        if self.uses_m_flow():
            return self.m_flow.master_branch
        if self.uses_git_flow():
            return self.git_flow.develop_branch
        return 'master'

    def verify_version(
        self,
        gh_latest: str,
        is_release_pr: bool,
        is_release: bool,
    ) -> Res[int]:
        """Verify that the configuration version is valid.

        Args:
            gh_latest:
                The version stored in `Github`. Checks are skipped if
                this value is empty.
            is_release_pr:
                Set to `True` if the build is done during a release pr.
            is_release:
                Set to `True` if the build is done during a release.

        Returns:
            A `OneOf` containing 0 if all is well, otherwise an `Issue`.
        """
        if not gh_latest:
            return Good(0)
        err_data = {
            'config_version': self.version,
            'gh_latest': gh_latest,
            'is_release': is_release,
            'is_release_pr': is_release_pr,
        }
        try:
            p_ver = Version(self.version)
        except Exception as ex:
            return issue('error parsing version', cause=ex, context=err_data)
        try:
            p_latest = Version(gh_latest)
        except Exception as ex:  # noqa: WPS440
            return issue('error parsing latest', cause=ex, context=err_data)
        ver_gt_latest = p_ver > p_latest
        ver_lt_latest = p_ver < p_latest
        msg: str = ''
        if is_release_pr:
            msg = _handle_release_pr(ver_gt_latest)
        elif is_release:
            msg = _handle_release(ver_gt_latest)
        else:
            msg = _handle_non_release(ver_lt_latest, ver_gt_latest)
        return issue(msg, context=err_data) if msg else Good(0)

get_default_branch()

Obtain the name of the branch that aliases the "default" branch.

This is dependent on the flow. For instance, in the git flow we use the develop branch as default.

Returns:

Type Description
str

The name/alias assigned to the default branch.

Source code in m/ci/config.py
def get_default_branch(self) -> str:
    """Obtain the name of the branch that aliases the "default" branch.

    This is dependent on the flow. For instance, in the git flow we
    use the `develop` branch as default.

    Returns:
        The name/alias assigned to the default branch.
    """
    if self.uses_m_flow():
        return self.m_flow.master_branch
    if self.uses_git_flow():
        return self.git_flow.develop_branch
    return 'master'

get_develop_branch()

Obtain the name of the branch that aliases the "develop" branch.

Returns:

Type Description
str

The name/alias assigned to the develop branch.

Source code in m/ci/config.py
def get_develop_branch(self) -> str:
    """Obtain the name of the branch that aliases the "develop" branch.

    Returns:
        The name/alias assigned to the `develop` branch.
    """
    if self.uses_git_flow():
        return self.git_flow.develop_branch
    return 'develop'

get_master_branch()

Obtain the name of the branch that aliases the "master" branch.

Returns:

Type Description
str

The name/alias assigned to the master branch.

Source code in m/ci/config.py
def get_master_branch(self) -> str:
    """Obtain the name of the branch that aliases the "master" branch.

    Returns:
        The name/alias assigned to the `master` branch.
    """
    if self.uses_m_flow():
        return self.m_flow.master_branch
    if self.uses_git_flow():
        return self.git_flow.master_branch
    return 'master'

uses_free_flow()

Check if configuration is using the free flow.

Returns:

Type Description
bool

True if workflow is the free flow.

Source code in m/ci/config.py
def uses_free_flow(self) -> bool:
    """Check if configuration is using the free flow.

    Returns:
        True if workflow is the free flow.
    """
    return self.workflow == Workflow.free_flow

uses_git_flow()

Check if configuration is using the git flow.

Returns:

Type Description
bool

True if workflow is the git flow.

Source code in m/ci/config.py
def uses_git_flow(self) -> bool:
    """Check if configuration is using the git flow.

    Returns:
        True if workflow is the git flow.
    """
    return self.workflow == Workflow.git_flow

uses_m_flow()

Check if configuration is using the m flow.

Returns:

Type Description
bool

True if workflow is the m flow.

Source code in m/ci/config.py
def uses_m_flow(self) -> bool:
    """Check if configuration is using the m flow.

    Returns:
        True if workflow is the m flow.
    """
    return self.workflow == Workflow.m_flow

verify_version(gh_latest, is_release_pr, is_release)

Verify that the configuration version is valid.

Parameters:

Name Type Description Default
gh_latest str

The version stored in Github. Checks are skipped if this value is empty.

required
is_release_pr bool

Set to True if the build is done during a release pr.

required
is_release bool

Set to True if the build is done during a release.

required

Returns:

Type Description
Res[int]

A OneOf containing 0 if all is well, otherwise an Issue.

Source code in m/ci/config.py
def verify_version(
    self,
    gh_latest: str,
    is_release_pr: bool,
    is_release: bool,
) -> Res[int]:
    """Verify that the configuration version is valid.

    Args:
        gh_latest:
            The version stored in `Github`. Checks are skipped if
            this value is empty.
        is_release_pr:
            Set to `True` if the build is done during a release pr.
        is_release:
            Set to `True` if the build is done during a release.

    Returns:
        A `OneOf` containing 0 if all is well, otherwise an `Issue`.
    """
    if not gh_latest:
        return Good(0)
    err_data = {
        'config_version': self.version,
        'gh_latest': gh_latest,
        'is_release': is_release,
        'is_release_pr': is_release_pr,
    }
    try:
        p_ver = Version(self.version)
    except Exception as ex:
        return issue('error parsing version', cause=ex, context=err_data)
    try:
        p_latest = Version(gh_latest)
    except Exception as ex:  # noqa: WPS440
        return issue('error parsing latest', cause=ex, context=err_data)
    ver_gt_latest = p_ver > p_latest
    ver_lt_latest = p_ver < p_latest
    msg: str = ''
    if is_release_pr:
        msg = _handle_release_pr(ver_gt_latest)
    elif is_release:
        msg = _handle_release(ver_gt_latest)
    else:
        msg = _handle_non_release(ver_lt_latest, ver_gt_latest)
    return issue(msg, context=err_data) if msg else Good(0)

get_m_filename(m_dir)

Obtain the path to the m configuration file.

Parameters:

Name Type Description Default
m_dir str

The directory with the m configuration.

required

Returns:

Type Description
Res[str]

The name of the configuration file or an issue if it doesn't exist.

Source code in m/ci/config.py
def get_m_filename(m_dir: str) -> Res[str]:
    """Obtain the path to the m configuration file.

    Args:
        m_dir: The directory with the m configuration.

    Returns:
        The name of the configuration file or an issue if it doesn't exist.
    """
    filenames = (f'{m_dir}/m.yaml', f'{m_dir}/m.yml', f'{m_dir}/m.json')
    for filename in filenames:
        if Path(filename).exists():
            return Good(filename)
    return issue('m_file not found', context={
        'm_dir': m_dir,
        'valid_m_files': filenames,
    })

read_config(m_dir)

Read an m configuration file.

Parameters:

Name Type Description Default
m_dir str

Directory containing m.json.

required

Returns:

Type Description
Res[Config]

A OneOf containing the m configuration or an Issue.

Source code in m/ci/config.py
def read_config(m_dir: str) -> Res[Config]:
    """Read an m configuration file.

    Args:
        m_dir: Directory containing `m.json`.

    Returns:
        A `OneOf` containing the `m` configuration or an `Issue`.
    """
    return one_of(lambda: [
        Config(m_dir=m_dir, **m_cfg)
        for m_filename in get_m_filename(m_dir)
        for m_cfg in yaml_fp.read_yson(m_filename)
    ]).flat_map_bad(hone('read_config failure'))