Skip to content

gh_workflow_multi

TemplateVars

Bases: TemplateVars

Template variables.

Attributes:

Name Type Description
m_dir

...

ci_dir

...

default_runner

...

container

...

global_env

...

docker_login

...

build_steps

...

extra_inputs

...

build_architectures str

...

create_manifest str

...

push_manifest str

...

manifest_strategy_options str

...

buildx_setup str

...

Source code in m/ci/docker/gh_workflow_multi.py
class TemplateVars(DefaultTemplateVars):
    """Template variables."""

    build_architectures: str

    create_manifest: str

    push_manifest: str

    manifest_strategy_options: str

    buildx_setup: str

Workflow

Bases: Workflow

Helper class to write the m workflow.

Attributes:

Name Type Description
m_dir

...

ci_dir

...

global_env

...

default_runner

...

docker_registry

...

extra_build_steps

...

extra_inputs

...

container

...

images

...

architectures dict[str, str | list[str]]

...

max_parallel_manifests int | None

...

use_buildx bool

...

platforms dict[str, str] | None

...

Source code in m/ci/docker/gh_workflow_multi.py
class Workflow(DefaultWorkflow):
    """Helper class to write the `m` workflow."""

    architectures: dict[str, str | list[str]]

    max_parallel_manifests: int | None

    use_buildx: bool = False

    platforms: dict[str, str] | None

    def build_architectures(self: 'Workflow') -> str:
        """Generate a github action str with the build architectures.

        Returns:
            A string to add to the Github workflow.
        """
        arch_strs = '\n'.join([
            f'- arch: {arch}\n  os: {os}{platform}'
            for arch, os in self.architectures.items()
            for platform in (self._add_platform(arch),)
        ])
        return _indent(f'\n{arch_strs}', 5)

    def buildx_setup_str(self: 'Workflow') -> str:
        """Generate a github action str to setup buildx.

        Returns:
            A string to add to the Github workflow.
        """
        if not self.platforms:
            return ''
        platforms = ','.join(self.platforms.values())
        setup_obj = f"""
            - name: buildx-setup
              uses: docker/setup-buildx-action@v3
              with:
                platforms: {platforms}"""
        return _indent(dedent(setup_obj), 3)

    def create_manifest_str(self: 'Workflow') -> str:
        """Generate the script to create the manifest.

        Returns:
            The manifest create script.
        """
        cmd = 'docker manifest create'
        registry = self.docker_registry
        image = '${{ matrix.image-name }}'
        tag = '${{ matrix.image-tag }}'
        m_tag = '${{ inputs.m-tag }}'
        lines = [f'{cmd} {registry}/{image}:{tag}']
        for arch in self.architectures:
            lines.append(f'  {registry}/{arch}-{image}:{m_tag}')
        # wants it be a raw string but i need a new line after `\`
        full_cmd = ' \\\n'.join(lines)   # noqa: WPS342
        return _indent(f'|-\n{full_cmd}', 5)

    def push_manifest_str(self: 'Workflow') -> str:
        """Generate the script to run to push the manifest.

        Returns:
            The command to push.
        """
        cmd = 'docker manifest push'
        registry = self.docker_registry
        image = '${{ matrix.image-name }}'
        tag = '${{ matrix.image-tag }}'
        full_cmd = f'|-\n{cmd} {registry}/{image}:{tag}'
        return _indent(full_cmd, 5)

    def manifest_strategy_options_str(self: 'Workflow') -> str:
        """Generate the strategy options for the manifest job.

        Returns:
            The strategy options.
        """
        options = ''
        if self.max_parallel_manifests:
            options = f'\n      max-parallel: {self.max_parallel_manifests}'
        return options

    def container_str(self: 'Workflow') -> str:
        """Generate a string specifying a container to run on.

        Returns:
            A string to add to the Github workflow.
        """
        if not self.container:
            lines = [
                '',
                '    env:',
                '      ARCH: ${{ matrix.arch }}',
            ]
            if self.platforms:
                lines.append('      PLATFORM: ${{ matrix.platform }}')
            return '\n'.join(lines)
        lines = ['\n    container:']
        content_str = _indent(yaml.dumps(self.container), 3)
        lines.append(f'      {content_str}')
        return '\n'.join(lines).rstrip()

    def __str__(self: 'Workflow') -> str:
        """Stringify the workflow file.

        Returns:
            The github workflow.
        """
        template_vars = TemplateVars(
            m_dir=self.m_dir,
            default_runner=self.default_runner,
            global_env=self.global_env_str(),
            extra_inputs=self.extra_inputs_str(),
            ci_dir=self.ci_dir,
            build_architectures=self.build_architectures(),
            docker_login=self.docker_login_str(),
            buildx_setup=self.buildx_setup_str(),
            build_steps=self.build_steps_str(),
            create_manifest=self.create_manifest_str(),
            push_manifest=self.push_manifest_str(),
            manifest_strategy_options=self.manifest_strategy_options_str(),
            container=self.container_str(),
        )
        template = TEMPLATE_BUILDX if self.use_buildx else TEMPLATE
        return template.format(**template_vars.model_dump())

    def _add_platform(self: 'Workflow', arch: str) -> str:
        if not self.platforms:
            return ''
        platform = self.platforms[arch]
        return f'\n  platform: {platform}'

