# Path: hotness/builders/builder.py
# class Builder:
#     """
#     Abstract class for builders used by the-new-hotness to start a build of the package.
#     This class must be inherited by every external builder.
#     """
# 
#     def build(self, package: Package, opts: dict) -> dict:
#         """
#         Build method that should be implemented by every child class.
#         It should start build of the provided package and return output
#         of the operation.
# 
#         Params:
#             package: Package to work with
#             opts: Any specific options for the builder
# 
#         Returns:
#             Dictionary containing output from builder.
#         """
#         raise NotImplementedError
# 
# Path: hotness/domain/package.py
# class Package:
#     """
#     Internal representation of package object.
# 
#     Attributes:
#         name (str): Name of the package
#         version (str): Latest version of the package
#         distro (str): Distribution assigned
#     """
# 
#     def __init__(self, name: str, version: str, distro: str) -> None:
#         """
#         Class constructor
#         """
#         self.name = name
#         self.version = version
#         self.distro = distro
# 
#     def to_dict(self) -> dict:
#         """
#         Convert object to dictionary.
# 
#         Return:
#             Dictionary created from package object.
#         """
#         return {"name": self.name, "version": self.version, "distro": self.distro}
# 
#     @classmethod
#     def from_dict(cls, package_dict: dict) -> object:
#         """
#         Convert dictionary to object.
# 
#         Params:
#             package_dict: Dictionary to create package from.
# 
#         Return:
#             Package object.
#         """
#         package = Package(
#             name=package_dict["name"],
#             version=package_dict["version"],
#             distro=package_dict["distro"],
#         )
#         return package
# 
#     def __eq__(self, other: object) -> bool:
#         """
#         Compare method for package object.
# 
#         Params:
#             other: object to compare with
# 
#         Return:
#             Compare result.
#         """
#         return self.to_dict() == other.to_dict()  # type: ignore
# 
# Path: hotness/exceptions/builder_exception.py
# class BuilderException(BaseHotnessException):
#     """
#     Class representing builder exception.
#     This exception is raised by builder
#     when it encounters error with external builder system.
# 
#     Attributes:
#         message: Error message.
#         value: Partial output from builder, needed for koji build id.
#         std_out: Standard output of command if the exception was triggered by subprocess call.
#         std_err: Error output of command if the exception was triggered by subprocess call.
#     """
# 
#     def __init__(
#         self,
#         message: str,
#         value: Dict = {},
#         std_out: str = "",
#         std_err: str = "",
#     ) -> None:
#         """
#         Class constructor.
#         """
#         self.message = message
#         self.value = value
#         self.std_out = std_out
#         self.std_err = std_err
#         super(BuilderException, self).__init__(self.message, self.value)
# 
#     def __str__(self) -> str:
#         """
#         String representation of error.
#         """
#         message = ""
#         if self.value:
#             if "build_id" in self.value:
#                 message = (
#                     "Build started, but failure happened "
#                     "during post build operations:\n{}\n"
#                 ).format(self.message)
#         if not message:
#             message = "Build failed:\n{}\n".format(self.message)
#         if self.std_out:
#             message = message + "\nStdOut:\n{}\n".format(self.std_out)
#         if self.std_err:
#             message = message + "\nStdErr:\n{}\n".format(self.std_err)
#         return message
# 
# Path: hotness/exceptions/download_exception.py
# class DownloadException(BaseHotnessException):
#     """
#     Class representing download exception.
#     This exception is raised by builder
#     when downloading of sources fails.
# 
#     Attributes:
#         message: Error message.
#     """
# 
#     def __init__(self, message: str):
#         """
#         Class constructor.
#         """
#         self.message = message
#         super(DownloadException, self).__init__(self.message)


import hashlib
import logging
import os
import random
import string
import subprocess as sp
import threading
import time
import typing
import koji  # type: ignore
from tempfile import TemporaryDirectory
from . import Builder
from hotness.domain.package import Package
from hotness.exceptions import DownloadException, BuilderException
            if not session:
                raise BuilderException("Can't authenticate with Koji!")
            output["build_id"] = self._scratch_build(session, package.name, srpm)

            # Now, craft a patch to attach to the ticket
            try:
                sp.check_output(
                    ["git", "config", "user.name", self.user_email[0]],
                    cwd=tmp,
                    stderr=sp.STDOUT,
                )
                sp.check_output(
                    ["git", "config", "user.email", self.user_email[1]],
                    cwd=tmp,
                    stderr=sp.STDOUT,
                )
                sp.check_output(
                    ["git", "commit", "-a", "-m", comment], cwd=tmp, stderr=sp.STDOUT
                )
                filename = sp.check_output(
                    ["git", "format-patch", "HEAD^"], cwd=tmp, stderr=sp.STDOUT
                )
                filename_str = filename.decode("utf-8").strip()
            except sp.CalledProcessError as exc:
                std_out = ""
                std_err = ""
                if exc.stdout:
                    std_out = exc.stdout.decode()
                if exc.stderr:
                    std_err = exc.stderr.decode()

