<commit_before>"""
Copyright (c) 2022 Red Hat, Inc
All rights reserved.

This software may be modified and distributed under the terms
of the BSD license. See the LICENSE file for details.
"""
import logging
import subprocess
from dataclasses import dataclass
from typing import Any, Dict, Iterator, Optional

from osbs.utils import ImageName

from atomic_reactor import dirs
from atomic_reactor import util
from atomic_reactor.tasks.common import Task, TaskParams
from atomic_reactor.utils import retries


logger = logging.getLogger(__name__)


class BuildTaskError(Exception):
    """The build task failed."""


class BuildProcessError(BuildTaskError):
    """The subprocess that was supposed to build the image failed."""


class PushError(BuildTaskError):
    """Failed to push the built image."""


@dataclass(frozen=True)
class BinaryBuildTaskParams(TaskParams):
    """Binary container build task parameters"""
    platform: str


class BinaryBuildTask(Task[BinaryBuildTaskParams]):
    """Binary container build task."""

    def execute(self) -> None:
        """Build a container image for the platform specified in the task parameters.

        The built image will be pushed to the unique tag for this platform, which can be found
        in tag_conf.get_unique_images_with_platform() (where tag_conf comes from context data).
        """
        platform = self._params.platform

        data = self.load_workflow_data()
        enabled_platforms = util.get_platforms(data)

        if platform not in enabled_platforms:
            logger.info(
                r"Platform %s is not enabled for this build (enabled platforms: %s). Exiting.",
                platform,
                enabled_platforms,
            )
            return

        config = self.load_config()
        build_dir = self.get_build_dir().platform_dir(platform)
        dest_tag = data.tag_conf.get_unique_images_with_platform(platform)[0]

        logger.info("Building for the %s platform from %s", platform, build_dir.dockerfile_path)

        try:
            output_lines = self.build_container(
                build_dir=build_dir,
                build_args=data.buildargs,
                dest_tag=dest_tag,
            )
            for line in output_lines:
                logger.info(line.rstrip())

            logger.info("Build finished succesfully! Pushing image to %s", dest_tag)
            self.push_container(dest_tag, registry_config=config.registry)
        finally:
            logger.info("Dockerfile used for build:\n%s", build_dir.dockerfile_path.read_text())

    def build_container(
        self,
        *,
        build_dir: dirs.BuildDir,
        build_args: Dict[str, str],
        dest_tag: ImageName,
    ) -> Iterator[str]:
        """Build a container image from the specified build directory.<commit_msg>Switch from try/finally to contextlib.ExitStack<commit_after>"""
Copyright (c) 2022 Red Hat, Inc
All rights reserved.

This software may be modified and distributed under the terms
of the BSD license. See the LICENSE file for details.
"""
import contextlib
import logging
import subprocess
from dataclasses import dataclass
from typing import Any, Dict, Iterator, Optional

from osbs.utils import ImageName

from atomic_reactor import dirs
from atomic_reactor import util
from atomic_reactor.tasks.common import Task, TaskParams
from atomic_reactor.utils import retries


logger = logging.getLogger(__name__)


class BuildTaskError(Exception):
    """The build task failed."""


class BuildProcessError(BuildTaskError):
    """The subprocess that was supposed to build the image failed."""


class PushError(BuildTaskError):
    """Failed to push the built image."""


@dataclass(frozen=True)
class BinaryBuildTaskParams(TaskParams):
    """Binary container build task parameters"""
    platform: str


class BinaryBuildTask(Task[BinaryBuildTaskParams]):
    """Binary container build task."""

    def execute(self) -> None:
        """Build a container image for the platform specified in the task parameters.

        The built image will be pushed to the unique tag for this platform, which can be found
        in tag_conf.get_unique_images_with_platform() (where tag_conf comes from context data).
        """
        platform = self._params.platform

        data = self.load_workflow_data()
        enabled_platforms = util.get_platforms(data)

        if platform not in enabled_platforms:
            logger.info(
                r"Platform %s is not enabled for this build (enabled platforms: %s). Exiting.",
                platform,
                enabled_platforms,
            )
            return

        config = self.load_config()
        build_dir = self.get_build_dir().platform_dir(platform)
        dest_tag = data.tag_conf.get_unique_images_with_platform(platform)[0]

        logger.info("Building for the %s platform from %s", platform, build_dir.dockerfile_path)

        with contextlib.ExitStack() as defer:
            defer.callback(
                logger.info, "Dockerfile used for build:\n%s", build_dir.dockerfile_path.read_text()
            )

            output_lines = self.build_container(
                build_dir=build_dir,
                build_args=data.buildargs,
                dest_tag=dest_tag,
            )
            for line in output_lines:
                logger.info(line.rstrip())

            logger.info("Build finished succesfully! Pushing image to %s", dest_tag)
            self.push_container(dest_tag, registry_config=config.registry)

    def build_container(
        self,
        *,
        build_dir: dirs.BuildDir,
        build_args: Dict[str, str],
        dest_tag: ImageName,
    ) -> Iterator[str]:
        """Build a container image from the specified build directory.
{'fraction_non_alphanumeric': 0.07997230396399516, 'fraction_numerical': 0.0017310022503029255, 'mean_word_length': 2.8189028420356905, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '666450', 'n_tokens_mistral': 1577, 'n_tokens_neox': 1505, 'n_words': 487}