__str__()

Stringify the workflow file.

Returns:

Type Description
str

The github workflow.

Source code in m/ci/docker/gh_workflow_multi.py
def __str__(self: 'Workflow') -> str:
    """Stringify the workflow file.

    Returns:
        The github workflow.
    """
    template_vars = TemplateVars(
        m_dir=self.m_dir,
        default_runner=self.default_runner,
        global_env=self.global_env_str(),
        extra_inputs=self.extra_inputs_str(),
        ci_dir=self.ci_dir,
        build_architectures=self.build_architectures(),
        docker_login=self.docker_login_str(),
        buildx_setup=self.buildx_setup_str(),
        build_steps=self.build_steps_str(),
        create_manifest=self.create_manifest_str(),
        push_manifest=self.push_manifest_str(),
        manifest_strategy_options=self.manifest_strategy_options_str(),
        container=self.container_str(),
    )
    template = TEMPLATE_BUILDX if self.use_buildx else TEMPLATE
    return template.format(**template_vars.model_dump())

build_architectures()

Generate a github action str with the build architectures.

Returns:

Type Description
str

A string to add to the Github workflow.

Source code in m/ci/docker/gh_workflow_multi.py
def build_architectures(self: 'Workflow') -> str:
    """Generate a github action str with the build architectures.

    Returns:
        A string to add to the Github workflow.
    """
    arch_strs = '\n'.join([
        f'- arch: {arch}\n  os: {os}{platform}'
        for arch, os in self.architectures.items()
        for platform in (self._add_platform(arch),)
    ])
    return _indent(f'\n{arch_strs}', 5)

buildx_setup_str()

Generate a github action str to setup buildx.

Returns:

Type Description
str

A string to add to the Github workflow.

Source code in m/ci/docker/gh_workflow_multi.py
def buildx_setup_str(self: 'Workflow') -> str:
    """Generate a github action str to setup buildx.

    Returns:
        A string to add to the Github workflow.
    """
    if not self.platforms:
        return ''
    platforms = ','.join(self.platforms.values())
    setup_obj = f"""
        - name: buildx-setup
          uses: docker/setup-buildx-action@v3
          with:
            platforms: {platforms}"""
    return _indent(dedent(setup_obj), 3)

container_str()

Generate a string specifying a container to run on.

Returns:

Type Description
str

A string to add to the Github workflow.

Source code in m/ci/docker/gh_workflow_multi.py
def container_str(self: 'Workflow') -> str:
    """Generate a string specifying a container to run on.

    Returns:
        A string to add to the Github workflow.
    """
    if not self.container:
        lines = [
            '',
            '    env:',
            '      ARCH: ${{ matrix.arch }}',
        ]
        if self.platforms:
            lines.append('      PLATFORM: ${{ matrix.platform }}')
        return '\n'.join(lines)
    lines = ['\n    container:']
    content_str = _indent(yaml.dumps(self.container), 3)
    lines.append(f'      {content_str}')
    return '\n'.join(lines).rstrip()

create_manifest_str()

Generate the script to create the manifest.

Returns:

Type Description
str

The manifest create script.

Source code in m/ci/docker/gh_workflow_multi.py
def create_manifest_str(self: 'Workflow') -> str:
    """Generate the script to create the manifest.

    Returns:
        The manifest create script.
    """
    cmd = 'docker manifest create'
    registry = self.docker_registry
    image = '${{ matrix.image-name }}'
    tag = '${{ matrix.image-tag }}'
    m_tag = '${{ inputs.m-tag }}'
    lines = [f'{cmd} {registry}/{image}:{tag}']
    for arch in self.architectures:
        lines.append(f'  {registry}/{arch}-{image}:{m_tag}')
    # wants it be a raw string but i need a new line after `\`
    full_cmd = ' \\\n'.join(lines)   # noqa: WPS342
    return _indent(f'|-\n{full_cmd}', 5)

manifest_strategy_options_str()

Generate the strategy options for the manifest job.

Returns:

Type Description
str

The strategy options.

Source code in m/ci/docker/gh_workflow_multi.py
def manifest_strategy_options_str(self: 'Workflow') -> str:
    """Generate the strategy options for the manifest job.

    Returns:
        The strategy options.
    """
    options = ''
    if self.max_parallel_manifests:
        options = f'\n      max-parallel: {self.max_parallel_manifests}'
    return options

push_manifest_str()

Generate the script to run to push the manifest.

Returns:

Type Description
str

The command to push.

Source code in m/ci/docker/gh_workflow_multi.py
def push_manifest_str(self: 'Workflow') -> str:
    """Generate the script to run to push the manifest.

    Returns:
        The command to push.
    """
    cmd = 'docker manifest push'
    registry = self.docker_registry
    image = '${{ matrix.image-name }}'
    tag = '${{ matrix.image-tag }}'
    full_cmd = f'|-\n{cmd} {registry}/{image}:{tag}'
    return _indent(full_cmd, 5)