# Path: hotness/builders/builder.py
# class Builder:
#     """
#     Abstract class for builders used by the-new-hotness to start a build of the package.
#     This class must be inherited by every external builder.
#     """
# 
#     def build(self, package: Package, opts: dict) -> dict:
#         """
#         Build method that should be implemented by every child class.
#         It should start build of the provided package and return output
#         of the operation.
# 
#         Params:
#             package: Package to work with
#             opts: Any specific options for the builder
# 
#         Returns:
#             Dictionary containing output from builder.
#         """
#         raise NotImplementedError
# 
# Path: hotness/domain/package.py
# class Package:
#     """
#     Internal representation of package object.
# 
#     Attributes:
#         name (str): Name of the package
#         version (str): Latest version of the package
#         distro (str): Distribution assigned
#     """
# 
#     def __init__(self, name: str, version: str, distro: str) -> None:
#         """
#         Class constructor
#         """
#         self.name = name
#         self.version = version
#         self.distro = distro
# 
#     def to_dict(self) -> dict:
#         """
#         Convert object to dictionary.
# 
#         Return:
#             Dictionary created from package object.
#         """
#         return {"name": self.name, "version": self.version, "distro": self.distro}
# 
#     @classmethod
#     def from_dict(cls, package_dict: dict) -> object:
#         """
#         Convert dictionary to object.
# 
#         Params:
#             package_dict: Dictionary to create package from.
# 
#         Return:
#             Package object.
#         """
#         package = Package(
#             name=package_dict["name"],
#             version=package_dict["version"],
#             distro=package_dict["distro"],
#         )
#         return package
# 
#     def __eq__(self, other: object) -> bool:
#         """
#         Compare method for package object.
# 
#         Params:
#             other: object to compare with
# 
#         Return:
#             Compare result.
#         """
#         return self.to_dict() == other.to_dict()  # type: ignore
# 
# Path: hotness/exceptions/builder_exception.py
# class BuilderException(BaseHotnessException):
#     """
#     Class representing builder exception.
#     This exception is raised by builder
#     when it encounters error with external builder system.
# 
#     Attributes:
#         message: Error message.
#         value: Partial output from builder, needed for koji build id.
#         std_out: Standard output of command if the exception was triggered by subprocess call.
#         std_err: Error output of command if the exception was triggered by subprocess call.
#     """
# 
#     def __init__(
#         self,
#         message: str,
#         value: Dict = {},
#         std_out: str = "",
#         std_err: str = "",
#     ) -> None:
#         """
#         Class constructor.
#         """
#         self.message = message
#         self.value = value
#         self.std_out = std_out
#         self.std_err = std_err
#         super(BuilderException, self).__init__(self.message, self.value)
# 
#     def __str__(self) -> str:
#         """
#         String representation of error.
#         """
#         message = ""
#         if self.value:
#             if "build_id" in self.value:
#                 message = (
#                     "Build started, but failure happened "
#                     "during post build operations:\n{}\n"
#                 ).format(self.message)
#         if not message:
#             message = "Build failed:\n{}\n".format(self.message)
#         if self.std_out:
#             message = message + "\nStdOut:\n{}\n".format(self.std_out)
#         if self.std_err:
#             message = message + "\nStdErr:\n{}\n".format(self.std_err)
#         return message
# 
# Path: hotness/exceptions/download_exception.py
# class DownloadException(BaseHotnessException):
#     """
#     Class representing download exception.
#     This exception is raised by builder
#     when downloading of sources fails.
# 
#     Attributes:
#         message: Error message.
#     """
# 
#     def __init__(self, message: str):
#         """
#         Class constructor.
#         """
#         self.message = message
#         super(DownloadException, self).__init__(self.message)


# Path: hotness/builders/koji.py
import hashlib
import logging
import os
import random
import string
import subprocess as sp
import threading
import time
import typing
import koji  # type: ignore
from tempfile import TemporaryDirectory
from . import Builder
from hotness.domain.package import Package
from hotness.exceptions import DownloadException, BuilderException

            if not session:
                raise BuilderException("Can't authenticate with Koji!")
            output["build_id"] = self._scratch_build(session, package.name, srpm)

            # Now, craft a patch to attach to the ticket
            try:
                sp.check_output(
                    ["git", "config", "user.name", self.user_email[0]],
                    cwd=tmp,
                    stderr=sp.STDOUT,
                )
                sp.check_output(
                    ["git", "config", "user.email", self.user_email[1]],
                    cwd=tmp,
                    stderr=sp.STDOUT,
                )
                sp.check_output(
                    ["git", "commit", "-a", "-m", comment], cwd=tmp, stderr=sp.STDOUT
                )
                filename = sp.check_output(
                    ["git", "format-patch", "HEAD^"], cwd=tmp, stderr=sp.STDOUT
                )
                filename_str = filename.decode("utf-8").strip()
            except sp.CalledProcessError as exc:
                std_out = ""
                std_err = ""
                if exc.stdout:
                    std_out = exc.stdout.decode()
                if exc.stderr:
                    std_err = exc.stderr.decode()

                raise BuilderException(